Rendering is the process of converting React code into HTML. The rendering method you choose depends on the data you are working with and how much you care about performance.

In Next.js, rendering is very versatile. You can render pages client-side or server-side, statically or incrementally.

Take a look at how these methods work and how each performs.

Server-Side Rendering

With server-side rendering (SSR), when a user visits a web page, the browser sends a request to the server for that page. The server fetches the necessary data from the database, if needed, and sends it together with the page's content to the browser. The browser then displays it to the user.

The browser makes this request for every link that the user clicks which means the server processes the request every time.

This can reduce the website's performance. However, server-side rendering is perfect for pages that consume dynamic data.

Use getServerSideProps to rebuild the page every time a user requests it.

        export default function Home({ data }) {
  return (
    <main>
      // Use data
    </main>
  );
}
 
export async function getServerSideProps() {
  // Fetch data from external api
 const res = await fetch('https://.../data')
  const data = await res.json()
 
  // Will be passed to the page component as props
  return { props: { data } }
}

getServerSideProps only runs on the server and this is how it runs:

  • When a user accesses the page directly, it runs at request time and the page is pre-rendered with the props it returns.
  • When a user accesses the page through a Next link, the browser sends a request to the server which runs it.

In the new version, you can opt into server-side rendering using dynamic data fetches in a page or a layout.

Dynamic data fetches are fetch() requests that specifically opt out of caching by setting the cache option to “no-store”.

        fetch('https://...', { cache: 'no-store' });

Alternatively, set revalidate to 0:

        fetch('https://...', { next: { revalidate: 0 } });

This feature is currently in beta so keep that in mind. You can read more about dynamic data fetches in the Next.js 13 beta docs.

Client-Side Rendering

You should use client-side rendering (CSR) when you need to update data frequently or when you don’t want to pre-render your page. You can implement CSR at the page level or component level. At the page level, Next.js fetches data at run time and when done at the component level, it fetches data on mount. Because of this, CSR can contribute to slow performance.

Use the useEffect() hook to render pages on the client like this:

        import { useState, useEffect } from 'react'

function Home() {
  const [data, setData] = useState(null)
  const [isLoading, setLoading] = useState(false)
 
  useEffect(() => {
    setLoading(true)
 
    fetch('/api/get-data')
      .then((res) => res.json())
      .then((data) => {
        setData(data)
        setLoading(false)
      })
  }, [])
 
  if (isLoading) return <p>Loading...</p>
  if (!data) return <p>No data</p>
 
  return (
    <div>
      // Use data
    </div>
  )
}

You can also use the SWR hook. It caches the data and revalidates it in case it goes stale.

        import useSWR from 'swr'
const fetcher = (...args) => fetch(...args).then((res) => res.json())

function Home() {
  const { data, error } = useSWR('/api/data', fetcher)
  if (error) return <div>Failed to load</div>
  if (!data) return <div>Loading...</div>
 
  return (
    <div>
      // Use data
    </div>
  )
}

In Next.js 13, you need to use a client component by adding the "use client" directive at the top of the file.

        "use client";

export default () => {
  return (
      <div>
       // Client component
      </div>
  );
};

The difference between SSR and CSR is that the data is fetched on every page request on the server in SSR while the data is fetched on the client side in CSR.

Static-Site Generation

With static-site generation (SSG), the page fetches data once during build-time. Static-generated pages are very fast and perform well because all the pages are built beforehand. SSG is therefore perfect for pages that use static content like sales pages or blogs.

In Next.js, you must export the getStaticProps function in the page you want to statically render.

        export default function Home({ data }) {
  return (
    <main>
      // Use data
    </main>
  );
}
 
export async function getStaticProps() {
  // Fetch data from external API at build time
  const res = await fetch('https://.../data')
  const data = await res.json()
 
  // Will be passed to the page component as props
 return { props: { data } }
}

You can also query the database inside getStaticProps.

        export async function getStaticProps() {
  // Call function to fetch data from database
  const data = await getDataFromDB()
  return { props: { data } }
}

In Next.js 13, static rendering is the default, and content is fetched and cached unless you set the caching option to off.

        async function getData() {
  const res = await fetch('https://.../data');
  return res.json();
}

export default async function Home() {
  const data = await getData();

  return (
    <main>
      // Use data
    </main>
  );
}

Learn more about static rendering in Next.js 13 from the docs.

Incremental-Static Generation

There are times when you want to use SSG but also want to update the content regularly. This is where incremental static generation(ISG) helps.

ISG allows you to create or update static pages after you have built them after the time interval you specify. This way, you don’t need to rebuild the entire site only the pages that need it.

ISG retains the benefits of SSG with the added benefit of serving up-to-date content to users. ISG is perfect for those pages on your site that consume changing data. For example, you can use ISR to render blog posts such that the blog stay updated when you edit posts or add new ones.

To use ISR, add the revalidate prop to the getStaticProps function on a page.

        export async function getStaticProps() {
  const res = await fetch('https://.../data')
  const data = await res.json()
 
  return {
    props: {
      data,
    },
    revalidate: 60
  }
}

Here, Next.js will try to rebuild the page when a request comes in after 60 seconds. The next request will result in a response with the updated page.

In Next.js 13, use revalidate in fetch like this:

        fetch('https://.../data', { next: { revalidate: 60 } });

You can set the time interval to whatever works best with your data.

How to Choose a Rendering Method

So far you have learned about the four rendering methods in Next.js — CSR, SSR, SSG, and ISG. Each of these methods is appropriate for different situations. CSR is useful for pages that need fresh data, where strong SEO is not a concern. SSR is also great for pages that consume dynamic data, but it's more SEO-friendly.

SSG is suitable for pages whose data is mostly static while ISG is best for pages containing data you want to update in intervals. SSG and ISG are great in terms of performance and SEO since data is pre-fetched and you can cache it.