Static Rendering

In the Start the HTML used to served by the server as individual files, here we don’t have any interactivity, just text on the webpage and nothing else

flowchart TB
     Client box
    subgraph C[client]
        direction TB
        D["/home"]
        E[index.html]
    end

     Server box
    subgraph S[server]
        direction LR
        A[index.html]
        B[about.html]
        Ajs[index.js]
        Bjs[about.js]
    end

     Arrows
    D -- "request" --> A
    A -- "response (HTML)" --> E
    Ajs -- "response (JS)" --> Ejs

Server Side Rendering via ejs(SSR) or Multi-Page Applications(MPA)

Here we solved the interactivity, but now we have dynamic data to deal with so, what we do is we get the data in the server that we need and put it in HTML and send it back to the browser again

flowchart TB
     Client box
    subgraph C[client]
        direction TB
        Req["/home"]
        H[index.html + dynamic data]
        J[index.js]
    end

     Server box
    subgraph S[server]
        direction TB
        A["index.html (empty skeleton)"]
        B["script.js"]
        DB[(Data API)]
    end

     Flows
    Req -- "request" --> A
    A -- "response (HTML skeleton)" --> HTML
    B -- "response (JS)" --> JS
    JS -- "routing + insert HTML" --> Render
    Render -- "fetch data if needed" --> DB
    DB -- "API response" --> Data
    Render -- "injects HTML content" --> HTML

This works fine but there are some problems

  1. Initial rendering is slow
  2. SEO sucks as there is empty HTML in the start

Server Side Rendering via Next.js(SSR)

Next.js is a React framework that supports SSR reactively. SSR in Next.js can either be per-page using getServerSideProps or static generation

  • Sends fully rendered HTML initially (SSR), then React takes over
  • Supports dynamic routing easily
  • Hydration allows client-side interactivity after HTML loads
  • Can mix SSR, SSG (Static Site Generation), and CSR in the same app

Simple when we need Home page component to be rendered, then the .jsx or .tsx will be converted into .html file and returned to the client with only the required javascript part

flowchart TB
     Client box
    subgraph C[client]
        direction TB
        Req["/home"]
        HTML["Pre-rendered HTML"]
        JS["Hydration JS (React)"]
        Interact["Client-side Interactivity"]
    end

     Server box (Build time)
    subgraph S["server (build time)"]
        direction TB
        Comp["Home.jsx / Home.tsx Component"]
        FetchData["getStaticProps / fetch data"]
        DB["Data API"]
        Render["Generate Static HTML"]
    end

     Flows
    Comp --> FetchData
    FetchData --> DB
    Comp --> Render
    Render --> HTML
    Req --> HTML
    JS --> Interact
    Interact --> HTML

To force Next.js to use SSG we use a function called getStaticProps and getStaticPaths if we want to generate for routes

export async function getStaticPaths() {
  const res = await fetch("https://api.example.com/posts");
  const posts = await res.json();
 
  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }));
 
  return { paths, fallback: false };
}
 
export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`);
  const post = await res.json();
 
  return { props: { post } };
}
  • getStaticPaths → tells Next.js which dynamic pages to pre-render at build time
  • fallback: false → any path not returned by getStaticPaths shows 404

Now we have almost everything, the SEO, it’s fast, low server load and we get real time data, but here comes another problem, as now we optimized for re-rendering the same blog title again and again, but If we edit the existing blog title, it will still show the old data and that’s a problem

Incremental Static Regeneration (ISR)

It’s Simply SSG with an expiration time. Next.js supports ISR, which combines the benefits of SSG (static HTML for performance) with the ability to update pages after build time. Pages can be regenerated on-demand without rebuilding the entire site

  • Uses getStaticProps like SSG, but allows pages to revalidate after a defined interval
  • Pre-rendered HTML is served initially, then regenerated in the background if stale
  • Ideal for pages where data changes occasionally, like blogs, product listings, or news
  • Hydration allows client-side interactivity after HTML loads
  • Reduces server load compared to SSR while keeping content up-to-date

Behavior:

  • At build time, Next.js pre-renders static HTML for the page
  • Subsequent requests:
    • Serve the cached static HTML immediately
    • If revalidate time has passed, Next.js regenerates the page in the background
  • The updated HTML replaces the old one for future requests
flowchart TB
     Client box
    subgraph C["client"]
        direction TB
        Req["/home"]
        HTML["Pre-rendered HTML"]
        JS["Hydration JS (React)"]
        Interact["Client-side Interactivity"]
    end

    %% Flows
    Comp --> FetchData
    FetchData --> DB
    Comp --> Render
    Render --> HTML
    Req --> HTML
    JS --> Interact
    Interact --> HTML

To Implement ISR in Next.js we use revalidate variable and export it

export async function getStaticProps() {
  const res = await fetch("https://api.example.com/posts");
  const posts = await res.json();
 
  return {
    props: { posts },
    revalidate: 60, // Page will be regenerated in the background at most once every 60 seconds
  };
}

Note:

  • revalidate → time in seconds after which a page becomes stale and can be regenerated
  • Combines SSG performance with near real-time content updates
  • No need to rebuild the entire site for occasional content changes

Everything at Once

FeatureStatic RenderingSSR via EJSSSR via Next.jsCSR (SPA)SSGISR
Rendering TimeAt build timeOn every requestOn every requestOn client (after JS loads)At build timeAt build time + background regeneration
Data FreshnessStatic until rebuildAlways freshAlways freshDepends on client API fetchStatic until next buildUpdated periodically based on revalidate
PerformanceVery fast (cached)ModerateModerateFast after initial loadVery fast (cached)Fast (mostly cached)
Server LoadLowHigherHigherLow (mostly client)LowerLow to moderate
Ideal Use CaseDocs, marketing pagesSimple dynamic pagesDynamic pages, SEO pagesHighly interactive appsBlogs, docs, marketing pagesBlogs, product listings, news sites