Programmatic SEO in Next.js

@iannuttall // October 9, 2023 // 432 views

So maybe you’ve heard of this new and trendy thing called “programmatic SEO”, but chances are, many of the tutorials you have found involve WordPress, Google Sheets, or even dedicated “Programmatic Website” softwares and apps.

While WordPress is the leading website builder and CMS for content-driven niche websites, there are plenty of alternatives.

And if you’ve spent any time in Next.js, you know how powerful it can be for creating performant, content-driven websites.

In this guide, I’ll be walking you through the basics of building programmatic SEO websites with Next.js.

Why Next.js for Programmatic SEO?

Throughout this post, I’ll make the assumption that you have heard of or even used Next.js before. To follow along, you’ll need a basic understanding of web development fundamentals as I will be diving into some intermediate concepts here.

So… Why Next.js?

Next.js is a great option for programmatic SEO for several reasons:

  1. With Next.js, you can choose your rendering strategy and leverage server-side rendering to build content-filled pages that Google can easily crawl and index
  2. As of the latest version and introduction of App Router and React server components, Next.js has SEO built into the framework. Gone are the days of installing extra packages to handle this.
  3. Next.js is infinitely scalable—you don’t have to worry about your site getting slower as you add more pages thanks to dedicated rendering strategies like ISR and SSR which come out of the box with Next.js

Is Next.js better than WordPress for Programmatic SEO?

As a Next.js developer myself, I can’t help but recommend it. That said, it isn’t necessarily “better” than WordPress or other common platforms.

While you’ll gain an infinite amount of flexibility by using Next.js, it comes with tradeoffs.

For example, if you use Next.js instead of WordPress, you’ll be on your own when it comes to developing a website theme. One of the huge benefits of WordPress and other popular website builders is the vast number of free and paid website themes that you can use as starting templates. Next.js has a few themes on the web, but nothing in comparison to WordPress. This can be a huge tradeoff for some since a custom Next.js website theme could take months to develop.

What limitations does Next.js have?

Take this with a grain of salt… But from a technical limitations standpoint, there are none.

Next.js is super powerful when it comes to programmatic SEO and being a production-ready web framework, you can get it to do anything you could possibly dream of. That’s the beauty of code!

Part 1: SEO Research and Choosing a Dataset

While you might be excited to start writing code, the most important step in building a programmatic SEO website with Next.js is the research phase.

Like I mentioned earlier, you can build pretty much whatever you want. But if your pages aren’t full of useful information and your site isn’t structured well, you won’t get very far.

It’s important to find a great dataset, clean it up, and find keyword patterns with potential search traffic that can be achieved by using your dataset.

For example, in my pSEO Next.js Masterclass (part of the Practical Programmatic course), we walk through the process of building a programmatic Movies website. I started by finding a dataset to use. In the real world, I’d find a full database of movies, but for example purposes, I chose the Mflix Sample MongoDB dataset.

Once I had my dataset in hand, I studied it for a bit, and after finding some promising ideas hidden in the data, I moved on to the keyword research.

All my keyword research happens in Ahrefs. After exploring some of the ideas I had come up with, I found this:

As you can see, with relatively low keyword difficulties, there is a pattern in the search results: Movies with [actor or actress].

We could use Next.js to generate several hundred pages using this pattern. For example, we might create a page called “Top 12 Movies With Adam Sandler” that targets the keyword “movies with adam sandler”.

Once you’ve spent some time researching and have validated that there is an opportunity, it’s time to pick a data store and find a dataset to use!

Part 2: Choosing a Data Store

Next.js is a web framework, so you can use any data store you want! Heck, you could even pull data from multiple sources if needed.

With all these options available, which is the best for programmatic SEO with Next.js? Below, I’ll walk through a couple common options and when you might use each.

When to use a Relational Database (SQL)

One of the most common database types is an SQL, or “relational” database. While this isn’t necessarily the perfect match for a pSEO website due to the highly normalized nature of an SQL database, there’s nothing a good set of query joins can’t fix!

Here are some of the scenarios when a relational database is a good choice for your site:

  1. Your dataset is already in a SQL database and you don’t want to change it
  2. You have tons of experience with SQL databases and prefer them

When to use a Document Database (NoSQL)

A Document, or NoSQL database is what I’d consider to be the best choice for a programmatic SEO website with Next.js. You’ll get super powerful query patterns, great query performance, and a data model that maps very closely to the pages you will be creating.

Personally, this is what I have used for many of my projects. For example, I run a programmatic Next.js site called Local Golf Spot that uses a MongoDB database and leverages Atlas Search for many of my queries. By using this as my data store, I’m able to write powerful geospatial queries and faceted search queries that find relevant golf courses by region, public vs. private access, and much more in my “Best Golf Courses” guides as shown below:

