Next.jsjavascriptfront-end

Understanding Next.js UI Rendering Architecture: A Comprehensive Guide

Picture of the author
Published on
Understanding Next.js UI Rendering Architecture: A Comprehensive Guide

Next.js has emerged as a leading React framework, offering remarkable capabilities for both server-side rendering (SSR) and static site generation (SSG). This guide will take you through the intricacies of Next.js rendering architecture, complete with code snippets and insightful comments.

Architectural Overview

Next.js facilitates three primary rendering methods:

  1. Server-Side Rendering (SSR): Renders pages on the server upon each request.
  2. Static Site Generation (SSG): Pre-renders pages during the build process and serves them as static HTML files.
  3. Client-Side Rendering (CSR): Executes JavaScript in the browser to render content post-initial page load.

Page Components in Next.js

A page component in Next.js is a simple React component. Below is an example of a typical Next.js page component:

// pages/index.js

import React from 'react';

// Basic React component that displays fetched data.
const HomePage = ({ data }) => {
  return (
    <div>
      <h1>Welcome to Next.js!</h1>
      <p>Data fetched from the server: {data}</p>
    </div>
  );
};

export default HomePage;

In this snippet:

  • HomePage is a React functional component that receives data as a prop and displays it.

Using Server-Side Rendering (SSR)

To enable SSR in Next.js, you leverage getServerSideProps, which executes on the server for each incoming request.

Example: Implementing getServerSideProps

// pages/index.js

import React from 'react';

const HomePage = ({ data }) => (
  <div>
    <h1>Welcome to Next.js!</h1>
    <p>Data fetched from the server: {data}</p>
  </div>
);

// SSR function executed on the server for every request.
export async function getServerSideProps() {
  // Fetch data from an external API or a database.
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: {
      data, // Passed to the page component as props.
    },
  };
}

export default HomePage;

This example illustrates:

  • getServerSideProps fetches data during each request.
  • The fetched data is provided as props to the HomePage component.

Using Static Site Generation (SSG)

You can utilize getStaticProps to pre-render pages at build time. This is particularly useful for content that doesn't change frequently.

Example: Implementing getStaticProps

// pages/index.js

import React from 'react';

const HomePage = ({ data }) => (
  <div>
    <h1>Welcome to Next.js!</h1>
    <p>Data fetched at build time: {data}</p>
  </div>
);

// SSG function called at build time.
export async function getStaticProps() {
  // Fetch data from an external API or a database.
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: {
      data, // Passed to the page component as props.
    },
  };
}

export default HomePage;

In this example:

  • getStaticProps runs exclusively during the build process.
  • This makes it ideal for static, unchanging content, enhancing loading performance.

Implementing Client-Side Rendering (CSR)

For dynamic data fetching post-initial render, you can use React's useEffect hook.

Example: Using useEffect

// pages/client-side.js

import React, { useState, useEffect } from 'react';

const ClientSidePage = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    // Fetch data on the client side.
    async function fetchData() {
      const res = await fetch('https://api.example.com/data');
      const result = await res.json();
      setData(result);
    }

    fetchData();
  }, []); // Empty array ensures this runs only once after the initial render.

  return (
    <div>
      <h1>Client-Side Data Fetching</h1>
      <p>Data: {data ? data : 'Loading...'}</p>
    </div>
  );
};

export default ClientSidePage;

In this example:

  • Data is fetched asynchronously in the browser after the component mounts.
  • This approach is suitable for actions requiring client-side interaction.

Creating API Routes with Next.js

Next.js allows you to create API routes, offering serverless functions that can be used as data endpoints.

Example: Defining an API Route

// pages/api/data.js

export default async function handler(req, res) {
  const data = { message: 'Hello from API!' };
  res.status(200).json(data);
}

Example: Consuming the API Route in a Page Component

// pages/api-client-side.js

import React, { useState, useEffect } from 'react';

const ApiClientSidePage = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      const res = await fetch('/api/data');
      const result = await res.json();
      setData(result.message);
    }

    fetchData();
  }, []);

  return (
    <div>
      <h1>Client-Side Data Fetching from API</h1>
      <p>Data: {data ? data : 'Loading...'}</p>
    </div>
  );
};

export default ApiClientSidePage;

In this example:

  • The data is fetched from a custom API route /api/data.

Conclusion

Next.js offers a versatile and powerful architecture for rendering UIs efficiently:

  • SSR: Best suited for dynamic data that needs to be fresh for each request.
  • SSG: Ideal for static, unchanging content, enhancing performance by pre-rendering at build time.
  • CSR: Useful for dynamic interactions and data loaded post-initial render.
  • API Routes: Perfect for creating serverless endpoints to fetch or manipulate data.

By carefully selecting the appropriate rendering method for each aspect of your application, you can achieve optimal performance and an excellent user experience with Next.js.

Stay Tuned

Want to become a Next.js pro?
The best articles, links and news related to web development delivered once a week to your inbox.