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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@enfyra/nuxt-seo

v0.1.12

Published

A powerful, configurable SEO module for Nuxt 4 with automatic OG image generation

Downloads

1,060

Readme

@enfyra/nuxt-seo

A powerful SEO optimization tool for Nuxt 4 that provides comprehensive SEO features including meta tags, Open Graph, Twitter Cards, structured data, robots.txt, sitemap generation, and automatic OG image generation.

Optimized by Enfyra Team.

Features

  • Meta Tags: Title, description, keywords, robots
  • Open Graph: Full OG tags support for social media sharing
  • Twitter Cards: Summary large image cards
  • Structured Data: JSON-LD schema.org markup (Organization, Website, WebPage, Article, Product, etc.)
  • Robots.txt: Dynamic generation with configurable rules
  • Sitemap.xml: Automatic sitemap generation from page configurations
  • Web Manifest: PWA support with configurable icons and manifest properties
  • OG Image Generation: Automatic screenshot-based OG image generation with caching
  • Full TypeScript Support: Complete type definitions with IntelliSense
  • Auto-imports: Zero-config auto-imports for all composables (usePageSEO, useSEO)
  • Type-safe Configuration: Type-safe config in nuxt.config.ts with autocomplete
  • Per-page Configuration: Configure SEO per page in nuxt.config.ts or runtime
  • Global Configuration: Set defaults for all pages
  • Disable SEO: Easy toggle for staging/testing environments

Installation

npm install @enfyra/nuxt-seo
# or
yarn add @enfyra/nuxt-seo
# or
pnpm add @enfyra/nuxt-seo

Configuration

Add the module to your nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['@enfyra/nuxt-seo'],
  
  seo: {
    enabled: true, // Set to false to disable SEO (useful for staging)
    siteUrl: 'https://example.com',
    siteName: 'My Website',
    defaultLocale: 'en',
    defaultImage: '/og-image.png',
    defaultType: 'website', // 'website' | 'article' | 'product' | 'profile'
    
    // Per-page SEO configuration
    pages: {
      '/': {
        title: 'Homepage - My Website',
        description: 'Welcome to my website',
        keywords: ['keyword1', 'keyword2'],
        changefreq: 'daily',
        priority: 1.0,
        structuredData: {
          '@type': 'SoftwareApplication',
          name: 'My App',
          // ... more structured data
        }
      },
      '/about': {
        title: 'About Us',
        description: 'Learn more about our company',
        changefreq: 'monthly',
        priority: 0.8
      }
    },
    
    // Robots.txt configuration
    robots: {
      enabled: true,
      disallow: ['/api/', '/admin/'],
      sitemap: true,
      sitemapPath: '/sitemap.xml'
    },
    
    // Social media configuration
    social: {
      twitter: {
        site: '@yourhandle',
        creator: '@yourhandle'
      },
      facebook: {
        appId: 'your-app-id'
      }
    },
    
    // Web Manifest configuration (PWA)
    webmanifest: {
      icons: [
        {
          src: '/icon-192x192.png',
          sizes: '192x192',
          type: 'image/png',
        },
        {
          src: '/icon-512x512.png',
          sizes: '512x512',
          type: 'image/png',
        },
      ],
      start_url: '/',
      display: 'standalone',
      background_color: '#ffffff',
      theme_color: '#000000',
    }
  }
})

TypeScript & Auto-imports

🎯 Zero-Configuration Auto-imports

No manual imports required! All composables are automatically available in your Nuxt app:

  • usePageSEO - Automatically imported
  • useSEO - Automatically imported
  • ✅ Full TypeScript IntelliSense support
  • ✅ Type-safe configuration in nuxt.config.ts

📦 Importing Types

If you need to use types in your code, you can import them:

import type { 
  SEOConfig, 
  ModuleOptions,
  OgImageConfig,
  WebManifestConfig
} from '@enfyra/nuxt-seo'

🔍 Type Definitions

