How to do Really Cool Page Transitions

June 13, 2023

written by
Joren Mathews

the idea

Illustration of a grid of pages, with the center one highlighted

To provide a fun and intuitive user experience, new pages are revealed in a unique way when you navigate to them, creating an immersive experience reminiscent of wandering through interconnected ideas or a gallery.  This is a quick technical post exploring one way to accomplish that.

To get this effect, we shift the a larger overall page around beneath the viewport.  I’m calling that the “canvas” even though it’s not an actual HTML “canvas” element.

You can view the working example here.  It’s built on Next.js.

I had a few considerations when building this:

  1. It had to feel smooth/seamless.
  2. It couldn’t load an unreasonable amount of HTML into the DOM at once.
  3. It had to be reasonably practical for a real website (no janky aspects).

To satisfy point 1, I decided my best bet was to have the real dimensions and relative positions of the pages exist at all times. I wouldn’t try to create/position/animate a page on navigation but rather just transform the canvas to the correct position.

To achieve point 2, I broke each page’s content into above fold and below fold. Only the above fold content exists in the DOM for any page not currently in the viewport.
For the type of moderately sized sites I’d use this for, that’s a reasonable amount of HTML to have in the DOM at once.

Point 3 is satisfied by the implementation having no major data/structure requirements. This basic proof of concept has all the data available on first page load due to Next.js static site generation, but the first page load could easily include only the above fold content and asynchronously load the below fold content.

I’m reasonably happy with the results, and I think I could use it on a production website. I want to try an alternative version using createDocumentTransition and actual page navigation to evaluate the pros and cons of each method.

I expect createDocumentTransition to be lighter weight, but less customizable/flexible.

The repo is publicly available here: