DEV Community

vs7ironman
vs7ironman

Posted on

Why I Stopped Using Static Site Generation for Large Next.js Projects

For years, Static Site Generation (SSG) felt like the obvious choice for SEO-focused websites.

Pre-render everything, serve static HTML, and enjoy fast page loads.

That works well until your site grows beyond a few hundred pages.

While building a software comparison platform, I ran into a problem: every new record in the database could generate dozens of new pages. What started as a few hundred URLs quickly became thousands.

At that point, traditional static generation started becoming a bottleneck rather than an advantage.

The Scaling Problem

Imagine a site with:

  • 150 software products
  • Individual product pages
  • Alternatives pages
  • Category pages
  • Comparison pages

The total URL count grows surprisingly fast.

The challenge isn't serving the pages.

The challenge is rebuilding them.

If every deployment requires regenerating thousands of pages, build times eventually become unacceptable.

A deployment that takes 30 seconds at 100 pages can become several minutes once the site reaches tens of thousands of URLs.

My Initial Approach

My original implementation relied heavily on static generation.

The benefits were obvious:

  • Excellent Lighthouse scores
  • Fast initial loads
  • Predictable SEO behavior
  • Simple hosting requirements

However, every time I added or updated records, I needed to consider regeneration costs.

The larger the dataset became, the more painful deployments became.

Moving to Dynamic Rendering

Instead of generating every page at build time, I moved most pages to dynamic rendering backed by a PostgreSQL database.

The application uses:

  • Next.js App Router
  • Route Handlers
  • Server Components
  • PostgreSQL via Supabase

A route simply receives a slug and retrieves the relevant record:

export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params

  const { data } = await supabase
    .from("tools")
    .select("*")
    .eq("slug", slug)
    .single()

  return <ToolPage tool={data} />
}
Enter fullscreen mode Exit fullscreen mode

The page template remains the same.

The data changes.

Why Performance Wasn't the Problem I Expected

Many developers assume dynamic rendering automatically means slow rendering.

In practice, database query optimization mattered far more than rendering strategy.

The biggest improvements came from:

  • Proper indexing
  • Eliminating unnecessary queries
  • Caching common lookups
  • Reducing over-fetching

Once those issues were addressed, page response times remained consistently fast.

Metadata at Scale

One advantage of dynamic rendering is metadata generation.

Every page can generate unique metadata directly from database fields:

export async function generateMetadata() {
  return {
    title: tool.name,
    description: tool.description,
  }
}
Enter fullscreen mode Exit fullscreen mode

This allows thousands of pages to maintain unique titles and descriptions without manual intervention.

What I Learned

1. Build Time Is a Cost

Developers often optimize runtime performance while ignoring deployment performance.

At scale, build times become part of the user experience for the development team.

2. Data Models Matter More Than Templates

The quality of a large content platform depends far more on the structure of the underlying data than on the page templates.

Well-designed schemas create flexibility.

Poor schemas create technical debt.

3. Dynamic Doesn't Mean Slow

Modern server rendering is extremely capable when paired with efficient database queries.

4. Start With Simplicity

Many scaling problems don't appear until later.

It's often better to start with a straightforward implementation and optimize once real bottlenecks emerge.

A Real-World Example

One project where I applied this approach is SoftwareDuel, a software comparison platform that relies heavily on dynamic routes and structured data rather than manually managed content.

The architecture would have been significantly more difficult to maintain using a traditional static-generation-first approach.

Final Thoughts

Static Site Generation remains an excellent tool.

But once a project begins generating thousands of URLs from structured data, it's worth reconsidering whether every page truly needs to exist at build time.

In my experience, moving to a dynamic architecture simplified deployments, reduced maintenance overhead, and made future growth far easier to manage.

The best solution wasn't eliminating complexity.

It was moving that complexity from the build process into the application itself.

Top comments (0)