🚀 Big News:Socket Has Acquired Secure Annex.Learn More →
Socket
Book a DemoSign in
Socket

@cooperco/nuxt-layer-seo

Package Overview
Dependencies
Maintainers
3
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cooperco/nuxt-layer-seo

SEO Nuxt layer for cooperco projects

latest
Source
npmnpm
Version
1.1.0
Version published
Maintainers
3
Created
Source

Nuxt SEO Layer

A Nuxt layer that provides the full Nuxt SEO suite plus Nuxt AI Ready for Nuxt projects, plus automated linting.

Features

The layer registers each of the @nuxtjs/seo bundled sub-modules directly in its modules array, alongside AI Ready and its MCP toolkit:

  • @nuxtjs/sitemap — XML sitemap generation
  • @nuxtjs/robots — robots.txt configuration
  • nuxt-og-image — Open Graph image generation
  • nuxt-schema-org — Schema.org structured data
  • nuxt-link-checker — broken link detection
  • nuxt-seo-utils — meta tag utilities
  • nuxt-site-config — shared site config (site.url, site.name, etc.)
  • nuxt-ai-ready — generates /llms.txt and /llms-full.txt for LLM crawlers and exposes an MCP surface (list_pages, search_pages) that AI agents can query. Per-page markdown companions (e.g. /about.md) are emitted automatically when pages are prerendered.
  • @nuxtjs/mcp-toolkit — runtime MCP server that hosts the AI Ready tools/resources at /mcp over Streamable HTTP. Shipped alongside AI Ready so the MCP surface activates out of the box.
  • ESLint integration with stylistic rules

Why register each @nuxtjs/seo sub-module explicitly instead of just listing @nuxtjs/seo? The @nuxtjs/seo meta-module uses a declarative moduleDependencies field to tell Nuxt which sub-modules to load. In practice, that auto-loading does not kick in when the meta-module is registered from within a Nuxt layer's modules array — no auto-imports are registered and defineSitemapEventHandler is not found at build time. Enumerating the sub-modules directly sidesteps the issue. nuxt-ai-ready and @nuxtjs/mcp-toolkit are not meta-modules, so they're registered normally.

Configuration

The SEO layer's own nuxt.config.ts:

// nuxt.config.ts in the seo layer
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/sitemap',
    '@nuxtjs/robots',
    'nuxt-og-image',
    'nuxt-schema-org',
    'nuxt-link-checker',
    'nuxt-seo-utils',
    'nuxt-site-config',
    'nuxt-ai-ready',
    '@nuxtjs/mcp-toolkit',
    '@nuxt/eslint',
  ],
  eslint: {
    config: {
      stylistic: true
    }
  }
})

Consumer apps should set site.url (and optionally site.name) in their own nuxt.config.ts so the modules can generate absolute URLs:

export default defineNuxtConfig({
  extends: ['@cooperco/nuxt-layer-seo'],
  site: {
    url: 'https://example.com',
    name: 'My Site'
  }
})

Consumer app setup: known gotchas

Three things a consuming app needs beyond just extends-ing the layer:

1. Vite SSR externals (required — production build will 500 without this)

Vue's @vue/compiler-core CJS build does require('entities/decode'), which Rollup rewrites to an ESM default import. entities@^7.0.1 dropped default exports from its subpath ESM builds, so the bundled production server crashes at import time with "does not provide an export named 'default'". Fix: externalize the Vue compiler packages so Node handles the CJS/ESM interop at runtime.

// consumer app nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@cooperco/nuxt-layer-seo'],
  vite: {
    ssr: {
      external: ['@vue/compiler-core', '@vue/compiler-dom', '@vue/compiler-ssr'],
    },
  },
  nitro: {
    externals: {
      external: ['entities', 'entities/decode', 'entities/escape'],
    },
  },
})

2. nuxt-og-image renderer + ejected template (required for OG image generation)

nuxt-og-image needs at least one image renderer. The layer ships @takumi-rs/core as a runtime dependency, so the takumi renderer resolves automatically for every consumer — no manual install needed on your end.

The bundled "community" templates (e.g., NuxtSeo, BlogPost) are dev-only, however. For production builds you must eject the template you want to use — copy it from the module into your app's components/OgImage/ directory so the build compiles it as a first-party component:

cp node_modules/nuxt-og-image/dist/runtime/app/components/Templates/Community/NuxtSeo.takumi.vue \
   app/components/OgImage/NuxtSeo.takumi.vue

