A beginner-friendly explanation of why and how SSGs use this function.
Published on December 31, 2023
Warning: Almost There but Not Quite
The post you're about to read is marked as pre-beta!
While the content should be overall accurate and
understandable, it was not reviewed for flow, imprecisions, typos,
or accidentally-abandoned sentences.
Static Site Generators (SSGs) don’t create pages on demand when a visitor
requests them. Instead, they generate them all ahead of time (the so-called
“build time”) before your site is uploaded to the web. This creates a problem
when page definitions have parameters within their URLs: if a URL is of the
form /posts/[postSlug], what are all the possible valid values that
[postSlug] can have? Without knowing this, your SSG cannot know what pages
it’s being asked to generate. This is the question that getStaticPaths exists
to answer.
When an SSG goes to generate a page that has a dynamic parameter, it first
searches the code within in search of an exported function (the only ones it
can see) named getStaticPaths. When called, this function will give the SSG
the list of all the pages it needs to work its magic on. The form this list
takes is an array made up of one JavaScript object per page with the values of
the URL parameter(s) for that page.
Let’s leverage TypeScript to show what this looks like:
Or, for a concrete example:
This will generate 3 pages:
/posts/my-shipping-manifesto
/posts/typescript-is-your-friend-i-swear
/posts/thirstposting-as-bonding-activity
If you want, you can stop here: this is all your SSG needs to start using getStaticPaths.
If you want to go deeper, however, there is yet another thing that
getStaticPaths can do for you: given that it already has to work to produce
the value of the URL parameters for every page (a task that might require
potentially-expensive data loading), wouldn’t it be neat if it could use that
chance to also tell the SSG about the dynamic properties (”props”) that
change page by page?
Continuing our blog example, our dynamic properties could be tags, title,
created_at, and mostly anything that depends on the specific post and that we
want to avoid having to recalculate later. The SSG doesn’t really care what
props you give it (and if you give any): it will simply pass them as-is to
each page as it goes generate it, similarly to how we manually pass props to
components.
Our TypeScript signatures will then become:
In practice, our function will now look like this:
Note that unlike params (that the SSG must have to know what to generate), props
are completely optional: if we preferred, we could simply load the post data
again in the page itself by using the value in the postSlug URL parameter.
“Props” is simply a “since we’re here, we might as well” convenience.
In Astro, getStaticPaths is often used in tandem with content collections. There’s
nothing particularly special about using getStaticPaths this way, but let’s go through
how such code typically looks bit by bit, and make sure that what’s happening is clear.
Once again, our goal is to let the SSG know about:
The value of postSlug for each one of our posts
The properties that change page by page (we’ll assume this is
every property of entry.data for our collection, plus the function to render
the content)
Calling this getStaticPaths yields a similar result to the above version: an array with
one entry for each entry in your collection, with the value of the postSlug URL
parameter, and props that contain the dynamic data for each page.
If you want to directly return the rendered component (rather than the
function to render it), you will run into a problem: the render function is
an async function (that is returns a Promise), which means the function you
call to map every element of the array will also need to be async (and thus
return a Promise).
In TypeScript terms:
If we want to pre-render the content, we can fix this in a somewhat-simple way: by waiting for
all the promises in the array to be resolved, and thus turning DataForEachPage back
to a simple array of objects. This is done using Promise.all(array), which takes an
array of Promise and waits for them all to be resolved.
Now we’re back to the previous type signature, and all’s right with the world
Astro.
We can then use the rendered component in our .astro files by doing:
Astro has its own way to get these values in your page, using the special Astro object
that is available in every .astro file. Simply access the params and props elements
in this object to get your values.
Here’s the final typescript types of the whole thing, which (if you’ve surrendered
to the power of our Lord TypeScript) will help you remember how the concepts are related:
…and that is all! Go forth and use the power of getStaticPaths and let me know
if anything is unclear.
Liked this? Help me change the face of web development!
Changing the web means changing who builds it! To this end, I create
accessible education targeted at growing hobbyists webdevs from isolated
beginners to open-source collaborators and maintainers. Learn how to
support this and more work on my Support Me page!