Free YouTube Transcribe

Video transcript

The $10,000 Next JS Page Transition That Took Me One Coffee and Four Divs

Codegrid · 2,808 words · 13 min read

Want to search this transcript, jump the video from any line, or download it as TXT, SRT, or VTT?

Open in the transcript tool

Full transcript

0:00A couple of months ago, this site called

0:02the Obsidian Assembly picked up site of

0:04the day on Awwwards. It has a lot going

0:06on, but one [music] thing stood out to

0:08me immediately, the page transition.

0:10Click any link in the navigation and

0:12four full-width bars sweep across the

0:14screen from left to right, one after

0:16another, sealing the page shut. The

0:18brand name rises into view, sits there

0:20for a beat, and then everything leaves

0:22the way it came, [music] wiping across

0:23and revealing the new page underneath.

0:25It looks expensive, but strip it back

0:27and this is one of the most reused

0:29transition patterns [music] in modern

0:30web design. You will find it across

0:32award-winning sites all year, dressed up

0:34differently every time, but the

0:35mechanics underneath are always [music]

0:37the same. That is exactly why I thought

0:39this would make a good enough breakdown

0:41to cover on the channel. It is a pattern

0:43you will reach for again and again,

0:44[music] and we have not touched a

0:45Next.js project on the channel in a

0:47while, so I put together a small

0:49three-page experience that recreates

0:51this transition [music] using GSAP and

0:53Next Transition Router. The entire

0:55transition provider comes in at well

0:57under 100 lines. In this video, I will

1:00take [music] you through the full build

1:01from scratch, step-by-step. If you would

1:03like to grab the source code for this

1:05[music] project, along with hundreds of

1:07other micro projects, and a brand new

1:08website template every month, you can

1:10check [music] out the pro membership via

1:12the link in the description. And if you

1:14find this kind of content useful, a like

1:16on the video and a subscribe really do

1:18help the channel grow. All right, let's

1:20get into the code.

1:23To save some time, I have already set up

1:24the three pages we are going to be

1:26working with, [music] Genesis, which is

1:28the home page, Threshold, and Sanctum.

1:30Each one follows the exact same

1:32structure, a section element acting as

1:34the hero, and an H1 with the page name

1:36inside it. Nothing more than that. A

1:38simple heading is enough to keep the

1:40page from looking empty, and the focus

1:42here is entirely on the transition. I

1:44have also put together a simple navbar

1:46component. It imports [music] the link

1:48component from Next.js, so we get

1:50client-side navigation between our

1:52routes. Inside, the brand name sits on

1:54the left, [music] and the three page

1:55links sit on the right. One for the home

1:57page, one for threshold, and one for

1:59Sanctum. Each link [music] is wrapped in

2:01its own div, which gives us something to

2:03hang padding off later. And finally, I

2:06have cleaned up the layout file. I

2:07swapped out the boilerplate fonts for

2:09Instrument Serif and Instrument [music]

2:11Sans, wired them up as CSS variables,

2:13and imported the navbar, so it renders

2:15above the children. Since [music] this

2:17is the root layout, it shows up across

2:19the entire site automatically. As

2:21[music] you can see on this side,

2:22everything is running fine. Before we

2:24get into the styling, we need our

2:26assets. I will head into the public

2:28directory and drop in [music] three

2:29images. These will serve as the

2:31full-screen backgrounds for each page.

2:33With those in place, let's move on to

2:35the CSS. Let's open up the global CSS

2:37[music] file and start building out the

2:39look and feel of the project. First,

2:41I'll define a few variables inside the

2:43root selector, a background color, a

2:45foreground color, and a separate

2:47variable for the transition block color.

2:49Keeping these at the top means we can

2:50adjust the entire palette from one place

2:52later. [music]

2:53Next, a quick reset, removing the

2:55default margins and paddings, and

2:57switching everything over to border-box

2:59sizing. [music] Now, let's style the

3:00navbar. I'll position it fixed at the

3:02top of the screen, so it stays visible

3:04across all pages, and use flexbox to

