10 common mistakes with the Next.js App Router
Summary
TLDRThe video outlines 10 common mistakes when building Next.js applications using the app router. Topics covered include: incorrectly implementing route handlers, misunderstanding route handler defaults and caching behaviors, unnecessarily using route handlers with client components, improperly utilizing Suspense boundaries with server components, not accessing request data in server components, misplacing context providers in the component tree, misunderstanding client/server component relationships, forgetting to revalidate data after mutations, throwing redirects inside try/catches instead of using routers or actions. The goal is to help developers avoid these pitfalls and build better Next.js apps.
Takeaways
- 😯 Don't call route handlers from React components, call functions directly instead
- 💡 Route handlers are cached by default like Pages, make them dynamic if needed
- 😀 Server actions work with client components too
- 🤔 Place Suspense boundaries above data fetching components
- i️ Use component props to access request info like headers and params
- 🔀 Context providers belong in Root layout, not wrapped around each component
- 👪 Client components don't need explicit `useClient` if parent is already client
- 😠 Common mistake to forget revalidating data after mutations
- ❗️ Don't throw redirects inside try/catch blocks
- 🎓 Lots of opportunities to learn from mistakes building with Next.js!
Q & A
What is a common mistake developers make when using route handlers in Next.js applications?
-A common mistake is thinking that route handlers need to be called from React server components, leading to unnecessary network requests by fetching data from an API route that could be directly accessed or function calls within the server component itself.
How does the behavior of route handlers differ between local development and production environments in Next.js?
-In local development, route handlers might return new values on each request due to caching behavior being different. However, in production, after building and starting the server, the data is cached by default and does not change on each request, showcasing the difference between local and production environments.
Why is it considered a mistake to use fetch within React server components for accessing local data in Next.js?
-Using fetch within server components to access local data is unnecessary because both the data fetching and the component rendering occur on the server. Developers can directly call functions to retrieve or manipulate data without making an extra network request.
What is the purpose of route handlers in Next.js applications?
-Route handlers allow developers to handle HTTP verbs like GET, POST, PUT, and DELETE, enabling API routes for fetching or mutating data. They are particularly useful for handling webhooks or external requests.
Can server actions be used with client components in Next.js?
-Yes, server actions can be used with client components to simplify interactions, such as form submissions, without the need for additional code to handle route handlers, thus streamlining the development process.
What is a common misconception about using suspense with server components in Next.js?
-A common misconception is placing suspense boundaries incorrectly, specifically not placing them higher than the data fetching component. For suspense to work effectively, the suspense boundary needs to encompass the component where data fetching occurs.
How can developers handle incoming request data in server components with Next.js?
-Developers can handle incoming request data by utilizing built-in functionalities like headers, cookies, params, and search params. These allow reading information from the request, such as headers or URL parameters, directly within server components.
What is a common mistake when integrating context providers in Next.js applications?
-A common mistake is incorrectly placing context providers within the component tree. The best practice is to place them in the root layout, allowing the application to maintain server components as children while still utilizing client components where necessary.
Why is revalidating data after a mutation important in Next.js applications?
-Revalidating data after a mutation is crucial to ensure that the UI reflects the latest state. Without revalidation, changes made through server actions may not be immediately visible, leading to a mismatch between the server state and the client display.
What is the recommended way to perform redirects in Next.js applications?
-The recommended way to perform redirects is to use them outside of try-catch blocks or in a finally block to avoid conflicts with error handling. For client-side redirects, the useRouter hook should be used for programmatic navigation.
Outlines
😊 Common mistakes with route handlers
The paragraph discusses common mistakes developers make with route handlers in Next.js, such as incorrectly calling route handlers from React components instead of directly calling the handler function, not understanding route handler caching behaviors, and using route handlers unnecessarily with client components when server actions can be used instead.
😩 Forgetting to revalidate data after mutations
This paragraph explains the common mistake of developers forgetting to revalidate data with cache tags or revalidate path after mutating data, such as after submitting a form. It provides the example of adding a todo but not seeing the todo list update.
😖 Incorrect suspense usage with server components
The paragraph discusses the mistake of improperly wrapping suspense boundaries around server components that fetch data, which results in suspense fallbacks not being triggered. It explains how to correctly use suspense above server components to enable proper pre-rendering.
😟 Context provider placement with server/client components
This paragraph explains the ideal placement of context providers is around the root layout server component, allowing server components and client components to consume the context properly. It also clarifies that client components do not need extra useClient calls.
😕 Redirects incorrectly used inside try/catch blocks
The paragraph points out that developers commonly make the mistake of trying to do redirects inside try/catch blocks, when redirects internally throw errors. It explains redirects should be used after try/catch instead.
Mindmap
Keywords
💡route handlers
💡caching
💡server actions
💡suspense
💡request context
💡context providers
💡client components
💡revalidation
💡redirects
💡mistakes
Highlights
Mistake: Calling route handlers from React server components is unnecessary; direct function calls are more efficient.
Explanation of route handlers and HTTP verbs, emphasizing their use for data retrieval and mutation.
Clarification on route handlers vs. API routes, highlighting common misconceptions.
Illustration of an unnecessary network request scenario with route handlers and its efficient alternative.
Discussion on the defaults for route handlers and common confusion points.
Insight into local vs. production behavior of route handlers and caching mechanisms.
Introduction to the concept of partial pre-rendering and its implications for route handlers.
Highlighting the misuse of suspense with server components and the correct application.
Understanding the need for dynamic route handlers through example of a git pattern.
Clarifying the use of server actions with client components to simplify code.
Demonstration of server actions replacing the need for route handlers in form submission.
Discussion on context providers and their placement within Next.js applications.
Explanation of server and client component interweaving and common mistakes.
Advice on revalidating data after mutations to ensure consistency.
Caution against improper use of redirects in try-catch blocks within Next.js.
Transcripts
I've talked to hundreds of developers
building with the xgs app router
probably looked at over a thousand
different repositories and here are the
top 10 mistakes that I've seen that
hopefully can help improve your next
nextjs application let's get into it
okay first is thinking you need to call
route handlers from your react server
component now I have to step back a
little bit here and talk about what a
route Handler is in the first place a
route Handler allows you to have verbs
httv verbs like get post put and delete
for example and have a API exposed that
allows you to get some data or mutate
some data now for folks coming from the
pages router World they might think wow
this seems basically like the same thing
as API routes and that's usually where
the mistake comes in you make a route
Handler like this which is a g it
returns some Json response that is hello
world and I go back inside of my page I
mark it is async I say okay I want to
have fetch to localhost sl3000 API I'll
get back this Json data and then I'll
printed out here in the H1 and this does
work but it's actually kind of a mistake
because for two reasons one you have to
hardcode the full URL because you're
here in the node.js context but two you
actually don't even need that extra step
or that extra Network request because
this runs securely on the server as well
as this running cely on the server
instead you could just put that Json
here you could just call that await get
user or whatever that function is here
whatever that promise is directly
without needing to use the route Handler
in the middle so typically when I see
something like this I would cut it and
it would be let data equals away either
fetching to somewhere externally and not
having the route Handler in between or
just calling that function that you've
abstracted out here as well too so
that's number one okay building off this
for number two let's talk about the
defaults for Route handlers this is
another place that I see people get
mixed up so let's say you did need a
route Handler for some reason I think
most of the time when I see people
actually needing route handlers it's for
handling web hooks or for handling some
sort of external request in their
application maybe it's for scaffolding
out something like next off for example
but they create a route Handler just to
test things out so they make something
like this and they're wondering why
let's say I put in something like
date and let's do yeah let's do
something like this so I have a date and
I have a new date and a local string of
New York now if I go to localhost
3000i I'm going to see this new date
string and if I reload the page I'm
getting a new date on every single
request now there's two opportunities
here to better understand how this model
works the first is understanding the
difference between local and production
so in local even though this data is
Cash which I'm going to get to in a
second you're going to get a new value
it's going to get recreated on every
single request and secondly is
understanding why this is cashed by
default so let me show the first one let
me open up my terminal and I'm going to
run um run build and run starts so I'm
going to do a production build of my
application and then I'm going to start
up my local Dev server my production
server on my Local Host so now if I go
back to here and I reload the page it
never changes so this is a difference
between how your local uh your local Deb
environment is working through next Dev
versus doing that production build and
then serving your production server now
the question is why does it work this
way well this is actually pretty
powerful if you understand more of the
intention behind it a route Handler is
the base of how Pages work inside of the
app router as well too and Pages can be
cached by default so even if I'm doing
an asynchronous component or a server
component and I'm fetching some external
data that doesn't necessarily have to
run on demand on every single request
it's kind of like instead of a server
component it's a data component because
you can fetch data you can pre-render a
page that only was pre-rendered on the
server and then you don't have to have
any additional client.js for that long
story short the options for how route
handlers work are the same as pages so
by default this is cached static which
means that anything that I do here to
opt into making this Dynamic it will
then work the same way as a traditional
API route so for a git for example if I
were to read request. something so maybe
I want to read the incoming search
parameters or maybe I want to read uh
the headers that would opt it into being
Dynamic you can also use the helper
functions that we have headers as well
as cookies that will read from the
incoming request or more commonly what I
see is that folks are doing a post
request so if you're doing a post
request because you're probably um maybe
it's something with web hooks again post
requests will be dynamic by default as
well too now the reason that this git
pattern and this is very interesting is
because maybe inside of here I want to
do let's say data equals await
fetch um wow shout out to Luke who has
their user named on
here and their amazing contributions to
open source um let's say I want
something like this and then maybe I'll
do
this and let me just go back to my Deb
server
here and we're going to see that it
prints out some information about my my
GitHub profile now the really
interesting thing here is in the pages
router model having an API route
required that you had a server but
there's also an option in xgs that
allows you to do a static export and
basically take the generated files and
put them anywhere put them on a server
somewhere um put them on an S3 bucket
somewhere it doesn't have to be
something that's always on right so the
cool thing about this again if I do the
build and I start it up since I have the
ability to Define this arbitrary route
that can generate maybe it generates
Json maybe it generates an XML file
maybe it generates a text file I can do
this and it can still be compatible with
that static export model so this would
work and even if we wanted to do a
static export we can still use that now
I will say most of the time you probably
don't need to do anything like this with
route handlers because you can just call
that function directly on the page the
server comp component instead okay third
is thinking that you need a route
Handler because you're using a client
component so so far I've talked about
server components and your next question
might be yeah but I'm using route
handlers because I have client
components let's talk about that one as
well too so I change my page to be a
client component I added a form and a
button that takes some Advent Handler
for onsubmit and then in onsubmit I just
preventing the default and I'm logging
submit so inside of here what you might
say is okay so now I need to make a
fetch to Local Host API have my route
Handler and then I'm in that tricky
State again with having to set up route
handlers when maybe I I don't need to so
this is what happens I click send it I
see in the console I hit submitted what
if I told you that you didn't need to do
it this way and you could skip writing
all this additional code and just call a
server action instead yes server actions
can work with your client components as
well too so what if if we delete this
and we take this and we say you know
what action is going to equal
send and we import send from our actions
notably we're importing it here and
we're not putting it in line inside the
file this is a use client file we're in
the client uh bound we're on the client
boundary here versus this actions on the
server so inside of actions we have used
server we have this function send it and
if we go back here hit save click send
it now you notice the logging position
was different too I didn't see my client
side event handler say that it was
submitted in the browser instead it just
directly called the server and said send
it which is where I would have that
logic that was in my route Handler in
the first place so this can drastically
simplify things uh if you want to take
this further as well too there's some
cool things you can do with use
optimistic and use form State use form
status some other built-in utilities to
make working with forms better in nextjs
so definitely check that out too okay
fourth we have using suspense with
server components and I'm going to talk
about this through the lens of partial
pre-rendering which is an upcoming
addition to nextjs it's out
experimentally right now but you'll
start using suspense more and more in
the future so it's helpful to start
thinking about this now so in this
partial pre-rendering demo I reload the
browser I see these loading States this
is all defined by my suspense boundaries
which is super super helpful so
understanding how suspense works is a
big unlock for the future of nextjs so
let's talk about it let me move this
back here what I have is a server
component it's marked async and I'm
making a call to fetch a list of blogs
and then I've enumerated them and listed
them out on the page so for example if I
said Okay I want this function let's
just say this is blog
post
and we're going to say export
default function
page uh and maybe I want to have some
additional information on this page so
I've got this section for my page I've
got an H1 I've got blog post that all
looks fantastic now in a partial
pre-rendered World you'll be able to
pre-render all of this right now you're
pre-rendering everything because there's
no suspense ball back but there's a
reason why you're moving this data
fetching closer to your UI is because
this will be pre-rendered when you run
your build so you might say okay that
means that I want to use suspense with
my blog post so that I can have a
fallback and let it get pre-rendered or
have a loading state so you know what
let's do this let's wrap let's do um
suspense we'll import that we'll have a
fallback cool so we have this fallback
loading and then we render this here
okay so I reload the page
but nothing is happening now the mistake
here and I don't know who would make
this I've definitely never made this
mistake just kidding I've definitely
made this mistake uh is that you want
your suspense boundary to be higher than
your data fetching component seems
obvious in retrospect but it definitely
happens and I I can understand how
people fall into this so in reality
you'd want something like
this now not only can my blog be
pre-rendered in the future but also the
fallback State you probably wouldn't say
loading in that world you might have um
you might have it be empty or you might
have a loading skeleton for example but
yeah this is this is the difference here
now what you're wondering is okay well
why am I not seeing that state and
that's because this by default is being
cached so another thing to prepare us
for this partial pre-rendering future is
that I can say I want to
make this
Dynamic and right now this function is
called unstable no store uh so I can
import this here I can reload and now
we're seeing this Flicker and that
flicker means that this bit should not
be cash it should run dynamically and
that means that we are seeing the
suspense fall back just to make it more
dramatic you see loading as well too so
hopefully that helps uh we'll be talking
a lot more about suspense in the future
but at least for now as you start
thinking about your application
structure as you start exploring
suspense helpful tip to know I think
okay the fifth tip here the fifth
mistake that I see sometimes is
understanding how you can use
information about the incoming request
in your server component so maybe you
want to forward headers to a fetch for
example you can use the headers function
to read those headers or maybe you want
to read the cookies for example because
you want to look at an authorization
cookie that you've stored or JWT that
you've stored um maybe you want to look
at the parameters or the route segment
parameters in your url those as well as
the URL search parameters are actually
props on the server component so you
have params and you have search params
so if I wanted to for example let's say
inside of here instead of this let's do
a
H1 and we're going to read search par
dot let's say hello
awesome so inside of here I'm going to
make a new search program hello and I'm
going to say
world and now I'm able to read that
incoming information from the request or
I could read the prams if I have Dynamic
route segments in my URL I can read
those as well too so there's a couple
different ways you can read from that
incoming request using some of these
built-in functionalities in nextjs okay
six and seven for most common mistakes
are how to use context providers and
where you place them in your application
sometimes placed incorrectly and the
second is the in weaving or the inter
leaving between server and client
components so for example if I have my
root layout here on the left you might
be thinking okay I have some components
I have some dependencies that require
react context where do I place that
provider in my application do I have to
put it around the lowest component in
the tree do I need to have it at the top
of the tree does that mean that
everything below it is going to be a
client component well this pattern it's
explained in the docs I want to visually
show it a little bit here let's imagine
that I have some theme provider here and
this is using react context now this can
take in some children and this is a
client component but the ideal place to
place this is actually in the root
layout so what we'd want to do like we
show here is actually take this and I
think I have my file name differently I
think it's called
providers but we would actually want to
take that theme provider wrap it around
the children of our root layout so wrap
it around the children of our
application this is still a server
component and that still allows children
of this like the page for example that
still allows this to be a server
component so the child of a server
component can be a client component this
works as we would expect this actually
goes into the next most common mistake I
see which is when you apply used client
does that apply for everything does that
apply to its children and how does that
relationship work and I will say this is
all new so while I'm framing these as
mistakes I think they're all learning
opportunities for us so don't feel harsh
you know I've made this I've made all of
these mistakes in this video um so this
top level layout is a server component
but then the child of it is this
children prop here that goes to the page
so the page is the child of the layout
now the child of this page is this
button component let's take a look at
this button component this button
component is a simple client component
that has a counter so it has some State
you click on it the state increments and
we see this new uh this new state
reflected in the button text so this is
already showing how you can weave server
and client components but the question
then becomes what about the children of
this component so for example let's say
that instead here I'll just do a
fragment and then I'll have this but
then I'll also have another button and
this other button is also a client
component so I have this button I have
another button and another button here
do I need to explicitly have used client
at the top well the answer
let's find out the answer is no and the
reason you don't is because you're
already in the client boundary here so
the child of this component are going to
be client components unless for example
I take some children and let's say I
want to have the children here you can
still weave in the server components as
well too so then maybe here for
example I know this is kind of a
contrived example but I think it's still
works this is a client component this is
a client component this is a server
component and they can all work together
now the big opportunity here and why yes
it's a mistake but it's also an
opportunity is that we need to have Dev
tools that help you visualize these
things and we're definitely working on
making this experience better so that
you can understand the state of how your
server and client components work
together okay two more second to last
the mistake I see most commonly is just
forgetting to revalidate data after a
mutation so in the nextjs repo we have
this fors example which I'd recommend
checking out it shows a server component
that fetches a list of to-dos it renders
out the list of to-dos it has a form to
add a new to-do and inside of this form
it uses a lot of the latest features
like server actions so I have an action
to submit the form to add the new to-do
and what I see a lot of folks do is they
start using server actions maybe they're
moving from the older model where they
were trying to map it one to one to
Route handlers and they move to this and
they hit the button and they hit submit
and they don't see that list of to-dos
update and they're trying to figure out
what exactly is happening well what
they're probably missing is inside of
their action in this instance I have a
SQL statement here that's inserting a
new value into the to-dos table what
they're probably missing is some
revalidation either revalidating the
path or by adding a cach tag onto the
fetch or onto that bit of data and then
revalidating that specific tag so pretty
commonly I see this I know that caching
is still very new right now and a lot of
these are new patterns we're also
working on making caching a little bit
easier as well too but this is one that
I see pretty common okay and while we're
here we can talk about the last one
which is throwing a redirect inside of a
tri catch so I can understand how this
happens I definitely made this mistake
myself as well too you're inside of here
you've revalidated your path and you say
okay awesome now I want to
redirect back to the index route for
example well internally the way this
redirect is working is it's actually
throwing an xgs specific error so you
would actually want this at the
conclusion here either that would be
after the try catch or in like a finally
block um and I think most common where I
see this is either in a server action or
if you're trying to use it from a client
component so I recently updated the docs
here to show an example when you're
using a server component for example
rather than trying to do a redirect
inside of an event handler you would
want that to be inside of your server
action if you're wanting to do a
redirect on the client side in an event
handler you'd be using the used router
hook which does have the ability to
programmatically route and
programmatically redirect okay those are
10 common mistakes or common
opportunities to learn more about the
nextjs app router that I've seen I'm
sure there's others and we're definitely
working on making some of these more
obvious both in the framework as well in
the documentation so would love to hear
what other things you'd like to see me
talk more about or go more in depth on
any of these as well too peace
Weitere ähnliche Videos ansehen
5.0 / 5 (0 votes)