Skip to content

Next.js SSG — Static Site Generation with Next.js Explained

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Next.js SSG. We cover key concepts, practical examples, and best practices to help you master this topic.

Next.js SSG pre-builds pages at compile time using getStaticProps and getStaticPaths, generating static HTML that loads instantly from CDN edge.

What You'll Learn

By the end of this tutorial, you'll understand how to configure Next.js for static generation, use getStaticProps and getStaticPaths, optimize builds, and deploy Next.js SSG sites.

Why It Matters

Next.js makes SSG practical for real-world applications by handling routing, data fetching, image optimization, and build optimization out of the box. It's the most popular framework for static site generation in the React ecosystem.

Real-World Use

A documentation site built with Next.js SSG generates hundreds of doc pages at build time from markdown files. Each page is pre-rendered, optimized, and deployed to Vercel's edge network. Users get instant page loads while the team writes content in markdown.

Next.js SSG Architecture

graph TD
    A[Next.js SSG App] --> B[Build Process
next build] B --> C{Page Type?} C -->|Static| D[getStaticProps] C -->|Dynamic| E[getStaticPaths +
getStaticProps] C -->|Client| F[Client-side fetch] D --> G[Pre-rendered HTML] E --> G F --> H[Runtime rendering] G --> I[Static Export
or CDN Deploy] H --> J[Browser] I --> J style D fill:#27ae60,color:#fff style E fill:#3498db,color:#fff style G fill:#e67e22,color:#fff

Setting Up Next.js SSG

// pages/index.js — Static homepage
export default function Home({ posts, buildTime }) {
    return (
        <div>
            <h1>My SSG Blog</h1>
            <p>Built at: {new Date(buildTime).toLocaleString()}</p>
            <ul>
                {posts.map(post => (
                    <li key={post.id}>
                        <a href={`/posts/${post.slug}`}>{post.title}</a>
                    </li>
                ))}
            </ul>
        </div>
    );
}

// getStaticProps runs at build time
export async function getStaticProps() {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts');
    const posts = await res.json();

    return {
        props: {
            posts: posts.slice(0, 10),
            buildTime: Date.now()
        }
    };
}

Dynamic Routes with SSG

// pages/posts/[slug].js — Dynamic SSG page
export default function Post({ post, buildTime }) {
    return (
        <article>
            <h1>{post.title}</h1>
            <p className="meta">
                Built at: {new Date(buildTime).toISOString()}
            </p>
            <div>{post.body}</div>
        </article>
    );
}

// getStaticPaths: define which slugs to pre-build
export async function getStaticPaths() {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts');
    const posts = await res.json();

    const paths = posts.slice(0, 10).map(post => ({
        params: { slug: post.slug || String(post.id) }
    }));

    return { paths, fallback: false };
}

// getStaticProps: fetch data for each slug
export async function getStaticProps({ params }) {
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.slug}`);
    const post = await res.json();

    return {
        props: {
            post,
            buildTime: Date.now()
        }
    };
}

Static Export Configuration

// next.config.js — Full static export
module.exports = {
    output: 'export',  // Generates static HTML

    // Optional: Configure image optimization
    images: {
        unoptimized: true  // Required for static export
    },

    // Optional: Set base path for subdirectory deployment
    basePath: '/docs',

    // Optional: Internationalization
    i18n: {
        locales: ['en', 'fr', 'es'],
        defaultLocale: 'en',
    },
};

// Build command: next build && next export
// Output goes to "out/" directory by default

// Package.json scripts
{
    "scripts": {
        "dev": "next dev",
        "build": "next build",
        "export": "next export",
        "deploy": "next build && next export && npx netlify-cli deploy --dir=out"
    }
}

Common Mistakes

  1. Using getServerSideProps when getStaticProps works. If your data doesn't change per request, use getStaticProps. It's faster and cheaper.
  2. Not using fallback for dynamic routes. Without fallback, missing routes return 404. Use fallback: true or fallback: 'blocking' for uncached pages.
  3. Forgetting to configure output: export. Without output: 'export', Next.js generates a hybrid app that needs a Node.js server.
  4. Over-fetching data in getStaticPaths. Generating thousands of paths at once slows builds. Batch paths or use fallback for large sites.
  5. Storing secrets in getStaticProps. Build-time code can be exposed. Use environment variables and server-only imports for API keys.

Practice Questions

  1. What functions does Next.js provide for SSG data fetching?
  2. How do you configure Next.js to output fully static files?
  3. What does the fallback option do in getStaticPaths?
  4. How does getStaticPaths know which pages to pre-build?
  5. Can you use client-side data fetching alongside SSG in Next.js?

Challenge: Build a Next.js SSG blog with at least 10 posts from an external API, dynamic routes for individual posts, a static homepage listing all posts, and configure it for full static export.

FAQ

Does Next.js SSG require a server at runtime?

No. With output: 'export', Next.js generates pure static files that work on any static host. Without that flag, it runs as a hybrid app needing a Node.js server.

Can I use Next.js SSG with a CMS?

Yes. Fetch CMS content in getStaticProps at build time. This works with any headless CMS (Contentful, Sanity, Strapi, WordPress API).

How does image optimization work with static export?

Next.js's built-in image optimizer needs a server. For static export, use unoptimized images or optimize them during build with a custom script.

What happens during next build for SSG pages?

Next.js executes getStaticPaths to find all routes, then calls getStaticProps for each route, renders the page to HTML, and saves static files.

How does ISR relate to SSG in Next.js?

ISR (Incremental Static Regeneration) extends SSG by adding the revalidate property. Next.js re-renders ISR pages in the background when traffic arrives after the revalidation window.

Mini Project

Create a Next.js SSG portfolio site: build a homepage with static content, projects page that fetches data from an API at build time, individual project pages with dynamic routes, a contact page with static content, and configure it for full static export.

What's Next

Now you can build Next.js SSG pages. Dive deeper into getStaticProps to master build-time data fetching strategies.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro