Summary
Allow Next.js to become fully hybrid by providing methods to do both static generation and server-side rendering on a per-page basis.
- Two new per-page data fetching methods
getStaticProps
- Opt-in to static generation (SSG) at next build
time.
getServerSideProps
- Opt-in to server-side rendering (SSR) which renders on-demand.
- A new method for statically generating (SSG) a set of routes from dynamic sources
getStaticPaths
- Return list of parameters for dynamic routes to do static generation (SSG)
This RFC exclusively discusses API additions. All new functionality is completely backwards compatible and can be incrementally adopted. This RFC introduces no deprecations.
Background
When building websites or web applications you generally have to choose between 2 strategies: Static generation (SSG) or server-side rendering (SSR).
Next.js instead lets you build hybrid applications that allow you to choose per-page which strategy is used. Starting with Next.js 9, pages without getInitialProps
get statically optimized and output as .html
files upon next build
.
However, you might want to do data fetching while generating static pages for your specific use case.
For example, to statically generate marketing pages from a CMS or a blog section of the site.
Using getInitialProps
would opt you into SSR in that case.
Next.js currently has a next export
command, that makes the application fully SSG, losing the hybrid nature of Next.js.
If you use next export
with getInitialProps
there is another problem that surfaces. getInitialProps
is called at build time (which is great), but then when you use next/link
to move between pages getInitialProps
is called client-side, instead of using the next export
result.
This also means the data source (CMS / API endpoint) is called directly on client-side transitions, if your data source is down, client-side transitions break while moving between pages.
We've collaborated with heavy users of SSG and next export
in Next.js like HashiCorp (thanks @jescalan) and extensively investigated the right constraints for introducing two new data fetching methods: getStaticProps
and getServerSideProps
. But also a way to provide parameters to statically generate static pages for dynamic routes: getStaticPaths
(replacement for exportPathMap
that is per-page).
These new methods have many advantages over the getInitialProps
model as there is a clear distinction between what will become SSG vs SSR.
getStaticProps
marks the page to be statically generated at build time (when running next build
)
getStaticPaths
allows for returning a list of parameters to generate at build time for dynamic routes
getServerSideProps
marks the page to be server-side rendered on every request and is the most similar to the current getInitialProps
behavior when using a server.
Separating these methods also allows us to provide the correct context object that can be typed using TypeScript. When you opt for a specific rendering strategy you get the correct values, currently with getInitialProps
you have to guess what is available on SSG vs SSR when using TypeScript.
Furthermore, by making these methods explicit, it'll allow us to document the different trade-offs more clearly.
Implementation
Note that all these methods are top-level on the page component file and can't be nested, similar to getInitialProps
.
getStaticProps
Using getStaticProps
means the page will be rendered statically at build time (SSG).
This new method will allow you to do data fetching for a page that will be statically generated into a .html
file at next build
time.
Next.js will also automatically generate a JSON file that holds the result of getStaticProps
at next build
time. This is being used for client-side routing.
When client-side routing through next/link
or next/router
, Next.js will fetch this JSON file to get the props needed to render the page client-side.
Properties are returned under a props
key so that other options can be introduced in the future.
// pages/index.js
// getStaticProps is only called server-side
// In theory you could do direct database queries
export async function getStaticProps(context) {
return {
// Unlike `getInitialProps` the props are returned under a props key
// The reasoning behind this is that there's potentially more options
// that will be introduced in the future.
// For example to allow you to further control behavior per-page.
props: {}
};
}
The context
will contain:
params
- The parameters when on a dynamic route.
getStaticPaths
This is an extension on getStaticProps
usage for dynamic routes.
getStaticPaths
replaces the need for having a exportPathMap
and works per-page.
Since you might want to statically generate a list of urls that have a dynamic parameter, like in the example below a slug
. Next.js will provide a getStaticPaths
method that allows for returning a list of urls. Since it's a async
method you can also fetch that list from a data source like your CMS.
// pages/blog/[slug].js
// `getStaticProps` gets a `params` object holding the dynamic parameters
// For `/blog/hello-world` it would look like `{ slug: 'hello-world }`
export async function getStaticProps({ params }) {
return {
props: {}
};
}
// `getStaticPaths` allows the user to return a list of parameters to
// render to HTML at build time.
export async function getStaticPaths() {
return {
paths: [
// This renders /blog/hello-world to HTML at build time
{ params: { slug: "hello-world" } }
]
};
}
Fallback
In many cases you might not want to pre-render every possible route in your application at build-time (for example if you have millions of products). For this reason Next.js will automatically generate a fallback
page that is a render of the page without data (so that a loading state can be shown) for when the page hasn’t been generated yet.
The exact behavior of serving will be:
- Incoming request
- Next.js checks if the path was generated at build time
- If the path was generated
- If the path was not generated
- Serve the fallback
- Next.js renders the page (with data) in the background and adds it to the list of generated pages
- Subsequent request to the same path will serve the generated page
- This ensures that users always have a fast experience and never have slow TTFB from server-rendering while preserving fast builds and static-generation properties
In case you want paths that weren’t generated at build time to result in a 404 that is also possible by returning fallback: false
from getStaticPaths
// `getStaticPaths` allows the user to return a list of parameters to
// render to HTML at build time.
export async function getStaticPaths() {
return {
// Opt-out of the described fallback behavior
fallback: false,
paths: [
// This renders /blog/hello-world to HTML at build time
{ params: { slug: "hello-world" } }
]
};
}
getServerSideProps
When using getServerSideProps
, the page is not statically generated (SSG) and instead renders on-demand on every request to the server (SSR).
Next.js will also automatically expose an API endpoint that returns the result of calling getServerSideProps
. This is being used for client-side routing.
When client-side routing through next/link
or next/router
, Next.js will fetch this exposed API endpoint to get the JSON data that is turned into the props needed to render the page client-side.
This method is the most similar to the current getInitialProps
, with the main difference being getServerSideProps
is always executed server-side instead of in the browser. Either on server-side rendering or the API fetch when client-side routing.
Similarly to getStaticProps
the properties are returned under a props
key.
// pages/index.js
// getServerSideProps is only called server-side
// In theory you could do direct database queries
export async function getServerSideProps(context) {
return {
// Unlike `getInitialProps` the props are returned under a props key
// The reasoning behind this is that there's potentially more options
// that will be introduced in the future.
// For example to allow you to further control behavior per-page.
props: {}
};
}
The context
will contain:
params
- The parameters on a dynamic route
req
- The HTTP request object
res
- The HTTP response object
query
- The query string (not entirely sure about this one, but probably needed)
Authored by @timneutkens, @Timer, @ijjk, @lfades. Collaborated with @rauchg, @jescalan and others 🚀