Static Site Generation in React

By Syncfusion®, Inc. · Last updated

How SSG works: build-time rendering, static HTML generation, the build process, build-time data fetching, dynamic routes, deployment to a CDN, SSG pros and cons, and a Documentation Website capstone to download.

Introduction to Static Site Generation

Static site generation (SSG) means React renders your pages once, at build time, into plain HTML files. You upload these files to a host. Every visitor then gets a ready-made file. Nothing is rendered when they visit.

It is a third render mode, next to CSR and SSR:

When is the HTML made? CSR in the browser, SSR on each request, SSG once at build time CSR

made in each browser, after the JavaScript loads SSR

made on the server, for every request SSG

made once, at build time, before anyone visits

All three use the same React components. Only the time and place change.

Real-World Example

A bookshop's help center has 30 pages, such as "Shipping" and "Returns". The text changes only a few times a year.

At build time, the build script renders every page to an HTML file. The host sends these ready files to every visitor, so the pages load very fast and no server code runs.

Benefits of SSG

  • Very fast. The files are ready. A host or CDN just sends them.
  • Cheap and simple hosting. Any static host works. No Node.js server is needed.
  • Great SEO. Every page is full HTML, like SSR.
  • Safe and reliable. No server code runs per request, so there is less that can break or be attacked.

SSG is a great fit for content that does not change every minute: documentation, blogs, marketing pages and portfolios.

SSG Architecture

Build-Time Rendering

Build time is when you run npm run build, on your computer or a build server. Request time is when a user visits the site.

In SSG, React renders at build time. In SSR, it renders at request time. That is the main difference.

Static site generation: pages are rendered once at build time, then the same files are sent to every visitor

npm run build once

React renders pages

index.html

