getStaticProps
should only be used for pages that do not require
authentication. The statically generated pages will be publicly available
via your server and via a CDN if you have one. Therefore they cannot
contain any personal or sensitive data.
Also, this is why there is no req
and res
objects for
getStaticProps
, because the pages are generated at build time, not at
runtime. And at build time, there is no user http request to handle.
If you export an async
function called getStaticProps
from a
page, Blitz will pre-render this page at build time using the
props returned by getStaticProps
.
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
The context
parameter is an object containing the following keys:
params
contains the route parameters for pages using dynamic routes.
For example, if the page name is [id].js
, then params
will look
like { id: ... }
. To learn more, take a look at the
Dynamic Routing documentation.
You should use this together with getStaticPaths
, which we’ll explain
later.
preview
is true
if the page is in the preview mode and undefined
otherwise. See the Preview Mode documentation.
previewData
contains the preview data set by setPreviewData
. See the
Preview Mode documentation.
locale
contains the active locale (if enabled).
locales
contains all supported locales (if enabled).
defaultLocale
contains the configured default locale (if enabled).
getStaticProps
should return an object with:
props
- A required object with the props that will be received by
the page component. It should be a
serializable object
revalidate
- An optional amount in seconds after which a page
re-generation can occur. Defaults to false
. When revalidate
is
false
it means that there is no revalidation, so the page will be
cached as built until your next build. More on
Incremental Static Regeneration
notFound
- An optional boolean value to allow the page to return a
404 status and page. Below is an example of how it works:
export async function getStaticProps(context) {
const res = await fetch(`https://.../data`)
const data = await res.json()
if (!data) {
return {
notFound: true,
}
}
return {
props: {}, // will be passed to the page component as props
}
}
Note:
notFound
is not needed forfallback: false
mode as only paths returned fromgetStaticPaths
will be pre-rendered.
redirect
- An optional redirect value to allow redirecting to
internal and external resources. It should match the shape of
{ destination: string | RouteUrlObject, permanent: boolean }
. In some
rare cases, you might need to assign a custom status code for older HTTP
Clients to properly redirect. In these cases, you can use the
statusCode
property instead of the permanent
property, but not both.
Below is an example of how it works:
export async function getStaticProps(context) {
const res = await fetch(`https://...`)
const data = await res.json()
if (!data) {
return {
redirect: {
destination: "/", // or using Blitz routes: Routes.HomePage()
permanent: false,
},
}
}
return {
props: {}, // will be passed to the page component as props
}
}
Note: Redirecting at build-time is currently not allowed and if the redirects are known at build-time they should be added in
blitz.config.js
.
Note: You can import modules in top-level scope for use in
getStaticProps
. Imports used ingetStaticProps
will not be bundled for the client-side.This means you can write server-side code directly in
getStaticProps
. This includes reading from the filesystem or a database.
Note: You should not use
fetch()
to call an API route ingetStaticProps
. Instead, directly import the logic used inside your API route. You may need to slightly refactor your code for this approach.Fetching from an external API is fine!
Example: Your blog page needs to load it's data from your database or from a CMS (content management system).
function Blog({posts}) {
// Render posts...
}
// This function gets called during pre-rendering
export async function getStaticProps() {
// 1. Use a Blitz query
// 2. Directly access your database
// 3. Or call an external API endpoint to get posts
const posts = /* ... */
// By returning { props: posts }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
export default Blog
getStaticProps
?You should use getStaticProps
if:
getStaticProps
generates HTML and JSON files, both of which can be
cached by a CDN for performance.GetStaticProps
For TypeScript, you can use the GetStaticProps
type from blitz
:
import { GetStaticProps } from "blitz"
export const getStaticProps: GetStaticProps = async (context) => {
// ...
}
If you want to get inferred typings for your props, you can use
InferGetStaticPropsType<typeof getStaticProps>
, like this:
import { InferGetStaticPropsType } from "blitz"
type Post = {
author: string
content: string
}
export const getStaticProps = async () => {
const res = await fetch("https://.../posts")
const posts: Post[] = await res.json()
return {
props: {
posts,
},
}
}
function Blog({ posts }: InferGetStaticPropsType<typeof getStaticProps>) {
// will resolve posts to type Post[]
}
export default Blog
With getStaticProps
you don't have to stop relying on dynamic content,
as static content can also be dynamic. Incremental Static Regeneration
allows you to update existing pages by re-rendering them in the
background as traffic comes in.
Inspired by stale-while-revalidate, background regeneration ensures traffic is served uninterruptedly, always from static storage, and the newly built page is pushed only after it's done generating.
Consider our previous getStaticProps
example, but now
with regeneration enabled:
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
// 1. Use a Blitz query
// 2. Directly access your database
// 3. Or call an external API endpoint to get posts
const res = await fetch("https://.../posts")
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every second
revalidate: 1, // In seconds
}
}
export default Blog
Now the list of blog posts will be revalidated once per second; if you add a new blog post it will be available almost immediately, without having to re-build your app or make a new deployment.
This works perfectly with
fallback: true
. Because now you
can have a list of posts that's always up to date with the latest posts,
and have a blog post page that
generates blog posts on-demand, no matter how many posts you add or
update.
Unlike traditional SSR, Incremental Static Regeneration ensures you retain the benefits of static:
process.cwd()
Files can be read directly from the filesystem in getStaticProps
.
In order to do so you have to get the full path to a file.
Since Blitz compiles your code into a separate directory you can't use
__dirname
as the path it will return will be different from the pages
directory.
Instead you can use process.cwd()
which gives you the directory where
Blitz is being executed.
import fs from "fs"
import path from "path"
// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>
<h3>{post.filename}</h3>
<p>{post.content}</p>
</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries. See the "Technical details" section.
export async function getStaticProps() {
const postsDirectory = path.join(process.cwd(), "posts")
const filenames = fs.readdirSync(postsDirectory)
const posts = filenames.map((filename) => {
const filePath = path.join(postsDirectory, filename)
const fileContents = fs.readFileSync(filePath, "utf8")
// Generally you would parse/transform the contents
// For example you can transform markdown to HTML here
return {
filename,
content: fileContents,
}
})
// By returning { props: posts }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
export default Blog
Because getStaticProps
runs at build time, it does not receive data
that’s only available during request time, such as query parameters or
HTTP headers as it generates static HTML.
Note that getStaticProps
runs only on the server-side. It will never be
run on the client-side. It won’t even be included in the JS bundle for the
browser. That means you can write code such as direct database queries
without them being sent to browsers. You should not fetch an API route
from getStaticProps
— instead, you can write the server-side code
directly in getStaticProps
.
You can use this tool to verify what Blitz eliminates from the client-side bundle.
When a page with getStaticProps
is pre-rendered at build time, in
addition to the page HTML file, Blitz generates a JSON file holding the
result of running getStaticProps
.
This JSON file will be used in client-side routing through <Link>
or the
Blitz router
. When you navigate to a page that’s pre-rendered using
getStaticProps
, Blitz fetches this JSON file (pre-computed at build
time) and uses it as the props for the page component. This means that
client-side page transitions will not call getStaticProps
as only
the exported JSON is used.
getStaticProps
can only be exported from a page. You can’t
export it from non-page files.
One of the reasons for this restriction is that React needs to have all the required data before the page is rendered.
Also, you must use export async function getStaticProps() {}
— it will
not work if you add getStaticProps
as a property of the page
component.
In development (blitz dev
), getStaticProps
will be called on every
request.
Even though a page with getStaticProps
will be pre-rendered on the
server, it may fallback to the page's loading state if your page contains
queries that request data missing from the initial page props. This can
create a flickering effect where the contents of the page rapidly changes
when the query data gets loaded into the app.
Click here to discover ways you can improve the first render User Experience (UX) of your pages.
In some cases, you might want to temporarily bypass Static Generation and render the page at request time instead of build time. For example, you might be using a headless CMS and want to preview drafts before they're published.
This use case is supported by Blitz by the feature called Preview Mode. Learn more on the Preview Mode documentation.