400. Pre-rendering
What and Why Pre-rendering
What is Pre-rendering
- By default, Next.js pre-renders every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript.
- Pre-rendering can result in better performance and SEO.
Why Pre-rendering
- better performance
- SEO benefit
Types of Rendering
- Next.js has two forms of pre-rendering: Static Generation and Server-side Rendering. The difference is in when it generates the HTML for a page.
- Static site generation(SSG) =
getStaticProps
= data fetched at build time- Incremental static site regeneration(ISR)
- Dynamic static site generation =
getStaticPaths
- Server side rendering =
getServerSideProps
= data fetched on each request
- Static site generation(SSG) =
- Client side data fetching can be added on both type of rendering
Static Site Generation
- Next.js get-static-props
- The HTML with all the data that makes up the content of the web page are generated in advance when you build your application.
- ==The default pre-rendering method==
- The recommended method to pre-render pages whenever possible.
- Can be built once and served cache version from CDN. resulting faster delivery time.
- variation of SSG
- without data
- with data
- incremental static regeneration
- dynamic parameters when fetching data
//static generation without data
function Home(){
return <h1>about us page</h1>
}
export default Home;
//static generation with data
function Home({ users }) {
return (
<div>
{users.map((user) => {
return (
<>
<p>
{user.firstName} {user.lastName}{" "}
</p>
</>
);
})}
</div>
);
}
export default Home;
export async function getStaticProps() {
const response = await fetch("https://dummyjson.com/users");
const data = await response.json();
return {
props: {
users: data.users,
},
};
}
- getStaticProps
- can write any back-end logic here,
- ex: accessing file system, DB
- code her wont't be shipped to browser, only back-end
- on runs on the back-end
- only can be used in pages,
- not in components, client-side or anywhere else.
- must return an object with props key
- will run only once during build time: production
- except on development time. it will run on every request
- can write any back-end logic here,
getStaticProps does not have access to the incoming request (such as query parameters or HTTP headers) as it generates static HTML.
SSG and Link Component
- Any
Link
component in the view-port(initially or on scroll) will be prefetched by default including the corresponding data for pages using static site generation. - When a page with
getStaticProps
is pre-rendered at build time, nextjs renders.- HTML page,
- JSON (holding the result of
getStaticProps
) - and the required JS chunk.
- The JSON file will be used in client-side routing through next/link or next/router.
- When you navigate to a page that's pre-rendered using
getStaticProps
, Next.js fetches- The JSON file which is pre-computed at build time
- This JSON is used as a prop to create the page component client-side.
- The JSON file which is pre-computed at build time
SSG + getStaticPaths
- If a page has Dynamic Routes and uses getStaticProps, it needs to define a list of paths to be statically generated.
- When you export a function called getStaticPaths (Static Site Generation) from a page that uses dynamic routes, Next.js will statically pre-render all the paths specified by getStaticPaths.
function UserDetail({user}) {
//...component ui
}
export default UserDetail;
// generate pre-render for all ids
export async function getStaticPaths() {
const response = await fetch(`https://dummyjson.com/users`);
const data = await response.json();
const paths = data.users.map((user) => ({
params: { userid: `${user.id}` },
}))
return {
paths: paths,
// paths: [{params: {userid: '1'}}, {params: {userid: '2'}}],
fallback: false, // can also be true or 'blocking'
}
}
// generate pre-render for certain ids
export async function getStaticPaths() {
return {
paths: [
{params: {userid: '1'}},
{params: {userid: '2'}},
{params: {userid: '3'}},
],
fallback: false, // can also be true or 'blocking'
}
}
export async function getStaticProps(context) {
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();
return {
props: {
user: data,
},
};
}
Fallback Key
- fallback key is a required value with getstaticPath
- can have the following possible values
- false
- any paths not returned by getStaticPaths, will result in 404 page.
- when should you use this?
- if you have a small number of pages pre-render with getStaticPaths
- when new pages are not added often
- ex: a blog site with a few articles
- true
- instead of return a 404 page, next.js will serve a "fallback" version of the page on the first request to such path.
- instead in the background, Next.js will statically generate the request path HTML and JSON. This includes running
getStaticProps
. - subsequent request to the same path will be served from the generated pages, just like other pages pre-rendered at build time.
- instead in the background, Next.js will statically generate the request path HTML and JSON. This includes running
- When should you use this?
- if the app has a very large number of static pages that depend on data
- if you want to statically generate a small subset of pages that are popular and use
fallback:true
for the rest,- think: e-commerce product listing
- instead of return a 404 page, next.js will serve a "fallback" version of the page on the first request to such path.
blocking
- similar to
fallback: true
, except, nextjs will not serve a "fallback" version of the page on the first request to such path.- instead next.js will render that page on the server and return the generated HTML.
- the browser waits for nextjs to respond and once the HTML and JSON are received it will load the page, with no flash or loading state.
- When to use this?
- basically the same reason as
fallback: true
, except when usingblocking
you wont see a loading screen - to support crawler, so instead of showing a loading page while pre-rendering, we would wait and show the actual page
- basically the same reason as
- Recommendation
- Nextjs recommends using
fallback: true
- use
fallback: true
is you care more for user's UX, and don't have an SEO issue - use
fallback: 'blocking'
, is you are having an SEO issue
- Nextjs recommends using
- similar to
- false
//fallback true
import {useRouter} from "next/router";
function UserDetail({user}) {
const router = useRouter();
if(router.isFallback){
return "Loading..."; //will be visible when next.js is busy pre-rendering for not-found ids
}
return <h1>{user.firstName} - {user.lastName} - {user.age}</h1>
}
export default UserDetail;
// generate pre-render for certain ids
export async function getStaticPaths() {
return {
paths: [
{params: {userid: '1'}},
{params: {userid: '2'}},
{params: {userid: '3'}},
],
fallback: true,
}
}
export async function getStaticProps(context) {
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();
return {
props: {
user: data,
},
};
}
What if Key is Not Found
- To handle 404 pages, return not found from
getStaticProps
- nextjs by default will return 404 for you
export async function getStaticProps(context) {
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();
//is the result is null or undefined
if(!data.id){
return {
notFound: True,
}
}
return {
props: {
user: data,
},
};
}
Pros and Cons of SSG
- pros
- pre-rendered pages can be pushed to CDN, cached and served to users globally instantly.
- better performance with SEO, as they are fast and HTML pages
- cons
- the build time is dependent on the number of pages you have, for large pages it will take significant time,
- ex:, you have a million users, and are using SSG for profile page
- data might expire, if you didn't build the app recently stale data
- the build time is dependent on the number of pages you have, for large pages it will take significant time,
ISR(incremental Static Site regeneration)
- A solution to SSG cons.
- With ISR, Next.js allows you to update static pages after you've build your application
- you can statically generate individual pages without needing to rebuild the entire site, effectively solving the issue of dealing with stale data
- How to?
- in the
getStaticProps
function, apart from the props key, we can specify arevalidate
key - the value for re-validate is the number of seconds after which a page re-generation can occur
- in the
export async function getStaticProps(context) {
const {params} = context;
const response = await fetch(`https://dummyjson.com/users/${params.userid}`);
const data = await response.json();
//is the result is null or undefined
if(!data.id){
return {
notFound: True,
}
}
return {
props: {
user: data,
},
revalidate: 10, //10 seconds
};
}
- re-generation doesn't happen exactly after n seconds, but
- a re-generation is initiated only if a user makes a request after the re-validation time.
- thus pages that are not frequently access won't be re-generated.
- the re-generation can also fail and the previous cached HTML could be served till the subsequent re-generation succeed
Programmatic Revalidation
- You can programmatically invoke regeneration with next.js, for instance; if you have a webhook you can connect it to nextjs api that will invoke a regeneration
- this pattern is called
on-demand-ISR
export default function handler(req,res) {
//check if proper app initiated this
if(req.query.secret !== process.env.REVALIDATION_PASSWORD ){
return res.status(401).json({msg: 'Invalid token'})
}
try{
await res.unstable_revalidate("/blog"); //we're regernating about page
return res.json({revalidated: true})
}catch(err){
return res.status(500).send('Error revalidating')
}
}