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

@eight25-factory/seo-essentials

v0.8.0

Published

SEO essentials for Next.js and Nuxt with Contentful or Contentstack: per-entry metadata into <head>, environment-aware robots.txt, XML sitemap that skips untranslated locales, canonical URLs, og:type QA, and CMS field provisioning. Wires itself on install

Readme

@eight25-factory/seo-essentials

Per-entry metadata into <head>, an XML sitemap that lists only published, translated, indexable pages, an environment-aware robots.txt, canonical URLs, and an og:type QA — for Next.js and Nuxt, with Contentful or Contentstack. Plus SKILLS.md, an instruction file that teaches an AI agent to keep every new page SEO-correct.

npm install               → the package detects the app and wires itself (postinstall)
Editor fills the SEO tab  → title, description, canonical, og:*, twitter:*, robots in <head>
Publishes a page          → it appears in that language's sitemap (/sitemaps/<locale>.xml, listed by /sitemap.xml)
Preview / staging deploy  → robots.txt says Disallow: /, every response says X-Robots-Tag: noindex
Production                → Allow, Sitemap: line, indexable

Install

One command in the project root. The package's postinstall runs init --yes --write for you: it detects Next or Nuxt, the CMS (from live-preview.config.mjs, CMS_PROVIDER, credential variable names or the SDK in package.json), asks the CMS for its locales when delivery credentials are in .env*, and writes the wiring:

npm install @eight25-factory/seo-essentials

What lands in the project (Next.js; Nuxt paths in docs/nuxt.md):

| File | Purpose | | -------------------------------- | ----------------------------------------------------------------------- | | seo-essentials.config.mjs | Application-owned, non-secret configuration (site, locales, robots, …) | | .env.seo-essentials.example | The variable names the generated files read | | lib/seo/config.ts | Parsed configuration + seoEnvironment() | | lib/seo/server.ts | pageMetadata(entry, { path }), rootMetadata(), notFoundMetadata() | | lib/seo/pages.ts | Lists published pages from the CMS Delivery API (native fetch, no SDK) | | app/sitemap.xml/route.ts | The sitemap (index of the language sitemaps when multi-locale) | | app/sitemaps/[file]/route.ts | One sitemap per language: /sitemaps/en-us.xml, /sitemaps/de-de.xml | | app/robots.txt/route.ts | The robots route | | app/sitemap.xsl/route.ts | Browser view of the sitemaps: formatted, clickable (crawlers get XML) | | .claude/skills/seo-essentials/ | The AI skill, plus a pointer in AGENTS.md |

It also patches next.config.* (default export wrapped in withSeoEssentials, which adds X-Robots-Tag: noindex, nofollow outside production) and app/layout.tsx (export const metadata = rootMetadata({…})) when they have the create-next-app shape, and reports anything it left alone. Existing files are never overwritten.

Then, once, create the SEO fields in the CMS with the management token from .env and add demo values so there is something to look at:

npx seo-essentials provision --seed        # Contentful: `seo` content type + link on every page type
                                           # Contentstack: `seo` global field + reference on every page type

Prefer to see the plan first? npx seo-essentials init (no --write) prints every file; npx seo-essentials provision --dry-run prints every CMS change. pnpm and --ignore-scripts do not run dependency postinstalls: run npx seo-essentials init --write yourself.

Per-page metadata

Wired for you on Next: init patches every dynamic page route under app/ ([slug], [...slug], [[...slug]], with or without a [locale] segment) to export

// app/[[...slug]]/page.tsx — added by init
import { routeMetadata, type RouteParams } from "@/lib/seo/server";

export async function generateMetadata({
  params,
}: {
  params: Promise<RouteParams>;
}): Promise<Metadata> {
  return routeMetadata("/[[...slug]]", await params);
}

routeMetadata turns the route pattern and params into the page path and locale, fetches the entry whose URL field holds that path from the Delivery API (SEO group included, cached for five minutes), and emits title (templated), description, canonical, robots, Open Graph (with a valid og:type), Twitter and hreflang. The page query needs no change. A route that already had a generateMetadata keeps it: it is renamed generateMetadataWithoutSeo and the CMS metadata is layered over its result.