The module provides complete TypeScript definitions for:

  • SEOConfig - Main SEO configuration interface
  • ModuleOptions - Module configuration options
  • OgImageConfig - OG image generation configuration
  • WebManifestConfig - Web manifest (PWA) configuration
  • PageSEOConfig - Extended config for per-page SEO
  • RobotsConfig - Robots.txt configuration
  • SocialConfig - Social media configuration

All types are automatically available in your IDE with full IntelliSense support.

Usage

Auto-imported Composables

No manual imports needed! The module automatically imports usePageSEO and useSEO composables. Just use them directly in your components:

Basic Usage in Pages

<script setup lang="ts">
// Automatically uses config from nuxt.config.ts for current route
usePageSEO()
</script>

<template>
  <div>Your page content</div>
</template>

Override SEO in Pages

<script setup lang="ts">
// Override with page-specific config
usePageSEO({
  title: 'Custom Page Title',
  description: 'Custom description for this page',
  keywords: ['custom', 'keywords'],
  image: '/custom-og-image.png'
})
</script>

Advanced Usage with Structured Data

<script setup lang="ts">
usePageSEO({
  title: 'Article Title',
  description: 'Article description',
  type: 'article',
  publishedTime: '2024-01-01T00:00:00Z',
  modifiedTime: '2024-01-02T00:00:00Z',
  author: 'John Doe',
  structuredData: {
    '@type': 'Article',
    headline: 'Article Title',
    description: 'Article description',
    author: {
      '@type': 'Person',
      name: 'John Doe'
    },
    publisher: {
      '@type': 'Organization',
      name: 'My Website'
    }
  }
})
</script>

Using useSEO Directly

<script setup lang="ts">
// For more control, use useSEO directly
const { meta, structuredData } = useSEO({
  title: 'Page Title',
  description: 'Page description',
  url: 'https://example.com/page',
  image: '/og-image.png',
  type: 'website'
})
</script>

Reactive usage (using computed / state)

Both usePageSEO and useSEO accept either a plain config object or a function that returns a config object, so you can safely use reactive state/computed values.

<script setup lang="ts">
const article = computed(() => ({
  title: 'Article Title',
  description: 'Article description',
  image: '/article-og.png',
}))

// Option 1: Use a function to read reactive values each time (recommended)
usePageSEO(() => ({
  title: article.value.title,
  description: article.value.description,
  image: article.value.image,
  type: 'article',
}))

// Option 2: Use directly with useSEO
const { meta, structuredData } = useSEO(() => ({
  title: article.value.title,
  description: article.value.description,
  url: `https://example.com/blog/article`,
  image: article.value.image,
  type: 'article',
}))
</script>

API Reference

Module Options

interface ModuleOptions {
  enabled?: boolean // Enable/disable SEO globally
  siteUrl?: string // Your site URL
  siteName?: string // Your site name
  defaultLocale?: string // Default locale (e.g., 'en', 'en_US')
  defaultImage?: string // Default OG image path
  defaultType?: 'website' | 'article' | 'product' | 'profile' // Default OG type
  pages?: Record<string, PageSEOConfig> // Per-page configuration
  robots?: RobotsConfig // Robots.txt configuration
  social?: SocialConfig // Social media configuration
  webmanifest?: WebManifestConfig // Web manifest (PWA) configuration
}

SEOConfig

interface SEOConfig {
  title?: string
  description?: string
  keywords?: string[]
  image?: string
  url?: string
  type?: 'website' | 'article' | 'product' | 'profile'
  author?: string
  publishedTime?: string
  modifiedTime?: string
  siteName?: string
  locale?: string
  alternateLocales?: string[]
  noindex?: boolean
  nofollow?: boolean
  canonical?: string
  structuredData?: Record<string, any> | Record<string, any>[]
}

PageSEOConfig

Extends SEOConfig with:

interface PageSEOConfig extends SEOConfig {
  changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'
  priority?: number // 0.0 to 1.0
  lastmod?: string // ISO date string
}

Composables

usePageSEO(pageConfig?: Partial<SEOConfig> | (() => Partial<SEOConfig>))

