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
- Initial rendering is slow
- 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 timefallback: false→ any path not returned bygetStaticPathsshows 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
getStaticPropslike 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
revalidatetime 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
| Feature | Static Rendering | SSR via EJS | SSR via Next.js | CSR (SPA) | SSG | ISR |
|---|---|---|---|---|---|---|
| Rendering Time | At build time | On every request | On every request | On client (after JS loads) | At build time | At build time + background regeneration |
| Data Freshness | Static until rebuild | Always fresh | Always fresh | Depends on client API fetch | Static until next build | Updated periodically based on revalidate |
| Performance | Very fast (cached) | Moderate | Moderate | Fast after initial load | Very fast (cached) | Fast (mostly cached) |
| Server Load | Low | Higher | Higher | Low (mostly client) | Lower | Low to moderate |
| Ideal Use Case | Docs, marketing pages | Simple dynamic pages | Dynamic pages, SEO pages | Highly interactive apps | Blogs, docs, marketing pages | Blogs, product listings, news sites |