Course
SSR & SSG
Next.js Mastery: From Fundamentals to Full-Stack
Unlock the power of Next.js with this comprehensive course! Starting with the basics, you’ll learn essential skills such as routing, data fetching, and styling. Progress through practical projects, including building your own React Notes app, to gain hands-on experience. Dive into the source code to understand the inner workings and core principles of Next.js. Perfect for developers with a basic knowledge of React and Node.js, this course ensures you’ll be ready to create high-performance full-stack applications efficiently. Join us and master Next.js, backed by industry experts and a supportive learning community.
Server-Side Rendering (SSR)
Concept
SSR stands for "Server-side Rendering". As the name suggests, the rendering work is mainly performed on the server side.
Let's consider a practical example: a blog post page. In many cases, it's inefficient for the client to make a request every time the page loads, especially if the client's network connection is slow. Instead, with SSR, the server takes on the responsibility of requesting the API, fetching the necessary data, and rendering it into a static HTML file. This pre-rendered HTML is then sent to the user.
While SSR does involve making requests, the server environment typically boasts superior network conditions and device performance compared to the average client. As a result, the final rendering speed, particularly the time it takes for the first screen to load, is generally faster.
However, it's important to note that while the overall speed is improved, the initial response time for SSR can be longer than that of Client-Side Rendering (CSR). This is because a CSR response only needs to return a minimal HTML structure, whereas an SSR response must request data from the API and render the full HTML. This difference is reflected in the performance metric known as Time To First Byte (TTFB), where SSR will typically have a longer TTFB than CSR.
Implementation in Next.js
Next.js supports SSR. In the Pages Router, you can implement SSR by exporting an async function named
getServerSideProps
. This function is called on every request, and the returned data is passed to the component as props.Example:
// pages/ssr.jsexport default function Page({ data }) { return <p>{JSON.stringify(data)}</p>}
export async function getServerSideProps() { const res = await fetch(`https://jsonplaceholder.typicode.com/todos`) const data = await res.json() return { props: { data } }}
To use SSR, you need to export an async function named
getServerSideProps
. This function will be called on every request. The returned data will be passed to the component through the props property.
When a request is made to this page, the server compiles the HTML file and returns it to the client. If you were to view the source of the returned HTML, you would see the data directly embedded:
<body> <div id="__next"> <p>{"uerlId ":1,"id ":1,"title ":"delectus aut autem " ,"completed ":false}</p> </div>...
Static Site Generation (SSG)
Concept
SSG stands for "Static Site Generation", where pages are pre-rendered into static HTML files during the build phase of an application.
Let's return to our blog post example. If the content of a blog post is the same for all users, there's no need for the server to request data from the API every time a user visits the page. Instead, it's more efficient to fetch this data in advance, compile it into HTML files during the build process, and serve these pre-generated HTML files directly when a user visits. This approach significantly improves loading speed. When combined with CDN caching, the performance improvements can be even more substantial.
Implementation in Next.js
Next.js has built-in support for SSG. In fact, when a page doesn't require data fetching, SSG is used by default. Here's a simple example using the Pages Router:
// pages/ssg1.jsfunction About() { return <div>About</div>}
export default About
For pages like this that don't require data fetching, Next.js will generate a separate HTML file during the build process.
By default, Next.js doesn't export these generated files. If you want to see the HTML files generated during the build, you need to modify the
next.config.js
file:const nextConfig = { output: 'export'}
module.exports = nextConfig
After making this change, when you run
npm run build
, you'll see a generated out
folder in the root directory containing the HTML files created during the build process.
SSG with Data Fetching
There are two scenarios to consider when implementing SSG with data fetching:
- Page content requires data fetching: Next.js provides
getStaticProps
for this scenario. Here's an example:
// pages/ssg2.jsexport default function Blog({ posts }) { return ( <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> )}
export async function getStaticProps() { const res = await fetch('https://jsonplaceholder.typicode.com/posts') const posts = await res.json() return { props: { posts }, }}
getStaticProps
is called during the build process and passes the fetched data to the page through the props
object. It's important to note the difference between getStaticProps
and getServerSideProps
: while they are similar in usage, getServerSideProps
is called on every request, whereas getStaticProps
is only called during each build.
2. Page path requires data fetching:
For scenarios where you need to generate multiple pages based on data (e.g., 100 articles in a database), Next.js provides
getStaticPaths
. This function is used in conjunction with dynamic routing. Here's an example:// /pages/post/[id].jsexport default function Blog({ post }) { return ( <> <header>{post.title}</header> <main>{post.body}</main> </> )}
export async function getStaticPaths() { const res = await fetch('https://jsonplaceholder.typicode.com/posts') const posts = await res.json() const paths = posts.map((post) => ({ params: { id: String(post.id) }, })) // { fallback: false } means other routes should 404 return { paths, fallback: false }}
export async function getStaticProps({ params }) { // If the route is /posts/1, params.id is 1 const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`) const post = await res.json() return { props: { post } }}
In this example, both
getStaticPaths
and getStaticProps
are called during the build process. getStaticPaths
defines which paths should be pre-rendered, while getStaticProps
uses the path parameters to request data and pass it to the page.
When you run npm run build
, you'll see multiple HTML files generated under the post
folder, one for each path defined by getStaticPaths
.
This approach allows you to generate static pages for dynamic content, combining the performance benefits of static generation with the flexibility of data-driven pages.