Composable to set SEO for the current page. Automatically merges:

  1. Global defaults from nuxt.config.ts
  2. Per-page config from nuxt.config.ts (based on current route)
  3. Runtime config passed to the composable (highest priority)

Returns: Result from useSEO()

Example (static object):

usePageSEO({
  title: 'Custom Title',
  description: 'Custom Description',
})

Example (reactive, using function):

const title = computed(() => `Blog - ${post.value.title}`)

usePageSEO(() => ({
  title: title.value,
  description: post.value.excerpt,
  image: post.value.image,
}))

useSEO(config: SEOConfig | (() => SEOConfig))

Core SEO composable that sets meta tags, Open Graph, Twitter Cards, and structured data.

Returns:

{
  meta: Record<string, any>
  structuredData?: Record<string, any> | Record<string, any>[]
}

Example:

const { meta, structuredData } = useSEO({
  title: 'Page Title',
  description: 'Page Description',
  url: 'https://example.com/page'
})

Components

SEOHead

Component for rendering structured data (JSON-LD).

<script setup lang="ts">
import { SEOHead } from '@enfyra/nuxt-seo/src/components/SEOHead'

const seoConfig = {
  title: 'Page Title',
  description: 'Page Description',
  structuredData: {
    '@type': 'WebPage',
    // ... more structured data
  }
}
</script>

<template>
  <SEOHead :config="seoConfig" />
</template>

Breadcrumbs

Component for breadcrumb navigation with structured data.

<script setup lang="ts">
import { Breadcrumbs } from '@enfyra/nuxt-seo/src/components/Breadcrumbs'

const breadcrumbItems = [
  { name: 'Home', url: '/' },
  { name: 'Category', url: '/category' },
  { name: 'Current Page', url: '/category/page' }
]
</script>

<template>
  <Breadcrumbs :items="breadcrumbItems" />
</template>

OG Image Generation

The module can automatically generate OG images by taking screenshots of your pages. This feature is disabled by default and can be enabled in your configuration.

Configuration

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@enfyra/nuxt-seo'],
  seo: {
    ogImage: {
      enabled: true,
      viewport: {
        width: 1440,
        height: 754,
      },
      quality: 85,
      format: 'webp',
      cache: {
        ttl: 24 * 60 * 60 * 1000, // 24 hours
        memoryTtl: 60 * 60 * 1000, // 1 hour
      },
    }
  }
})

Usage

Once enabled, the module automatically creates an API endpoint at /api/og that generates OG images. You can use it in your pages:

<script setup lang="ts">
const config = useRuntimeConfig()
const seoConfig = config.public.seo as any
const siteUrl = seoConfig?.siteUrl || 'https://example.com'

usePageSEO({
  title: 'My Page Title',
  description: 'My page description',
  image: `${siteUrl}/api/og?path=${encodeURIComponent('/my-page')}`,
})
</script>

How It Works

  1. Screenshot Capture: Uses Puppeteer to capture screenshots of your pages
  2. Image Optimization: Converts to WebP/JPEG format with configurable quality
  3. Caching: Two-tier caching system:
    • Memory cache: Fast access for frequently requested images (1 hour default)
    • File cache: Persistent cache stored in .enfyra-og-cache/ directory (24 hours default)
  4. Automatic URL Detection: Automatically detects the correct URL based on request headers (supports staging/production)

Requirements

  • Development: Requires Chrome/Chromium installed locally
  • Production: Uses @sparticuz/chromium for serverless environments (Vercel, Netlify, etc.)

Environment Variables

  • CHROME_PATH: Custom path to Chrome/Chromium executable (optional, for development)

Cache Management

Cache files are stored in .enfyra-og-cache/ directory. You can safely delete this folder to clear the cache. The cache key is based on the page path and host, so different environments (staging/production) have separate caches.

Performance

  • First request: ~2-5 seconds (screenshot generation)
  • Cached requests: <100ms (served from cache)
  • File size: ~100-150KB (WebP format, 1440x754)