3:07push the brand name to the left and the

3:08nav links to the right. I'll also give

3:10it a Z index, so it always sits above

3:12the page content. Then the nav links

3:14themselves, I'll lay them [music] out in

3:16a row with some spacing between them,

3:18add a bit of padding to each one, and

3:20style the links using the Instrument

3:21Sans font and our foreground color, so

3:23they stay readable against the dark

3:25backgrounds. Next, the hero sections.

3:28Each one [music] takes up the full

3:29viewport height, and I'll center the

3:30content both horizontally and vertically

3:33using flexbox. I'll also hide any

3:35overflow, so nothing spills outside the

3:37viewport [music] during the transition.

3:38Then I'll assign each page its own

3:40background image using the class we set

3:42up earlier. Genesis gets the first

3:44image, Threshold gets the second, and

3:46Sanctum gets the third. Each one is set

3:48to cover the full section and stay

3:49centered. For the heading, I'll use the

3:52Instrument Serif font. The font size

3:54scales fluidly using clamp, so it looks

3:56good on every screen size, and the line

3:58height is kept at one, so the large text

4:00sits cleanly in the center of the

4:02screen. Then a media query for smaller

4:04screens, I'll stack the nav links

4:06vertically on the right side, so the

4:07layout holds up nicely on mobile as

4:09well. Now the last few things we need

4:11are the styles for the transition

4:13overlay itself. First, the transition

4:15[music] grid. This is the container that

4:17will sit fixed on top of everything

4:19else. It covers the entire viewport,

4:21uses flexbox in a column direction,

4:23ignores pointer events, so it never

4:25blocks any clicks, and hides overflow,

4:27so none of the animated blocks spill

4:29outside the screen. Then the transition

4:31block. Each block inside the grid gets a

4:33flex value [music] of one, which means

4:35all four of them share the container

4:36height equally, no matter what size the

4:38screen is. The background comes from the

4:40transition color variable we defined at

4:42the top. We start each block at a scale

4:44of zero on the x-axis, [music] so they

4:46are invisible by default, and set the

4:48transform origin to the left, which is

4:50what [music] makes them grow outward

4:51from the left edge. We are also adding

4:53will change transform here to hint to

4:55the browser that these elements are

4:57going to be animated, which helps

4:58[music] keep things smooth. Next, the

5:00transition text. This is a separate

5:02layer that sits on top of the blocks

5:04with a higher z-index. It covers the

5:06full viewport, centers the text in the

5:08middle of the screen, and ignores

5:09[music] pointer events as well. Keeping

5:11it outside the grid means it stays

5:13completely unaffected by the block

5:15scaling underneath it. Then the heading

5:17inside it, Instrument Serif again,

5:19scaling fluidly with clamp, and this

5:21time colored with our background

5:22variable. Since the blocks are light and

5:24we want the text to read against them.

5:26And finally, the word class. Every word

5:28inside that heading starts pushed fully

5:30[music] down out of view, and we add

5:32will change transform here as well. And

5:34that is all the styling done. The

5:36project is looking good on the side, and

5:38now we can start working on the

5:40transition provider, which is what

5:41actually handles the page changes and

5:43drives the entire animation. [music]

5:45Before we can touch the transition

5:46logic, we need two packages. So, I'll

5:48open the integrated terminal and pull

5:50them both in at once. The first one is

5:52next transition router. This is what

5:54makes animated page transitions in next

5:56JS actually manageable. It sits between

5:59the link click and the page swap and

6:01says a leave callback and an enter

6:03callback and then waits for us. Whatever

6:05animation we run inside those callbacks,

6:06[music]

6:07it will hold the page change until we

6:09tell it we are done and it does not care

6:11which animation library we use. The

6:13second [music] one is GSAP which is what

6:15we'll use to drive the actual animation.

6:17Once both finish installing, I'll close

6:19the terminal. Now, I'll create a new

6:21folder called providers in the source

6:23directory and inside it a file called

6:26transition provider.jsx.

6:28Everything that controls the transition

6:30is going to live in this one file and it