Here are some scenarios where a document database is a great choice:

  1. You have tens of thousands of pages and a general CMS can’t handle all this data
  2. You don’t plan on doing a lot of editing to each page after publishing (database GUIs aren’t the best for editing data)
  3. You need certain query features that NoSQL engines excel at (full text search, geospatial, etc.)

When to use a Headless CMS

A headless CMS is when you store all your content in a CMS with a GUI and then query that CMS with Next.js. This allows for great separation of concerns between your content editorial team and your developers. A few examples of this would include Headless WordPress via the GraphQL plugin, Strapi, Sanity, PayloadCMS, and more.

My other golf site, The DIY Golfer, is a semi-programmatic site (some routes are programmatic while others are hand-written). For data storage, I use Sanity and it has been a great choice so far! It gives me a powerful query language (GROQ) that I can use for my programmatic routes while offering a typical CMS editing experience for my hand-written posts and pages.

Here are some scenarios where a headless CMS makes sense:

  1. After publishing your pSEO pages, you plan on coming back and making lots of manual edits over time
  2. You are comfortable publishing in WordPress already and don’t want to change
  3. Your site is a blend of programmatic pages and non-programmatic pages

Part 3: Building Next.js Page Templates

Once you’ve chosen a dataset, done keyword research, and selected your database type, it’s time to build out those page templates!

If you remember from earlier, we had found an opportunity in Ahrefs to build out a bunch of pages such as “Top 12 Movies with Adam Sandler”, or “Top 20 Movies with Meryl Streep”.

To build this in Next.js, we can use the ISR rendering technique and dynamic routing to generate a “Top X Movies With [Actor/Actress]” page for every actor/actress in the database!

Dynamic Routing with Next.js
Dynamic Routing with Next.js

Dynamic Routing with Next.js

On your page, you’d then set up some data fetching that reaches out and finds a list of movies for this particular actor or actress:

type Movie = {
  title: string;
  poster: string;
  year: number;
  plot: string;
};

type QueryResponse = {
  movies: Movie[];
};

/**
 * Write a query to your data source to get a list of movies for the actor
 */
async function fetchMovies(actor: string): Promise<QueryResponse> {
  return Promise.resolve({
    movies: [],
  });
}

And finally, you’d take this data and render it into a page template that displays the list of movies along with other important information you identified in your research phase.

/**
 * A simplified template.  In a real app, you'd want to add a lot more
 * information to this page.
 */
export default async function Page({ params }: Props) {
  const data = await fetchMovies(params.actorSlug);

  return (
    <>
      <div>
        <h1 className="mb-4">{`Top ${data.movies.length} Movies With ${params.actorSlug}`}</h1>

        <ul className="divide-y-2 space-y-8">
          {data.movies.map((movie) => (
            <li key={movie.title} className="pt-8">
              <div className="flex gap-8">
                <Image src={movie.poster} alt={`${movie.title} poster`} />

                <h2>
                  {movie.title} ({movie.year})
                </h2>
              </div>
            </li>
          ))}
        </ul>
      </div>
    </>
  );
}

Part 4: Adding SEO Metadata to Next.js Pages

Once you have your page templates built, you’ll want to go back through and add SEO metadata that Google can read to display rich search results for each of your pages.

With Next.js, you have two primary options when it comes to generating SEO metadata.

Synchronous Metadata

For simple pages that don’t have any dynamic routing, you can simply export a special variable called metadata from your page:

import { Metadata } from "next";

export const metadata: Metadata = {
  title: "About Title Placeholder",
  description: "About Excerpt Placeholder",
};

export default function Page() {
  return (
    <div>
      <h1>About Page Placeholder</h1>
    </div>
  );
}

Asynchronous Metadata

You can also fetch data from your data store prior to generating the title, excerpt, and OpenGraph metadata tags. This is a huge advantage because all this happens server-side and therefore, Google will be able to easily crawl it. Here’s how we might add the metadata to our example from above with the movies page:

export async function generateMetadata({ params }: Props) {
  const data = await fetchMovies(params.actorSlug);

  return {
    title: `Top ${data.movies.length} Movies With ${params.actorSlug}`,
		description: `Read our list of top ${data.movies.length} Movies With ${params.actorSlug}`,
		openGraph: {
			// Placeholder...
		}
  };
}

Obviously, everything I’ve shown above has been abbreviated to keep this guide concise. This page template isn’t going to rank in Google because it doesn’t have much information (often called “thin content” by SEOs)!

