The ReviewsPage now automatically
lists all the reviews.
And each individual ReviewPage is a dynamic route,
so whenever we write a new Markdown file,
a new page will be available automatically
for the same path.
But so far we only tested this dynamic
route in the dev server.
Let's add a log statement here,
printing a message every time
this component is rendered.
And let's log the "slug" as well,
so we can tell exactly which route was generated.
Now, we know that the dev server
always re-renders each page at every request.
But what will happen in production?
Let's stop the server, and see our how
dynamic route works in production.
First of all, we need to build our app,
to create an optimized production build.
The build output itself is quite interesting.
If you notice, "/reviews/[slug]"
has a lambda symbol next to it,
not a circle like the other pages.
At the bottom, it explains that
the "lambda" means that
that route "server-side renders at runtime".
So, it's not a static page, like the other ones.
Let's see what this means in practice,
if we start the production server.
Let's reload this page.
You can see that the server rendered
the ReviewPage 3 times:
once for every different slug.
Now, that's because of prefetching.
We opened the Reviews page,
that contains links to those three other pages,
so the browser prefetched all the links.
But what's more interesting thing is that
the server rendered each page at runtime.
This will be clearer if we look
at an individual review.
There's nothing new in the server logs,
because this page had already been
prefetched by the browser.
But if we reload the same page,
you can see that the server re-rendered
the "hellblade" route.
And every time we click reload,
the server will re-render this page again.
Every time the browser requests this URL path
the server runs our component code
and regenerates the HTML sent to the browser.
So, this is what server-side rendering means.
It basically works like the dev server,
with the difference that, in production,
it also prefetches all the links.
But with this approach we lose the benefits
of statically generated pages.
Returning a static page is faster,
because the HTML file was generated
during the build,
there's no need to render it at runtime.
And also, server-side rendering requires
running a Node.js server.
It's not compatible with doing
a static HTML export.
So, is there any way to generate static pages,
even when using a dynamic route?
It is in fact possible.
But we need to tell Next.js
which routes to generate.
Because "/reviews/[slug]" is just a pattern;
it will match any path named
"/reviews/" something.
To generate some static pages
Next.js will need to know
exactly which "slugs" are available.
We can provide that information
by exporting an async function
called "generateStaticParams".
And here we need to return an array,
specifying which routes are valid.
Each element in this array must be an object,
containing the params that will be passed
to the ReviewPage component.
Since this route has a "slug" parameter
that's what we need to put inside this object.
For example, we have a review for "hellblade".
Then we can add another similar object,
this time for "hollow-knight".
Let's just return these two for the moment,
and see what happens now,
if we build our app for production.
You can see that our dynamic route
was treated differently.
Under the "/reviews/[slug]" pattern
there are now two specific paths:
"hellblade", and "hollow-knight".
Those are, of course, the two slugs we returned
from our "generateStaticParams" function.
Also, this route is now marked
with a filled circle.
At the bottom, it explains that
that symbol stands for "SSG",
which means "Static Site Generation".
Now, this legend shows SSG as
different from "Static",
but, in practice,Â they're
basically the same thing.
SSG is also static.
You may also notice that
our ReviewPage was rendered during the build,
with different values for each slug.
This didn't happen before we
added generateStaticParams.
So this is how we can get Next.js
to generate static pages,
even when using a dynamic route.
But we don't really want to hard-code
all the possible slug values
in generateStaticParams.
We already have a "getReviews"
function in this other file,
that we could call to get all the reviews.
However, this code reads each Markdown file,
to load all the properties, like title and body.
While in generateStaticParams
we only need the slugs.
So, to avoid reading many files unnecessarily,
let's write a separate function,
called "getSlugs".
And here we can use the same code
we wrote for getReviews,
that lists all the files in the "content" folder,
and extracts the "slugs" from the file names.
We can simply return the slugs directly from here.
Now, to avoid duplication, we
can also replace this code
with a call to our new "getSlugs" function,
and the rest will work in the same way.
We basically just moved some code
into a different function.
But this way, in "generateStaticParams",
we can get all the "slugs"
by awaiting the Promise returned
by our new function.
generateStaticParams is already async.
Now, here we need to return an
array like the one below,
where each element is an object
with a "slug" property.
But this "slugs" variable is an array of strings.
So we need to use "map" to transform each string
into an object with the "slug" as a property.
Note that we need parentheses
around the object literal,
otherwise JavaScript will treat the curly
brackets as the function body.
Anyway, we can now delete this hard-coded array,
save this file,
and go and build our app again,
to see if our code works as intended.
You can see that the dynamic route
now resulted in three statically generated pages,
one for each available review.
That's what we wanted!
Let's start the production server,
and see how our application works now.
If we reload the Reviews page,
there's nothing in the server logs.
And if we open the Hellblade review,
even if we try reloading it a few times,
again the server never renders any component.
Because all the pages have been statically
generated at build time.
So, to recap: we can write a generateStaticParams
function
returning all the possible paths,
and Next.js will generate static
pages even for a dynamic route.