6:32will sit at the very top of the app

6:34wrapping every single page. Let's get

6:36the basic structure down first. So, I'll

6:38paste this in. Right at the top, we are

6:40marking this as a [music] client

6:41component. We'll be reaching into the

6:43DOM and running animations on it and

6:45neither of those work on the server. So,

6:47this line is not optional. Then, we

6:49bring in the transition router itself

6:51and wrap the children with it. The

6:53important bit here is the auto prop.

6:55With auto enabled, the router watches

6:57for clicks on any next JS link component

6:59and intercepts [music]

7:00them for us. It figures out that a

7:02navigation is happening, pauses it and

7:04fires our callbacks [music]

7:05which means every link in the navbar we

7:07built earlier is already wired into

7:09this. We just have not given it anything

7:11to run yet. Back over in the layout

7:13file, I'll import the provider and wrap

7:15the entire body content with it. The

7:17navbar and the page children both sit

7:19inside now. So, the provider has control

7:21over every route change on the site.

7:23Nothing will look any different [music]

7:24at this point. The plumbing is

7:26connected, but there is no animation on

7:28the other end of it. Let's go build

7:30that. Let's start by bringing in the

7:32imports we need. First, use ref from

7:34React since we are going to be holding

7:36on to the actual DOM elements and

7:38handing them straight to GSAP. Just

7:40above the component, I'll define a

7:41constant for the number of rows and set

7:44it to four. That is how many blocks the

7:46transition is going to have. [music] And

7:47keeping it up here means we can change

7:49the whole feel of the effect from one

7:51line later. Inside the component, I'll

7:53[music] create two refs. The first one,

7:55grid ref, points to the container that

7:57holds all our blocks. The second one,

7:59blocks ref, starts as an empty array and

8:01will store every individual block.

8:03Keeping them in refs means GSAP [music]

8:05can target them directly without React

8:07getting in the way. Now, let's render

8:09the blocks. Inside the transition

8:10router, right before the children, I'll

8:12add a div, attach the grid ref to it,

8:14and give it the transition grid class.

8:16Then I'll loop over our row count and

8:18return a block for each one with the

8:20transition block class and a ref

8:22callback that pushes each element into

8:23the blocks array. And that is the entire

8:26grid. Four divs, because we set flex to

8:28one on the block class earlier, they

8:30automatically split the container height

8:32evenly between them, no matter what size

8:34the screen is. Next, let's bring in GSAP

8:37along with a plugin called CustomEase.

8:39CustomEase lets us define our own easing

8:41curve instead of relying on the built-in

8:43ones. So, I'll register it and then

8:45create a curve called hop. This gives us

8:47a much sharper motion than something

8:48like power four. It accelerates hard out

8:51of the gate and then breaks hard right

8:53at the end. We are creating it outside

8:55the component, so it only ever runs once

8:57and not on every single render. Now, for

8:59the fun part, let's write the leave

9:01animation. I'll define a function called

9:03animateIn, which takes an onComplete

9:05callback. Inside, I'll create a GSAP

9:08timeline and pass that callback straight

9:10to it. So, the timeline fires it

9:11automatically the moment [music] the

9:12animation finishes. First, I'll set the

9:15transform origin on every block to the

9:16left and reset their scale on the x-axis

9:19back to zero. This makes sure we are

9:21always starting from a clean state. Then

9:23I'll animate all four blocks up to a

9:24scale of one. Because the origin is on

9:26the left, each block grows outward from

9:28the left edge, sweeping across the

9:30screen. I'm using our hop ease here and

9:32adding a small stagger. So, instead of

9:34all four moving together, each one

9:36starts a fraction of a second after the

9:38one above it. That is what gives the

9:39effect its cascade. Finally, I return

9:42the timeline so we can kill it later if

9:44we need to. Now, the enter animation,

9:46animate out works exactly the same way,

9:48but in reverse. The key difference is

9:50right here. This time, the transform

9:52origin is set to the right. So, when we

9:54animate the blocks back down to zero,

9:56they do not shrink back the way they

9:57came. They collapse toward the left,

