#Pre Rendering
Next.js is a hybrid framework on top of React, which enables static and server-side rendering. It is packed with awesome features like file system routing, dynamic routing, image optimization, typescript support, code-splitting & bundling, etc., which are useful for any project. All in all, it is an impressive production framework for React.
When a page is loaded by the browser (client), its JavaScript code runs, making the page fully interactive. In addition to client-side rendering, Next.js offers pre-rendering for your views, which means that the HTML is generated in advance by Next.js and it need not rely on the client-side Javascript.
Pre-rendering can happen in two ways
- Server-side
- Static Generation
Server Side Rendering
When we cannot pre-render a page’s view ahead of a user's request, i.e in cases where we need to check the request and respond accordingly with a dynamically up-to-date page, we can either use server-side rendering or client-side rendering, as the data on the page depends on the request.
In server-side rendering, the request is received from the client, the server then generates the HTML and sends it back to the client. So here, the server generates the HTML for each request.
Static Generation
When the data on a page is static, i.e. it doesn’t matter who is requesting, the response will always be the same. For instance, for a blog post, it doesn’t matter who requests it, it would always deliver the same content.
Since we have to deliver the same page, we can pre-render it during build time i.e statically generate it. This will result in faster responses than client-side or server-side rendering as the HTML page is generated at the build time itself and not run time.
In the case of a blog application, our static frontend pages in a Next.js App can be structured as below
/pages/post/post1.js
/pages/post/post2.js
/pages/post/post3.js
When we run the next build
command, these pages will be statically generated once and the same copy can be quickly served to the users every time.
Now let’s say that we want to add 10 more blogs, one option to do so is to create additional static react components for each and every blog like post4.jsx, post5.jsx, post6.jsx,
and so on, and then build and deploy it. One caveat with this approach is that it will probably duplicate the same code over and over, and create additional components. This is not a neat scalable solution, imagine the code directory of a blog site with thousands of blogs built with this approach.
We need a way to dynamically generate any number of routes and we need to do that at build time in order to serve statically pre-rendered pages. Next.js supports dynamic routing which helps us to achieve this kind of use case. Let us try to learn more about dynamic routing with the help of an example.
#Example
Use Case
We want to build a simple frontend blog application, it should use Static Site Generation in a way that it will dynamically create any number of routes (example: post/1, post/post2, post/3, ... post/n). The number of routes can vary and can come from an external data source (eg: a database), The pages should be statically generated at build time, so we do not have to server side render them on run time. We assume that you are comfortable with the basics of Next.js and React and that you have a Next.js app up and running.
If you are not familiar with Next.js, we recommend you to go through some articles below and get started.
Mocking API Data
Let us create two simple functions that will provide data to our application. For demo purposes we have mocked data in this frontend itself. In a real-world application, you would want to replace this dummy data with actual API calls to get data from your data source.
First, add a function getPostIdList()
that will return the list of posts in the format below.
lib/posts.js
export async function getPostIdList() {return [{params: {id: '1'}}, {params: {id: '2'}}, {params: {id: '3'}}]}
Two important things to note here are:
- The response array should have the same format as shown above, this will help next.js to generate paths for our routes dynamically. We used
id
as our inner key since we will be creating our dynamic route component ahead aspages/post/[id].js
.If our dynamic route component would have been/pages/post/[postId].js
, we would usepostId
as the key here. - Next.js requires the value corresponding to the
id
key to be always a string.
Now, let's add another function getPostDetails()
that will return some dummy data for each post.
lib/posts.js
export async function getPostDetails(postId) {const dataSet = {'1': {title: 'Post 1',description: 'Lorem ipsum dolor sit amet...',date: 'Oct 10, 2022'},'2': {title: 'Post 2',description: 'Lorem ipsum dolor sit amet...',date: 'Oct 20, 2022'},'3': {title: 'Post 3',description: 'Lorem ipsum dolor sit amet...',date: 'Oct 30, 2022'}}return dataSet[postId]}
Adding a Dynamic Route Component
We want to display our blog on routes like /post/1, /post/2, etc. In Next.js, we can create a dynamic segment by wrapping file or folder name in square brackets like this [segmentName]
. For our use case let us create a dynamic route component at pages/post/[id].js
, any request in the browser like /post/1, /post/2, /post/a will match to this page.
Since each of our posts will have 3 fields - title, description, and date, we can render this simple template.
pages/post/[id].js
export default function Post({ postData }) {return (<div className='bg-gray-800 h-screen p-16 text-gray-100'><div className='text-center font-bold text-3xl'>{postData.title}</div><div className='text-justify my-8 text-gray-200'>{postData.description}</div><div className="text-gray-400">Published On: {postData.date}</div></div>);}
getStaticPaths and getStaticProps
We have our data ahead of users request and we need to pre-render our component at build time. Our component expects a postData
prop, we just need a way to provide this prop to our component at build time.
getStaticProps
method from Next.js allows us to do this. Let us add it to our component as shown below
export async function getStaticProps({ params }) {const postData = await getPostDetails(params.id);return {props: {postData,},};}
We expect getStaticProps function to get the route parameters as an input. It can then use that post id and our mocked API function getPostDetails
to get postData
. Then it returns the postData in the format shown above. That’s it, Next.js will ensure our component function will get the postData
prop.
Whenever we use dynamic routes and getStaticProps. We will need to also use getStaticPaths
method provided by Next.js. When we 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
. Also, in the above code getStaticProps
is expecting params
object which contains the post id that is needed to retrieve the post data. getStaticPaths
can also be used to provide the params object to getStaticProps
. Let us use it as shown below:
pages/post/[id].js
import { getPostDetails, getPostIdList } from '../../lib/posts';export async function getStaticPaths() {const paths = await getPostIdList();return {paths,fallback: false,};}
getStaticPaths()
gets the list of paths from our mocked API function getPostIdList()
and Next.js will generate the respective routes and also pass the context ahead to getStaticProps
.
In production, both getStaticProps
and getStaticPaths
will run only at the server at build time, it will not run on runtime.
Below is our final component
pages/post/[id].js
import { getPostDetails, getPostIdList } from '../../lib/posts';export async function getStaticPaths() {const paths = await getPostIdList();return {paths,fallback: false,};}export async function getStaticProps({ params }) {const postData = await getPostDetails(params.id);return {props: {postData,},};}export default function Post({ postData }) {return (<div className='bg-gray-800 h-screen p-16 text-gray-100'><div className='text-center font-bold text-3xl'>{postData.title}</div><div className='text-justify my-8 text-gray-200'>{postData.description}</div><div className="text-gray-400">Published On: {postData.date}</div></div>);}