Server Routes

The module automatically generates:

/site.webmanifest

Web App Manifest file for PWA support. The manifest is automatically generated based on your configuration. If no icons are configured, the manifest will be generated without icons to prevent errors.

Configuration:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@enfyra/nuxt-seo'],
  seo: {
    webmanifest: {
      icons: [
        {
          src: '/icon-192x192.png',
          sizes: '192x192',
          type: 'image/png',
        },
        {
          src: '/icon-512x512.png',
          sizes: '512x512',
          type: 'image/png',
        },
      ],
      start_url: '/',
      display: 'standalone', // 'fullscreen' | 'standalone' | 'minimal-ui' | 'browser'
      background_color: '#ffffff',
      theme_color: '#000000',
    }
  }
})

WebManifestConfig Interface:

interface WebManifestConfig {
  icons?: Array<{
    src: string
    sizes: string
    type?: string
    purpose?: string
  }>
  start_url?: string
  display?: 'fullscreen' | 'standalone' | 'minimal-ui' | 'browser'
  background_color?: string
  theme_color?: string
}

Important Notes:

  • Icons are optional: If you don't configure icons, the manifest will be generated without them. This prevents errors when icon files don't exist.
  • Automatic link tag: The module automatically adds a <link rel="manifest"> tag to your pages pointing to /site.webmanifest.
  • Default values: If not configured, defaults are:
    • start_url: /
    • display: standalone
    • background_color: #ffffff
    • theme_color: #000000

Example Output:

{
  "name": "My Website",
  "short_name": "My Website",
  "description": "Website description",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    {
      "src": "/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

/robots.txt

Dynamically generated based on your configuration:

User-Agent: *
Allow: /
Disallow: /api/
Disallow: /admin/

Sitemap: https://example.com/sitemap.xml

/sitemap.xml

Automatically generated from your page configurations:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://example.com/</loc>
    <lastmod>2024-01-01</lastmod>
    <changefreq>daily</changefreq>
    <priority>1.0</priority>
  </url>
  <!-- More URLs -->
</urlset>

TypeScript Support

🚀 Full TypeScript Support

The module is built with TypeScript from the ground up and provides:

  • Auto-imported Composables: usePageSEO and useSEO are automatically available with full type inference
  • Type-safe Configuration: All configuration options in nuxt.config.ts are fully typed with IntelliSense
  • Complete Type Definitions: All interfaces, types, and configurations are fully typed
  • IntelliSense Support: Full autocomplete and type checking in VS Code, WebStorm, and other IDEs
  • Type Inference: Automatic type inference for reactive configurations

📝 Type Definitions

All types are exported and available for import:

import type { 
  SEOConfig,           // Main SEO configuration
  ModuleOptions,       // Module configuration
  OgImageConfig,       // OG image generation config
  WebManifestConfig,   // Web manifest (PWA) config
  PageSEOConfig,       // Extended page SEO config
  RobotsConfig,        // Robots.txt config
  SocialConfig         // Social media config
} from '@enfyra/nuxt-seo'

💡 Type-safe Usage Examples

// Type-safe configuration in nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@enfyra/nuxt-seo'],
  seo: {
    siteUrl: 'https://example.com', // ✅ Type-checked
    defaultType: 'website',         // ✅ Literal type: 'website' | 'article' | 'product' | 'profile'
    ogImage: {
      enabled: true,                  // ✅ Type-checked
      format: 'webp',                // ✅ Literal type: 'png' | 'jpeg' | 'webp'
      quality: 85,                   // ✅ Number type
    }
  }
})
<script setup lang="ts">
// ✅ usePageSEO is auto-imported with full type inference
// ✅ All parameters are type-checked
usePageSEO({
  title: 'My Page',           // ✅ string
  description: 'Description',  // ✅ string
  type: 'article',            // ✅ 'website' | 'article' | 'product' | 'profile'
  keywords: ['tag1', 'tag2'], // ✅ string[]
  image: '/og.png',           // ✅ string
})