9:59continuing in the same direction they

10:01were already traveling. That one change

10:02is the entire trick behind this effect.

10:05grows from the left and then it

10:06collapses toward the left. Put those two

10:09halves together and it reads as one

10:10continuous sweep moving across the

10:12screen, rather than a bar that grows and

10:14then reverses. Same duration, same ease,

10:17same stagger. Now, let's wire both of

10:19these into the transition router. I'll

10:21add the leave callback and call animate

10:23in, passing it the next function

10:24directly. The router will wait until our

10:27timeline finishes and calls next before

10:29it swaps the page. Then the enter

10:31callback, doing exactly the same thing

10:33with animate out. And in both cases,

10:34[music]

10:35I return a cleanup function that kills

10:37the timeline if the animation needs to

10:39stop early. And with that, the

10:40transition is live. Click any link and

10:43the block sweep in, the page changes

10:44underneath, and the block [music] sweep

10:46back out.

10:50Now, let's add the text. For this, we

10:52need one more plugin, Split Text. I'll

10:54register it alongside custom ease and

10:56[music] then add three more refs,

10:58heading ref for the actual H1 element,

11:00words ref, which will hold each

11:02individual word once it is split, and

11:04split ref [music] to hold on to the

11:05Split Text instance itself, so we can

11:08clean it up later. Down in the return,

11:10below our grid, I'll add another div

11:12with the transition text class and drop

11:14the H1 inside it with our brand name and

11:16the heading ref attached. Remember, this

11:18sits on a higher layer than the blocks,

11:20so it stays completely unaffected by

11:22them scaling underneath. Now, let's

11:24[music] split it. I'll bring in use

11:26effect and inside it create a new Split

11:28Text instance [music] on our heading. We

11:30are splitting by words, giving each word

11:32the word class, and then this line here

11:34is the important one. Mask set to words.

11:36[music]

11:37This tells split text to wrap every

11:39single word in its own container with

11:41overflow hidden, which means we do not

11:43have to write a single line of extra

11:45markup or CSS to get that clipping

11:47effect. It handles it for us. Then I

11:49store the words in our ref and use

11:51[music] GSAP to push all of them fully

11:53down and out of view, so nothing is

11:55visible by default. And I return a

11:57cleanup function that reverts the split,

11:59which stops us from accidentally

12:00splitting the same heading twice on a

12:02refresh. Back in animate [music] in,

12:04I'll reset the words to their hidden

12:06position, right alongside where we reset

12:08the blocks. [music] Then, after the

12:10block animation, I'll bring the words up

12:12into view. And notice this position

12:13parameter at the end. Without it, the

12:15words would sit and wait for the blocks

12:17to completely finish. Instead, this

12:19pulls the animation backwards, so the

12:21words start rising while the last block

12:23is still sweeping across. Those two

12:25motions overlap [music]

12:26and the whole thing feels like one

12:27movement instead of two. I'm also using

12:30a different ease here [music] on

12:31purpose. The blocks get hop, which is

12:33sharp and mechanical. The words get

12:35power four out, so they rise quickly and

12:37settle softly. Now, in animate [music]

12:39out, we do the same thing in reverse,

12:41but the order flips. The words leave

12:43first, dropping back down out of view,

12:46and then [music] the blocks follow,

12:47pulled almost entirely back on top of

12:49the word animation, so the text and the

12:51[music] blocks leave together as one

12:53motion rather than one waiting on the

12:55other. And that is the transition

12:57provider complete. When we leave a page,

12:59the blocks sweep in from the left and

13:01the brand name rises up behind them.

13:03When we enter the new page, the text

13:04drops away and the blocks continue in

13:06the same direction, wiping the screen

13:08clean. The whole thing comes in at under

13:10100 lines, and every part of it is

13:12adjustable. Hope you found the video

13:14helpful. See you in the next one.

13:29>> Mhm.

This transcript was generated from the captions YouTube publishes for this video. Get the transcript of any YouTube video atfreeyoutubetranscribe.com: free, unlimited, no sign-up.