Then call defineOgImage with the v6 API (the old object syntax was removed — see nuxt-modules/og-image#495):

// any page
defineOgImage('NuxtSeo', {
  title: 'Your Page Title',
  description: 'Your Page Description',
})

3. AI Ready endpoints and the dev/prod page index

Once the layer is extended, the following endpoints are live:

  • /llms.txt — canonical LLM discovery manifest for the site (overview, sitemap links, MCP URL).
  • /llms-full.txt — full markdown content of every indexed page, for retrieval pipelines.
  • /<route>.md — per-page markdown companion for any prerendered route (e.g. /about.md, /index.md). Written at build time as pages are prerendered.
  • /mcp — Model Context Protocol endpoint exposing list_pages and search_pages tools and a pages resource. Reachable via POST /mcp with Accept: application/json, text/event-stream.

AI Ready populates its page index at prerender time, which means:

  • Dev mode (nuxt dev): endpoints are reachable but the page index is empty — /llms.txt shows the structure and a "dev mode - run nuxi generate for page titles" note. This is expected.
  • Production build (nuxt build + normal Nitro deploy): .output/server/package.json declares the SQLite driver that AI Ready needs at runtime. Running npm install --production inside .output/server/ on the deploy host (the standard Nitro deployment pattern) installs the driver and the endpoints work fully. You never need to install better-sqlite3 in your own app — Nitro's deploy flow handles it.
  • Static generation (nuxt generate): /llms.txt, /llms-full.txt, and all *.md companions are emitted as static files during the prerender pass.

nuxt-link-checker ships two ESLint rules (link-checker/valid-route, link-checker/valid-sitemap-link) that statically validate <NuxtLink to="…"> and <a href="…"> values against the app's route manifest. If your app uses a custom router.options.ts instead of file-based routing, the rules can't discover your routes and false-positive on valid links. Disable them:

// eslint.config.mjs
export default withNuxt({
  rules: {
    'link-checker/valid-route': 'off',
    'link-checker/valid-sitemap-link': 'off',
  },
})

The runtime link-checker scanner (visible in Nuxt Devtools) still works.

Development

# Install dependencies
npm install

# Start development server
npm run dev

# Run ESLint
npm run lint

# Fix linting issues automatically
npm run lint:fix

# Run TypeScript type checking
npm run typecheck

Usage

To use this layer in your Nuxt project:

// nuxt.config.ts
export default defineNuxtConfig({
  extends: [
    '@cooperco/nuxt-layer-seo'
  ]
})

This will automatically include the SEO layer features.

Dependencies

All of the layer's runtime Nuxt modules live in dependencies (not devDependencies), so downstream consumers inherit them transitively when they install @cooperco/nuxt-layer-seo — no manual installs needed on the consumer side:

  • @nuxtjs/seo — the Nuxt SEO umbrella, which brings all seven sub-modules (sitemap, robots, og-image, schema-org, link-checker, seo-utils, site-config) as transitive deps.
  • nuxt-ai-ready — AI/LLM discoverability module (llms.txt, llms-full.txt, per-page .md companions, MCP tool/resource definitions).
  • @nuxtjs/mcp-toolkit — runtime MCP server that hosts AI Ready's tools and resources at /mcp.
  • @takumi-rs/core — image renderer backend used by nuxt-og-image for OG image generation.
  • @nuxt/eslint — ESLint integration for the consumer's eslint.config.mjs.

The layer's own build-time deps (nuxt, vue, vue-tsc, typescript, eslint, @types/node) remain in devDependencies — they're only used to build and lint this layer's own source code in isolation, never by downstream consumers.

Together these provide a solid SEO + AI discoverability foundation for your Nuxt application.

Claude Code Skills

This repo includes Claude Code skills for common tasks in apps that use these layers. To use them, copy the relevant skill files from the skills/ directory into your app's .claude/skills/ directory.

Skills provided by the SEO layer:

SkillFileDescription
/add-sitemap-sourceskills/add-sitemap-source.mdAdd dynamic sitemap URL sources with defineSitemapEventHandler and configure sitemap.sources
# Copy SEO layer skills into your app
mkdir -p .claude/skills
cp node_modules/@cooperco/nuxt-layer-seo/../../skills/add-sitemap-source.md .claude/skills/

Or copy them directly from the nuxt-layers repository.

Publishing to npm (tag-based)

This layer is published using GitHub Actions when you push a tag that matches the seo-vX.Y.Z pattern.

High-level flow:

  • Bump the version in layers/seo/package.json (SemVer).
  • Commit and push your changes to main (or ensure the commit is on main).
  • Create and push a tag named seo-vX.Y.Z (matching the package.json version).
  • The Publish SEO Layer workflow installs deps, checks if that exact version already exists on npm, and if not, publishes to npm.

Important notes:

  • Do NOT rely on npm version creating a tag for you (it will create vX.Y.Z). We use a custom tag prefix seo-v.
  • The workflow will skip if the version already exists.

Step-by-step

  • Bump the version in layers/seo/package.json
  • Option A (recommended): use npm version without creating a tag
    • Bash:
      cd layers/seo
      npm version patch --no-git-tag-version  # or minor | major
      
    • PowerShell:
      Set-Location layers/seo
      npm version patch --no-git-tag-version  # or minor | major
      
  • Option B: manually edit the version field in layers/seo/package.json (SemVer: MAJOR.MINOR.PATCH)
  • Commit and push the change (from repo root or layers/seo)
git add layers/seo/package.json
git commit -m "chore(seo): bump version"
git push origin main
  • Create and push the tag using the new version
  • Get the new version value:
    • Bash:
      cd layers/seo
      VERSION=$(node -p "require('./package.json').version")
      cd ../..
      git tag "seo-v$VERSION"
      git push origin "seo-v$VERSION"
      
    • PowerShell:
      Set-Location layers/seo
      $version = node -p "require('./package.json').version"
      Set-Location ../..
      git tag "seo-v$version"
      git push origin "seo-v$version"
      
  • GitHub Actions will publish
  • Workflow: .github/workflows/publish-seo.yml
  • Auth: uses NPM_TOKEN configured as a GitHub secret
  • Behavior: installs, checks npm view @cooperco/nuxt-layer-seo@<version>, publishes if not found

Troubleshooting

  • Version already exists: bump the version again (patch/minor/major) and push a new tag.
  • Auth errors (401/403): ensure NPM_TOKEN is set in repo secrets and org access is correct.

FAQs

Package last updated on 15 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts