Rendering with NextJS

Rendering with NextJS

·

6 min read

NextJS has been around for quite a few years already, and I loved it since I first tried it. It is an amazing framework with many good solutions, but what sold it for me was server-side rendering. This is something that React supported for a long time in a way, but until recently and React v18 improvements, it was quite complex to do. NextJS solves this in a very easy and quite flexible way and that is why in the rest of this article, you will have an overview of it.

Image description

Rendering types

There are three rendering types supported. Those are:

1. Client-side rendering

Once the application is loaded, any required data is fetched on the client. When everything is fetched, content is generated and displayed in UI. This is also the reason why it is called client-side because all work is being done on the client.

2. Server-side rendering

This one is quite similar client-side but with the obvious difference that everything happens on the server. Once the user makes a request, on the server, all the data is prepared and content is generated, and sent as a response to the user. After that content is loaded and shown, all the JavaScript is loaded and all listeners are attached in the process that is called hydration. To use this type of rendering, you need to expose the getServerSideProps function which is where you get all the data needed for your component.

3. Static Side Generation

Static Side Generation is a kind of special type of server-side rendering. The difference is that rendering does not happen on the request but during the build time. Maybe you have some pages that you want to have ready but they won't change often, then you can generate their content during the build and just serve them already prepared. There is also a possibility to generate new content post-build by using incremental static regeneration, but that is a bit more advanced topic at the moment. To use this type of rendering, you expose the getStaticProps function where you get all required props.

Examples

In the following three sections, I will show how each type of rendering works. First, you will see a small component displaying the first and last name of a person, and after it, for each type of rendering and how data for it would be loaded.

export default function Person() {
  const person = {
    firstName: "John",
    lastName: "Doe"
 };

 return (
   <>
     Hello {person.firstName} {person.lastName}!
   </>
   )
 }

Above is the starting page component I will be using in the rest of the examples. As you can see, it is very simple. It has a hard-coded object with personal details, and displays them in HTML. For the rest of the examples, I will be using an API endpoint /api/person to load that data.

Client-side rendering

When doing everything client-side there are multiple ways to load data. Recommended way by the NextJS team is using the useSWR hook from the SWR npm package. The following code snippet is an example of doing that.

import useSwr from 'swr';

export default function ClientPerson() {
  const fetcher = (...args) => fetch(...args).then((res) => res.json())
  const { data, error } = useSwr('/api/person', fetcher)

  if(error) return <div>error</div>;
  if(!data) return <div>loading</div>;

  return (
    <>
      Hello {data.firstName} {data.lastName}!
    </>
  )
}

When you are using that hook, it returns an object containing a few properties and some of them are data and error, but there are others as well. Also, you can run it conditionally by setting the value of the URL as null for cases when you don't want to run. This hook also has other benefits like caching. If the component gets re-rendered but the fetching value doesn't change, it will load the response from the cache.

To view the effects of this type of rendering, you could open inspector tools. In the network tab, you would first see the page being loaded, then an additional request made to get personal data from API.

Other than this hook, you might want to use regular fetch. The problem with that is that you need to wrap it into something like the useEffect hook. The reason is that all NextJS components might be rendered on the server, and no browser-specific functions should be executed on the initial render.

Server-side rendering

When rendering on the server, if your component does not depend on the data, you can just leave it as it is. But if it does, which is the point of this example, you need to export the getServerSideProps function which returns an object that will be passed as component props.

export default function ServerPerson({ person }) {
  return (
    <>
      Hello {person.firstName} {person.lastName}!
    </>
  )
}

export async function getServerSideProps(context) {
  return {
    props: {
      person: { firstName: "John", lastName: "Doe" }
    }, // will be passed to the page component as props
  }
}

As you can see in the code example above, getServerSideProps is an async function. And that is for the reason. The data you return can be taken from some asynchronous source, like a database or other API.

But now let us cover a bit about how it works underneath. If you check the network tab of your inspector, this time will be just one request. When users request hits the server, props are first getting prepared. Once that is done, those are sent to the component and the final HTML is generated. That HTML is returned as a response, and once it is shown, a process called hydration is run. This is where all click handlers and other listeners are bound to the elements.

Static Site Generation

Static generation is kind of similar to server-side rendering, but with some differences. The first difference is the function it uses. While the server-side used getServerSideProps, this one uses getStaticProps.

export default function PersonStatic({ person }) {
  return (
    <>
      Hello {person.firstName} {person.lastName}!
    </>
  )
}

export async function getStaticProps(context) {
  return {
    props: {
      person: { firstName: "John", lastName: "Doe" }
    }, // will be passed to the page component as props
  }
}

Looking at this you might be wondering, what is the difference, and why use this instead of the other function? And the reason is when content gets generated. With the server-side rendering, I mentioned content generation is done on the request, this one is done on the application build. Once the user makes a request, content is already existing and is being returned immediately. This might also give you a hint on when to use which type of rendering. If your content is dynamic and changes, server-side rendering might be a better option. If you have some content that won't change, then static generation might be better.

In addition, there are some more advanced options with static generation. Above, I gave you an example of when there is a fixed page, but you might also do it for the dynamic routes. Let's say a list of items that rarely changes. In that case, you could expose another function, getStaticPaths which will expose a list of routes you want to have pre-rendered. Others might return 404 or you might have them behave as a server-side generation.

##Wrap-up

NextJS is an amazing framework and as you could see above, supports a lot when it comes to rendering. However, do keep in mind that every type has its use case. Hopefully, from the examples above you will have a better understanding of each type and you will be able to make the best decision. The code used in the examples above can be found in my GitHub repository.


For more, you can follow me on Twitter, LinkedIn, GitHub, or Instagram.