docs/*.html

CDN stores files Later: every visitor gets a ready file. Nothing is rendered per request.

The work happens before anyone visits.

Static HTML Generation

During the build, a script calls React for each page, and saves the result as a file. For pure static pages, React has renderToStaticMarkup. It is like renderToString, but it leaves out extra markers that only hydration needs, so the HTML is a little smaller.

import { renderToStaticMarkup } from "react-dom/server";

const html = renderToStaticMarkup(<DocPage doc={doc} />);
fs.writeFileSync("dist/docs/props/index.html", html);

If the page also needs to be interactive later, use renderToString and hydrate it in the browser, like in SSR.

Pre-Rendered Pages

The output is a folder of pre-rendered pages: .html files that already contain the content. Each URL maps to a file:

  • / → index.html
  • /docs/props/ → docs/props/index.html

Run the lab. The "build" renders three pages. Click a file to see its HTML.

SSG Rendering Fundamentals

Build Process

A typical SSG build has four steps:

  1. Read the content (files, a CMS, an API).
  2. List every page that must exist. This list of paths is often made by a function called getStaticPaths.
  3. Render each page with React.
  4. Write each result as an .html file in the dist folder, together with CSS, JavaScript and images.

The build process: read content, list the pages, render each one, write the files

1. Read content + data

2. List paths getStaticPaths

3. Render each page

4. Write dist/*.html

runs on your computer or a CI server, not when users visit

After the build, the site is just a folder of files.

Page Generation

Each path becomes one file. The same layout component is used for every page, so all pages look alike. Pages are made one after another, so a site with 1,000 pages makes 1,000 files. A big site can take a while to build.

Serving Static Content

After the build, there is no React work left for visitors. The host just finds the file for the URL and sends it.

The simulator below compares SSR and SSG. Press Send 5 requests and compare how many times each one renders.

Easy Way to Remember: CSR renders in the browser, SSR renders per request, SSG renders once per build.

Content Generation

Build-Time Data Fetching

Pages often need data: posts from an API, docs from files. In SSG, this data is fetched during the build, once. The result is baked into the HTML. Visitors never wait for it.

Generating Dynamic Routes

A dynamic route like /blog/[id] is one template for many pages. At build time, you must say which ids exist. You fetch the list, and make one path per item. Then each path is rendered with its own data.

The lab does both. The "build" fetches the posts, makes one path per post, and renders a file for each.

Generating dynamic routes: one template plus a list of slugs makes many HTML files

/docs/[slug] one template

slugs from data

docs/introduction/index.html

docs/installation/index.html

docs/props/index.html

Add a doc to the data, rebuild, and a new page appears.

Managing Static Content

Content for SSG sites usually lives in one of these places:

  • Files in the project, like Markdown (.md) files. Writers edit text, not code. This is common for docs and blogs.
  • A headless CMS, an online editor (such as Contentful or Strapi) that the build reads through an API.
  • Data files like JSON, for lists such as products or team members.

Keep content separate from components. Then the layout can change without touching the text, and the text can change without touching code.

Tools like Astro, Next.js (static export), Gatsby and VitePress do SSG for you. They give you getStaticPaths-style functions, Markdown support and a build command. The ideas in this lesson are the same in all of them.

Deployment and Delivery

Static Assets

Static assets are files that do not change per visitor: the HTML pages, CSS, JavaScript bundles, images and fonts. After an SSG build, the whole site is static assets. Build tools add a short code (a hash) to file names like index-4zsi.js. When the file changes, its name changes, so browsers never use an old copy.

Content Delivery Workflow

You upload the dist folder to a static host such as Netlify, Vercel, Cloudflare Pages or GitHub Pages. Most of them use a CDN (Content Delivery Network): copies of your files are kept on servers around the world. Each visitor gets the files from the nearest one.

Content delivery: a CDN keeps copies of the files near users all over the world

Origin dist/ folder

CDN Mumbai copy

CDN London copy

CDN New York copy

Each user gets the files from the nearest copy, so pages load fast.

Updating Generated Pages

Static pages do not change by themselves. To update one, you change the content, rebuild and deploy again. Most hosts do this automatically when you push to Git.

Try it below. Edit the price, and see that the live site still shows the old one until you rebuild and deploy.

Updating generated pages: change the content, rebuild, deploy

Edit content fix a typo

Rebuild npm run build

Deploy upload dist/

until you deploy, visitors still see the old files

Static pages change only when you build again.

Advantages and Limitations of SSG

AdvantagesLimitations
Fastest delivery: files are readyContent is only as fresh as the last build
Cheap hosting on any static host or CDNBig sites can take a long time to build
Full HTML for SEO and link previewsNot for per-user pages (a cart, a dashboard)
No server code per request: safe and reliableEvery change needs a rebuild and deploy
CSRSSRSSG
When HTML is madeIn the browserOn each requestOnce, at build time
First viewSlowFastFastest
Fresh dataYesYesOnly after a rebuild
HostingStatic filesNode.js serverStatic files
Best forApps behind loginDynamic public pagesDocs, blogs, marketing

Many real sites mix modes: SSG for docs and marketing pages, SSR for search results, and CSR for the logged-in dashboard. Interactive parts of an SSG page can still be hydrated, like in SSR.

Capstone Project: Documentation Website

You will build a Documentation Website for "Book Corner UI", a pretend component library. It has docs grouped into categories, a shared layout with a sidebar, and one static page per doc, made at build time.

Create multiple documentation pages

All content lives in data.ts: each doc has a slug (its URL name), a title, a category and its body. DocPage shows one doc. To add a page, you add an item to the data. No new component is needed.

Create reusable layout components

DocsLayout wraps every page with the same header and sidebar. It takes current (the active slug) and children (the page content). Each page looks consistent, and the layout is written once.

Organize content using categories

groupByCategory(docs) turns the flat list into groups: Getting Started, Core Concepts and Guides. The sidebar shows one heading per category with its docs below. Click the sidebar links in the lab to switch docs.

Configure dynamic documentation routes

The route /docs/[slug]/ is one pattern for all docs. getStaticPaths() in build.tsx lists every real path: / plus one /docs/<slug>/ per doc.

Generate pages at build time

build() loops over the paths, renders each page with renderToStaticMarkup inside DocsLayout, and stores it as a file like docs/props/index.html.

Implement navigation and routing

The sidebar uses plain <a href="/docs/props/"> links. On a static site, each click asks the host for the next HTML file. The host finds docs/props/index.html and sends it. No React runs for the visit.

The finished lab plays both parts: it runs the build once, then acts as the static host. Click the sidebar links. The console shows that each page is a file lookup, not a render.

Download Project Files

Download SSG Project Source Code

The complete Documentation Website is ready as a real SSG project with Vite, React, TypeScript and React Router. The docs are Markdown files. npm run build pre-renders one HTML file per doc into dist/static. In the browser, the pages are hydrated, so moving between docs is instant.

Download SSG Project: Download Documentation Website Project

docs-website/
  package.json            scripts and dependencies
  prerender.js            build step: render every route to dist/static
  vite.config.ts          Vite settings
  index.html              template with <!--app-html-->
  content/docs/           the Markdown docs, one folder per category
  src/
    entry-client.tsx      hydrate (or render in dev)
    entry-server.tsx      render(url) + getStaticPaths()
    App.tsx               routes
    content/docs.ts       reads and parses the Markdown at build time
    layouts/DocsLayout.tsx
    components/           Sidebar, Header, DocNav (previous / next)
    pages/                HomePage, DocPage, NotFoundPage

Project Setup Instructions

  1. Install Node.js 18 or newer from nodejs.org, if you do not have it.
  2. Download the ZIP with the link above, and unzip it. You get a folder called docs-website.
  3. Open a terminal in that folder (in VS Code: File, Open Folder, then Terminal, New Terminal).
  4. Install the packages and start the dev server:
npm install
npm run dev

Open http://localhost:5173. To make the static site, run npm run build, then npm run preview to serve the generated files from dist/static. Add a new .md file in content/docs, build again, and a new page appears.

After npm run build, open the dist/static folder. You will find a real index.html for every doc, already filled with its text. That folder is the whole website: you can upload it to any static host.

Common mistakes

MistakeWhat you seeFix
Expecting content changes to show without a rebuildThe live site still shows old textRebuild and deploy, or let the host build on every push
Using SSG for per-user data, like a cartEvery visitor sees the same pageLoad user data on the client, or use SSR
Forgetting a slug in getStaticPathsThat page is a 404Make the paths from the same data as the pages
Using window while rendering at build timeBuild error: window is not definedUse browser-only code in useEffect
Mixing content into componentsEvery text change needs a code changeKeep content in Markdown or data files
Using renderToStaticMarkup for pages you want to hydrateHydration warnings, clicks do not workUse renderToString for pages that will be hydrated

Check Yourself

Exercises

What to Remember

  • SSG renders pages once at build time into HTML files. Visitors get ready files from a static host or CDN.
  • The build reads content, lists paths with getStaticPaths, renders each page and writes the files. Dynamic routes become one file per item.
  • SSG is fast, cheap and great for SEO, but content is only as fresh as the last build. Use it for docs, blogs and marketing pages.

Next: Incremental Static Generation