Table of Contents
What is Server-Side Rendering (SSR )?
In this post, I will talk about one of the ways that Next.js pre-render web pages which is server-side rendering ( SSR ).
Server-side rendering is not a new approach to web page generation. In fact, it is the traditional way of creating dynamic web pages. If you are familiar with languages such as PHP, Java, or .Net frameworks, this is how it has been generating dynamic web pages. With SSR, HTML is generated at each request.
However, Next.js, as a static site generator does not recommend or apply this form of rendering by default. Next.js recommends the use of SSR only for your specific needs that SSG (Static Site Generation) fails to address.
Why should you use Server-Side Rendering in Next.js?
In my previous post, I was discussing using revalidate prop on getStaticProps. As you can see in that post, even with revalidate prop, the user might still fetch stale data from the cache. Stale data can be detrimental for sites relying on dynamic data such as news-feeding sites or social media sites. SSR allows these web applications to update their content as soon the data source is updated.
Another reason to use SSR is to bring user-specific data that is SEO friendly. Although you can request user-specific data with the client-side generation, these web pages generated at the client-side by Javascript ( for example as in React.js, by default), can not be indexed by Google search bots as these bots indexed only the rendered-HTML. Therefore, pre-rendered HTML pages with user-specific data with SSR are critical in bringing user-specific data that is also SEO friendly.
Server-Side Rendering with getServerSideProps function
Next.js provides a specific function that makes SSR possible in Next.js This function is getServerSideProps. getServerSideProps runs in the server with each request. It will not run in the build-time. Developers can add the server-side code within this function.
That means you can add code to access file systems, and database queries, or include API keys without being worried about compromising as it never reaches the browser.
Similar to getStaticProps, getServerSideProps also returns an object with ‘props’ as the key. ‘props’ itself represents another object in which you can include the data passing.
How to use getServerSideProps
We are going to create a simple web app to understand how the getServerSideProps works.
- First, you need to start and set up a fake REST API in your development environment. I have explained how to do this under the heading “Let’s build an application …” in my post: Ultimate guide to incremental static regeneration(ISR ) in next-js. Please follow the steps from 1 to 6.
- But, do not add any product data as it says in that post. Instead of that, please add the following JSON object to db.json.
{
"movie_category":[{ "id": 1, "category":"drama"} , { "id": 2, "category":"sci-fi" }],
"movies": [
{ "id": 1, "name": "Inceptions", "inStock": "10" , "category":"sci-fi" },
{ "id": 2, "name": "Beautiful mind", "inSstock": "20" , "category":"drama" },
{ "id": 3, "name": "Back to the future", "inStock": "5" , "category": "sci-fi" }
]
}
- After you set up the development environment, add the above JSON object. Then, run the server, and create a folder named “movies” in the pages folder.
- Inside the “movies” folder, create a file: index.js, and add the code below to it. The index.js will show the list of movies in db.json on the route localhost:3000/movies.
import Link from "next/link"
function MovieList({ movies }){
return(
<><h1>Movie Rental System</h1>
{
movies.map( movie => <div key={ movie.id }>
<Link href={ `movies/${movie.category}` }>
<div> { movie.id }.) { movie.name } || { movie.category }</div>
</Link>
</div>
)
}
</>
)
}
export default MovieList
export async function getServerSideProps() {
const response = await fetch('http://localhost:4000/movies')
const data = await response.json()
return {
props: {
movies: data,
},
}
}
- Now, inside the same folder( movies folder), create another file called [category].js and the following code. As you can see, this is a dynamic route. We are going to filter the movies by category.
export default function MovieListByCategory({ movies , category }){
return(
<>
<h2>Category: { category }</h2>
{ movies.map( movie => <li key={ movie.id }>
name : { movie.name } || amount in stock: { movie.inStock }
</li>) }
</>
)
}
export async function getServerSideProps( context ){
const { params } = context;
const { category } = params;
const response = await fetch(`http://localhost:4000/movies?category=${ category }`)
const data = await response.json()
return {
props : {
movies: data,
category,
},
}
}
- When a user clicks on a category on the index page, a list of names of movies belonging to the particular category shows on http://localhost:3000/movies/category. Here, the category in the URL is dynamic.
getServerSideProps with a context parameter
In [category].js , there is a getServerSideProps function with a context parameter. You can use any name you want. The term context is only a convention. A context is an object. This context is different from that of the getStaticProps function. This object has a lot of keys that have other objects as values. If you are familiar with backend programming in node.js, you will recognize some of the objects inside the context object. You can reference the context parameter of the getServerSideProps here.
Build and Run the app
In order to see the behavior of getServerSideProps, as it is in the production, you must build the application with npm run build.
Remember when you run the build command, you need to keep the server running in the background on loaclhost:4000.
After having built the application, you will see details of the page built on the terminal.
You will also see there is a .next folder generated in the root folder. If you expand this folder and go to server/pages/movies, you will notice there are no .html files in the folder. This is because getServerSideProps does not run at build time.
Now, you can run the application with npm start.
Experimenting with Data
You can change the data in db.json and refresh the browser. You will see immediately the data in the browser changes.
I will encourage you to do some experiments at this stage. You can open dev-tools of the browser, go to the “Network” tab, and observe how JSON files are generated when you click on links. Then, you can refresh same the page and observe a static HTML file generating in the cache.