generateStrapiMetadata
Generate Next.js metadata from Strapi SEO data for optimal SEO performance.
Import
import { generateStrapiMetadata } from 'strapi-nextgen-framework';Usage
import { generateStrapiMetadata } from 'strapi-nextgen-framework';
export async function generateMetadata() {
const data = await strapi.getPage('home', GetPageDocument);
const seo = data.page?.data?.attributes?.seo;
return generateStrapiMetadata(seo);
}Parameters
seo (required)
Type: StrapiSEO | null | undefined
The SEO data from Strapi. Typically extracted from your page/content type.
type StrapiSEO = {
metaTitle?: string | null;
metaDescription?: string | null;
keywords?: string | null;
canonicalURL?: string | null;
metaRobots?: string | null;
metaImage?: StrapiMedia | null;
metaSocial?: Array<{
socialNetwork: 'Facebook' | 'Twitter';
title?: string | null;
description?: string | null;
image?: StrapiMedia | null;
}> | null;
structuredData?: any | null;
};defaultMetadata (optional)
Type: Metadata
Default metadata to merge with Strapi SEO. Useful for site-wide defaults.
generateStrapiMetadata(seo, {
metadataBase: new URL('https://example.com'),
applicationName: 'My App',
});Return Value
Type: Metadata
Returns a Next.js Metadata object compatible with App Router's generateMetadata function.
Generated Fields
The function generates the following metadata fields:
Basic SEO
| Strapi Field | Next.js Field | Description |
|---|---|---|
metaTitle | title | Page title |
metaDescription | description | Meta description |
keywords | keywords | SEO keywords |
canonicalURL | alternates.canonical | Canonical URL |
metaRobots | robots | Robots directives |
Open Graph (Facebook)
| Strapi Field | Next.js Field | Description |
|---|---|---|
metaSocial[0].title | openGraph.title | OG title |
metaSocial[0].description | openGraph.description | OG description |
metaSocial[0].image | openGraph.images | OG image |
| - | openGraph.type | Always 'website' |
Twitter Cards
| Strapi Field | Next.js Field | Description |
|---|---|---|
metaSocial[1].title | twitter.title | Twitter title |
metaSocial[1].description | twitter.description | Twitter description |
metaSocial[1].image | twitter.images | Twitter image |
| - | twitter.card | Always 'summary_large_image' |
Structured Data
| Strapi Field | Generated Output | Description |
|---|---|---|
structuredData | <script type="application/ld+json"> | JSON-LD for rich snippets |
Examples
Basic Page Metadata
import { generateStrapiMetadata } from 'strapi-nextgen-framework';
import { strapi } from '@/lib/strapi';
import { GetAboutPageDocument } from '@/graphql/generated';
export async function generateMetadata() {
const data = await strapi.getPage('about', GetAboutPageDocument);
const seo = data.page?.data?.attributes?.seo;
return generateStrapiMetadata(seo);
}
export default function AboutPage() {
// ... page content
}With Default Metadata
export async function generateMetadata() {
const data = await strapi.getPage('blog', GetBlogPageDocument);
const seo = data.page?.data?.attributes?.seo;
return generateStrapiMetadata(seo, {
metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL!),
applicationName: 'My Blog',
authors: [{ name: 'John Doe' }],
});
}Dynamic Routes
export async function generateMetadata({ params }: { params: { slug: string } }) {
const data = await strapi.getCollection('articles', GetArticleDocument, {
filters: { slug: { eq: params.slug } },
});
const article = data.articles?.data[0]?.attributes;
const seo = article?.seo;
return generateStrapiMetadata(seo, {
openGraph: {
type: 'article',
publishedTime: article?.publishedAt,
authors: [article?.author?.data?.attributes?.name],
},
});
}Fallback for Missing SEO
export async function generateMetadata() {
const data = await strapi.getPage('home', GetHomePageDocument);
const page = data.page?.data?.attributes;
const seo = page?.seo;
// If SEO is missing, use page data as fallback
return generateStrapiMetadata(seo, {
title: page?.title || 'Home',
description: page?.description || 'Welcome to our site',
});
}Strapi SEO Component Setup
To use this function, you need the SEO component in Strapi:
1. Install Strapi SEO Plugin (Recommended)
npm install @strapi/plugin-seo2. Or Create Custom SEO Component
Create a component in Strapi with these fields:
{
"collectionName": "components_shared_seos",
"info": {
"displayName": "SEO",
"description": "SEO meta tags"
},
"attributes": {
"metaTitle": { "type": "string" },
"metaDescription": { "type": "text" },
"keywords": { "type": "string" },
"canonicalURL": { "type": "string" },
"metaRobots": { "type": "string" },
"metaImage": {
"type": "media",
"allowedTypes": ["images"]
},
"metaSocial": {
"type": "component",
"repeatable": true,
"component": "shared.meta-social"
},
"structuredData": { "type": "json" }
}
}3. GraphQL Query
Include SEO in your queries:
query GetPage($slug: String!) {
pages(filters: { slug: { eq: $slug } }) {
data {
attributes {
title
seo {
metaTitle
metaDescription
keywords
canonicalURL
metaRobots
metaImage {
data {
attributes {
url
}
}
}
metaSocial {
socialNetwork
title
description
image {
data {
attributes {
url
}
}
}
}
structuredData
}
}
}
}
}Generated Output Example
Given this Strapi SEO data:
{
"metaTitle": "About Us - My Company",
"metaDescription": "Learn about our mission and team",
"keywords": "about, company, team",
"canonicalURL": "https://example.com/about",
"metaSocial": [
{
"socialNetwork": "Facebook",
"title": "About My Company",
"description": "Discover our story"
}
]
}The function generates:
{
title: "About Us - My Company",
description: "Learn about our mission and team",
keywords: ["about", "company", "team"],
alternates: {
canonical: "https://example.com/about"
},
openGraph: {
title: "About My Company",
description: "Discover our story",
type: "website"
},
twitter: {
card: "summary_large_image"
}
}Behavior
Null Handling
- ✅ Returns empty object
{}ifseoisnullorundefined - ✅ Merges with
defaultMetadataif provided - ✅ Skips undefined/null fields
Keywords Processing
- ✅ Splits comma-separated strings into arrays
- ✅ Trims whitespace from each keyword
- ✅ Handles both string and array inputs
Social Media Fallbacks
- ✅ Falls back to basic SEO if social metadata missing
- ✅ Uses
metaImageif social image not specified - ✅ Defaults to appropriate card types (summary_large_image)
Structured Data
- ✅ Automatically injects JSON-LD script into
<head> - ✅ Validates JSON before injection
- ✅ Logs errors in development mode
TypeScript
Fully typed with TypeScript:
import type { Metadata } from 'next';
import type { StrapiSEO } from 'strapi-nextgen-framework';
const seo: StrapiSEO = {
metaTitle: 'My Page',
metaDescription: 'Page description',
};
const metadata: Metadata = generateStrapiMetadata(seo);Common Issues
Metadata Not Appearing
Problem: SEO tags don't appear in page source
Solutions:
- Ensure
generateMetadatais exported from page component - Check that SEO data exists in Strapi
- Verify GraphQL query includes
seofield - Check browser's View Source (not DevTools)
Images Not in Social Previews
Problem: Social media doesn't show image previews
Solutions:
- Add full URL to images:
metadataBase: new URL('https://example.com') - Verify image URL is absolute
- Check image meets platform requirements (min 1200x630px for Facebook)
- Test with Facebook Debugger (opens in a new tab)
Structured Data Errors
Problem: Google Search Console shows structured data errors
Solutions:
- Validate JSON-LD with Schema.org Validator (opens in a new tab)
- Check
structuredDatafield in Strapi is valid JSON - Use Rich Results Test (opens in a new tab)