If you want to dive deeper and see how to generate a “rank-worthy” page with Next.js, I cover all of this in more detail along with some tips and tricks when it comes to routing, OG Image generation, structured data and more in my Next.js pSEO Masterclass that’s part of the Practical Programmatic course.

Part 5: Generating Sitemaps with Next.js

The final step in building a great programmatic SEO website with Next.js is to generate a sitemap.

If you’re using SSG rendering, Next.js does all this for you, but in our example, we’re using data from an external database and might have thousands of pages to generate.

With this many pages, your build times will start to get pretty high and on some hosting platforms, this means extra costs. If you’re okay with this tradeoff, SSG is the way to go. If not, ISR is a great compromise!

When we use the ISR rendering strategy, we’ll need to utilize a package called next-sitemap, which allows us to generate a server-side sitemap using Next.js API routes!

The next-sitemap documentation outlines all this in detail, but here’s a quick example of how you might approach this.

First, add a postbuild step to your package.json:

{
	"build": "next build",
	"postbuild": "next-sitemap"
}

Create a config file called next-sitemap.config.js at the root of your project with the following in it:

/** @type {import('next-sitemap').IConfig} */
module.exports = {
  siteUrl: process.env.VERCEL_URL || "http://localhost:3000",
  generateRobotsTxt: true,
  exclude: ["/server-sitemap.xml"],
  robotsTxtOptions: {
    additionalSitemaps: [
      `${process.env.VERCEL_URL || "http://localhost:3000"}/server-sitemap.xml`,
    ],
  },
};

And finally, create an API route matching the file you specified in the config above.

In this route, you’ll want to write a query to your database to grab all the potential pages. You’ll then call the getServerSideSitemap method from the next-sitemap package with all of the paths that you fetched from your database:

import { getServerSideSitemap, ISitemapField } from "next-sitemap";

async function fetchSitePaths(): Promise<ISitemapField[]> {
  const baseURL = process.env.SITE_URL;

  // Replace this with a query to your CMS or Database 
  const paths = [] as any[];

  return paths.map((path) => ({
    loc: `${baseURL}/movie/${path.slug}`,
    lastmod: new Date().toISOString(),
    changefreq: "monthly",
    priority: 0.8,
  }));
}

export async function GET(request: Request) {
  const allPaths = await fetchSitePaths();

  return getServerSideSitemap(allPaths);
}

Concluding Thoughts

The steps outlined above are a great quickstart for any developer trying to leverage Next.js to capture hundreds of long-tail keywords on Google.

But above all else, remember this—you have to write for people. With Next.js, it’s easy to generate thousands of pages. What’s harder is making each and every one of those pages useful to someone reading them.

Programmatic SEO with Next.js is as much an art as it is a science. Get creative, build a pleasant user experience, and go get some organic traffic!

Frequently Asked Questions

Where can I learn more about Programmatic SEO with Next.js?

If you read this post and want to take things to the next level, I have filmed a 90 minute masterclass covering all of this and more in detail. Even better, I’ve packaged it all up into a starter Github repository that you can download and use as a template for building your own programmatic websites with Next.js!

To access the video masterclass and the repository, you can purchase the full Practical Programmatic course here. This course has it all—programmatic SEO theory, case studies, and as mentioned, my programmatic SEO with Next.js Masterclass!

What is the best Next.js rendering strategy to use for Programmatic SEO Websites?

This is a tradeoffs question. With pSEO, here is the hierarchy of rendering strategies from best to worst:

  1. Best: Static Rendering served over CDN
  2. Incremental static regeneration
  3. Server side rendering
  4. Worst: Client side rendering

Does this mean you should render all your pages with SSG? For a site with 100-200 pages, this could be a good option. But for a site of 15,000 pages, your build times are going to start getting pretty big and it becomes an operational burden to maintain.

My general recommendation is this—as long as it’s not hurting your workflows or build costs, go for SSG. After that, opt for ISR, and if your content has dynamic data points (like weather), go for SSR.

CSR has its place, but you better have a great reason for using it or you’ll be hurting on the SEO side of things.

What is the max number of pages Next.js can generate?

With Next.js, the answer is… Infinite!

Next.js allows us to use ISR (incremental static regeneration) or full-on SSR (server side rendering), so your site can have as many pages as your database can hold (which is billions of pages). Furthermore, with next-sitemap package, we can generate an infinite number of sitemaps that are maxed out at a set number of pages per sitemap. All these sitemaps will then be stored in the “sitemap index” which is what we will submit to Google for crawling.

The catch? Google will NOT index all of these pages right away. If you generate 50,000 pages and deploy them to a brand new domain, Google is going to take months to index even a small portion of them. Great internal linking and discovery helps speed up the process, but don’t count on this to be a fast process.