A route that loads its own entry can call pageMetadata(entry, { path, locale }) instead. It accepts the raw entry in any delivery shape (GraphQL object, REST { sys, fields }, Contentstack entry) and reads the SEO group by the conventional names — seo.metaTitle (Contentful) or seo.meta_title (Contentstack), plus legacy flat fields such as seoNoIndex — falling back to the page title and the site defaults.

Nuxt: usePageSeo(page, { path: route.path }) from the generated composable.

What it does

  • Metadata in one call: title template, description, canonical, robots, og:*, twitter:*, hreflang + x-default, keywords; fallbacks are deliberate and reported as warnings.
  • One sitemap per language: with more than one locale /sitemap.xml is a sitemap index listing /sitemaps/<locale>.xml for every language that has pages; each file carries that locale's URLs with xhtml:link alternates. Single-locale sites get one flat file.
  • Sitemap that tells the truth: only published entries that answer 200 (each URL is checked, cached an hour; 404/410 and redirects drop out); per locale only when the entry is actually translated (Contentful: the entry stores its own value for translationField in that locale; Contentstack: the entry did not fall back); never noIndex or excludeFromSitemap pages; never duplicates; xhtml:link alternates grouped per entry; lastmod from the CMS; static paths appended; chunking helpers for > 50,000 URLs.
  • Environment-aware robots: production (from SEO_ENVIRONMENT, then VERCEL_ENV, CONTEXT; never NODE_ENV) serves your policy plus the Sitemap: line; anything else serves Disallow: / and adds X-Robots-Tag: noindex, nofollow to every response.
  • Canonicals with a policy: lowercase, no trailing slash, no query (configurable); entry-level override for deliberate duplicates; og:url and sitemap loc always equal the canonical.
  • og:type QA: validateOgType classifies a stored value as valid, normalizable (Web page → website) or invalid; the helper renders the fix and reports it; npx seo-essentials audit <url> --sitemap fails a deployment on invalid values after a migration.
  • CMS provisioning: provision creates the fields (idempotent) and, with --seed, demo values — published, with one page localized on request to demonstrate the translation rule.
  • AI skill: SKILLS.md with rules, recipes, a verification protocol and a diagnosis table, installed by init and by npx seo-essentials install-skills.

What it deliberately does not do

  • No runtime dependency on a CMS SDK or a framework. The package is pure functions; the Delivery API calls live in the generated lib/seo/pages.ts inside your app, where you can read and change them.
  • No JSON-LD / structured data. That is content-type specific; add it in the page next to pageMetadata.
  • No redirects or URL rewriting. Canonicals describe URLs; routing owns them.
  • No NEXT_PUBLIC_* tokens. Only the Delivery token is read, on the server, by name.

Verify

With the app running:

npx seo-essentials audit http://localhost:3000            # one page's <head>
npx seo-essentials audit http://localhost:3000 --sitemap  # robots.txt + sitemap + every listed page
npx seo-essentials check-env                              # which variable names are set (never values)

Supported integrations

| Integration | Entry point | | ----------------- | ------------------------------------------------ | | Next.js 13+ | @eight25-factory/seo-essentials/next | | Nuxt 3+ | @eight25-factory/seo-essentials/nuxt | | Contentful | @eight25-factory/seo-essentials/contentful | | Contentstack | @eight25-factory/seo-essentials/contentstack | | Framework-neutral | @eight25-factory/seo-essentials | | Generated files | @eight25-factory/seo-essentials/templates | | CLI | npx seo-essentials | | AI skill | SKILLS.md, npx seo-essentials install-skills |

Documentation

| Document | What it covers | | -------------------------------------------------------------- | -------------------------------------------------- | | SKILLS.md | How to keep pages, sitemap and robots correct | | AGENTS.md | Instructions for an agent wiring the package | | docs/nextjs.md | Complete Next.js wiring | | docs/nuxt.md | Complete Nuxt wiring | | docs/contentful.md | Contentful: fields, queries, translation rule | | docs/contentstack.md | Contentstack: global field, queries, fallback rule | | docs/setup-questions.md | The install questions and what answers them | | docs/default-configuration.md | Every option and its default | | docs/api-reference.md | Every public export | | docs/lifecycle.md | What runs where, and when | | docs/troubleshooting.md | Symptom → cause | | docs/cli.md | Every CLI command | | docs/local-install.md | Vendored installation | | DEVELOPMENT.md | Working on this package |

Requirements

Node 22 or newer. No runtime dependencies.