@enfyra/nuxt-seo
v0.1.12
Published
A powerful, configurable SEO module for Nuxt 4 with automatic OG image generation
Downloads
1,060
Maintainers
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.tswith autocomplete - ✅ Per-page Configuration: Configure SEO per page in
nuxt.config.tsor 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-seoConfiguration
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 interfaceModuleOptions- Module configuration optionsOgImageConfig- OG image generation configurationWebManifestConfig- Web manifest (PWA) configurationPageSEOConfig- Extended config for per-page SEORobotsConfig- Robots.txt configurationSocialConfig- 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:
- Global defaults from
nuxt.config.ts - Per-page config from
nuxt.config.ts(based on current route) - 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
- Screenshot Capture: Uses Puppeteer to capture screenshots of your pages
- Image Optimization: Converts to WebP/JPEG format with configurable quality
- 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)
- 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/chromiumfor 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:standalonebackground_color:#fffffftheme_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:
usePageSEOanduseSEOare automatically available with full type inference - ✅ Type-safe Configuration: All configuration options in
nuxt.config.tsare 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:
- Restart TypeScript Server: In VS Code, press
Cmd/Ctrl + Shift + P→ "TypeScript: Restart TS Server" - Run Nuxt Prepare:
yarn nuxt prepareornpm run prepare - Check Module Installation: Ensure
@enfyra/nuxt-seois innode_modules - 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
- Always set
siteUrl: Required for canonical URLs and OG tags - Use per-page config: Configure SEO in
nuxt.config.tsfor static pages - Override at runtime: Use
usePageSEO()for dynamic content - Provide structured data: Enhances search engine understanding
- Set appropriate
changefreqandpriority: Helps search engines crawl efficiently - Use unique titles and descriptions: Avoid duplicate content
- Optimize images: Use high-quality images for OG tags (1200x630px recommended)
Troubleshooting
SEO not working?
- Check
seo.enabledistrueinnuxt.config.ts - Verify
siteUrlis set correctly - Check browser console for errors
- Ensure you're calling
usePageSEO()in your pages
Types or Auto-imports not working?
- Restart TypeScript Server: In VS Code, press
Cmd/Ctrl + Shift + P→ "TypeScript: Restart TS Server" - Run Nuxt Prepare:
yarn nuxt prepareornpm run prepareto regenerate types - Check Module Installation: Ensure
@enfyra/nuxt-seois properly installed innode_modules - Verify Auto-imports: Composables should be available without imports - if not, check your Nuxt version (requires Nuxt 4+)
- Clear Cache: Delete
.nuxtfolder and runyarn nuxt prepareagain
Robots.txt not generating?
- Check
robots.enabledistrue - Verify the route
/robots.txtis accessible - Check server logs for errors
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
