00:00:00(upbeat music)
00:00:02- Okay, hello everyone.
00:00:06My name is Aurora.
00:00:07I'm a web developer from Norway.
00:00:09I work as a consultant at Crane Consulting and also,
00:00:12and I'm actively building with the Next.js app router
00:00:14in my current consultancy project.
00:00:16Today, I'm gonna be teaching you
00:00:18patterns regarding composition, caching, and architecture
00:00:20in modern Next.js that will help you ensure
00:00:22scalability and performance.
00:00:24Let me first refresh the most fundamental concept
00:00:28for this talk, static and dynamic rendering.
00:00:30We encountered them both in the Next.js app router.
00:00:33Static rendering allows us to build faster websites
00:00:36because pre-rendered content can be cached
00:00:39and globally distributed,
00:00:40ensuring users can access it quicker.
00:00:42For example, the Next.js Conf website.
00:00:46Static rendering reduces server load
00:00:47because content does not have to be generated
00:00:49for each user request.
00:00:51Pre-rendered content is also easier
00:00:54for search engine callers to index
00:00:56as the content is already available on page load.
00:00:58Dynamic rendering, on the other hand,
00:01:01allows our application to display real time
00:01:03or frequently updated data.
00:01:05It also enables us to serve personalized content,
00:01:07such as dashboards and user profiles.
00:01:09For example, the Vercel dashboard.
00:01:12With dynamic rendering, we can access information
00:01:14that can only be known at request time.
00:01:16In this case, which user is accessing their dashboard,
00:01:19which is me.
00:01:19There are certain APIs that can cause a page
00:01:23to dynamically render.
00:01:25Usage of the params and search params props
00:01:27that are passed to pages or their equivalent hooks
00:01:30will cause dynamic rendering.
00:01:32However, with params, we can predefine
00:01:34a set of pre-rendered pages using generic static params,
00:01:37and we can also cache the pages
00:01:38as they're being generated by users.
00:01:40Furthermore, reading incoming request cookies and headers
00:01:44will opt the page into dynamic rendering.
00:01:46Unlike with params, though,
00:01:47trying to cache or pre-render anything using headers
00:01:50or cookies will throw errors during build
00:01:52because that information cannot be known ahead of time.
00:01:56Lastly, using fetch with a data cache configuration
00:01:58no store will also force dynamic rendering.
00:02:00So these are the few, there are a few more APIs
00:02:03that can cause dynamic rendering,
00:02:04but these are the ones we most commonly encounter.
00:02:06In previous versions of Next,
00:02:09a page would be rendered either as fully static
00:02:11or fully dynamic.
00:02:13One single dynamic API on a page
00:02:15will opt the whole page into dynamic rendering.
00:02:17For example, doing a simple auth check
00:02:19for the value of a cookie.
00:02:20By utilizing React server components with suspense,
00:02:24we can stream in dynamic content
00:02:26like a personalized welcome banner
00:02:27or recommendations as they become ready
00:02:29and provide only fallbacks with suspense
00:02:31while showing static content like a newsletter.
00:02:34However, once we add in multiple async components
00:02:38on a dynamic page like a feature product,
00:02:40they too would run at request time
00:02:42even though they didn't depend on dynamic APIs.
00:02:45So to avoid blocking the initial page load,
00:02:48we would suspend and stream those components down as well,
00:02:52doing extra work, creating skeletons
00:02:53and worrying about things like human-to-layout shift.
00:02:56However, pages are often a mix of static and dynamic content.
00:03:01For example, an e-commerce app dependent on user information
00:03:04while still containing mostly static data.
00:03:07Being forced to pick between them,
00:03:11between static or dynamic,
00:03:12causes lots of redundant processing on the server
00:03:15on content that never or very rarely changes
00:03:17and isn't optimal for performance.
00:03:19So to solve this problem, at last year's Next.js Conf,
00:03:23the use cache directive was announced.
00:03:26And this year, as we saw on the keynote,
00:03:28it's available in Next.js 16.
00:03:30So with use cache, pages will no longer be forced
00:03:34into either static or dynamic rendering.
00:03:36They can be both.
00:03:37And Next.js no longer has to guess what a page is
00:03:40based on whether it accesses things like params.
00:03:43Everything is dynamic by default
00:03:45and use cache lets us explicitly opt into caching.
00:03:47Use cache enables composable caching.
00:03:51We can mark either a page, a React component,
00:03:53or a function as cacheable.
00:03:55Here, we can actually cache the feature products component
00:03:59because it does not need the request and processing
00:04:01and doesn't use dynamic APIs.
00:04:03And these cache segments can further be pre-rendered
00:04:07and included as a part of the static shell
00:04:09with partial pre-rendering,
00:04:10meaning the feature products is now available on page load
00:04:13and does not need to be streamed.
00:04:14So now that we have this important background knowledge,
00:04:18let's do a demo.
00:04:19An improvement of a code base with common issues
00:04:22often encountered in Next.js apps.
00:04:24These include deep prop drilling,
00:04:25making it hard to maintain a refactor features,
00:04:27redundant client-side JavaScript and large components
00:04:30with multiple responsibilities,
00:04:31and lack of static rendering,
00:04:33leading to additional server costs and degraded performance.
00:04:36So yeah, let's begin.
00:04:37And give me one sec over here.
00:04:50All right, great.
00:04:54So this is a very simple application.
00:04:56It's inspired by an e-commerce platform.
00:04:59And let me do an initial demos here.
00:05:01So I can load this page.
00:05:03I have some content like this feature product.
00:05:06I have feature categories, different product data.
00:05:09There's also this Browse All page over here
00:05:13where I can see all of the products in the platform
00:05:18and page between them.
00:05:20Then we have this About page over here, which is just static.
00:05:24I can also sign in as a user.
00:05:27And that will log me into my user.
00:05:30And also get then personalized content on my dashboard here.
00:05:33Like for example, recommended products
00:05:35or this personalized discounts here.
00:05:38So notice here, there's a pretty good mix.
00:05:42Oh, also one more page that I forgot to show you.
00:05:45The product page, the most important one.
00:05:47Also here, we can see product information
00:05:49and then save it if we like for our user.
00:05:52So notice that there's a pretty good mix here
00:05:54of static and dynamic content in this app because of all
00:05:57of our user dependent features.
00:05:59Let's also have a look at the code, which would be over here.
00:06:05So I'm using the app router here, of course, in Next.js 16.
00:06:08I have all of my different pages, like the About page,
00:06:11the All page, our product page.
00:06:13I also have-- I'm using feature slicing here
00:06:15to keep my app folder clean.
00:06:17I have different components and queries talking
00:06:20to my database with Prisma.
00:06:23So yeah, and I purposely slowed all of this down.
00:06:25So that's why we have this really long loading
00:06:27stage, just so we can easier see what's happening.
00:06:30So the common issues that we wanted to work on here
00:06:33that we actually have in this application
00:06:34was the prop drilling, making it hard to maintain
00:06:37a refactor features, access client-side JavaScript,
00:06:41and lack of static rendering leading to additional server
00:06:44cost integrated performance.
00:06:47So the goal here of the demo is basically
00:06:48just to improve this app with some smart patterns regarding
00:06:53composition, caching, and architecture
00:06:54to fix those common features and make it faster and more
00:06:58scalable and easier to maintain.
00:07:01So let's begin with that.
00:07:02The first issue we want to fix is actually
00:07:04related to prop drilling.
00:07:05And that would be over here in the page.
00:07:10Notice right here, I have this logged in variable
00:07:12at the top here.
00:07:14And you can see I'm passing it down to a couple of components.
00:07:17It's actually been passed multiple levels through
00:07:19into this personal banner.
00:07:20So this is going to be making it hard to reuse things here
00:07:23because we're always having this logged
00:07:25in dependency for our welcome banner.
00:07:28So with server components, the best practice
00:07:30would be to actually push data fetching down
00:07:33into the components that's using this and resolve promises deeper
00:07:36into the tree.
00:07:37And for this to get as authenticated,
00:07:39as long as this is using either fetch or something
00:07:41like React cache, we can duplicate multiple calls
00:07:44of this, and we can just reuse it anywhere
00:07:46we like inside our components.
00:07:48So that would be totally fine to reuse.
00:07:50So now we can actually move this into the personized section
00:07:53here.
00:07:53And we don't-- not going to need this prop anymore.
00:07:57And just put it directly--
00:07:59whoops-- in here.
00:08:01And we're not going to need to pass this anymore.
00:08:04And since we're now moving this asynchronous call
00:08:06into the personized section, we're
00:08:07no longer blocking the page.
00:08:09We can go ahead and suspend this with just a simple suspense
00:08:13here.
00:08:13And we're not going to need this fallback.
00:08:16As for the welcome banner, I suppose
00:08:18we're going to do the same.
00:08:22But trying to use the-- get the logged in variable here
00:08:26or value, it doesn't work, right?
00:08:27Because this is a client component.
00:08:29So we need to solve this a different way.
00:08:30And we're going to do a pretty smart pattern here
00:08:32to solve this.
00:08:33We're actually going to go into the layout
00:08:35and wrap everything here with a auth provider.
00:08:39So I'm just going to put this around my whole app here
00:08:42and get this logged in variable over here.
00:08:45And I definitely don't want to block my whole root layout.
00:08:48Let's go ahead and remove the await here.
00:08:50And just pass this down as a promise into this auth provider.
00:08:55And this can just contain that promise.
00:08:57It can just be chilling there until we're ready to read it.
00:09:01So now we have this set up.
00:09:03That means we can actually go ahead
00:09:05and we'll get rid of this prop, first of all.
00:09:09And we'll get rid of this one drilling down
00:09:11to the personal banner.
00:09:12And we'll get rid of the prop drilling also here
00:09:14or the signature.
00:09:16And now we can use this auth provider
00:09:18to fetch this logged in value locally
00:09:20inside the personal banner with use auth with that provider we
00:09:24just created.
00:09:26And read it with use.
00:09:28So this will actually work kind of like in a way
00:09:30where we need to suspend this while it's resolving.
00:09:33So now I just co-located that little small data fetch
00:09:36inside the personal banner.
00:09:37And I don't have to pass those props around.
00:09:40And while this is resolving, let's just
00:09:41go ahead and suspend this one also with a fallback.
00:09:44And let's just do a general banner over here
00:09:47to avoid any weird cumulative shift.
00:09:51And finally, also get rid of this one.
00:09:53So now this welcome banner is composable.
00:09:58It's reusable.
00:09:59We don't have any weird props or dependencies in the homepage.
00:10:02And since we're able to reuse this so easy,
00:10:05let's actually go ahead and add it also to this browser page
00:10:07over here, which will be here.
00:10:11And I can just go ahead and use it over here
00:10:14without any dependencies.
00:10:15So through these patterns, we're able to maintain
00:10:22good component architecture utilizing
00:10:25React cache, React use, and make our components
00:10:27more usable and composable.
00:10:30All right.
00:10:31Let's tackle the next common challenge,
00:10:34which would be excessive client-side JavaScript
00:10:36and large components with multiple responsibilities.
00:10:40Actually, that's also in the All page here.
00:10:43And again, we'll have to work on this welcome banner.
00:10:46It's currently a client component.
00:10:48And the reason it's a client component
00:10:49is because I have this very simple dismissed state here.
00:10:53I can just click this.
00:10:54It's a nice UI interaction.
00:10:56That's fine.
00:10:57What's not so fine, though, is because of that,
00:10:59I convert this whole component into a client-side component
00:11:03or a client component.
00:11:04And I even use swr to client-side fetch.
00:11:07I have now this API layer here.
00:11:08I don't have type safety anymore in my data.
00:11:11Yeah, this is not necessary.
00:11:12And we're also breaking the separation of concerns here
00:11:14because we're involving UI logic with data.
00:11:18So let's go ahead and use another smart pattern to fix this.
00:11:21It's called the donut pattern.
00:11:23Basically, what I'm going to do is extract this
00:11:25into a client-side wrapper.
00:11:27So let's create a new component here.
00:11:29And let's call it banner container.
00:11:32And this is going to contain our interactive logic
00:11:34with the use client directive.
00:11:37We can create the signature.
00:11:38We can paste everything we just had earlier.
00:11:42And instead of using these banners,
00:11:44I'm just going to slot a prop here, which
00:11:46is going to be the children.
00:11:48So this is why it's called the donut pattern.
00:11:50We're just making this wrapper UI logic around server
00:11:53render content, or it could be server render content.
00:11:56And then since we no longer have this client-side dependency,
00:11:59we can go ahead and remove the use client.
00:12:01We can use our isAuth asynchronous function here
00:12:05instead.
00:12:06We can make this into an async server component.
00:12:09We can even replace client-side fetching
00:12:10with server-side fetching.
00:12:11So let me go ahead and just get the discount data directly here.
00:12:16Discount data.
00:12:18And just utilize our regular mental model
00:12:20like before with type safety.
00:12:24And that means I can also delete this API layer
00:12:26that I don't want to work with anyway.
00:12:29Finally, for the isLoading, we can just
00:12:31export a new welcome banner here with our donut pattern banner
00:12:35container containing server render content.
00:12:38And that means we don't need this isLoading anymore.
00:12:40So we basically refactor this whole thing
00:12:42into a server component and extract a UI logic point.
00:12:46But what is that?
00:12:48It looks like I have another error.
00:12:51So this is actually because of Motion.
00:12:52Do use Motion.
00:12:54It's a really great animation library,
00:12:56but it requires the useClient directive.
00:12:59And again, we don't have to make this useClient just
00:13:02for animation.
00:13:03We can create, again, a donut pattern wrapper
00:13:07and just extract wrappers for these animations.
00:13:10And that means we don't have to convert anything here
00:13:12into client-side.
00:13:14And I'm probably missing something down here.
00:13:17Yep.
00:13:18There we go.
00:13:21So now everything here has been converted to server.
00:13:23We have the same interaction.
00:13:24We still have our interactive logic here,
00:13:26but now we have this one way to fetch data.
00:13:29And we have a lot less client-side JS.
00:13:31Actually, I'm using this donut pattern myself
00:13:38for this UI boundary helper, which looks like this.
00:13:42Do you see that?
00:13:43So this kind of shows, again, what I mean, right?
00:13:45With the donut pattern, we have this client component
00:13:48around a server component.
00:13:49I also marked a lot of my other components
00:13:51with this UI helper here.
00:13:53Also here, I have more server components.
00:13:56Let's go ahead and improve those also,
00:13:59since we're getting pretty good at this by now.
00:14:01They are in the footer.
00:14:03These categories-- I mean, I have this nice component
00:14:06fetching its own data.
00:14:08And I just wanted to add this showMore thing, just
00:14:12in case it gets really long.
00:14:14And with the donut pattern, I can just wrap a showMore component
00:14:16here.
00:14:20And this will contain my UI logic.
00:14:23And it looks like this, right?
00:14:26Pretty cool.
00:14:28And this is now containing the client logic,
00:14:31allowing us to use state.
00:14:33We're using the children count and to array to slice this.
00:14:36And what's so cool here is that these two are now
00:14:38entirely composable, reusable components
00:14:40that work together like this.
00:14:42So this is really the beauty of these patterns
00:14:44that we're learning here.
00:14:45You can use this for anything.
00:14:50I also use it for this modal over here.
00:14:52Yeah, just remember this next time you're considering adding
00:14:54any sort of client logic to your server components.
00:14:59OK, we know the donut pattern.
00:15:01We know how to utilize it to create
00:15:04these composable components and avoid plans.js,
00:15:06so we can move further to the final issue.
00:15:10Let me go ahead and close this again.
00:15:13So that would be with lack of static rendering strategies,
00:15:16right?
00:15:18Looking at my build output, I actually
00:15:20have every single page be a dynamic page here.
00:15:24So that means that whenever I load something here,
00:15:27this is going to be running for every single user.
00:15:29Sorry.
00:15:30Every single user that's open to this
00:15:31is going to get this loading state.
00:15:33It's going to be wasting server costs,
00:15:34making the performance worse.
00:15:36And that means also that something inside my pages
00:15:40is causing dynamic rendering or forcing dynamic rendering
00:15:42for all of my pages.
00:15:45Actually, it's inside my root layout.
00:15:48I don't know if you experienced this.
00:15:51It's over here.
00:15:53In my header, I have this user profile.
00:15:57And this is, of course, using cookies
00:15:58to get the current user, and that means that everything else is
00:16:01also dynamic rendered.
00:16:02Because again, pages could be either dynamic or static,
00:16:05right?
00:16:06This is a pretty common problem and something
00:16:08that has been solved before in previous versions of Next,
00:16:11so let's just see what we might do.
00:16:13One thing we could do is create a route group
00:16:16and split our app into static and dynamic sections that would
00:16:21allow me to extract my About page.
00:16:23I could render this statically.
00:16:25It's OK for some apps, but in my case,
00:16:27the important page is the product page,
00:16:29and this is still dynamic, so not really helpful.
00:16:33How about this strategy?
00:16:35So here I'm creating this request context param encoding
00:16:38a certain state into my URL, and then I
00:16:41can use generate static params to generate all
00:16:44of the different variants of my pages.
00:16:46That would actually, combined with client-side fetching
00:16:49the user data, allow me to get this cached on my product page.
00:16:54Definitely a viable pattern.
00:16:55It's recommended by the Vercel Flags SDK called
00:16:59the precompute pattern, I think.
00:17:00But this is really complex, and I have multiple ways
00:17:03to fetch data.
00:17:04And actually, I don't want to rewrite my whole app into this.
00:17:07So what if we didn't have to do any of those workarounds?
00:17:09What if there was a simpler way?
00:17:12Well, there is.
00:17:14Let's get back to our application again.
00:17:17So we can actually go to the next config
00:17:19and just enable cache components.
00:17:23Oh, nice.
00:17:25OK, and what this does, as you know from the keynote,
00:17:28will actually opt all of our asynchronous calls
00:17:31into request time or dynamic.
00:17:34And it will also give us errors whenever
00:17:35we have some asynchronous call not suspended,
00:17:38and it will give us this use cache directive
00:17:40that we can use to granularly cache either a page, a function,
00:17:44or a component.
00:17:48So yeah, let's go ahead and utilize this.
00:17:51We can begin with the home page here.
00:17:55Let's have a look.
00:17:56So again, I have this mix of static and dynamic content.
00:17:59I have my welcome banner for me, something for you also for me.
00:18:03Let's have a look at that with this UI helper again.
00:18:06So for example, the banner is dynamically rendered
00:18:09with this over here.
00:18:10Whereas I marked this as hybrid rendering because the hero,
00:18:14it's fetching this asynchronous thing
00:18:17and it's going pretty slowly.
00:18:18But it doesn't depend on any sort of user data or dynamic APIs.
00:18:21So that means that everything that is hybrid rendered here
00:18:24can actually be reused across requests and across users.
00:18:27And we can use the use cache directive on that.
00:18:30So let's add the use cache directive here
00:18:33and mark this as cached.
00:18:35And that will allow me to-- whenever I reload this page--
00:18:38I didn't save this.
00:18:43There we go.
00:18:44It will not reload this part because it's cached.
00:18:47It's now static, right?
00:18:49And there's also other related APIs like the cache tag
00:18:55to allow me to type this or validate the specific cache
00:18:58entry granularly or define my revelation period.
00:19:01But for this demo, let's just focus on the plain directive.
00:19:05Now that I have this use cache directive,
00:19:06I can actually remove my suspense boundary around this hero.
00:19:10And that means-- well, what this will do
00:19:13is that partial prerendering can actually
00:19:15go ahead and include this in the statically prerendered shell
00:19:19so that this hero will, in this case,
00:19:21be a part of my build output.
00:19:23Let's do the same thing for everything else
00:19:25on this page that can be shared.
00:19:28For example, I have this feature categories over here.
00:19:31Let's go ahead and do the same there.
00:19:33And add the use cache directive and mark this as cached.
00:19:37Like that.
00:19:39And we can remove the suspense boundary.
00:19:40We're not going to need this anymore.
00:19:43Same for the feature products.
00:19:44Let's add use cache and mark this as cached.
00:19:48Oops.
00:19:50And then remove the suspense boundary.
00:19:52So notice how much complexity I'm just able to remove here.
00:19:55I don't have to worry about my skeletons, my cumulative layout
00:19:57shift that I was doing before.
00:20:00And the page is no longer-- or we no longer
00:20:03have this page-level static versus dynamic limitation.
00:20:07So now when I load this page, you'll
00:20:10see everything here is cached except for this truly user
00:20:14specific content.
00:20:16Right.
00:20:18So that's pretty cool.
00:20:19Let's go to the Browse page and do the same thing over there.
00:20:24Yeah.
00:20:25I already marked all of my boundaries
00:20:26here so you can easily understand what's happening.
00:20:29And I want to at least cache these categories.
00:20:33Looks like I'm getting an error, though.
00:20:37Maybe you recognize this.
00:20:38So it means I have a blocking route.
00:20:40And I'm not using suspense boundary
00:20:42when I should be doing it.
00:20:43Refreshing this, it's true, huh?
00:20:46This is really slow.
00:20:47And it's causing performance issues and bad UX.
00:20:50So this is great.
00:20:51Use cache or cache components is helping
00:20:53me identify my blocking routes.
00:20:55Let's actually see what's happening inside that.
00:20:57So this is the problem, right?
00:20:59I'm fetching these categories top level
00:21:00and I don't have any suspense boundary above it.
00:21:03Basically, we need to make a choice.
00:21:05Either we add a suspense boundary above
00:21:07or we opt into caching.
00:21:09Let's do the simple thing first and just add a loading TSX here.
00:21:12And let's add a loading page over here, some nice skeleton UI.
00:21:20That's pretty good.
00:21:21It resolved the error, but I don't
00:21:23have anything useful happening on this page while I'm waiting.
00:21:25I can't even search.
00:21:27So with cache components, dynamic is like--
00:21:31or static versus dynamic is like a scale.
00:21:33And it's up to us to decide how much
00:21:35static we want in our pages.
00:21:37So let's shift this page more towards static
00:21:40and just delete this loading TSX again.
00:21:43And then utilize the patterns that we were learning earlier
00:21:46to push this data fetch into the component
00:21:48and co-locate it with the UI.
00:21:50So move this down into my responsive category filters
00:21:53here.
00:21:54I have two because responsive design.
00:21:57I can actually go ahead and just add it here.
00:22:01Oops.
00:22:03And import this.
00:22:05I don't need this prompt anymore.
00:22:06Actually, my component is becoming more composable.
00:22:09And instead of suspending it, let's just
00:22:11add the use cache directive.
00:22:14And that should be enough.
00:22:16So notice how I'm being forced to think more about where
00:22:18I'm resolving my promises and actually
00:22:21improving my component architecture through this.
00:22:24I don't need to suspend this.
00:22:25This will just be included in the static shell here.
00:22:28The product list, let me just keep this fresh.
00:22:35So I can reload that every time.
00:22:37Whereas the categories at the bottom,
00:22:40I also want to cache this.
00:22:41So let's go ahead and go to the footer.
00:22:44And since I'm using the donut pattern over here,
00:22:48this can actually be cached even though it's
00:22:50inside this part of the UI that's interactive.
00:22:54So this is totally fine.
00:22:55So that pattern was not only good for composition,
00:22:57but also for caching.
00:22:58I think I have one more error there.
00:23:03Let's see what that is.
00:23:04Still have this error.
00:23:08This is actually because of these search frames.
00:23:10Search frames, as we know, is a dynamic API.
00:23:12I can't cache this.
00:23:13But I can resolve it deeper down to reveal more of my UI
00:23:17and make it static.
00:23:18So let's go ahead and move this down, pass it down
00:23:20as a promise to the product list.
00:23:24We'll make this typed as a promise over here, like that.
00:23:30Let's resolve it inside the product list,
00:23:32use the resolved search parameters over here
00:23:34and over here.
00:23:36And since this is suspended here, the error will be gone.
00:23:40So reloading this, the only thing that's reloading here
00:23:45is just the part that I picked specifically to be dynamic.
00:23:47Everything else can be cached.
00:23:49And that means that I can interact with my banner
00:23:51or even search because that part has been already pre-rendered.
00:23:57All right, let's do the final page here,
00:23:59which is the product page, which is the most difficult
00:24:03and the most important one.
00:24:05It's really bad right now.
00:24:08This is super important for an e-commerce platform,
00:24:10apparently.
00:24:11All right, let's go ahead and fix that one, too.
00:24:15So here I have this product page.
00:24:18Let's start caching just the reusable content here,
00:24:21for example, the product itself.
00:24:23And just add use cache here and mark this as cached.
00:24:27That should be fine.
00:24:28That means we can remove the suspense boundary over here.
00:24:33All right, and this is no longer reloading on every request
00:24:36here, right?
00:24:38For the product details, let's do the same thing.
00:24:40Let's add use cache.
00:24:41Let's mark it as cached and see if that will also work.
00:24:47It did not.
00:24:48Actually, this is a different error.
00:24:49It's telling me that I'm trying to use dynamic APIs
00:24:52inside of this cached segment.
00:24:54And that is true.
00:24:54I'm using the Save Product button, right?
00:24:56That allowed me to click and toggle the saved state.
00:25:00So what do you think we can do with this?
00:25:03We can use the donut pattern again.
00:25:06Actually, we can also slot in dynamic segments
00:25:09into cache segments.
00:25:10So we're interleaving them just like before, but with cache.
00:25:12So this is pretty cool.
00:25:14Let's go ahead and add the children here like that.
00:25:19And this will remove the error.
00:25:21And I can just wrap this around this one dynamic segment
00:25:25of my page here, remove the suspense boundary,
00:25:28and add in just a very small bookmark
00:25:31UI for that one dynamic piece of the page.
00:25:34And let's see how that looks now.
00:25:40So notice how almost the entire UI is available,
00:25:42but I have this one small chunk that is dynamic,
00:25:45and that's fine.
00:25:47Everything else is still there.
00:25:48And let's leave the reviews dynamic
00:25:50because we could keep those fresh.
00:25:53There's still one more error.
00:25:54Let's just quickly tackle that.
00:25:56Again, this is the params.
00:25:58I'm getting help that I need to make a choice either add
00:26:01a loading fallback or cache this.
00:26:04Let's just use generate static params in this case.
00:26:07Kind of depends on your use case and your data set.
00:26:10But for this case, I'm just going
00:26:11to add a couple pre-rendered predefined pages
00:26:14and then just cache the rest as they're generated by users.
00:26:17And this will remove my error here.
00:26:20So I think I'm actually done with my refactor.
00:26:22Let's go ahead and have a look at the deployed version
00:26:25and see what that looks like.
00:26:26So I just deployed this on Vercel.
00:26:27And remember, I purposely slowed down a lot of data fishes here.
00:26:35And still, when I load this page initially,
00:26:39everything is just available already.
00:26:40The only thing here is just those few dynamic segments
00:26:43like the discounts and the for you.
00:26:46Same with the browse all.
00:26:47All of the UI is already available.
00:26:50And for the product itself, it just feels instant.
00:26:54And remember, again, that all of these cache segments
00:26:57will be included with the static shell with partial pre-rendering.
00:27:00And it can be prefetched using the improved prefetching
00:27:03in the new Next 16 client router.
00:27:05So that means that every navigation just--
00:27:08it just feels so fast, right?
00:27:09All right, to summarize, with cache components,
00:27:15there is no more static versus dynamic.
00:27:17And we don't need to be avoiding dynamic APIs
00:27:24or compromising dynamic content.
00:27:28And we can skip these complex hacks and workarounds
00:27:31using multiple data fetching strategies just for that one--
00:27:34this cache hit, as I showed you.
00:27:37So in modern Next.js, dynamic versus static is a scale.
00:27:40And we decide how much static we want in our apps.
00:27:43And as long as we follow certain patterns,
00:27:45we can have one mental model, which
00:27:47is performant, composable, and scalable by default.
00:27:50So let's get back to the slides.
00:27:52So if you're not-- we're not already
00:27:54impressed by the speed of that, this is the Lighthouse score.
00:27:56So I collected some field data with the Vercel Speed Insights.
00:28:00So we have a 100 score on all of the most important pages,
00:28:03the home page, the product page, and the product list,
00:28:05even though they are highly dynamic.
00:28:08So let's just finally summarize the patterns
00:28:10that will ensure scalability and performance in Next.js apps
00:28:13and allow us to take advantage of the latest innovations
00:28:15and get scores like this.
00:28:18So firstly, we can refine our architecture
00:28:20by resolving promises deep in the component tree
00:28:23and fetching data locally inside components using React Cache
00:28:26to do duplicate work.
00:28:28We can avoid excessive prop passing to client components
00:28:30by using context providers combined with React Use.
00:28:35Second, we can compose serving client components
00:28:37using the donut pattern to reduce client-side JavaScript,
00:28:40keep a clear separation of concerns,
00:28:41and allow for component reuse.
00:28:43And this pattern will further enable
00:28:45us to cache our composed server components later.
00:28:50And finally, we can cache and pre-render with use cache
00:28:52either by page, component, or function
00:28:54to eliminate redundant processing,
00:28:56boost performance and SEO, and let partial pre-rendering
00:28:58statically render these segments of the app.
00:29:01And if our content is truly dynamic,
00:29:03we can suspend it with appropriate loading fallbacks.
00:29:07And remember that all of this is connected.
00:29:09So the better your architecture, the easier it is to compose,
00:29:11and the easier it will be to cache and pre-render
00:29:13with the best results.
00:29:15For example, resolving dynamic APIs deep in the tree
00:29:17will allow you to create a bigger partially-printed static shell.
00:29:22And with that, this is the repo of the completed
00:29:24version of the application.
00:29:25There's so many things that I didn't even
00:29:27show in there that you can check out.
00:29:29And you can scan the QR code to find my socials there
00:29:32alongside the repo if you don't want to take
00:29:34a picture and type it in yourself.
00:29:36So yeah, that's it for me.
00:29:37Thank you Next.js Conf for having me here.
00:29:39[MUSIC PLAYING]