npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nextjs-indexnow

v0.1.4

Published

IndexNow integration for Next.js — submit added and changed URLs to Bing, Yandex, and other IndexNow search engines after every build.

Downloads

96

Readme

nextjs-indexnow

IndexNow integration for Next.js — automatically ping Bing, Yandex, Seznam, Naver, and Yep with URLs that were added or changed in the latest build.

  • Zero sitemap dependency. Discovers URLs directly from Next's build output (.next/prerender-manifest.json + .next/routes-manifest.json), including every result of generateStaticParams.
  • Diff-based by default. Persists a state file between builds and only submits URLs that are new or whose content fingerprint changed.
  • Bundler-agnostic. Runs as a postbuild CLI, so it works with both webpack and Turbopack (Next 15/16).
  • Safe defaults. Dry-run unless INDEXNOW_SUBMIT=true.
  • Pluggable state. Ship your own adapter for Vercel KV, Redis, S3, etc.

Installation

pnpm add -D nextjs-indexnow
# or: npm i -D nextjs-indexnow / yarn add -D nextjs-indexnow

Quickstart

1. Generate a key and host the key file:

npx nextjs-indexnow init
# writes public/<key>.txt and prints the key

2. Wire up next.config.ts:

import { withIndexNow } from "nextjs-indexnow";

export default withIndexNow(
  {
    // your existing Next config
  },
  {
    host: "example.com",
    exclude: ["/api/**", "/admin/**"],
  },
);

3. Add a postbuild script:

// package.json
{
  "scripts": {
    "build": "next build",
    "postbuild": "nextjs-indexnow"
  }
}

4. Configure env vars:

| Var | Required | Description | | ----------------- | -------- | --------------------------------------------------------------- | | INDEXNOW_KEY | yes | The key you generated above. Must match public/<key>.txt. | | INDEXNOW_SUBMIT | no | Set to "true" on production deploys to actually POST. Default: dry-run. |

Set INDEXNOW_SUBMIT=true only in production — preview deploys should stay in dry-run to avoid flooding the IndexNow API with short-lived URLs.

How URL discovery works

After next build, nextjs-indexnow reads:

  • .next/prerender-manifest.json → concrete URLs from static pages and generateStaticParams.
  • .next/routes-manifest.json → additional static routes, basePath, and redirects (to strip redirect sources).

It then filters:

  • Internal routes (/_error, /_not-found, .rsc endpoints).
  • Anything under /api/*.
  • URLs matched by your redirects() config.
  • Your exclude globs.

…and keeps only URLs matched by your include globs (default: everything).

Each URL is fingerprinted from manifest metadata plus the generated .html file's size and mtime. On the next build, URLs whose fingerprint hasn't changed are skipped.

Config reference

interface IndexNowOptions {
  /** Bare hostname, e.g. "example.com". Required. */
  host: string;

  /** Env var holding the IndexNow key. Default "INDEXNOW_KEY". */
  keyEnv?: string;

  /** Public URL where the key is served. Default: https://{host}/{key}.txt. */
  keyLocation?: string;

  /** Allowlist globs. Default ["/**"]. */
  include?: string[];

  /** Blocklist globs. Default []. */
  exclude?: string[];

  /** Pluggable state storage. Default FileStateAdapter(".indexnow-state.json"). */
  state?: StateAdapter;

  /** IndexNow endpoint URL. Override for testing. */
  endpoint?: string;

  /** Env var flag that must equal "true" to POST. Default "INDEXNOW_SUBMIT". */
  submitEnv?: string;

  /** .next directory. Default ".next". */
  distDir?: string;
}

Custom state adapters

Ship state somewhere persistent — Vercel KV, S3, Redis, Postgres — by implementing:

interface StateAdapter {
  read(): Promise<IndexNowState | null>;
  write(state: IndexNowState): Promise<void>;
}

Example (Vercel KV):

import { kv } from "@vercel/kv";
import { withIndexNow, type IndexNowState, type StateAdapter } from "nextjs-indexnow";

const kvState: StateAdapter = {
  async read() {
    return (await kv.get<IndexNowState>("indexnow:state")) ?? null;
  },
  async write(state) {
    await kv.set("indexnow:state", state);
  },
};

export default withIndexNow(
  { /* next config */ },
  { host: "example.com", state: kvState },
);

CLI

nextjs-indexnow [run]       Read .next and submit changed URLs (run is default)
nextjs-indexnow init        Generate a key and write public/<key>.txt
nextjs-indexnow --help

How config is resolved

withIndexNow writes your options to a sidecar file at node_modules/.cache/nextjs-indexnow/options.json the moment Next imports your config. The CLI reads that file after the build. This means:

  • You can use next.config.ts, .mjs, .js, or .cjs — we don't parse your config, so whatever Next loads works.
  • The function returns your Next config unchanged, so Next's config validator won't warn about unknown keys.
  • The sidecar lives under node_modules/.cache, which is already gitignored everywhere.

If you can't use withIndexNow — e.g. a setup where options need to live outside next.config — drop a standalone indexnow.config.mjs next to your project root:

// indexnow.config.mjs
/** @type {import('nextjs-indexnow').IndexNowOptions} */
export default {
  host: "example.com",
  exclude: ["/api/**"],
};

The Next-build JSON takes precedence; the standalone file is the fallback.

Why a postbuild CLI and not a build hook?

Next 16 defaults to Turbopack, which silently ignores next.config's webpack customization. A webpack-hook plugin would no-op for most users with no warning. A postbuild script runs once after every build, regardless of bundler.

License

MIT © Sebastian Grebe