Nextjs isn’t React?

To take advantage of the Nextjs framework, it’s necessary to have a different approach compared to developing in React.

Vasco Pinto Saraiva
MCTW_TDW
7 min readFeb 12, 2024

--

As one of the frameworks that is growing the most in popularity, Nextjs has aroused the curiosity of many React developers, primarily due to the array of features the framework offers in addition to the renowned Javascript library. With several companies like Apple, Netflix and TikTok transitioning their platforms to Nextjs, it’s never been a better time to learn more about the framework.

Its premise is relatively simple. It’s a framework based on React, with an integrated backend that also includes Server-side Rendering (SSR). This means that code that React renders on the client-side, through Client-Side Rendering (CSR), Nextjs has the capability to run on the server.

Of the various advantages that constitute SSR, one of the main ones is the significant improvement in performance. This is because server rendering reduces the amount of processing on the client, which is particularly important in large, complex, React applications where large amounts of Javascript typically run client-side.

Although Nextjs is based on React, SSR completely changes the development paradigm. This change in the way of programming, however, goes unnoticed by many React developers new to the framework who write code in the same way they programmed before, only now within the Nextjs environment. It is easy to make this mistake since the framework itself is not restrictive to the point of imposing this change. However, in order to harness the numerous benefits of Next.js, a React developer needs to adapt their programming approach.

Rendering in Nextjs

SSR vs CSR

On a technical level, how do these two rendering methods differ from one another?

A practical demonstration highlighting the contrasting functionality of both methods involves disabling JavaScript in the browser and examining the source code of a website page rendered with CSR and another with SSR.

<!-- Example of a web page rendered client-side (CSR) -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSR Example</title>
</head>
<body>
<div id="app"></div>
<script src="app-bundle.js"></script>
</body>
</html>

CSR sends a Javascript bundle needed to build the page client-side.

<!-- Example of a web page rendered server-side (SRR) -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSR Example</title>
</head>
<body>
<div id="app">
<h1>Hello World!</h1>
<p>Content is rendered on the server and sent to the client.</p>
(...)
</div>
</body>
</html>

SSR, however, sends a static HTML page with content and with a Javascript bundle that adds extra functionalities, for example DOM manipulation.

Figure 1: Differences between CSR and SSR.

For a React Developer, it can be a headache trying to choose between CSR and SSR. And indeed, it is simpler to develop exclusively with the traditional CSR. Nonetheless, there are multiple advantages in using SSR, when applied correctly:

Performance: By employing SSR, the website diminishes the amount of JavaScript transmitted, thereby mitigating the burden on initial loading times.

SEO: By sending static HTML pages over large Javascript bundles, Search Engine Crawlers can gather precise data about the web pages, thus contributing to a better SEO.

Resource Proximity: Server-side API calls have lower response time due to their proximity to data centers.

Security: Code that runs on the server does not expose sensitive data on the client.

Use Cases

Although Nextjs allows developers to use the power of SSR, this doesn’t mean that CSR becomes obsolete. These rendering methods are complementary, and their use always depends on the context.

In Nextjs, since the rendering is chosen on a component level, it’s possible to have an application that integrates both SSR and CSR. While in traditional React, all components are rendered client-side, on Nextjs it’s important to know which rendering method is most appropriate.

Figure 2: SSR and CSR use cases.

Component Hierarchy

Nextjs maintains React’s logic behind component hierarchy. Consequently, the way components are structured will also have a direct impact on how they are rendered.

When a user makes a request to the server to access a web page, SSR, as it occurs on the server, will always precede CSR, which happens exclusively on the client. Thus, an SSR component can include CSR components as its children, but the opposite is impossible. In other words, CSR cannot include SSR components as their children due to the nature of both server-side and client-side rendering.

Figure 3: The impact of React’s component structure on rendering.

Nextjs vs React: Examples

Having clarified the differences between SSR and CSR, it is necessary to understand how Nextjs differs from traditional React in terms of coding practices. To illustrate those differences, I will use a common web development scenario: Consuming data from an API.

Fetching

The following example shows the fetching of data from an API in React, that is, exclusively with CSR.

const Page = () => {

const [user, setUser] = useState(null)

useEffect(() => {
const getData = async () => {

try {
const res = await fetch('https://...')
let data = await res.json()
setUser(data)
} catch (error) {
console.log(error)
}
}

getData()
}, [])

return (
<>
{user ? <h1>{user}</h1> : <Loading />}
</>
)
}

export default Page

Similarly, the same example is doable in Nextjs. Since the rendering happens client-side, and the framework supports CSR, the API request could be made the same way. React developers may inadvertently opt for this less efficient approach due to its familiarity.

However, to take advantage of the benefits from Nextjs mentioned above, the same API request can be done with SSR:

async function getData() {
try {
const res = await fetch('https://...', { cache: 'no-store' })
return res.json()
} catch (error) {
console.log(error)
}
}


export default async function Page() {
const user = await getData()

return (
<h1>{user}</h1>
)
}

Since the code is rendered on the server, the component itself is asynchronous. That way it isn’t necessary to use the useEffect hook to make the API request and to use the useState hook to store the response data.

Loading

Although the time between the API request and response varies depending on the rendering method, there is typically a delay. Implementing loading mechanisms is critical for a good user experience during those waiting times. In CSR, the loading is usually managed through one or several useStates that are updated depending on the API response. This allows developers to present a loading spinner to the user, for example.

In the first example, in traditional React, the moment the fetch returns a response, the value of the state is updated.

try {
const res = await fetch('https://...')
let data = await res.json()
setUser(data)
}

And thanks to the condition that checks that same state, it is possible to inform the user if the data from the API is fully loaded or not.

return (
<main>
{user ? <h1>{user}</h1> : <Loading />}
</main>
)

In Nextjs, using SSR, the loading can’t be managed using useState since it is a React hook and, therefore, only works on the client-side. In truth, making an API request using SSR implies that the HTML page sent by the server is only sent to the client after the API response arrives. Until a response to original request is received, the page will not load, and the user will see a spinner in the browser tab.

Fortunately, Nextjs provides better ways to deal with loadings while using SSR. Since Next.js 13, developers have the option to create a file named loading.js for any route. This file acts as a fallback component that is sent to the client until there is an API response.

Figure 4: How loading state works in Nextjs.

In summary, the examples provided illustrate how Nextjs allows a programming methodology distinct from traditional React, doing away with the necessity for hooks and minimizing the amount of code needed to fetch data from an API.

As it is completely optional, this shift in the programming paradigm can be easily overlooked by React Developers. Its implications are primarily manifested in the user experience, particularly in terms of API response times and overall website performance.

In addition to the examples provided, there is a whole set of scenarios that require special consideration from React developers delving into the Nextjs framework. Indeed, Next.js transcends mere React usage. It reshapes, adjusts, and revolutionizes coding practices, demanding a distinct approach to programming.

Bibliography

--

--

Vasco Pinto Saraiva
MCTW_TDW

Entusiasta por tecnologia, videojogos, livros e gatos. Atualmente no Mestrado em Comunicação e Tecnologias Web na Universidade de Aveiro