Static Generation (SSG)
Static Generation (SSG)
Section titled “Static Generation (SSG)”Introduction
Section titled “Introduction”Static Generation (SSG) is a rendering strategy in Next.js where HTML is generated at build time and reused for every request. This approach provides excellent performance, SEO benefits, and low hosting costs since the HTML can be served directly from a CDN.
Why do we need this?
Section titled “Why do we need this?”Not all web content needs to be generated on every request. For content that doesn’t change frequently (like blog posts, documentation, or marketing pages), generating HTML at build time and serving it statically is much more efficient than computing it on each request.
Problem Statement
Section titled “Problem Statement”Generating the same HTML content on every request wastes server resources, increases latency, and increases hosting costs. For static or infrequently changing content, this approach is inefficient and unnecessary.
Real World Story
Section titled “Real World Story”Imagine you’re publishing a blog with 100 posts. Instead of generating the HTML for each post every time someone visits (which would require querying a database and rendering React components on every request), you generate all 100 posts’ HTML once when you deploy the blog. Then, every visitor gets the pre-generated HTML instantly from a CDN.
Real World Analogy
Section titled “Real World Analogy”Think of Static Generation like printing a book:
- You write the content once (at build time)
- You print many copies (the HTML files)
- Each reader gets an identical copy instantly
- If you need to update the content, you reprint the book (regenerate)
- Just as you wouldn’t rewrite the entire book for every reader, you don’t regenerate HTML for every request when the content is static
Visual Explanation
Section titled “Visual Explanation”Build Time:-----------[React Components] + [Data] → [HTML Files] → [CDN]
Request Time:-------------User Request → [CDN] → [Browser] → [HTML] → [Interactive Page]Internal Working
Section titled “Internal Working”When you use getStaticProps in a Next.js page:
- During
next build, Next.js callsgetStaticPropsto fetch data - It renders the page to HTML using the fetched data
- The HTML is saved as a static file (along with necessary JavaScript for hydration)
- On request, Next.js serves the pre-built HTML from the CDN (or file server)
- The browser hydrates the HTML to make it interactive
Mermaid Diagram 1: SSG Build Time Flow
Section titled “Mermaid Diagram 1: SSG Build Time Flow”flowchart TD A[Developer runs next build] --> B[Next.js scans pages/] B --> C{Page has getStaticProps?} C -->|Yes| D[Call getStaticProps to fetch data] D --> E[Render page to HTML with data] E --> F[Save HTML + JS to .next/] F --> G[Output ready for deployment] C -->|No| H[Skip data fetching] H --> EMermaid Diagram 2: SSG Request Time Flow
Section titled “Mermaid Diagram 2: SSG Request Time Flow”flowchart LR A[User requests /blog/post-1] --> B[Next.js checks .next/ for HTML] B -->|Found| C[Serve pre-built HTML] C --> D[Browser receives HTML] D --> E[Hydrate React components] E --> F[Interactive page ready] B -->|Not found| G[404 or fallback]Technical Explanation
Section titled “Technical Explanation”How SSG Works in Next.js
Section titled “How SSG Works in Next.js”- Build-time Data Fetching:
getStaticPropsruns at build time, not on each request - HTML Generation: The page component is rendered to HTML with the fetched data
- Static File Output: The resulting HTML (and associated JavaScript) is stored as a static asset
- CDN Delivery: Static assets are served via CDN for global low-latency access
- Client-side Hydration: Browser loads the JavaScript and attaches event listeners to make the page interactive
Key Characteristics
Section titled “Key Characteristics”- No server execution at request time: HTML is already generated
- Cacheable: CDN can cache the HTML indefinitely (or until next build)
- Fast TTFB: Time to First Byte is minimal since it’s just serving a static file
- SEO-friendly: Search engines see fully rendered content immediately
- Cost-effective: Serving static files is cheaper than running server instances
Example: Basic SSG Page
Section titled “Example: Basic SSG Page”- Create
pages/blog/[slug].js:
import { notFound } from 'next/navigation';
export default function Post({ post }) { if (!post) { notFound(); }
return ( <article> <h1>{post.title}</h1> <time>{post.date}</time> <div dangerouslySetInnerHTML={{ __html: post.content }} /> </article> );}
export async function getStaticProps({ params }) { const { slug } = params;
// Fetch data from external source (API, CMS, filesystem) const res = await fetch(`https://api.example.com/posts/${slug}`);
if (!res.ok) { return { notFound: true }; }
const post = await res.json();
return { props: { post }, // Optional: Revalidate every hour for ISR // revalidate: 3600 };}
export async function getStaticPaths() { // Get all possible slugs from your data source const res = await fetch('https://api.example.com/posts'); const posts = await res.json();
return { paths: posts.map(post => ({ params: { slug: post.slug } })), fallback: false // or 'blocking' or true for ISR };}Example: SSG with Markdown Files
Section titled “Example: SSG with Markdown Files”- Create
pages/blog/[slug].js:
import { notFound } from 'next/navigation';import { remark } from 'remark';import html from 'remark-html';
export default function Post({ title, date, contentHtml }) { if (!title) { notFound(); }
return ( <article> <h1>{title}</h1> <time>{date}</time> <div dangerouslySetInnerHTML={{ __html: contentHtml }} /> </article> );}
export async function getStaticProps({ params }) { const { slug } = params;
// Read markdown file from filesystem const filePath = `posts/${slug}.md`; const fileContents = await fs.promises.readFile(filePath, 'utf8');
// Convert markdown to HTML const processed = await remark() .use(html) .process(fileContents); const contentHtml = processed.toString();
// Extract metadata (simplified - in real app use gray-matter) const title = fileContents.match(/^# (.+)/m)?.[1] || 'Untitled'; const date = fileContents.match(/^Date: (.+)/m)?.[1] || new Date().toISOString();
return { props: { title, date, contentHtml } };}
export async function getStaticPaths() { const files = await fs.promises.readdir('posts');
return { paths: files.map(file => ({ params: { slug: file.replace('.md', '') } })), fallback: false };}Example: SSG with Fallback (ISR)
Section titled “Example: SSG with Fallback (ISR)”- Create
pages/products/[id].js:
import { notFound } from 'next/navigation';
export default function Product({ product }) { if (!product) { notFound(); }
return ( <div> <h1>{product.name}</h1> <p>Price: ${product.price}</p> <p>{product.description}</p> </div> );}
export async function getStaticProps({ params }) { const { id } = params;
// Fetch product data const res = await fetch(`https://api.example.com/products/${id}`);
if (!res.ok) { return { notFound: true }; }
const product = await res.json();
return { props: { product }, // Regenerate at most once every 10 minutes revalidate: 600 };}
export async function getStaticPaths() { // Get all possible product IDs from your data source const res = await fetch('https://api.example.com/products'); const products = await res.json();
return { paths: products.map(p => ({ params: { id: p.id.toString() } })), fallback: 'blocking' // Show fallback while generating };}Production Example
Section titled “Production Example”In production, SSG pages are optimized:
- Build Time:
next buildpre-generates all HTML for pages withgetStaticProps - Deployment: Static HTML files are deployed to a CDN (like Vercel’s Edge Network)
- Request Handling: CDN serves the HTML directly with minimal latency
- Updates: To update content, rebuild and redeploy (or use ISR with
revalidate) - Cost: Serving static HTML is extremely cheap - often free on CDNs
- Scalability: CDN handles traffic spikes effortlessly
Folder Structure Context
Section titled “Folder Structure Context”SSG pages exist alongside other page types in the pages/ directory:
pages/├── index.js # SSG (if no data fetching or getStaticProps)├── about.js # SSG├── blog/│ ├── [slug].js # SSG with getStaticProps│ └── index.js # SSG (blog listing)├── products/│ ├── [id].js # SSG with getStaticProps (ISR)│ └── index.js # SSG└── api/ └── ... # API routes (not SSG)Best Practices
Section titled “Best Practices”- Use SSG for static content: Blogs, documentation, marketing pages
- Leverage ISR for semi-static content: Content that updates periodically
- Prefetch data efficiently: Use
getStaticPropsto fetch all needed data at once - Optimize build time: For large numbers of static paths, consider incremental builds
- Use correct caching headers: Let CDNs cache SSG responses appropriately
- Handle errors gracefully: Return
{ notFound: true }for missing data - Validate data at build time: Catch issues before deployment
- Consider preview modes: Use Next.js preview mode for draft content
- Minimize client-side JavaScript: Hydration is faster with less JS
- Use next/image: Optimize images in SSG pages for better performance
Common Mistakes
Section titled “Common Mistakes”- Using SSG for frequently changing data: Leads to stale content
- Forgetting to implement getStaticPaths for dynamic routes: Causes build errors
- Not handling missing data: Results in undefined props or runtime errors
- Using getStaticProps for user-specific data: Should use SSR or CSR instead
- Ignoring build time increases: Large numbers of static paths can slow builds
- Not using ISR when appropriate: Missing opportunity for background updates
- Over-fetching in getStaticProps: Requesting more data than needed
- Forgetting to revalidate: Content never updates in ISR
- Setting revalidate too low: Negates benefits of static generation
- Not testing build process: Issues only appear during
next build
Performance Notes
Section titled “Performance Notes”- TTFB: Typically <50ms for CDN-served static HTML
- FCP: Fast since HTML is immediately available
- LCP: Depends on content optimization (images, fonts, etc.)
- FID: Low since minimal JavaScript is needed for hydration
- CLS: Minimal if dimensions are known upfront
- Build Time: Increases with number of static paths, but parallelizable
- Cache Hit Ratio: Near 100% for SSG content on CDN
- Cost: Orders of magnitude cheaper than SSR for equivalent traffic
Security Notes
Section titled “Security Notes”- No server exposure: No server execution at request time reduces attack surface
- Data safety: Ensure build-time data fetching doesn’t expose secrets
- Content integrity: Static HTML cannot be tampered with without redeploying
- Dependency scanning: Keep build-time dependencies updated
- Environment variables: Only
NEXT_PUBLIC_*variables are available togetStaticProps - Headers: Can set security headers via
_headersfile or middleware - File system access: Be cautious when reading files during build (path traversal)
SEO Considerations
Section titled “SEO Considerations”- Fully rendered HTML: Search engines see complete content immediately
- Fast loading: Improves Core Web Vitals (LCP, FID, CLS)
- No JavaScript dependency: Content is available even if JavaScript fails
- Structured data: Easy to include JSON-LD in static HTML
- Meta tags: Use
<Head>to set dynamic titles and descriptions - Canonical URLs: Prevent duplicate content issues
- Sitemap generation: Easy to generate sitemap from static paths
- Internationalization: Compatible with Next.js i18n routing
- Core Web Vitals: SSG excels at LCP and FID; CLS depends on layout stability
Interview Questions
Section titled “Interview Questions”- What is Static Generation (SSG) in Next.js?
- When would you choose SSG over Server-Side Rendering (SSR)?
- How does
getStaticPropswork in Next.js? - What is the purpose of
getStaticPathsin dynamic routes? - How do you implement Incremental Static Regeneration (ISR)?
- What happens if you don’t provide a
fallbackingetStaticPaths? - How does SSG affect build time and deployment?
- What are the performance benefits of SSG?
- How do you handle errors in SSG data fetching?
- How does SSG compare to SSG with ISR in terms of freshness and performance?
-
Which data fetching method is used for Static Generation in Next.js? a)
getServerSidePropsb)getStaticPropsc)getInitialPropsd)useEffectAnswer
-
What does the
revalidateoption ingetStaticPropsdo? a) Disables static generation b) Enables Incremental Static Regeneration c) Forces a rebuild on every request d) Sets the client-side cache durationAnswer
-
Which file creates a statically generated blog post page? a)
pages/blog.jsb)pages/blog/[slug].jswithgetStaticPropsc)pages/blog/index.jsd)pages/blog/[slug].jswithout any data fetchingAnswer
-
What is the default value of
fallbackingetStaticPaths? a)trueb)falsec)'blocking'd)undefinedAnswer
-
How does Next.js handle a request for a statically generated page? a) Executes
getStaticPropson every request b) Serves pre-built HTML from the.next/directory c) Redirects to the client-side rendering version d) Returns a 404 if the page isn’t ingetStaticPathsAnswer
Practice Exercise
Section titled “Practice Exercise”- Create a new Next.js project called
ssg-exercise - Create a static blog with:
- Homepage listing all blog posts (
/) - Individual blog post pages (
/posts/[slug])
- Homepage listing all blog posts (
- Fetch blog post data from a JSON file in the
public/directory - Implement
getStaticPropsandgetStaticPathsfor the post pages - Add metadata (title, description) to each post using the
<Head>component - Style the blog using CSS Modules
- Run
next buildto generate the static HTML - Verify the generated HTML in the
.next/directory - Start the production server and verify the blog works
Mini Project
Section titled “Mini Project”Build a documentation website with Static Generation:
- Create a Next.js project for documentation
- Organize content in Markdown files in a
docs/directory - Create a dynamic route
docs/[slug].jsthat:- Reads the Markdown file from
docs/ - Converts Markdown to HTML using
remark - Displays the documentation with proper styling
- Reads the Markdown file from
- Implement
getStaticPropsto fetch and convert the Markdown content - Implement
getStaticPathsto return all possible documentation pages - Add a sidebar navigation that lists all documentation pages
- Implement search functionality that filters documentation titles (client-side)
- Add versioning support (e.g.,
/docs/v1/[slug],/docs/v2/[slug]) - Deploy to Vercel and verify the documentation loads instantly
- Add a sitemap.xml generator that runs during build
Summary
Section titled “Summary”In this topic, you learned about Static Generation (SSG) in Next.js, how it works at build time, its benefits for performance and SEO, and how to implement it using getStaticProps and getStaticPaths. You also learned about Incremental Static Regeneration (ISR) as an extension of SSG for content that needs periodic updates.
Cheat Sheet
Section titled “Cheat Sheet”# Basic SSG Pageexport async function getStaticProps({ params }) { const data = await fetchData(params.id) return { props: { data } }}
export async function getStaticPaths() { return { paths: [{ params: { id: '1' } }, { params: { id: '2' } }], fallback: false // or true / 'blocking' }}
# SSG with ISRexport async function getStaticProps({ params }) { const data = await fetchData(params.id) return { props: { data }, revalidate: 60 // seconds }}
# Error Handling in SSGexport async function getStaticProps({ params }) { const res = await fetchData(params.id) if (!res.ok) { return { notFound: true } } const data = await res.json() return { props: { data } }}
# Markdown to HTML SSGimport { remark } from 'remark'import html from 'remark-html'
export async function getStaticProps({ params }) { const markdown = await fs.readFile(`posts/${params.slug}.md`, 'utf8') const processed = await remark().use(html).process(markdown) return { props: { content: processed.toString() } }}Related Topics
Section titled “Related Topics”- Server-Side Rendering (SSR)
- Incremental Static Regeneration (ISR)
- Client-Side Data Fetching
- Data Fetching with getStaticProps and getStaticPaths
- Data Fetching with getServerSideProps
- Choosing the Right Data Fetching Method
- Preview Mode
- Incremental Static Regeneration