Author: Michael Hungbo
Introduction
Next.js is an open-source React framework for building production and enterprise-ready applications. It includes many built-in features such as Server-side Rendering (SSR), Static Site Generation (SSG), automatic image optimization, backend functionality support using API Routes and so on, that make developing web applications a breeze.
One of the many interesting features in Next.js are its data fetching methods. Next.js offers different ways of fetching and rendering data in your applications depending on your use case.
At times, it may be confusing as to which of the methods or functions to use for data fetching if you're new to Next.js.
Next.js has two functions for server-side rendering (pre-rendering or pre-population of data/contents on the server) namely: getInitialProps
and getServerSideProps
. As we go on, we'll explore both functions, understand how they work and learn when it's appropriate to use them in our applications.
What is getInitialProps
?
getInitialProps
is an asynchronous function used for fetching data on the server and pre-rendering the resulting data (aka server-side rendering) in Next.js page components.
getInitialProps
behavior can be tricky for developers who are new to Next.js. The function runs only on the server at the initial page load. However, if you make client-side navigation to other parts of your application through the <Link>
component or next/router
and come back to the page using getInitialProps
, the function will then run in the browser this time.
Any data you are fetching in getInitialProps
is returned as an object and is used by Next.js to populate the props
parameter in the default export in a page component.
The example below shows how you can use getInitialProps
in a page component:
//pages/example.tsx
// Get the data returned from getInitialProps as props
function Page({ data }) {
// Render data...
}
Page.getInitialProps = async (context) => {
const res = await fetch("https://api.com");
const data = await res.json();
return {data} // this will be passed to the page component as props
}
export default Page
Context parameter
getInitialProps
accepts a single and optional parameter named context
. The context
parameter is an object containing the following keys:
req
: An instance of HTTP request object (this object is only available whengetInitialProps
runs on the server)res
: An instance of HTTP response object (this object is only available whengetInitialProps
runs on the server)pathname
: Returns the current route. For example, if you have a filepages/example.js
,pathname
will return/example
query
: Query string parsed as an object if the URL contains query parametersaspath
: Returns a string of the actual path (including the query) as shown in the browser.err
: Contains an error object if any error is encountered during the rendering
Visit the Next.js documentations here to learn more about getInitialProps
.
What is getServerSideProps
?
getServerSideProps
is an asynchronous function used in Next.js pages component for server-side rendering and works almost the same as getInitialProps
. It is a newer alternative to getInitialProps
introduced in Next.js 9.3.
getServerSideProps
runs at request time and is guaranteed to always run on the server and never in the browser. So unlike getInitialProps
, even if you refresh the page or perform client-side navigation using <Link>
or next/router
, getServerSideProps
will always run on the server.
// pages/example.js
// Get the data returned from getServerSideProps as props
function Page({ message }) {
return (
<>
<h1>Example page</h1>
// Render data
<h2>{message}</h2>
</>
)
}
export async function getServerSideProps() {
return {
props: {
message: 'Hello world!'
}
}
}
export default Page
Context parameter
Like getInitialProps
, getServerSideProps
accepts a single and optional context
parameter. The context
object contains the following keys:
req
: An instance of HTTP request objectres
: An instance of HTTP response objectparams
: Contains the route parameter if the page is a dynamic page. For example, a dynamic filepages/[id.js]
will have aparams
object that looks like{ id: ... }
query
: An object that contains the query string (if the page utilizes one), including dynamic route parameters.preview
: Returns a Boolean value. Returns true if the page utilizes Preview Mode and false otherwise.previewData
: The preview data set by setPreviewData if the page utilizes Preview ModeresolvedUrl
: A resolved version of the URL that removes the _next/data prefix for client transitionslocale
: Contains the active internationalized locale (if enabled)locales
: Contains all supported locales information (if enabled)defaultLocale
: Contains the configured default locale (if enabled)
Return value
getServerSideProps
must return an object with any of the following properties:
props
: An object key-value pair passed to the page component. It should be a serializable object that can be serialized withJSON.stringify
.notFound
: A boolean object which allows the page to return a 404 status and 404 Page. IfnotFound
is set to true, the page will return a404
error even if the page was previously generated successfully.redirect
: An object with the following structure{ destination: string, permanent: boolean }
. It allows redirecting to internal and external resources such as redirecting a user to a login page after a successful sign-up.
You can visit the Next.js documentations here to learn more about getServerSideProps
.
Automatic caching capabilities
Next.js provides getServerSideProps
(server-side Rendering) with caching capabilities that help improve server response times and reduces the number of requests to external services.
Next.js will automatically add caching headers to static assets served from /_next/static
including JavaScript, CSS, static images, and other immutable media.
Here's how you can set a custom header in getServerSideProps
using the response object:
// pages/example.js
function Page({}) {
return (
<>
// ...
</>
)
}
export async function getServerSideProps({ req, res }) {
res.setHeader(
'Cache-Control',
'public, s-maxage=10, stale-while-revalidate=59'
)
return {
props: {},
}
}
export default Page
Note that caching doesn't work when your application is running in development mode (next dev
).
You can learn more about configuration options for the HTTP header field Cache-Control
on MDN.
Does your CRUD app need server state management?
Meet the headless, React-based solution to build sleek CRUD applications. With refine, you can be confident that your codebase will always stay clean and boilerplate-free.
Try refine to rapidly build your next CRUD project, whether it's an admin panel, dashboard, internal tool or storefront.
getInitialProps
vs getServerSideProps
When you are doing server-side rendering in your applications, it's important to know which of the functions to use in your applications since the two functions work almost similarly, and are both included in the Next.js docs.
We'll go through some examples to see how both functions work in real-world applications.
getInitialProps
The GIF below shows how getInitialProps
behaves when it's used to fetch data for server-side rendering.
Here's the code for the example above:
// pages/example.js
import Link from 'next/link'
function Page({ data }) {
return (
<>
<h1>Example page using <code>getInitialProps</code></h1>
<h1 style={{textDecoration: 'underline'}}><Link href="/">Go home</Link></h1>
<br></br>
{
data.map((data, i) => {
return <h2 key={i}>{data.username}</h2>
})
}
</>
)
}
Page.getInitialProps = async () => {
const res = await fetch ('https://jsonplaceholder.typicode.com/users')
const data = await res.json();
// The next line will log the first item in the API data to the console in the server
// during the initial first page load. After making a client-side navigation to the home page
// or any other part of the application, `getInitialProps` will then run in the browser.
// This will make the first item in the data returned from the API to be logged to the browser console
// instead of the server.
console.log(data[0]);
return { data }
}
export default Page
In the code above, we're fetching a list of users from the JSONPlaceholder API using getInitialProps
, and then passing the data as props to the page component for rendering.
From the GIF, when the localhost:3000/example
page is first loaded, getInitialProps
runs only on the server so the line console.log(data[0])
is logged to the server, and nothing is logged to the browser console.
However, when we make client-side navigation to the home page localhost:3000
using the <Link>
component, and return to localhost:3000/example
, getInitialProps
then runs only in the browser and the line console.log(data[0])
logs the first item in the data returned from the API to the browser console.
getServerSideProps
The GIF below shows the same example from the previous section using getServerSideProps
to fetch a list of users from the JSONPlaceholder API for server-side rendering.
Here's the code for the example above:
// pages/example.js
import Link from 'next/link'
function Page({ data }) {
return (
<>
<h1>Example page using <code>getServerSideProps</code></h1>
<h1 style={{textDecoration: 'underline'}}><Link href="/">Go home</Link></h1>
<br></br>
{
data.map((data, i) => {
return <h2 key={i}>{data.username}</h2>
})
}
</>
)
}
export async function getServerSideProps() {
const res = await fetch ('https://jsonplaceholder.typicode.com/users')
const data = await res.json();
// The next line will only be logged on the server and never on the browser console even if we make
// client-side navigation.
// This confirms that `getServerSideProps` is guaranteed to run on the server and never on the client (or browser).
console.log(data[0]);
return {
props: {
data
}
}
}
export default Page
From the GIF, you can see that even though we made client-side navigation, and refresh the page, getServerSideProps
was never run in the browser.
In addition, writing server-side specific code such as using the fs
module and so on will work perfectly in getServerSideProps
. However, you'll run into errors if you try the same thing in getInitialProps
.
As you have learned from the previous sections and examples, getInitialProps
and getServerSideProps
are both used in fetching data for server-side rendering in Next.js.
Both functions share similarities as well as differences. One similar property is that both cannot be used in children components, but only in page components. The major and significant differences between the two functions is where they run and their behavior when making client-side navigation.
getInitialProps
will run on the server during the initial page load and, subsequently, run in the browser if you make client-side transition to other parts of the application. However, getServerSideProps
will only run on the server and never in the browser (even if you make client-side navigation, or refresh the page).
Using getInitialProps
for data fetching and SSR in Next.js is outdated and deprecated because its behavior can be confusing for beginners and could also lead to unintentional bugs and errors if used the wrong way.
Conclusion
In summary, the Next.js developers recommend using getServerSideProps
over getInitialProps
since the former has extra features such as automatic caching and handles all use cases for server-side rendering.
In this article, we covered in-depth, the two Next.js data fetching methods for server-side rendering (getInitialProps
and getServerSideProps
). You also learned about how each function works and their behavior.
And now, we hope this article will help you make the right choice to avoid unintentional errors and bugs when you do SSR in your Next.js applications.