// ✅ Reactive usage with type inference
const article = computed(() => ({
  title: 'Article Title',
  description: 'Article description',
}))

usePageSEO(() => ({
  title: article.value.title,      // ✅ Type-safe access
  description: article.value.description,
}))
</script>

🔧 Troubleshooting TypeScript

If you encounter TypeScript errors:

  1. Restart TypeScript Server: In VS Code, press Cmd/Ctrl + Shift + P → "TypeScript: Restart TS Server"
  2. Run Nuxt Prepare: yarn nuxt prepare or npm run prepare
  3. Check Module Installation: Ensure @enfyra/nuxt-seo is in node_modules
  4. Verify Auto-imports: Check that composables are available without imports (they should be!)

Examples

Example 1: Basic Setup

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@enfyra/nuxt-seo'],
  seo: {
    enabled: true,
    siteUrl: 'https://example.com',
    siteName: 'My Website',
    defaultLocale: 'en',
    defaultImage: '/og-image.png'
  }
})
<!-- pages/index.vue -->
<script setup lang="ts">
usePageSEO()
</script>

Example 2: Article Page

<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute()
const article = await fetchArticle(route.params.slug)

usePageSEO({
  title: article.title,
  description: article.excerpt,
  type: 'article',
  publishedTime: article.publishedAt,
  modifiedTime: article.updatedAt,
  author: article.author.name,
  image: article.image,
  structuredData: {
    '@type': 'Article',
    headline: article.title,
    description: article.excerpt,
    image: article.image,
    datePublished: article.publishedAt,
    dateModified: article.updatedAt,
    author: {
      '@type': 'Person',
      name: article.author.name
    }
  }
})
</script>

Example 3: Product Page

<!-- pages/products/[id].vue -->
<script setup lang="ts">
const route = useRoute()
const product = await fetchProduct(route.params.id)

usePageSEO({
  title: product.name,
  description: product.description,
  type: 'product',
  image: product.image,
  structuredData: {
    '@type': 'Product',
    name: product.name,
    description: product.description,
    image: product.image,
    brand: product.brand,
    offers: {
      '@type': 'Offer',
      price: product.price,
      priceCurrency: 'USD',
      availability: product.inStock ? 'InStock' : 'OutOfStock'
    }
  }
})
</script>

Example 4: Disable SEO for Staging

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@enfyra/nuxt-seo'],
  seo: {
    enabled: process.env.NODE_ENV === 'production', // Disable in staging
    siteUrl: 'https://example.com',
    // ... rest of config
  }
})

Best Practices

  1. Always set siteUrl: Required for canonical URLs and OG tags
  2. Use per-page config: Configure SEO in nuxt.config.ts for static pages
  3. Override at runtime: Use usePageSEO() for dynamic content
  4. Provide structured data: Enhances search engine understanding
  5. Set appropriate changefreq and priority: Helps search engines crawl efficiently
  6. Use unique titles and descriptions: Avoid duplicate content
  7. Optimize images: Use high-quality images for OG tags (1200x630px recommended)

Troubleshooting

SEO not working?

  1. Check seo.enabled is true in nuxt.config.ts
  2. Verify siteUrl is set correctly
  3. Check browser console for errors
  4. Ensure you're calling usePageSEO() in your pages

Types or Auto-imports not working?

  1. Restart TypeScript Server: In VS Code, press Cmd/Ctrl + Shift + P → "TypeScript: Restart TS Server"
  2. Run Nuxt Prepare: yarn nuxt prepare or npm run prepare to regenerate types
  3. Check Module Installation: Ensure @enfyra/nuxt-seo is properly installed in node_modules
  4. Verify Auto-imports: Composables should be available without imports - if not, check your Nuxt version (requires Nuxt 4+)
  5. Clear Cache: Delete .nuxt folder and run yarn nuxt prepare again

Robots.txt not generating?

  1. Check robots.enabled is true
  2. Verify the route /robots.txt is accessible
  3. Check server logs for errors

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.