Loading video player...
Nex.js16 is available now and comes with
a whole bunch of cool stuff, but the
thing I'm most excited about is cach
components. Cache components. Cach a
components. No, I'm going with cach
components. I'm sorry if that's
difficult for you to hear, but c
components are the stable way to do
partial pre-rendering and caching going
forward with Nex.js. I'm super excited
to learn more. So, let's get into it.
So here I have my stories application
with a list of things going on in the
tech industry. It has a bunch of
different combinations of static and
dynamic stuff. So this bit at the top
doesn't change. It's the same static
markup every time. Whereas the latest
stories are somewhat dynamic. And this
favorite section requires fetching those
stories from the database but also
knowing who the currently signed in user
is so it can get their specific
favorites. So this section would look
different for every single user that
visits our app. And then again, we've
got some static content at the bottom.
Now, if you're new to Nex.js or web
development in general, you probably
need to know that static content is
good. There are a lot of performance
wins, especially in Nex.js, when you can
have things that are predictable and
known up front, so they can be
calculated or rendered up front and it's
not doing a whole bunch of work when the
user visits the page. So, let's start
off with our about page. Literally,
everything on this page can be known
ahead of time and pre-rendered. Nothing
on this page is fetching from a database
or needs to know who the currently
signed in user is. So this page is a
great candidate for being fully static
rendered ahead of time and heavily ced
for every user. And because we're using
the next.js app router, we can run mpm
run build and see that by default
next.js has detected anything that can
be static and pre-rendered it and
flagged anything that is dynamic and
needs to be server rendered on demand.
So when the user visits this page and so
if we're looking for this little circle
to denote a static page, we can see
there are a bunch of static pages
including our about page. So this is
great. Next.js has already done the work
for us. It's looked at this page. It's
found nothing dynamic. Everything is
just static markup. So it's been able to
render that page at build time. So it
needs to do very little work when the
user actually visits that page. But that
isn't really very realistic in a lot of
applications. Let's run our dev server
again and say we want to add
authentication to our application. So
let's add a little user avatar for the
currently signed in user. Thankfully the
previous dev has already done all the
work for us in the navbar and has just
left it commented out. So let's give
that one a save and then head back over
to our application and we have a user
avatar. So this should be fine. Our
entire about page is still static and
this whole navbar is actually rendered
from our layout component which is
outside our page anyway and so shouldn't
affect it. Right? Well, we can check
again with another build. But it looks
like our assumption was incorrect. Our
entire application is now dynamic. So
every single page across our entire
application now needs to be rendered
dynamically when the user visits the
page again just to show this tiny little
user avatar up in the navbar. And even
if I wrap this in a suspense boundary.
So we say we want to render everything
in the navbar and then the user avatar
can be suspended until we've finished
working out who that user is. So now in
dev mode this looks good. If we refresh
the page, we can see all of this about
stuff and then when the user is ready,
they pop in. So again, I refresh,
they're gone, and then they pop in once
we've worked out who that user is. But
if we go and do another build, we'll see
again our entire application is dynamic.
And this is because in the app router,
prior to cage components, it was all or
nothing. Even though everything on our
page is static, because there's one
little part that's dynamic, it opts that
into being a dynamic route, which needs
to do all of that work when the user is
visiting the page. So having this one
very tiny part in the navbar being
dynamic and the navbar being rendered by
our layout component. So wrapping every
single page in our application, we've
unintentionally marked every page in our
application dynamic and made it so that
Nex.js JS can't do any static
pre-rendering or generation of our pages
ahead of time or it couldn't until C
components which we can opt into by
going to our next.js config file and
setting C components to true. And that's
it. We now have access to partial
pre-rendering and some new caching
capabilities that we'll look at soon. So
let's comment out our user avatar again
just so we can see the default behavior
and then build our application. We can
see Nex.js JS has identified some of our
routes that are static and some of them
that are dynamic, but has also given us
this new option for partial pre-render
where content is pre-rendered as static
HTML with dynamic server streamed
content popping in once it's ready. But
we're getting ahead of ourselves. What
about our about page? Is it static or
dynamic? Correct, it is static. Next.js
JS has been able to identify that there
is nothing dynamic happening in this
component and so it can pre-render that
HTML exactly the same as the app router
could do before we switched on cage
components. But now when we uncomment
our user avatar and have something
dynamic happening on every single page
when we build is this going to be static
or dynamic? Well, neither. But also
both. We can see this little partial
pre-render icon is next to pretty much
every page in our application, including
our about page. So now, rather than it
being all or nothing for every single
route of our application, we now have
this wonderful thing called partial
pre-rendering, which knows that all of
this stuff on the about page is static.
And the only thing that's dynamic is
this little user avatar. So it leaves a
little dynamic window within the rest of
this entirely static and pre-rendered
page so that content can be streamed in
from the server once we've checked the
cookies and know who the user is. So
let's go the entire other end of the
spectrum where we have dynamic content
that needs to be fetched from a
database. So for this stories page, we
have a little bit of static stuff at the
top, but then each of these stories is
fetched from the database. So can't
really be static. But since we're using
suspense to wrap our component that's
fetching each of those stories, we can
still benefit from that static partial
pre-rendering of all this markup that
doesn't change. And in fact, if we
remove that suspense boundary. So if we
just comment it out and go back to our
page and refresh, we'll see this has lit
up red with an issue that unced data was
accessed outside of suspense. This
delays the entire page from rendering,
resulting in a slow user experience and
next.js JS actually like throws this as
an error and a build error to ensure
that you fix it up and your app loads
instantly on every navigation. So you
may actually need to do some refactoring
of your application, especially if
you're doing something like fetching all
of your data at the page level. So up
here in our stories page and then prop
drilling that data down to the
components that are using it. This
blocks us from being able to use
suspense because all this asynchronous
data fetching stuff is happening before
we can render anything on the page. So,
React and Nex.js suggest moving this
data fetching logic further down the
tree into the components that need it
rather than passing it in as a prop from
a component higher up the tree. And that
makes this stories list component much
more reusable because its data fetching
logic is contained within the component
rather than being passed in as a prop.
And again, this allows us to wrap it in
a suspense boundary as low down the tree
as we possibly can. So just this stories
list component is waiting for that
promise to resolve to get that data and
the rest of the page can start
rendering. We can even render a fallback
like this stories list skeleton so that
our UI doesn't jump around while we're
fetching that data. So we can see this
stuff at the top is already available
while we're fetching the rest of these
stories from the database. And once
they're ready, we swap out that skeleton
component with our actual list of
stories. And our users get that
instantaneous feeling navigation where
they land on the page and they can see
something straight away while we're
still fetching those stories. But what
if this data doesn't actually change
that often? Let's say we only get one or
two new stories a day and so the
majority of our requests to the database
are going to return the same result set,
the same list of stories. Well, that's
where the C bit of C components comes
in. We can add the new use case
directive to the top of our component or
we can do it at the top of our file or
even within a specific function that we
want to c the result from. And now if we
go back to our page, we're going to see
this obscure error that we used cookies
inside of use cach. And this is because
to fetch our stories, we're using
superbase. And Superbase allows for
authenticated queries to the database.
So because we're using Superbase for
authentication, we know who the user is.
That gets piped through to the database.
So we can control exactly what we want
the user to see when they make a
request. But that's not actually what we
want in this case. These stories are
public. And so regardless of who the
user is or even whether or not they're
signed in, we want them to see the same
list of stories. So I've mocked up a
quick Superbase client for anonymous
queries. So it basically turns off all
the or stuff that relies on cookies. And
so when we're importing the create
client function from our Superbase
library, we just want to pull this in
from the anon file. So you don't need to
worry about this bit if you're not using
Superbase. This is just a specific thing
because of how we create a Superbase
client on the server. So now our stories
list component is making an
unauthenticated request or an anonymous
request to get all of the stories. Now
when we go back to the browser and
refresh, we see this list of stories is
already cached. And if we click between
our about page and our stories page, we
see instantaneous transitions because
Nex.js already has all of the data that
it needs. Now we are seeing a flash of
that skeleton screen. So we might decide
to remove this fallback. And now when
navigating between about and stories, we
see those pages are already ready to go,
which is actually another benefit of
partial pre-rendering, the router can
now prefetch any of that static content
and even make a request for the dynamic
stuff before the navigation has
finished. So we can even determine how
long we want that cach to live with the
c life function which comes in from next
cach and then we can pass this a
specific number of seconds. So like 30
for example or some convenient strings
like days, hours, seconds, weeks,
minutes. So in our case, let's say this
c can live for hours. So what about our
favorites page? So for some context,
this is a page for your saved stories
and articles. So this needs to know who
the currently signed in user is and tell
Superbase about it so that it can fetch
just their stories that they favored. In
the code, this follows a very similar
pattern to our stories page where we
wrap our favorites list in a suspense
component. And our favorites list has an
artificial wait time of 2 seconds just
so we can feel like this is doing
something. If Superbase wasn't so damn
fast, this wouldn't be a problem. But
we're fetching all of the users
favorites and then rendering them out on
the page. But if we try to c this data
within the component, we see that same
error in the browser cuz we need to use
cookies to know who this user is. And we
can't do that inside a component that
has the use case directive. And we can't
use that anonymous superbase client that
we used for our stories because in this
case we actually do need to know who the
user is. And so because this favorites
page will look different for every
single user that visits. It doesn't
really make sense to c that on the
server. Now, Nex.js is actually working
on something that looks very cool called
use case private. So, this is a variant
of use cach designed specifically for
users specific content that needs to be
prefetchable but not stored server side.
And so, I just edited out a whole chunk
of the video where I tried to use this.
And so to enable runtime prefetching,
you need to export out this unstable
prefetch configuration, which I think is
maybe just a little too unstable at the
moment, you need to provide it with an
array of samples. And so basically know
the value of the cookies that you're
trying to c, which doesn't really make
sense in our case because that's going
to be different for every single user.
So we can't really know it ahead of time
and put it in our code. So point is, I
think in the future, Nex.js JS is going
to have a really nice API around this,
but for now we need to decide between
user generated content and cing. So in
this case, no cing for us. But we still
get to benefit from partial
pre-rendering and those snappy
navigations between each of our pages
because this first bit of our favorites
can be pre-rendered and fetched ahead of
time. And again, it can even kick off
the request for this dynamic content
before it's finished navigating to that
page. So what about our homepage? So
this is a mix of some static content,
some dynamic content that can be cached
and some truly dynamic content that is
rendered uniquely for every user who
visits our page. So this page probably
benefits the most heavily from partial
pre-rendering because there's such a mix
of static and dynamic stuff. In fact, if
we were to slow down that request for
our favorites and make it painfully
slow, like 10 seconds. And while we're
here, we can c our recent stories
component and set the C life to ours.
And also remember to use our anonymous
client. We'll see even though those
favorites are painfully slow to render,
the user can already see this static
content at the top, the latest stories.
They can even scroll and see the static
content underneath.
With Next.js 16, Vercel released Cache Components - a new, stable way to do Partial Prerendering and Caching. In this video, Jon takes us through everything we know so far. He demonstrates how to build full static pages, cached pages for content that changes less frequently and truely dynamic content that needs to be rendered at request time. Additionally, he talks through the tradeoffs of caching vs user specific content that is unique for every user. 00:00 Static vs Dynamic 02:23 Adding authentication 04:27 Enabling Cache Components 05:33 Partial Prerendering 06:16 Mixing static and dynamic content on same page 08:45 Caching dynamic content with use-cache 11:15 Caching vs user specific content 13:30 Pulling it all together š» Videos to watch next: ā¶ Supabase Explained: https://youtu.be/T-qAtAKjqwc ā¶ We made Supabase Auth way faster: https://youtu.be/rwnOal_xRtM ā¶ 5 tips to make you a PRO at Cursor: https://youtu.be/YtTWNzOtkxU š Learn more about Supabase š šø Website: https://supabase.com/ š Get started: https://app.supabase.com/ š Docs: https://supabase.com/docs š Subscribe for more tutorials and feature updates from Supabase: https://www.youtube.com/channel/UCNTVzV1InxHV-YR0fSajqPQ?sub_confirmation=1 š± Connect with Us: š Github: https://www.github.com/supabase š¬ Discord: https://discord.supabase.com/ š¦ Twitter: https://www.twitter.com/supabase/ ā¶ Instagram (follow for memes): https://www.instagram.com/supabasecom/ ABOUT SUPABASE: Supabase is the open source Firebase alternative. Supabase provides a full Postgres database for every project with pgvector, backups, realtime, and more. Add and manage email and password, passwordless, OAuth, and mobile logins to your project through a suite of identity providers and APIs. Build in a weekend, scale to millions. #Supabase #AppDevelopment #DeveloperTools