Outrank
Get traffic and outrank competitors with Backlinks & SEO-optimized content while you sleep! I've been keeping a close eye on this new tool and it seems to be gaining a lot of traction and delivering great results. Try it now!

Have you seen the new Next.js newsletter?

Next SEO
Next SEO is a plugin that makes managing your SEO easier in Next.js projects. It provides components for structured data (JSON-LD) that helps search engines understand your content better.
📋 Table of Contents
Looking for v6 documentation? View Here
Still using component in Pages? View docs here [/src/pages/README.md]
🚀 Quick Start
Installation
npm install next-seo
# or
yarn add next-seo
# or
pnpm add next-seo
# or
bun add next-seoBasic Usage
import { ArticleJsonLd } from "next-seo";
export default function BlogPost() {
return (
<>
<ArticleJsonLd
headline="Getting Started with Next SEO"
datePublished="2024-01-01T08:00:00+00:00"
author="John Doe"
image="https://example.com/article-image.jpg"
description="Learn how to improve your Next.js SEO"
/>
<article>
<h1>Getting Started with Next SEO</h1>
{/* Your content */}
</article>
</>
);
}Note: For standard meta tags (
<meta>,<title>), use Next.js's built-ingenerateMetadatafunction.
Pages Router Support: If you're using Next.js Pages Router, import components from
next-seo/pages. See the Pages Router documentation for details.
Support This Project
Feel like supporting this free plugin?
It takes a lot of time to maintain an open source project so any small contribution is greatly appreciated.
Coffee fuels coding ☕️
Components
ArticleJsonLd
The ArticleJsonLd component helps you add structured data for articles, blog posts, and news articles to improve their appearance in search results.
Basic Usage
import { ArticleJsonLd } from "next-seo";
export default function ArticlePage() {
return (
<>
<ArticleJsonLd
headline="My Amazing Article"
datePublished="2024-01-01T08:00:00+08:00"
author="John Doe"
image="https://example.com/article-image.jpg"
description="This article explains amazing things about Next.js SEO"
/>
<article>
<h1>My Amazing Article</h1>
{/* Article content */}
</article>
</>
);
}Advanced Example with Multiple Authors
<ArticleJsonLd
type="NewsArticle"
headline="Breaking: Next SEO v7 Released"
url="https://example.com/news/next-seo-v7"
datePublished="2024-01-01T08:00:00+08:00"
dateModified="2024-01-02T10:00:00+08:00"
author={[
{
"@type": "Person",
name: "Jane Smith",
url: "https://example.com/authors/jane",
},
"John Doe", // Can mix objects and strings
]}
image={[
"https://example.com/images/16x9.jpg",
"https://example.com/images/4x3.jpg",
"https://example.com/images/1x1.jpg",
]}
publisher={{
"@type": "Organization",
name: "Example News",
logo: "https://example.com/logo.png",
}}
isAccessibleForFree={true}
/>Blog Posting Example
<ArticleJsonLd
type="BlogPosting"
headline="10 Tips for Better SEO"
url="https://example.com/blog/seo-tips"
datePublished="2024-01-01T08:00:00+08:00"
author={{
"@type": "Organization",
name: "SEO Experts Inc.",
url: "https://example.com",
}}
image={{
"@type": "ImageObject",
url: "https://example.com/blog-hero.jpg",
width: 1200,
height: 630,
caption: "SEO Tips Illustration",
}}
description="Learn the top 10 tips to improve your website's SEO"
mainEntityOfPage={{
"@type": "WebPage",
"@id": "https://example.com/blog/seo-tips",
}}
/>Props
PropertyTypeDescriptiontype"Article" | "NewsArticle" | "BlogPosting" | "Blog"The type of article. Defaults to "Article"headlinestringRequired. The headline of the articleurlstringThe canonical URL of the articleauthorstring | Person | Organization | Author[]The author(s) of the articledatePublishedstringISO 8601 date when the article was publisheddateModifiedstringISO 8601 date when the article was last modifiedimagestring | ImageObject | (string | ImageObject)[]Article images. Google recommends multiple aspect ratiospublisherOrganizationThe publisher of the articledescriptionstringA short description of the articleisAccessibleForFreebooleanWhether the article is accessible for freemainEntityOfPagestring | WebPageIndicates the article is the primary content of the pagescriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
Best Practices
Always include images: Google strongly recommends including high-resolution images with multiple aspect ratios (16x9, 4x3, 1x1)
Use ISO 8601 dates: Include timezone information for accuracy
Multiple authors: List all authors when applicable
Publisher logo: Include a logo for NewsArticle type
Update dateModified: Keep this current when updating content
ClaimReviewJsonLd
The ClaimReviewJsonLd component helps you add structured data for fact-checking articles that review claims made by others. This enables a summarized version of your fact check to display in Google Search results.
Basic Usage
import { ClaimReviewJsonLd } from "next-seo";
export default function FactCheckPage() {
return (
<>
<ClaimReviewJsonLd
claimReviewed="The world is flat"
reviewRating={{
ratingValue: 1,
bestRating: 5,
worstRating: 1,
alternateName: "False",
}}
url="https://example.com/fact-check/flat-earth"
author="Fact Check Team"
/>
<article>
<h1>Fact Check: The World is Flat</h1>
{/* Your fact check content */}
</article>
</>
);
}Props
PropertyTypeDescriptionclaimReviewedstringRequired. A short summary of the claim being evaluated (keep under 75 characters)reviewRatingobjectRequired. The assessment of the claim with rating value and textual ratingurlstringRequired. Link to the page hosting the full fact check articleauthorstring | Organization | PersonThe publisher of the fact check articleitemReviewedClaimDetailed information about the claim being reviewedscriptIdstringCustom ID for the script tagscriptKeystringCustom key for script identification
Review Rating Properties
PropertyTypeDescriptionalternateNamestringRequired. The truthfulness rating as human-readable text (e.g., "False", "Mostly true")ratingValuenumberRequired. Numeric rating (closer to bestRating = more true)bestRatingnumberBest value in the rating scale (must be greater than worstRating)worstRatingnumberWorst value in the rating scale (minimum value of 1)namestringAlternative to alternateName (use alternateName instead)
Advanced Example with Claim Details
<ClaimReviewJsonLd
claimReviewed="Climate change is not real"
reviewRating={{
ratingValue: 1,
bestRating: 5,
worstRating: 1,
alternateName: "Pants on Fire",
}}
url="https://example.com/fact-check/climate-denial"
author={{
name: "Climate Facts Organization",
url: "https://example.com",
logo: "https://example.com/logo.jpg",
}}
itemReviewed={{
author: {
name: "Climate Denial Institute",
sameAs: "https://climatedenial.example.com",
},
datePublished: "2024-06-20",
appearance: {
url: "https://example.com/original-claim",
headline: "The Great Climate Hoax",
datePublished: "2024-06-22",
author: "John Doe",
publisher: {
name: "Denial News",
logo: "https://example.com/denial-logo.jpg",
},
},
}}
/>Best Practices
Clear ratings: Use descriptive alternateName values that clearly indicate the verdict
Claim summary: Keep claimReviewed concise (under 75 characters) to prevent wrapping
Full context: Include itemReviewed when possible to provide claim origin details
Consistent scale: Use a consistent rating scale across all your fact checks
Author credibility: Clearly identify your fact-checking organization
CreativeWorkJsonLd
The CreativeWorkJsonLd component helps you add structured data for various types of creative content, with special support for marking paywalled or subscription-based content. This enables Google to differentiate paywalled content from cloaking practices.
Basic Usage
import { CreativeWorkJsonLd } from "next-seo";
export default function ArticlePage() {
return (
<>
<CreativeWorkJsonLd
type="Article"
headline="Premium Article"
datePublished="2024-01-01T08:00:00+08:00"
author="John Doe"
description="This premium article requires a subscription"
isAccessibleForFree={false}
hasPart={{
isAccessibleForFree: false,
cssSelector: ".paywall",
}}
/>
<article>
<h1>Premium Article</h1>
<div className="non-paywall">Free preview content here...</div>
<div className="paywall">
Premium content that requires subscription...
</div>
</article>
</>
);
}Props
PropertyTypeDescriptiontype"CreativeWork" | "Article" | "NewsArticle" | "Blog" | "BlogPosting" | "Comment" | ...The type of creative work. Defaults to "CreativeWork"headlinestringThe headline of the content (used for Article types)namestringThe name of the content (alternative to headline)urlstringURL of the contentauthorstring | Person | Organization | ArrayAuthor(s) of the contentdatePublishedstringISO 8601 publication datedateModifiedstringISO 8601 modification dateimagestring | ImageObject | ArrayImage(s) associated with the contentpublisherstring | Organization | PersonPublisher of the contentdescriptionstringDescription of the contentisAccessibleForFreebooleanWhether the content is free or requires payment/subscriptionhasPartWebPageElement | WebPageElement[]Marks specific sections as paywalledmainEntityOfPagestring | WebPageThe main page for this contentscriptIdstringCustom ID for the script tagscriptKeystringCustom key for script identification
WebPageElement Properties (for hasPart)
PropertyTypeDescriptionisAccessibleForFreebooleanRequired. Whether this section is free (false)cssSelectorstringRequired. CSS class selector (e.g., ".paywall")
Marking Paywalled Content
<CreativeWorkJsonLd
type="NewsArticle"
headline="Breaking News: Premium Coverage"
datePublished="2024-01-01T08:00:00+00:00"
isAccessibleForFree={false}
hasPart={{
isAccessibleForFree: false,
cssSelector: ".premium-content",
}}
/>Multiple Paywalled Sections
<CreativeWorkJsonLd
type="Article"
headline="In-Depth Analysis"
datePublished="2024-01-01T08:00:00+00:00"
isAccessibleForFree={false}
hasPart={[
{
isAccessibleForFree: false,
cssSelector: ".section1",
},
{
isAccessibleForFree: false,
cssSelector: ".section2",
},
]}
/>Different CreativeWork Types
// Blog with subscription content
<CreativeWorkJsonLd
type="Blog"
name="Premium Tech Blog"
description="Technology insights for subscribers"
isAccessibleForFree={false}
/>
// Comment
<CreativeWorkJsonLd
type="Comment"
text="Great article!"
author="Jane Smith"
datePublished="2024-01-01T10:00:00+00:00"
/>
// Course with provider
<CreativeWorkJsonLd
type="Course"
name="Advanced Programming"
provider="Tech University"
description="Learn advanced programming concepts"
isAccessibleForFree={false}
/>
// Review with rating
<CreativeWorkJsonLd
type="Review"
name="Product Review"
itemReviewed="Amazing Gadget"
reviewRating={{
ratingValue: 4.5,
bestRating: 5,
}}
author="Tech Reviewer"
/>Best Practices
Use specific types: Choose the most specific CreativeWork type (Article, NewsArticle, etc.) when applicable
Mark paywalled sections: Use
hasPartwithcssSelectorto identify paywalled content sectionsClass selectors only: Only use class selectors (e.g., ".paywall") for
cssSelector, not IDs or other selectorsConsistent selectors: Ensure your HTML classes match the
cssSelectorvalues exactlyComplete metadata: Include as much metadata as possible (author, dates, images) for better search results
RecipeJsonLd
The RecipeJsonLd component helps you add structured data for recipes to improve their appearance in search results with rich snippets that can include ratings, cooking times, and images.
Basic Usage
import { RecipeJsonLd } from "next-seo";
export default function RecipePage() {
return (
<>
<RecipeJsonLd
name="Simple Chocolate Chip Cookies"
image="https://example.com/cookies.jpg"
description="Classic chocolate chip cookies that are crispy on the outside and chewy on the inside"
author="Baker Jane"
datePublished="2024-01-01T08:00:00+00:00"
prepTime="PT20M"
cookTime="PT12M"
totalTime="PT32M"
recipeYield="24 cookies"
recipeCategory="dessert"
recipeCuisine="American"
recipeIngredient={[
"2 1/4 cups all-purpose flour",
"1 cup butter, softened",
"3/4 cup granulated sugar",
"3/4 cup packed brown sugar",
"2 large eggs",
"2 teaspoons vanilla extract",
"1 teaspoon baking soda",
"1 teaspoon salt",
"2 cups chocolate chips",
]}
recipeInstructions={[
"Preheat oven to 375°F (190°C)",
"Mix flour, baking soda, and salt in a bowl",
"In another bowl, cream butter and sugars until fluffy",
"Beat in eggs and vanilla",
"Gradually blend in flour mixture",
"Stir in chocolate chips",
"Drop by rounded tablespoons onto ungreased cookie sheets",
"Bake for 9 to 11 minutes or until golden brown",
]}
/>
<article>
<h1>Simple Chocolate Chip Cookies</h1>
{/* Recipe content */}
</article>
</>
);
}Advanced Example with Structured Instructions and Nutrition
<RecipeJsonLd
name="Gourmet Lasagna"
image={[
"https://example.com/lasagna-16x9.jpg",
"https://example.com/lasagna-4x3.jpg",
"https://example.com/lasagna-1x1.jpg",
]}
description="A rich and hearty lasagna with layers of meat sauce, cheese, and pasta"
author={{
"@type": "Organization",
name: "The Italian Kitchen",
url: "https://example.com",
}}
datePublished="2024-01-15T10:00:00+00:00"
url="https://example.com/recipes/gourmet-lasagna"
prepTime="PT45M"
cookTime="PT1H"
totalTime="PT1H45M"
recipeYield={8}
recipeCategory="main course"
recipeCuisine="Italian"
keywords="lasagna, italian, pasta, cheese"
recipeIngredient={[
"1 pound ground beef",
"1 onion, chopped",
"2 cloves garlic, minced",
"1 can (28 oz) crushed tomatoes",
"2 cans (6 oz each) tomato paste",
"16 oz ricotta cheese",
"1 egg",
"12 lasagna noodles",
"16 oz mozzarella cheese, shredded",
]}
recipeInstructions={[
{
"@type": "HowToStep",
name: "Prepare the meat sauce",
text: "Brown ground beef with onion and garlic. Add tomatoes and tomato paste. Simmer for 30 minutes.",
},
{
"@type": "HowToStep",
name: "Prepare cheese mixture",
text: "Mix ricotta cheese with egg and half of the mozzarella",
},
{
"@type": "HowToStep",
name: "Assemble lasagna",
text: "Layer meat sauce, noodles, and cheese mixture in a 9x13 pan. Repeat layers.",
},
{
"@type": "HowToStep",
name: "Bake",
text: "Cover with foil and bake at 375°F for 45 minutes. Remove foil, add remaining mozzarella, and bake 15 more minutes.",
},
]}
nutrition={{
"@type": "NutritionInformation",
calories: "450 calories",
proteinContent: "28g",
carbohydrateContent: "35g",
fatContent: "22g",
saturatedFatContent: "10g",
sodiumContent: "680mg",
fiberContent: "3g",
servingSize: "1 piece (1/8 of recipe)",
}}
aggregateRating={{
"@type": "AggregateRating",
ratingValue: 4.7,
ratingCount: 234,
reviewCount: 189,
}}
video={{
"@type": "VideoObject",
name: "How to Make Gourmet Lasagna",
description: "Watch our chef prepare this delicious lasagna step by step",
thumbnailUrl: "https://example.com/lasagna-video-thumb.jpg",
contentUrl: "https://example.com/videos/lasagna-tutorial.mp4",
embedUrl: "https://example.com/embed/lasagna-tutorial",
uploadDate: "2024-01-10T08:00:00+00:00",
duration: "PT8M30S",
}}
/>Props
PropertyTypeDescriptionnamestringRequired. The name of the dishimagestring | ImageObject | (string | ImageObject)[]Required. Images of the completed dish. Google recommends multiple high-resolution imagesdescriptionstringA short summary describing the dishauthorstring | Person | OrganizationThe creator of the recipedatePublishedstringISO 8601 date when the recipe was publishedurlstringThe canonical URL of the recipe pageprepTimestringISO 8601 duration for preparation time (e.g., "PT30M" for 30 minutes)cookTimestringISO 8601 duration for cooking timetotalTimestringISO 8601 duration for total time (prep + cook)recipeYieldstring | numberThe quantity produced (e.g., "4 servings", "1 loaf", or just 6)recipeCategorystringThe type of meal or course (e.g., "dessert", "main course")recipeCuisinestringThe cuisine of the recipe (e.g., "French", "Mexican")recipeIngredientstring[]List of ingredients with quantitiesrecipeInstructionsstring | HowToStep | HowToSection | (string | HowToStep | HowToSection)[]Step-by-step instructionsnutritionNutritionInformationNutritional information per servingaggregateRatingAggregateRatingThe aggregate rating from usersvideoVideoObjectA video showing how to make the recipekeywordsstringKeywords about the recipe, separated by commasscriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
Duration Format (ISO 8601)
Use these formats for time durations:
PT15M- 15 minutesPT1H- 1 hourPT1H30M- 1 hour 30 minutesPT2H15M- 2 hours 15 minutes
Best Practices
High-quality images: Include multiple high-resolution images (16x9, 4x3, 1x1 aspect ratios)
Detailed instructions: Use HowToStep objects for better structured data
Complete nutrition info: Include nutrition data when possible for better search visibility
Accurate times: Always provide prepTime and cookTime together
Ratings: Include aggregateRating when you have user reviews
Video content: Adding a video significantly improves search appearance
OrganizationJsonLd
The OrganizationJsonLd component helps you add structured data about your organization to improve how it appears in search results and knowledge panels.
Basic Usage
import { OrganizationJsonLd } from "next-seo";
export default function AboutPage() {
return (
<>
<OrganizationJsonLd
name="Example Corporation"
url="https://www.example.com"
logo="https://www.example.com/logo.png"
description="The example corporation is well-known for producing high-quality widgets"
sameAs={[
"https://twitter.com/example",
"https://facebook.com/example",
"https://linkedin.com/company/example",
]}
/>
<div>
<h1>About Example Corporation</h1>
{/* About page content */}
</div>
</>
);
}Advanced Example with Address and Contact
<OrganizationJsonLd
type="Organization"
name="Example Corporation"
url="https://www.example.com"
logo={{
"@type": "ImageObject",
url: "https://www.example.com/logo.png",
width: 600,
height: 400,
}}
description="Leading provider of innovative widget solutions"
sameAs={[
"https://example.net/profile/example1234",
"https://example.org/example1234",
]}
address={{
"@type": "PostalAddress",
streetAddress: "999 W Example St Suite 99",
addressLocality: "New York",
addressRegion: "NY",
postalCode: "10019",
addressCountry: "US",
}}
contactPoint={{
"@type": "ContactPoint",
contactType: "Customer Service",
telephone: "+1-999-999-9999",
email: "[email protected]",
}}
telephone="+1-999-999-9999"
email="[email protected]"
foundingDate="2010-01-01"
vatID="FR12345678901"
iso6523Code="0199:724500PMK2A2M1SQQ228"
numberOfEmployees={{
minValue: 100,
maxValue: 999,
}}
/>OnlineStore Example with Return Policy
<OrganizationJsonLd
type="OnlineStore"
name="Example Online Store"
url="https://www.example.com"
logo="https://www.example.com/assets/logo.png"
contactPoint={{
"@type": "ContactPoint",
contactType: "Customer Service",
email: "[email protected]",
telephone: "+47-99-999-9900",
}}
vatID="FR12345678901"
iso6523Code="0199:724500PMK2A2M1SQQ228"
hasMerchantReturnPolicy={{
"@type": "MerchantReturnPolicy",
applicableCountry: ["FR", "CH"],
returnPolicyCountry: "FR",
returnPolicyCategory: "https://schema.org/MerchantReturnFiniteReturnWindow",
merchantReturnDays: 60,
returnMethod: "https://schema.org/ReturnByMail",
returnFees: "https://schema.org/FreeReturn",
refundType: "https://schema.org/FullRefund",
}}
/>Props
PropertyTypeDescriptiontype"Organization" | "OnlineStore"The type of organization. Defaults to "Organization"namestringThe name of your organizationurlstringThe URL of your organization's websitelogostring | ImageObjectYour organization's logo (112x112px minimum)descriptionstringA detailed description of your organizationsameAsstring | string[]URLs of your organization's profiles on other sitesaddressstring | PostalAddress | (string | PostalAddress)[]Physical or mailing address(es)contactPointContactPoint | ContactPoint[]Contact information for your organizationtelephonestringPrimary phone number (include country code)emailstringPrimary email addressalternateNamestringAlternative name your organization goes byfoundingDatestringISO 8601 date when the organization was foundedlegalNamestringRegistered legal name if different from nametaxIDstringTax ID associated with your organizationvatIDstringVAT code (important trust signal)dunsstringDun & Bradstreet DUNS numberleiCodestringLegal Entity Identifier (ISO 17442)naicsstringNorth American Industry Classification System codeglobalLocationNumberstringGS1 Global Location Numberiso6523CodestringISO 6523 identifier (e.g., "0199:724500PMK2A2M1SQQ228")numberOfEmployeesnumber | QuantitativeValueNumber of employees or rangehasMerchantReturnPolicyMerchantReturnPolicy | MerchantReturnPolicy[]Return policy details (OnlineStore only)hasMemberProgramMemberProgram | MemberProgram[]Loyalty/membership program details (OnlineStore only)scriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
OnlineStore with Loyalty Program Example
<OrganizationJsonLd
type="OnlineStore"
name="Example Store"
url="https://www.example.com"
hasMemberProgram={{
name: "Rewards Plus",
description:
"Earn points and unlock exclusive benefits with our loyalty program",
url: "https://www.example.com/rewards",
hasTiers: [
{
name: "Bronze",
hasTierBenefit: "TierBenefitLoyaltyPoints",
membershipPointsEarned: 1,
},
{
name: "Silver",
hasTierBenefit: ["TierBenefitLoyaltyPoints"],
hasTierRequirement: {
value: 500,
currency: "USD",
},
membershipPointsEarned: 2,
},
{
name: "Gold",
hasTierBenefit: ["TierBenefitLoyaltyPoints", "TierBenefitLoyaltyPrice"],
hasTierRequirement: {
name: "Example Gold Credit Card",
},
membershipPointsEarned: 5,
url: "https://www.example.com/rewards/gold",
},
],
}}
/>Multiple Loyalty Programs Example
<OrganizationJsonLd
type="OnlineStore"
name="Premium Store"
hasMemberProgram={[
{
name: "Basic Rewards",
description: "Standard loyalty program for all customers",
hasTiers: {
name: "Member",
hasTierBenefit: "TierBenefitLoyaltyPoints",
membershipPointsEarned: 1,
},
},
{
name: "VIP Elite",
description: "Exclusive program for premium members",
hasTiers: [
{
name: "Silver VIP",
hasTierBenefit: [
"TierBenefitLoyaltyPoints",
"TierBenefitLoyaltyPrice",
],
hasTierRequirement: {
value: 2500,
currency: "USD",
},
membershipPointsEarned: {
value: 10,
unitText: "points per dollar",
},
},
{
name: "Gold VIP",
hasTierBenefit: [
"TierBenefitLoyaltyPoints",
"TierBenefitLoyaltyPrice",
],
hasTierRequirement: {
price: 9.99,
priceCurrency: "USD",
billingDuration: 12,
billingIncrement: 1,
unitCode: "MON",
},
membershipPointsEarned: 20,
},
],
},
]}
/>MemberProgram Properties
PropertyTypeDescriptionnamestringRequired. Name of the loyalty programdescriptionstringRequired. Description of program benefitsurlstringURL where customers can sign uphasTiersMemberProgramTier | MemberProgramTier[]Required. Tier(s) of the loyalty program
MemberProgramTier Properties
PropertyTypeDescriptionnamestringRequired. Name of the tierhasTierBenefitstring | string[]Required. Benefits for this tierhasTierRequirementvarious (see below)Requirements to join this tiermembershipPointsEarnednumber | QuantitativeValuePoints earned per unit spenturlstringURL for tier-specific signup@idstringUnique identifier for the tier
Tier Benefits
Benefits can be specified using short names or full URLs:
"TierBenefitLoyaltyPoints"or"https://schema.org/TierBenefitLoyaltyPoints"- Earn loyalty points"TierBenefitLoyaltyPrice"or"https://schema.org/TierBenefitLoyaltyPrice"- Special member pricing
Tier Requirements
The hasTierRequirement property accepts different types based on the requirement:
Credit Card Requirement:
hasTierRequirement: {
name: "Store Premium Credit Card";
}Minimum Spending Requirement (MonetaryAmount):
hasTierRequirement: {
value: 1000,
currency: "USD"
}Subscription Fee (UnitPriceSpecification):
hasTierRequirement: {
price: 9.99,
priceCurrency: "EUR",
billingDuration: 12, // Total duration
billingIncrement: 1, // Billing frequency
unitCode: "MON" // Unit (MON = monthly)
}Text Description:
hasTierRequirement: "By invitation only - must maintain $10,000+ annual spending";Membership Points Earned
Points can be specified as a simple number or as a detailed QuantitativeValue:
Simple:
membershipPointsEarned: 5;Detailed:
membershipPointsEarned: {
value: 10,
minValue: 10,
maxValue: 20,
unitText: "points per dollar (double on special events)"
}Best Practices
Place on homepage or about page: Add this markup to your homepage or a dedicated "about us" page
Use specific subtypes: Use "OnlineStore" for e-commerce sites rather than generic "Organization"
Include identifiers: Add VAT ID, ISO codes, and other identifiers for better trust signals
Complete address information: Provide full address details including country code
Multiple locations: Use array format for addresses if you have multiple locations
High-quality logo: Use a logo that looks good on white background, minimum 112x112px
LocalBusinessJsonLd
The LocalBusinessJsonLd component helps you add structured data for local businesses to improve their appearance in Google Search and Maps results, including knowledge panels and local business carousels.
Basic Usage
import { LocalBusinessJsonLd } from "next-seo";
<LocalBusinessJsonLd
type="Restaurant"
name="Dave's Steak House"
address={{
"@type": "PostalAddress",
streetAddress: "148 W 51st St",
addressLocality: "New York",
addressRegion: "NY",
postalCode: "10019",
addressCountry: "US",
}}
telephone="+12125551234"
url="https://www.example.com"
priceRange="$$$"
/>;Restaurant Example with Full Details
<LocalBusinessJsonLd
type="Restaurant"
name="Dave's Steak House"
address={{
"@type": "PostalAddress",
streetAddress: "148 W 51st St",
addressLocality: "New York",
addressRegion: "NY",
postalCode: "10019",
addressCountry: "US",
}}
geo={{
"@type": "GeoCoordinates",
latitude: 40.761293,
longitude: -73.982294,
}}
url="https://www.example.com/restaurant-locations/manhattan"
telephone="+12122459600"
image={[
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg",
]}
servesCuisine="American"
priceRange="$$$"
openingHoursSpecification={[
{
"@type": "OpeningHoursSpecification",
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
opens: "11:30",
closes: "22:00",
},
{
"@type": "OpeningHoursSpecification",
dayOfWeek: "Saturday",
opens: "16:00",
closes: "23:00",
},
{
"@type": "OpeningHoursSpecification",
dayOfWeek: "Sunday",
opens: "16:00",
closes: "22:00",
},
]}
menu="https://www.example.com/menu"
aggregateRating={{
"@type": "AggregateRating",
ratingValue: 4.5,
ratingCount: 250,
}}
/>Store with Departments
<LocalBusinessJsonLd
type="Store"
name="Dave's Department Store"
address={{
"@type": "PostalAddress",
streetAddress: "1600 Saratoga Ave",
addressLocality: "San Jose",
addressRegion: "CA",
postalCode: "95129",
addressCountry: "US",
}}
telephone="+14088717984"
department={[
{
type: "Pharmacy",
name: "Dave's Pharmacy",
address: "1600 Saratoga Ave, San Jose, CA 95129",
telephone: "+14088719385",
openingHoursSpecification: {
"@type": "OpeningHoursSpecification",
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
opens: "09:00",
closes: "19:00",
},
},
]}
/>Props
PropertyTypeDescriptiontypestring | string[]Business type (e.g., "Restaurant", "Store", or ["Restaurant", "BarOrPub"])namestringRequired. The name of the businessaddressstring | PostalAddress | (string | PostalAddress)[]Required. Physical location(s) of the businessurlstringThe fully-qualified URL of the business location pagetelephonestringPrimary contact phone number (include country code)imagestring | ImageObject | (string | ImageObject)[]Images of the business (multiple aspect ratios recommended)priceRangestringRelative price range (e.g., "$", "$$", "$$$", or "$10-15")geoGeoCoordinatesGeographic coordinates (min 5 decimal places precision)openingHoursSpecificationOpeningHoursSpecification | OpeningHoursSpecification[]Business hours including special/seasonal hoursreviewReview | Review[]Customer reviews (for review sites only)aggregateRatingAggregateRatingAverage rating based on multiple reviewsdepartmentLocalBusinessBase | LocalBusinessBase[]Departments within the businessmenustringURL of the menu (for food establishments)servesCuisinestring | string[]Type of cuisine served (for restaurants)sameAsstring | string[]URLs of business profiles on other sitesbranchOfOrganizationParent organization if this is a branchcurrenciesAcceptedstringCurrencies accepted (e.g., "USD")paymentAcceptedstringPayment methods acceptedareaServedstring | string[]Geographic areas servedemailstringBusiness email addressfaxNumberstringBusiness fax numbersloganstringBusiness slogan or taglinedescriptionstringDetailed description of the businesspublicAccessbooleanWhether the business location is accessible to the publicsmokingAllowedbooleanWhether smoking is allowed at the locationisAccessibleForFreebooleanWhether access is freescriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
Opening Hours Examples
Standard Business Hours:
openingHoursSpecification={[
{
"@type": "OpeningHoursSpecification",
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
opens: "09:00",
closes: "17:00",
},
{
"@type": "OpeningHoursSpecification",
dayOfWeek: ["Saturday", "Sunday"],
opens: "10:00",
closes: "16:00",
},
]}24/7 Operation:
openingHoursSpecification={{
"@type": "OpeningHoursSpecification",
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
opens: "00:00",
closes: "23:59",
}}Closed on Specific Days:
openingHoursSpecification={{
"@type": "OpeningHoursSpecification",
dayOfWeek: "Sunday",
opens: "00:00",
closes: "00:00",
}}Seasonal Hours:
openingHoursSpecification={{
"@type": "OpeningHoursSpecification",
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
opens: "10:00",
closes: "18:00",
validFrom: "2024-06-01",
validThrough: "2024-09-30",
}}Best Practices
Use specific business types: Use the most specific LocalBusiness subtype (e.g., "Restaurant" instead of "LocalBusiness")
Multiple types: For businesses that fit multiple categories, use an array (e.g.,
["Restaurant", "BarOrPub"])Complete address: Provide as many address fields as possible for better local SEO
High-quality images: Include multiple images with different aspect ratios (16:9, 4:3, 1:1)
Accurate coordinates: Use at least 5 decimal places for latitude and longitude
Opening hours: Be precise with opening hours and include seasonal variations
Department naming: Include the main store name with department name (e.g., "Store Name - Pharmacy")
Price range: Keep under 100 characters; use standard symbols (,$, $$$) or ranges
MerchantReturnPolicyJsonLd
The MerchantReturnPolicyJsonLd component helps you add structured data for merchant return policies, enabling Google Search to display return policy information alongside your products and in knowledge panels. This component supports both detailed policy specifications and simple links to policy pages.
Basic Usage - Option A (Detailed Properties)
Use this pattern when you want to provide detailed return policy information:
import { MerchantReturnPolicyJsonLd } from "next-seo";
<MerchantReturnPolicyJsonLd
applicableCountry={["US", "CA"]}
returnPolicyCountry="US"
returnPolicyCategory="https://schema.org/MerchantReturnFiniteReturnWindow"
merchantReturnDays={30}
returnMethod="https://schema.org/ReturnByMail"
returnFees="https://schema.org/FreeReturn"
refundType="https://schema.org/FullRefund"
returnLabelSource="https://schema.org/ReturnLabelDownloadAndPrint"
/>;Basic Usage - Option B (Link Only)
Use this pattern when you prefer to link to your return policy page:
import { MerchantReturnPolicyJsonLd } from "next-seo";
<MerchantReturnPolicyJsonLd merchantReturnLink="https://www.example.com/returns" />;Advanced Usage with All Features
import { MerchantReturnPolicyJsonLd } from "next-seo";
<MerchantReturnPolicyJsonLd
applicableCountry={["DE", "AT", "CH"]}
returnPolicyCountry="IE"
returnPolicyCategory="https://schema.org/MerchantReturnFiniteReturnWindow"
merchantReturnDays={60}
itemCondition={[
"https://schema.org/NewCondition",
"https://schema.org/DamagedCondition",
]}
returnMethod={[
"https://schema.org/ReturnByMail",
"https://schema.org/ReturnInStore",
]}
returnFees="https://schema.org/ReturnShippingFees"
returnShippingFeesAmount={{
value: 2.99,
currency: "EUR",
}}
refundType={[
"https://schema.org/FullRefund",
"https://schema.org/ExchangeRefund",
]}
restockingFee={{
value: 10,
currency: "EUR",
}}
returnLabelSource="https://schema.org/ReturnLabelInBox"
// Customer remorse specific
customerRemorseReturnFees="https://schema.org/ReturnShippingFees"
customerRemorseReturnShippingFeesAmount={{
value: 5.99,
currency: "EUR",
}}
customerRemorseReturnLabelSource="https://schema.org/ReturnLabelDownloadAndPrint"
// Item defect specific
itemDefectReturnFees="https://schema.org/FreeReturn"
itemDefectReturnLabelSource="https://schema.org/ReturnLabelInBox"
// Seasonal override
returnPolicySeasonalOverride={{
startDate: "2025-12-01",
endDate: "2025-01-05",
returnPolicyCategory: "https://schema.org/MerchantReturnFiniteReturnWindow",
merchantReturnDays: 30,
}}
/>;Product-Level Return Policy
You can also specify return policies for individual products:
import { ProductJsonLd } from "next-seo";
<ProductJsonLd
name="Premium Wireless Headphones"
offers={{
price: 349.99,
priceCurrency: "USD",
hasMerchantReturnPolicy: {
applicableCountry: "US",
returnPolicyCategory:
"https://schema.org/MerchantReturnFiniteReturnWindow",
merchantReturnDays: 45,
returnFees: "https://schema.org/FreeReturn",
refundType: "https://schema.org/FullRefund",
},
}}
/>;Organization-Level Return Policy
For online stores, specify a standard return policy at the organization level:
import { OrganizationJsonLd } from "next-seo";
<OrganizationJsonLd
type="OnlineStore"
name="Example Store"
hasMerchantReturnPolicy={{
applicableCountry: ["US", "CA"],
returnPolicyCategory: "https://schema.org/MerchantReturnFiniteReturnWindow",
merchantReturnDays: 60,
returnFees: "https://schema.org/FreeReturn",
refundType: "https://schema.org/FullRefund",
}}
/>;Props
PropertyTypeDescriptionOption A PropertiesapplicableCountrystring | string[]Required (Option A). Countries where products are soldreturnPolicyCategorystringRequired (Option A). Type of return policymerchantReturnDaysnumberDays for returns (required if finite window)returnPolicyCountrystring | string[]Countries where returns are processedreturnMethodstring | string[]How items can be returnedreturnFeesstringType of return feesreturnShippingFeesAmountMonetaryAmountShipping fee for returnsrefundTypestring | string[]Types of refunds availablerestockingFeenumber | MonetaryAmountRestocking fee (percentage or fixed)returnLabelSourcestringHow customers get return labelsitemConditionstring | string[]Acceptable return conditionsCustomer Remorse PropertiescustomerRemorseReturnFeesstringFees for change-of-mind returnscustomerRemorseReturnShippingFeesAmountMonetaryAmountShipping fee for remorse returnscustomerRemorseReturnLabelSourcestringLabel source for remorse returnsItem Defect PropertiesitemDefectReturnFeesstringFees for defective item returnsitemDefectReturnShippingFeesAmountMonetaryAmountShipping fee for defect returnsitemDefectReturnLabelSourcestringLabel source for defect returnsSeasonal OverridereturnPolicySeasonalOverrideSeasonalOverride | SeasonalOverride[]Temporary policy changesOption B PropertymerchantReturnLinkstringURL to return policy pageComponent PropertiesscriptIdstringCustom ID for the script tagscriptKeystringCustom key for React rendering
Return Policy Categories
https://schema.org/MerchantReturnFiniteReturnWindow- Limited return periodhttps://schema.org/MerchantReturnNotPermitted- No returns allowedhttps://schema.org/MerchantReturnUnlimitedWindow- Unlimited return period
Return Methods
https://schema.org/ReturnByMail- Return by mailhttps://schema.org/ReturnInStore- Return in storehttps://schema.org/ReturnAtKiosk- Return at kiosk
Return Fees
https://schema.org/FreeReturn- No charge for returnshttps://schema.org/ReturnFeesCustomerResponsibility- Customer pays for returnhttps://schema.org/ReturnShippingFees- Specific shipping fee charged
Refund Types
https://schema.org/FullRefund- Full monetary refundhttps://schema.org/ExchangeRefund- Exchange for same producthttps://schema.org/StoreCreditRefund- Store credit issued
Best Practices
Choose the right option: Use Option A for detailed policies, Option B for complex or frequently changing policies
Specify all countries: List all countries where your policy applies
Different return scenarios: Use customer remorse and item defect properties for different conditions
Seasonal variations: Use seasonal overrides for holiday return windows
Product overrides: Override organization-level policies for specific products when needed
Clear fee structure: Be transparent about any fees customers will incur
Multiple return methods: Offer multiple return options for customer convenience
Accurate time windows: Ensure merchantReturnDays matches your actual policy
MovieCarouselJsonLd
The MovieCarouselJsonLd component helps you add structured data for movie carousels, enabling your movie lists to appear as rich results in Google Search on mobile devices. This component supports both summary page (URLs only) and all-in-one page (full movie data) patterns.
Basic Usage - Summary Page Pattern
Use this pattern when you have separate detail pages for each movie:
import { MovieCarouselJsonLd } from "next-seo";
<MovieCarouselJsonLd
urls={[
"https://example.com/movies/a-star-is-born",
"https://example.com/movies/bohemian-rhapsody",
"https://example.com/movies/black-panther",
]}
/>;All-in-One Page Pattern
Use this pattern when all movie information is on a single page:
<MovieCarouselJsonLd
movies={[
{
name: "A Star Is Born",
image: "https://example.com/photos/6x9/star-is-born.jpg",
dateCreated: "2024-10-05",
director: "Bradley Cooper",
review: {
reviewRating: { ratingValue: 5 },
author: "John D.",
},
aggregateRating: {
ratingValue: 90,
bestRating: 100,
ratingCount: 19141,
},
},
{
name: "Bohemian Rhapsody",
image: "https://example.com/photos/6x9/bohemian.jpg",
dateCreated: "2024-11-02",
director: "Bryan Singer",
aggregateRating: {
ratingValue: 61,
bestRating: 100,
ratingCount: 21985,
},
},
]}
/>Advanced Example with All Features
<MovieCarouselJsonLd
movies={[
{
name: "Black Panther",
url: "https://example.com/movies/black-panther",
image: [
"https://example.com/photos/1x1/black-panther.jpg",
"https://example.com/photos/4x3/black-panther.jpg",
"https://example.com/photos/16x9/black-panther.jpg",
],
dateCreated: "2024-02-16",
director: {
name: "Ryan Coogler",
url: "https://example.com/directors/ryan-coogler",
},
review: {
reviewRating: {
ratingValue: 2,
bestRating: 5,
},
author: {
name: "Trevor R.",
url: "https://example.com/reviewers/trevor",
},
reviewBody:
"While visually stunning, the plot fell short of expectations.",
datePublished: "2024-02-20",
},
aggregateRating: {
ratingValue: 96,
bestRating: 100,
ratingCount: 88211,
},
},
]}
/>Props
PropertyTypeDescriptionurlsArray<string | {url: string; position?: number}>Required for summary pattern. URLs to individual movie pagesmoviesMovieListItem[]Required for all-in-one pattern. Array of movie datascriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
MovieListItem Properties
PropertyTypeDescriptionnamestringRequired. The name of the movieimagestring | ImageObject | (string | ImageObject)[]Required. Movie poster/image (6:9 aspect ratio recommended)urlstringURL to the movie's pagedateCreatedstringRelease date in ISO 8601 formatdirectorstring | PersonMovie director (accepts string or Person object)reviewReviewA review of the movieaggregateRatingAggregateRatingAverage rating based on multiple reviews
Best Practices
Mobile-only feature: Movie carousels only appear on mobile devices in Google Search
Image requirements: Use 6:9 aspect ratio images (Google's requirement for movie carousels)
High-quality images: Images must be high resolution and properly formatted (.jpg, .png, .gif)
Multiple images: Consider providing multiple aspect ratios for better compatibility
Complete movie data: Include as many properties as possible for richer search results
Consistent data: All movies in the carousel must be from the same website
URL structure: For summary pages, ensure all URLs point to pages on the same domain
BreadcrumbJsonLd
The BreadcrumbJsonLd component helps you add breadcrumb structured data to indicate a page's position in the site hierarchy. This can help Google display breadcrumb trails in search results, making it easier for users to understand and navigate your site structure.
Basic Usage
import { BreadcrumbJsonLd } from "next-seo";
export default function ProductPage() {
return (
<>
<BreadcrumbJsonLd
items={[
{
name: "Home",
item: "https://example.com",
},
{
name: "Products",
item: "https://example.com/products",
},
{
name: "Electronics",
item: "https://example.com/products/electronics",
},
{
name: "Headphones",
item: "https://example.com/products/electronics/headphones",
},
{
name: "Wireless Headphones XYZ",
},
]}
/>
<main>
<h1>Wireless Headphones XYZ</h1>
{/* Product content */}
</main>
</>
);
}Multiple Breadcrumb Trails
Some pages can be reached through multiple paths. You can specify multiple breadcrumb trails:
<BreadcrumbJsonLd
multipleTrails={[
// First trail: Category path
[
{
name: "Books",
item: "https://example.com/books",
},
{
name: "Science Fiction",
item: "https://example.com/books/sciencefiction",
},
{
name: "Award Winners",
},
],
// Second trail: Award path
[
{
name: "Literature",
item: "https://example.com/literature",
},
{
name: "Award Winners",
},
],
]}
/>Advanced Example with Thing Objects
You can use Thing objects with @id instead of plain URL strings:
<BreadcrumbJsonLd
items={[
{
name: "Home",
item: "https://example.com",
},
{
name: "Blog",
item: { "@id": "https://example.com/blog" },
},
{
name: "Technology",
item: { "@id": "https://example.com/blog/technology" },
},
{
name: "Understanding JSON-LD",
},
]}
scriptId="blog-breadcrumb"
scriptKey="blog-breadcrumb-key"
/>Props
PropertyTypeDescriptionitemsBreadcrumbListItem[]Array of breadcrumb items (required if not using multipleTrails)multipleTrailsBreadcrumbListItem[][]Array of breadcrumb trails (required if not using items)scriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
BreadcrumbListItem Type:
PropertyTypeDescriptionnamestringRequired. The title of the breadcrumbitemstring | { "@id": string }URL or Thing object (optional for the last breadcrumb)
Best Practices
Omit the last item's URL: The last breadcrumb (current page) typically shouldn't have an
itempropertyUse logical hierarchy: Breadcrumbs should represent a typical user path, not necessarily mirror URL structure
Keep names concise: Use clear, descriptive names that help users understand the hierarchy
Multiple trails: Use
multipleTrailswhen a page can be logically reached through different pathsInclude home: Start trails from a logical entry point (often "Home") but it's not required
Avoid duplicates: Each trail should represent a unique path to the page
Match visual breadcrumbs: The structured data should match the breadcrumbs shown on your page
CarouselJsonLd
The CarouselJsonLd component helps you add structured data for carousels (ItemList) to enable rich results that display multiple cards from your site in a carousel format. This component supports Course, Movie, Recipe, and Restaurant content types.
Basic Usage
Summary Page Pattern (URLs only):
import { CarouselJsonLd } from "next-seo";
// Simple array of URLs
<CarouselJsonLd
urls={[
"https://example.com/recipe/cookies",
"https://example.com/recipe/cake",
"https://example.com/recipe/pie"
]}
/>
// With custom positions
<CarouselJsonLd
urls={[
{ url: "https://example.com/movie/matrix", position: 1 },
"https://example.com/movie/inception", // position will be 2
{ url: "https://example.com/movie/interstellar", position: 3 }
]}
/>All-in-One Page Pattern (Full Data):
import { CarouselJsonLd } from "next-seo";
// Course Carousel
<CarouselJsonLd
contentType="Course"
items={[
{
name: "Introduction to React",
description: "Learn the fundamentals of React",
url: "https://example.com/courses/react",
provider: "Tech Academy"
},
{
name: "Advanced TypeScript",
description: "Master TypeScript features",
provider: {
name: "Code School",
url: "https://example.com/school"
}
}
]}
/>
// Movie Carousel
<CarouselJsonLd
contentType="Movie"
items={[
{
name: "The Matrix",
image: "https://example.com/matrix.jpg",
director: "The Wachowskis",
dateCreated: "1999-03-31",
aggregateRating: {
ratingValue: 8.7,
ratingCount: 1000
}
},
{
name: "Inception",
image: [
"https://example.com/inception1.jpg",
"https://example.com/inception2.jpg"
],
director: { name: "Christopher Nolan" }
}
]}
/>
// Recipe Carousel
<CarouselJsonLd
contentType="Recipe"
items={[
{
name: "Chocolate Chip Cookies",
image: "https://example.com/cookies.jpg",
description: "Classic chocolate chip cookies",
author: "Chef John",
prepTime: "PT20M",
cookTime: "PT12M",
recipeYield: 24,
recipeIngredient: [
"2 cups flour",
"1 cup butter",
"1 cup chocolate chips"
],
aggregateRating: {
ratingValue: 4.8,
ratingCount: 250
}
}
]}
/>
// Restaurant Carousel
<CarouselJsonLd
contentType="Restaurant"
items={[
{
name: "Joe's Pizza",
address: "123 Main St, New York, NY 10001",
telephone: "+1-212-555-0100",
servesCuisine: ["Italian", "Pizza"],
priceRange: "$$",
aggregateRating: {
ratingValue: 4.5,
ratingCount: 500
},
geo: {
latitude: 40.7128,
longitude: -74.0060
}
}
]}
/>Advanced Examples
Recipe Carousel with Full Details:
<CarouselJsonLd
contentType="Recipe"
items={[
{
name: "Perfect Pancakes",
image: [
"https://example.com/pancakes1.jpg",
"https://example.com/pancakes2.jpg",
],
description: "Fluffy and delicious pancakes",
author: [
"Chef Alice",
{ name: "Chef Bob", url: "https://example.com/chefs/bob" },
],
datePublished: "2024-01-01",
prepTime: "PT10M",
cookTime: "PT15M",
totalTime: "PT25M",
recipeYield: "4 servings",
recipeCategory: "Breakfast",
recipeCuisine: "American",
recipeIngredient: [
"2 cups all-purpose flour",
"2 tablespoons sugar",
"2 eggs",
"1 1/2 cups milk",
],
recipeInstructions: [
"Mix dry ingredients in a bowl",
{ text: "Whisk wet ingredients separately" },
{
name: "Cooking",
itemListElement: [
{ text: "Heat griddle to 375°F" },
{ text: "Pour batter and cook until bubbles form" },
{ text: "Flip and cook until golden" },
],
},
],
nutrition: {
calories: "250 calories",
proteinContent: "8g",
carbohydrateContent: "35g",
fatContent: "9g",
},
aggregateRating: {
ratingValue: 4.9,
ratingCount: 1200,
},
video: {
name: "How to Make Perfect Pancakes",
description: "Step-by-step video guide",
thumbnailUrl: "https://example.com/pancakes-thumb.jpg",
contentUrl: "https://example.com/pancakes-video.mp4",
uploadDate: "2024-01-01",
duration: "PT5M30S",
},
keywords: "pancakes, breakfast, easy recipe",
},
]}
/>Restaurant Carousel with Opening Hours:
<CarouselJsonLd
contentType="Restaurant"
items={[
{
name: "Fine Dining Restaurant",
address: {
streetAddress: "456 Oak Avenue",
addressLocality: "San Francisco",
addressRegion: "CA",
postalCode: "94102",
addressCountry: "US",
},
image: [
"https://example.com/restaurant1.jpg",
"https://example.com/restaurant2.jpg",
],
telephone: "+1-415-555-0200",
url: "https://example.com/restaurant",
menu: "https://example.com/restaurant/menu",
servesCuisine: ["French", "Mediterranean"],
priceRange: "$$$",
openingHoursSpecification: [
{
dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday"],
opens: "17:00",
closes: "22:00",
},
{
dayOfWeek: ["Friday", "Saturday"],
opens: "17:00",
closes: "23:00",
},
],
review: [
{
reviewRating: { ratingValue: 5 },
author: "Food Critic",
reviewBody: "Exceptional dining experience",
},
],
aggregateRating: {
ratingValue: 4.7,
bestRating: 5,
ratingCount: 850,
},
},
]}
/>Props
PropertyTypeDescriptionurlsSummaryPageItem[]Array of URLs for summary page patterncontentType"Course" | "Movie" | "Recipe" | "Restaurant"Type of content in the carousel (for all-in-one)itemsCourseItem[] | MovieItem[] | RecipeItem[] | RestaurantItem[]Array of items matching the content typescriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
SummaryPageItem Type:
TypeDescriptionstringSimple URL string{ url: string; position?: number }URL with optional custom position
Best Practices
Choose the right pattern:
Use summary page pattern when you have separate detail pages for each item
Use all-in-one pattern when all content is on a single page
Consistent content types: All items in a carousel must be of the same type (e.g., all recipes or all movies)
Required images:
Movies require at least one image
Recipes should include images for better visibility
Use multiple aspect ratios when possible
Position numbering:
Positions start at 1, not 0
If not specified, positions are auto-assigned sequentially
URL structure: For summary pages, ensure all URLs point to pages on the same domain
Rich content: Include as much relevant information as possible for better search results
Validation: Test your structured data with Google's Rich Results Test
CourseJsonLd
The CourseJsonLd component helps you add structured data for courses to enable course list rich results in Google Search. This can help prospective students discover your courses more easily.
Basic Usage
Single Course:
import { CourseJsonLd } from "next-seo";
<CourseJsonLd
name="Introduction to Computer Science"
description="An introductory CS course laying out the basics."
provider="University of Technology"
/>;Course List:
import { CourseJsonLd } from "next-seo";
// Summary page pattern - just URLs
<CourseJsonLd
type="list"
urls={[
"https://example.com/courses/intro-cs",
"https://example.com/courses/intermediate-cs",
"https://example.com/courses/advanced-cs"
]}
/>
// All-in-one page pattern - full course data
<CourseJsonLd
type="list"
courses={[
{
name: "Introduction to Programming",
description: "Learn the basics of programming.",
url: "https://example.com/courses/intro-programming",
provider: "Tech Institute"
},
{
name: "Advanced Algorithms",
description: "Study complex algorithmic solutions.",
provider: {
name: "University Online",
sameAs: "https://university.edu"
}
}
]}
/>Props
Single Course Props:
PropertyTypeDescriptiontype"single"Optional. Explicitly sets single course patternnamestringRequired. The title of the coursedescriptionstringRequired. A description of the course (60 char limit for display)urlstringThe URL of the course pageproviderstring | Organization | Omit<Organization, "@type">The organization offering the coursescriptIdstringCustom ID for the script tagscriptKeystringCustom key for React reconciliation
Course List Props:
PropertyTypeDescriptiontype"list"Required. Sets the course list patternurls(string | { url: string; position?: number })[]URLs for summary page patterncoursesCourseListItem[]Full course data for all-in-one patternscriptIdstringCustom ID for the script tagscriptKeystringCustom key for React reconciliation
Advanced Example
import { CourseJsonLd } from "next-seo";
export default function CourseCatalogPage() {
return (
<>
<CourseJsonLd
type="list"
courses={[
{
name: "Full-Stack Web Development",
description: "Master modern web development from front to back.",
url: "https://example.com/courses/fullstack",
provider: {
name: "Code Academy",
url: "https://codeacademy.com",
sameAs: [
"https://twitter.com/codeacademy",
"https://linkedin.com/company/codeacademy",
],
},
},
{
name: "Data Science with Python",
description:
"Learn data analysis and machine learning with Python.",
url: "https://example.com/courses/data-science",
provider: "Tech University",
},
{
name: "Mobile App Development",
description: "Build iOS and Android apps with React Native.",
url: "https://example.com/courses/mobile-dev",
provider: {
name: "Mobile Dev Institute",
logo: "https://example.com/logo.png",
},
},
]}
/>
<h1>Our Course Catalog</h1>
{/* Your course list UI */}
</>
);
}Best Practices
Minimum of 3 courses: Google requires at least 3 courses for course list rich results
Consistent provider: Use the same format for provider across all courses
Description length: Keep descriptions under 60 characters for optimal display
Valid URLs: Ensure all course URLs are accessible and on the same domain
Choose the right pattern:
Use summary page pattern when courses have their own detail pages
Use all-in-one pattern when all course information is on a single page
Avoid promotional content: Don't include prices, discounts, or marketing language in course names
EventJsonLd
The EventJsonLd component helps you add structured data for events to improve their discoverability in Google Search results and other Google products like Google Maps. Events can appear with rich features including images, dates, locations, and ticket information.
Basic Usage
import { EventJsonLd } from "next-seo";
<EventJsonLd
name="The Adventures of Kira and Morrison"
startDate="2025-07-21T19:00-05:00"
location="Snickerpark Stadium"
/>;Standard Event Example
<EventJsonLd
name="The Adventures of Kira and Morrison"
startDate="2025-07-21T19:00-05:00"
endDate="2025-07-21T23:00-05:00"
location={{
"@type": "Place",
name: "Snickerpark Stadium",
address: {
"@type": "PostalAddress",
streetAddress: "100 West Snickerpark Dr",
addressLocality: "Snickertown",
postalCode: "19019",
addressRegion: "PA",
addressCountry: "US",
},
}}
description="The Adventures of Kira and Morrison is coming to Snickertown in a can't miss performance."
image={[
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg",
]}
offers={{
"@type": "Offer",
url: "https://www.example.com/event_offer/12345_202403180430",
price: 30,
priceCurrency: "USD",
availability: "https://schema.org/InStock",
validFrom: "2024-05-21T12:00",
}}
performer={{
"@type": "PerformingGroup",
name: "Kira and Morrison",
}}
organizer={{
"@type": "Organization",
name: "Kira and Morrison Music",
url: "https://kiraandmorrisonmusic.com",
}}
/>Event Status Examples
Cancelled Event
<EventJsonLd
name="Summer Festival 2025"
startDate="2025-08-15T12:00:00"
location="City Park"
eventStatus="https://schema.org/EventCancelled"
/>Rescheduled Event
<EventJsonLd
name="Tech Conference 2025"
startDate="2025-09-20T09:00:00"
location="Convention Center"
eventStatus="https://schema.org/EventRescheduled"
previousStartDate="2025-07-15T09:00:00"
/>Props
PropertyTypeDescriptionnamestringRequired. The full title of the eventstartDatestringRequired. Start date/time in ISO-8601 formatlocationstring | PlaceRequired. Event venue (string or Place object)endDatestringEnd date/time in ISO-8601 formatdescriptionstringDetailed description of the eventeventStatusEventStatusTypeStatus: EventScheduled (default), EventCancelled, EventPostponed, EventRescheduledimagestring | ImageObject | (string | ImageObject)[]Event images (recommended: multiple aspect ratios)offersOffer | Offer[]Ticket/pricing informationperformerstring | Person | PerformingGroup | arrayPerformers at the eventorganizerstring | Person | OrganizationEvent host/organizerpreviousStartDatestring | string[]Previous date(s) for rescheduled eventsurlstringURL of the event pagescriptIdstringCustom ID for the script tagscriptKeystringCustom key prop for React
Offer Type
PropertyTypeDescriptionurlstringURL to purchase ticketspricenumberLowest available price (use 0 for free events)priceCurrencystring3-letter ISO 4217 currency code (e.g., "USD")availabilitystringAvailability status (InStock, SoldOut, PreOrder)validFromstringDate/time when tickets go on sale
Best Practices
Date/Time Format: Always use ISO-8601 format with timezone offset (e.g.,
2025-07-21T19:00-05:00)Day-long Events: For all-day events, use date only format (e.g.,
2025-07-04)Location Details: Provide complete address information for better discoverability
Multiple Images: Include images in different aspect ratios (16:9, 4:3, 1:1) for various display contexts
Event Status: Keep original dates when cancelling/postponing; only update the
eventStatusFree Events: Set
price: 0for events without chargeMultiple Performers: Use an array when listing multiple artists or speakers
Rescheduled Events: Always include
previousStartDatewhen usingEventRescheduledstatus
Date and Time Guidelines
Include timezone: Specify UTC/GMT offset (e.g.,
-05:00for EST)Multi-day events: Set both
startDateandendDateUnknown end time: Omit
endDaterather than guessingDate-only format: Use for all-day events (e.g., festivals)
Example timezone handling:
// New York event during standard time
startDate: "2025-12-21T19:00:00-05:00";
// California event during daylight saving time
startDate: "2025-07-21T19:00:00-07:00";
// All-day event
startDate: "2025-07-04";
endDate: "2025-07-04";FAQJsonLd
The FAQJsonLd component helps you add structured data for frequently asked questions (FAQ) pages. This can help your FAQ content appear as rich results in Google Search, making it easier for users to find answers to common questions.
Note: FAQ rich results are only available for well-known, authoritative government or health websites. However, implementing proper FAQ structured data is still valuable for SEO and can help search engines better understand your content.
Basic Usage
import { FAQJsonLd } from "next-seo";
export default function FAQPage() {
return (
<>
<FAQJsonLd
questions={[
{
question: "How to find an apprenticeship?",
answer:
"We provide an official service to search through available apprenticeships. To get started, create an account here, specify the desired region, and your preferences.",
},
{
question: "Whom to contact?",
answer:
"You can contact the apprenticeship office through our official phone hotline above, or with the web-form below.",
},
]}
/>
<h1>Frequently Asked Questions</h1>
{/* Your FAQ content */}
</>
);
}Advanced Example with HTML Content
FAQ answers support HTML content including links, lists, and formatting:
<FAQJsonLd
questions={[
{
question: "What documents are required for application?",
answer: `
<p>You'll need to provide the following documents:</p>
<ul>
<li>Valid government-issued ID</li>
<li>High school diploma or equivalent</li>
<li>Proof of residence</li>
<li><a href="/forms/medical">Medical clearance form</a></li>
</ul>
<p>All documents must be submitted within 30 days of application.</p>
`,
},
{
question: "How long does the application process take?",
answer:
"<p>The typical processing time is <strong>7-10 business days</strong> from the date we receive all required documents.</p>",
},
]}
scriptId="faq-structured-data"
/>Different Input Formats
The component supports multiple input formats for flexibility:
// Simple question/answer format (recommended)
<FAQJsonLd
questions={[
{
question: "What is the cost?",
answer: "The program is free for eligible participants.",
},
]}
/>
// Schema.org name/acceptedAnswer format
<FAQJsonLd
questions={[
{
name: "What is the cost?",
acceptedAnswer: "The program is free for eligible participants.",
},
]}
/>
// With Answer object
<FAQJsonLd
questions={[
{
name: "What is the cost?",
acceptedAnswer: {
"@type": "Answer",
text: "The program is free for eligible participants.",
},
},
]}
/>Props
PropertyTypeDescriptionquestionsQuestionInput[]Required. Array of questions and answers. See input formats below.scriptIdstringOptional. Sets the id attribute on the script tag.scriptKeystringOptional. Sets the data-testid attribute on the script tag. Defaults to "faq-jsonld".
Question Input Formats
The questions array accepts several formats:
Simple object (recommended):
{ question: "string", answer: "string" }Schema.org format:
{ name: "string", acceptedAnswer: "string" }Full Answer object:
{ name: "string", acceptedAnswer: { "@type": "Answer", text: "string" } }
Best Practices
Include complete Q&A: Each question and answer should contain the full text that appears on your page
Use HTML wisely: Google supports these HTML tags in answers:
<h1>through<h6>,<br>,<ol>,<ul>,<li>,<a>,<p>,<div>,<b>,<strong>,<i>, and<em>Match page content: The FAQ structured data must match the visible Q&A content on your page
Avoid promotional content: Don't use FAQPage for advertising purposes
One instance per page: If the same FAQ appears on multiple pages, only mark it up on one page
Expandable sections: It's fine if answers are hidden behind expandable sections, as long as users can access them
No user submissions: FAQPage is for questions with single, authoritative answers. For user-generated Q&A, use QAPage instead
ImageJsonLd
The ImageJsonLd component helps you add structured data for images to improve their appearance in Google Images. This enables features like the Licensable badge and displays metadata such as creator, credit, copyright, and licensing information.
Basic Usage
import { ImageJsonLd } from "next-seo";
<ImageJsonLd
contentUrl="https://example.com/photos/black-labrador-puppy.jpg"
creator="Brixton Brownstone"
license="https://example.com/license"
acquireLicensePage="https://example.com/how-to-use-my-images"
creditText="Labrador PhotoLab"
copyrightNotice="Clara Kent"
/>;Advanced Usage - Organization Creator
<ImageJsonLd
contentUrl="https://example.com/photos/product-photo.jpg"
creator={{
name: "PhotoLab Studios",
logo: "https://example.com/photolab-logo.jpg",
sameAs: ["https://twitter.com/photolab", "https://instagram.com/photolab"],
}}
license="https://creativecommons.org/licenses/by-nc/4.0/"
acquireLicensePage="https://example.com/licensing"
creditText="PhotoLab Studios"
copyrightNotice="© 2024 PhotoLab Studios"
/>Multiple Images
<ImageJsonLd
images={[
{
contentUrl: "https://example.com/photos/black-labrador-puppy.jpg",
creator: "Brixton Brownstone",
license: "https://example.com/license",
creditText: "Labrador PhotoLab",
},
{
contentUrl: "https://example.com/photos/adult-black-labrador.jpg",
creator: [
"Brixton Brownstone",
{
name: "Clara Kent",
url: "https://clarakent.com",
},
],
copyrightNotice: "© 2024 Clara Kent",
license: "https://example.com/license",
},
]}
/>Props
PropertyTypeDescriptioncontentUrlstringRequired. The URL of the actual image contentcreatorAuthor | Author[]The creator(s) of the image (photographer, designer, etc.). Can be string name(s), Person, or Organization object(s)creditTextstringThe name of the person/organization credited when the image is publishedcopyrightNoticestringThe copyright notice for claiming intellectual propertylicensestringURL to a page describing the license governing the image's useacquireLicensePagestringURL to a page where users can find information on how to license the imageimagesArray<ImageObject>Array of image objects with the above properties (for multiple images)scriptIdstringCustom ID for the script tagscriptKeystringCustom key for script deduplication
Note: You must include
contentUrland at least one of:creator,creditText,copyrightNotice, orlicensefor the image to be eligible for enhancements like the Licensable badge.
Best Practices
Always provide licensing information: Include the
licenseproperty to make your images eligible for the Licensable badgeCredit creators properly: Use structured creator information to ensure proper attribution
Include acquire license page: Help users understand how they can legally use your images
Use consistent copyright notices: Maintain clear copyright information across your images
Multiple creators: When multiple people contributed to an image, list all creators
Organization vs Person: Use Organization type for companies/studios, Person type for individuals
QuizJsonLd
The QuizJsonLd component helps you add structured data for educational quizzes and flashcards. This can help your educational content appear in Google's education Q&A carousel when users search for educational topics.
Basic Usage
import { QuizJsonLd } from "next-seo";
export default function BiologyQuizPage() {
return (
<>
<QuizJsonLd
questions={[
{
question: "What is the powerhouse of the cell?",
answer: "Mitochondria",
},
{
question:
"What process do plants use to convert sunlight into energy?",
answer: "Photosynthesis",
},
]}
about="Cell Biology"
educationalAlignment={[
{
type: "educationalSubject",
name: "Biology",
},
{
type: "educationalLevel",
name: "Grade 10",
},
]}
/>
{/* Your quiz content */}
</>
);
}Props
PropertyTypeDescriptionquestionsQuestionInput[]Required. Array of flashcard questions and answersaboutstring | ThingThe subject or topic of the quizeducationalAlignmentArray<{type: "educationalSubject" | "educationalLevel", name: string}>Educational alignments specifying subject and/or grade levelscriptIdstringCustom ID for the script tagscriptKeystringCustom key for React (defaults to "quiz-jsonld")
Question Formats
The questions array accepts several formats:
Simple object (recommended):
{ question: "What is 2 + 2?", answer: "4" }String format (for fact-based flashcards):
"The Earth revolves around the Sun in 365.25 days";Text/acceptedAnswer format:
{ text: "What is DNA?", acceptedAnswer: "Deoxyribonucleic acid" }Full Answer object:
{ text: "Explain photosynthesis", acceptedAnswer: { "@type": "Answer", text: "The process by which plants convert light energy into chemical energy" } }
Advanced Example
<QuizJsonLd
questions={[
// Simple flashcard fact
"The mitochondria is the powerhouse of the cell",
// Question/answer format
{
question: "What are the four bases of DNA?",
answer: "Adenine (A), Thymine (T), Guanine (G), and Cytosine (C)",
},
// Full format with Answer object
{
text: "Describe the water cycle",
acceptedAnswer: {
"@type": "Answer",
text: "The continuous movement of water through evaporation, condensation, precipitation, and collection",
},
},
]}
about={{
name: "Biology Fundamentals",
description: "Core concepts in cellular and molecular biology",
url: "https://example.com/biology-course",
}}
educationalAlignment={[
{
type: "educationalSubject",
name: "Biology",
},
{
type: "educationalLevel",
name: "High School",
},
]}
/>Best Practices
Educational content only: Quiz structured data is specifically for educational flashcards and Q&A
Use "Flashcard" type: All questions automatically use
eduQuestionType: "Flashcard"as required by GoogleClear answers: Provide concise, factual answers appropriate for the educational level
Subject alignment: Always specify the educational subject using
educationalAlignmentGrade level: Include the target grade or educational level when applicable
Match visible content: The structured data should match the quiz content displayed on your page
Single answer format: Each question should have one clear, authoritative answer
Note: The education Q&A carousel is available when searching for education-related topics in English, Portuguese, Spanish (Mexico), and Vietnamese.
DatasetJsonLd
The DatasetJsonLd component helps you add structured data for datasets, making them easier to find in Google's Dataset Search. This is ideal for scientific data, government data, machine learning datasets, and any other structured data collections.
Basic Usage
import { DatasetJsonLd } from "next-seo";
export default function DatasetPage() {
return (
<>
<DatasetJsonLd
name="NCDC Storm Events Database"
description="Storm Data is provided by the National Weather Service (NWS) and contain statistics on personal injuries and damage estimates."
url="https://example.com/dataset/storm-events"
creator="NOAA"
distribution={{
contentUrl: "https://www.ncdc.noaa.gov/stormevents/ftp.jsp",
encodingFormat: "CSV",
}}
/>
{/* Your dataset page content */}
</>
);
}Advanced Example with Full Features
<DatasetJsonLd
name="Global Climate Data 2020-2024"
description="Comprehensive climate measurements including temperature, precipitation, and atmospheric data collected from weather stations worldwide"
url="https://example.com/datasets/global-climate-2020-2024"
sameAs={[
"https://doi.org/10.1000/182",
"https://data.gov/dataset/climate-2020-2024",
]}
identifier={[
"https://doi.org/10.1000/182",
{
value: "ark:/12345/fk1234",
propertyID: "ARK",
},
]}
keywords={[
"climate",
"temperature",
"precipitation",
"weather",
"atmospheric data",
]}
license="https://creativecommons.org/publicdomain/zero/1.0/"
isAccessibleForFree={true}
creator={[
{
name: "National Centers for Environmental Information",
url: "https://www.ncei.noaa.gov/",
contactPoint: {
contactType: "customer service",
telephone: "+1-828-271-4800",
email: "[email protected]",
},
},
"Dr. Jane Smith",
]}
funder={{
name: "National Science Foundation",
sameAs: "https://ror.org/021nxhr62",
}}
includedInDataCatalog={{
name: "data.gov",
url: "https://data.gov",
}}
distribution={[
{
contentUrl: "https://example.com/data/climate-2020-2024.csv",
encodingFormat: "CSV",
contentSize: "2.5GB",
description: "Complete dataset in CSV format",
},
{
contentUrl: "https://example.com/data/climate-2020-2024.json",
encodingFormat: "JSON",
contentSize: "3.1GB",
description: "Complete dataset in JSON format",
},
]}
temporalCoverage="2020-01-01/2024-12-31"
spatialCoverage={{
name: "Global",
geo: {
box: "-90 -180 90 180",
},
}}
measurementTechnique="Satellite observation and ground station measurements"
variableMeasured={[
"temperature",
"precipitation",
{
name: "Atmospheric Pressure",
value: "hectopascals",
},
]}
version="2.1"
/>Props
PropertyTypeDescriptionnamestringRequired. A descriptive name of the datasetdescriptionstringRequired. A short summary describing the dataset (50-5000 characters)urlstringURL of the dataset landing pagesameAsstring | string[]URLs of pages that unambiguously indicate the dataset's identityidentifierstring | PropertyValue | (string | PropertyValue)[]Identifiers such as DOI or Compact Identifierskeywordsstring | string[]Keywords summarizing the datasetlicensestring | CreativeWorkLicense under which the dataset is distributedisAccessibleForFreebooleanWhether the dataset is accessible without paymenthasPartDataset | Dataset[]Smaller datasets that are part of this datasetisPartOfstring | DatasetA larger dataset that this dataset is part ofcreatorstring | Person | Organization | (string | Person | Organization)[]The creator or author of the datasetfunderstring | Person | Organization | (string | Person | Organization)[]Person or organization that provides financial supportincludedInDataCatalogDataCatalogThe catalog to which the dataset belongsdistributionDataDownload | DataDownload[]Download locations and formats for the datasettemporalCoveragestringTime interval covered by the dataset (ISO 8601 format)spatialCoveragestring | PlaceSpatial aspect of the dataset (location name or coordinates)alternateNamestring | string[]Alternative names for the datasetcitationstring | CreativeWork | (string | CreativeWork)[]Academic articles to cite alongside the datasetmeasurementTechniquestring | string[]Technique or methodology used in the datasetvariableMeasuredstring | PropertyValue | (string | PropertyValue)[]Variables that the dataset measuresversionstring | numberVersion number for the datasetscriptIdstringCustom ID for the script tagscriptKeystringCustom key for React (defaults to "dataset-jsonld")
Spatial Coverage Examples
// Named location
spatialCoverage="United States"
// Point coordinates
spatialCoverage={{
geo: {
latitude: 39.3280,
longitude: 120.1633,
}
}}
// Bounding box (format: "minLat minLon maxLat maxLon")
spatialCoverage={{
geo: {
box: "39.3280 120.1633 40.445 123.7878",
}
}}
// Circle (format: "latitude longitude radius")
spatialCoverage={{
geo: {
circle: "39.3280 120.1633 100",
}
}}Temporal Coverage Examples
// Single date
temporalCoverage = "2024";
// Date range
temporalCoverage = "2020-01-01/2024-12-31";
// Open-ended range
temporalCoverage = "2024-01-01/..";Best Practices
Comprehensive descriptions: Provide detailed descriptions (50-5000 characters) that clearly explain what the dataset contains
Use persistent identifiers: Include DOIs or other persistent identifiers when available
Multiple formats: If your dataset is available in multiple formats, list all distributions
Specify license: Always include license information to clarify usage rights
Include temporal/spatial coverage: Help users understand the scope of your data
Use ORCID/ROR IDs: When specifying creators or funders, use ORCID IDs for individuals and ROR IDs for organizations in the
sameAsfieldVersion your datasets: Include version numbers to help users track updates
Link to catalogs: If your dataset is in a repository like data.gov, include the
includedInDataCatalogproperty
Note: Dataset structured data helps your datasets appear in Google's Dataset Search, which is specifically designed for discovering research and government data.
JobPostingJsonLd
The JobPostingJsonLd component helps you add structured data for job postings to improve their appearance in Google's job search results and the Google Jobs experience.
Basic Usage
import { JobPostingJsonLd } from "next-seo";
export default function JobPage() {
return (
<>
<JobPostingJsonLd
title="Software Engineer"
description="<p>We are looking for a passionate Software Engineer to design, develop and install software solutions.</p>"
datePosted="2024-01-18"
hiringOrganization="Google"
jobLocation="Mountain View, CA"
baseSalary={{
currency: "USD",
value: {
value: 40.0,
unitText: "HOUR",
},
}}
/>
<article>
<h1>Software Engineer</h1>
{/* Job posting content */}
</article>
</>
);
}Advanced Example with Full Details
<JobPostingJsonLd
title="Senior Software Engineer"
description="<p>Google is looking for a Senior Software Engineer to join our Cloud team. You will be responsible for designing and developing large-scale distributed systems.</p><p>Requirements:</p><ul><li>5+ years of experience</li><li>Strong knowledge of distributed systems</li><li>Experience with cloud technologies</li></ul>"
datePosted="2024-01-18"
validThrough="2024-03-18T00:00"
hiringOrganization={{
name: "Google",
sameAs: "https://www.google.com",
logo: "https://www.google.com/images/logo.png",
}}
jobLocation={{
address: {
streetAddress: "1600 Amphitheatre Pkwy",
addressLocality: "Mountain View",
addressRegion: "CA",
postalCode: "94043",
addressCountry: "US",
},
}}
url="https://careers.google.com/jobs/123456"
employmentType="FULL_TIME"
identifier={{
name: "Google",
value: "1234567",
}}
baseSalary={{
currency: "USD",
value: {
minValue: 120000,
maxValue: 180000,
unitText: "YEAR",
},
}}
directApply={true}
educationRequirements={{
credentialCategory: "bachelor degree",
}}
experienceRequirements={{
monthsOfExperience: 60,
}}
experienceInPlaceOfEducation={true}
/>Remote Job Example
<JobPostingJsonLd
title="Remote Frontend Developer"
description="<p>Join our distributed team as a Frontend Developer. Work from anywhere in the US!</p>"
datePosted="2024-01-18"
validThrough="2024-02-28T00:00"
hiringOrganization="TechStartup Inc."
jobLocationType="TELECOMMUTE"
applicantLocationRequirements={{
name: "USA",
}}
employmentType="FULL_TIME"
baseSalary={{
currency: "USD",
value: {
value: 100000,
unitText: "YEAR",
},
}}
/>Hybrid Job Example (Remote + Office)
<JobPostingJsonLd
title="Product Manager"
description="<p>Hybrid position: work from our NYC office or remotely from NY/NJ/CT.</p>"
datePosted="2024-01-18"
hiringOrganization="Example Corp"
jobLocation={{
address: {
streetAddress: "123 Main St",
addressLocality: "New York",
addressRegion: "NY",
postalCode: "10001",
addressCountry: "US",
},
}}
jobLocationType="TELECOMMUTE"
applicantLocationRequirements={[
{ name: "New York, USA" },
{ name: "New Jersey, USA" },
{ name: "Connecticut, USA" },
]}
employmentType={["FULL_TIME", "CONTRACTOR"]}
/>Props
PropertyTypeDescriptiontitlestringRequired. The title of the job (not the posting). E.g., "Software Engineer"descriptionstringRequired. The full job description in HTML formatdatePostedstringRequired. ISO 8601 date when the job was postedhiringOrganizationstring | OrganizationRequired. The organization offering the jobjobLocationstring | Place | (string | Place)[]Physical location(s) where employee reports to workurlstringThe canonical URL for the job postingvalidThroughstringISO 8601 date when the job posting expiresemploymentTypeEmploymentType | EmploymentType[]Type of employment (FULL_TIME, PART_TIME, CONTRACTOR, etc.)identifierstring | PropertyValueThe hiring organization's unique identifier for the jobbaseSalaryMonetaryAmountThe base salary of the job (as provided by employer)applicantLocationRequirementsCountry | State | (Country | State)[]Geographic locations where employees may be located for remote jobsjobLocationType"TELECOMMUTE"Set to "TELECOMMUTE" for 100% remote jobsdirectApplybooleanWhether the URL enables direct applicationeducationRequirementsstring | EducationalOccupationalCredential | (string | EducationalOccupationalCredential)[]Education requirements for the positionexperienceRequirementsstring | OccupationalExperienceRequirementsExperience requirements for the positionexperienceInPlaceOfEducationbooleanWhether experience can substitute for education requirementsscriptIdstringCustom ID for the script tagscriptKeystringCustom key for React (defaults to "jobposting-jsonld")
Employment Type Values
Use these values for the employmentType property:
FULL_TIME- Full-time employmentPART_TIME- Part-time employmentCONTRACTOR- Contractor positionTEMPORARY- Temporary employmentINTERN- Internship positionVOLUNTEER- Volunteer positionPER_DIEM- Paid by the dayOTHER- Other employment type
Salary Examples
// Hourly wage
baseSalary={{
currency: "USD",
value: {
value: 25.00,
unitText: "HOUR",
},
}}
// Annual salary range
baseSalary={{
currency: "USD",
value: {
minValue: 80000,
maxValue: 120000,
unitText: "YEAR",
},
}}
// Monthly salary
baseSalary={{
currency: "EUR",
value: {
value: 5000,
unitText: "MONTH",
},
}}Best Practices
Complete job descriptions: Use HTML formatting with
<p>,<ul>, and<li>tags for better structureInclude salary information: Jobs with salary info get more visibility and engagement
Set expiration dates: Always include
validThroughto automatically expire old postingsUse employment type: Specify whether the job is full-time, part-time, contract, etc.
Remote job requirements: For remote jobs, always specify
applicantLocationRequirementsDirect apply: Set
directApply: trueif users can apply directly on your siteMultiple locations: List all office locations if the job can be performed at multiple sites
Remove expired jobs: Update or remove the structured data when jobs are filled
Note: Job postings must comply with Google's content policies. Jobs must be actual openings (not recruiting firms collecting resumes), include application instructions, and be removed when filled.
DiscussionForumPostingJsonLd
The DiscussionForumPostingJsonLd component helps you add structured data for forum posts and discussions to improve their appearance in Google's Discussions and Forums search feature.
Basic Usage
import { DiscussionForumPostingJsonLd } from "next-seo";
<DiscussionForumPostingJsonLd
headline="I went to the concert!"
text="Look at how cool this concert was!"
author="Katie Pope"
datePublished="2024-01-01T08:00:00+00:00"
url="https://example.com/forum/very-popular-thread"
comment={[
{
text: "Who's the person you're with?",
author: "Saul Douglas",
datePublished: "2024-01-01T09:46:02+00:00",
},
{
text: "That's my mom, isn't she cool?",
author: "Katie Pope",
datePublished: "2024-01-01T09:50:25+00:00",
},
]}
/>;Props
PropertyTypeDescriptiontype"DiscussionForumPosting" | "SocialMediaPosting"The type of posting. Defaults to "DiscussionForumPosting"authorstring | Person | Organization | Author[]Required. The author(s) of the postdatePublishedstringRequired. Publication date in ISO 8601 formattextstringThe text content of the postimagestring | ImageObject | (string | ImageObject)[]Images in the postvideoVideoObjectVideo content in the postheadlinestringThe title of the posturlstringThe canonical URL of the discussiondateModifiedstringLast modification date in ISO 8601 formatcommentComment[]Comments/replies to the postcreativeWorkStatusstringStatus of the post (e.g., "Deleted")interactionStatisticInteractionCounter | InteractionCounter[]User interaction statisticsisPartOfstring | CreativeWorkThe forum/subforum this post belongs tosharedContentstring | WebPage | ImageObject | VideoObjectContent shared in the postscriptIdstringCustom ID for the script tagscriptKeystringReact key for the script tag
Nested Comments Example
<DiscussionForumPostingJsonLd
headline="Very Popular Thread"
author={{
name: "Katie Pope",
url: "https://example.com/user/katie-pope",
}}
datePublished="2024-01-01T08:00:00+00:00"
text="Look at how cool this concert was!"
comment={[
{
text: "This should not be this popular",
author: "Commenter One",
datePublished: "2024-01-01T09:00:00+00:00",
comment: [
{
text: "Yes it should",
author: "Commenter Two",
datePublished: "2024-01-01T09:30:00+00:00",
},
],
},
]}
/>Social Media Posting Example
<DiscussionForumPostingJsonLd
type="SocialMediaPosting"
author="SocialUser"
datePublished="2024-01-01T12:00:00+00:00"
text="Just shared an amazing article!"
sharedContent={{
url: "https://example.com/amazing-article",
name: "Amazing Article Title",
description: "A brief description of the article",
}}
interactionStatistic={[
{
interactionType: "https://schema.org/LikeAction",
userInteractionCount: 150,
},
{
interactionType: "https://schema.org/ShareAction",
userInteractionCount: 25,
},
]}
/>Interaction Types
The following interaction types are supported for interactionStatistic:
https://schema.org/LikeAction- Likes or upvoteshttps://schema.org/DislikeAction- Dislikes or downvoteshttps://schema.org/ViewAction- View counthttps://schema.org/CommentActionorhttps://schema.org/ReplyAction- Comment counthttps://schema.org/ShareAction- Share count
Best Practices
Include all visible content: Add all text, images, and videos that appear in the post
Nested comments: Use the nested structure to represent threaded discussions accurately
Author information: Include author URLs linking to profile pages when available
Interaction statistics: Add engagement metrics to help Google understand post popularity
Deleted content: Use
creativeWorkStatus: "Deleted"for removed posts that remain for contextForum hierarchy: Use
isPartOfto indicate which subforum or category the post belongs toISO 8601 dates: Always use proper date formatting with timezone information
Multi-page threads: For paginated discussions, set the
urlto the first page
Note: DiscussionForumPosting is designed for forum-style sites where people share first-hand perspectives. For Q&A formats, use Q&A structured data instead.
EmployerAggregateRatingJsonLd
The EmployerAggregateRatingJsonLd component helps you add structured data for user-generated ratings about hiring organizations. This enables job seekers to see ratings in the enriched job search experience on Google.
Basic Usage
import { EmployerAggregateRatingJsonLd } from "next-seo";
<EmployerAggregateRatingJsonLd
itemReviewed="World's Best Coffee Shop"
ratingValue={91}
ratingCount={10561}
/>;Props
PropertyTypeDescriptionitemReviewedstring | OrganizationRequired. The organization being ratedratingValuenumber | stringRequired. The rating value (number, fraction, or percentage)ratingCountnumberThe total number of ratings (at least one of ratingCount or reviewCount required)reviewCountnumberThe number of reviews (at least one of ratingCount or reviewCount required)bestRatingnumberThe highest value allowed in this rating system (default: 5)worstRatingnumberThe lowest value allowed in this rating system (default: 1)scriptIdstringCustom ID for the script tagscriptKeystringReact key for the script tag
Advanced Example
<EmployerAggregateRatingJsonLd
itemReviewed={{
name: "World's Best Coffee Shop",
sameAs: "https://www.worlds-best-coffee-shop.example.com",
url: "https://www.worlds-best-coffee-shop.example.com",
logo: "https://example.com/logo.png",
address: {
streetAddress: "123 Main St",
addressLocality: "Seattle",
addressRegion: "WA",
postalCode: "98101",
addressCountry: "US",
},
}}
ratingValue={91}
ratingCount={10561}
bestRating={100}
worstRating={1}
/>Custom Rating Scale Example
<EmployerAggregateRatingJsonLd
itemReviewed="Percentage-Based Company"
ratingValue="85%"
reviewCount={250}
bestRating={100}
worstRating={0}
/>Best Practices
Always include organization details: Provide as much information about the organization as possible
Use sameAs property: Link to the organization's official website or social media profiles
Rating scales: If not using a 5-point scale, always specify bestRating and worstRating
Count accuracy: Ensure ratingCount and reviewCount reflect actual user ratings on your site
Rating derivation: The ratingValue must be accurately calculated from user ratings
Note: At least one of
ratingCountorreviewCountmust be provided. The component will throw an error if neither is present.
Looking for job posting structured data? Check out JobPostingJsonLd to add complete job listing structured data alongside employer ratings.
VacationRentalJsonLd
The VacationRentalJsonLd component helps you add structured data for vacation rental listings to improve their appearance in Google Search results. Users can see listing information such as name, description, images, location, rating, and reviews directly in search results.
Basic Usage
import { VacationRentalJsonLd } from "next-seo";
<VacationRentalJsonLd
containsPlace={{
occupancy: {
value: 5,
},
}}
identifier="abc123"
image="https://example.com/vacation-rental-main.jpg"
latitude={42.12345}
longitude={101.12345}
name="Beautiful Beach House"
/>;Advanced Usage
<VacationRentalJsonLd
containsPlace={{
additionalType: "EntirePlace",
bed: [
{
numberOfBeds: 1,
typeOfBed: "Queen",
},
{
numberOfBeds: 2,
typeOfBed: "Single",
},
],
occupancy: {
value: 5,
},
amenityFeature: [
{
name: "ac",
value: true,
},
{
name: "wifi",
value: true,
},
{
name: "poolType",
value: "Outdoor",
},
],
floorSize: {
value: 75,
unitCode: "MTK",
},
numberOfBathroomsTotal: 2.5,
numberOfBedrooms: 3,
numberOfRooms: 5,
petsAllowed: true,
smokingAllowed: false,
}}
identifier="lux-villa-123"
image={[
"https://example.com/image1.jpg",
"https://example.com/image2.jpg",
"https://example.com/image3.jpg",
"https://example.com/image4.jpg",
"https://example.com/image5.jpg",
"https://example.com/image6.jpg",
"https://example.com/image7.jpg",
"https://example.com/image8.jpg",
]}
latitude={42.12345}
longitude={101.12345}
name="Luxury Ocean View Villa"
additionalType="Villa"
address={{
addressCountry: "US",
addressLocality: "Malibu",
addressRegion: "California",
postalCode: "90265",
streetAddress: "123 Ocean Drive",
}}
aggregateRating={{
ratingValue: 4.8,
ratingCount: 120,
reviewCount: 95,
bestRating: 5,
}}
brand={{
name: "Luxury Vacation Rentals Inc",
}}
checkinTime="15:00:00-08:00"
checkoutTime="11:00:00-08:00"
description="Stunning beachfront villa with panoramic ocean views"
knowsLanguage={["en-US", "es-ES", "fr-FR"]}
review={[
{
reviewRating: {
ratingValue: 5,
bestRating: 5,
},
author: "Jane Smith",
datePublished: "2024-01-15",
contentReferenceTime: "2024-01-10",
},
]}
/>Props
PropertyTypeDescriptioncontainsPlaceAccommodationRequired. Details about the accommodation including occupancycontainsPlace.occupancyQuantitativeValueRequired. Maximum number of guestscontainsPlace.occupancy.valuenumberRequired. The numerical value of guestsidentifierstringRequired. A unique identifier for the propertyimagestring | ImageObject | arrayRequired. Minimum 8 photos (bedroom, bathroom, common area)latitudenumber | stringRequired. Latitude with 5 decimal precisionlongitudenumber | stringRequired. Longitude with 5 decimal precisionnamestringRequired. The name of the vacation rentaladditionalTypestringType of rental (e.g., House, Villa, Apartment, Cottage)addressPostalAddressFull physical address of the rentalaggregateRatingAggregateRatingAverage rating based on multiple reviewsbrandBrandThe brand associated with the propertycheckinTimestringEarliest check-in time in ISO 8601 formatcheckoutTimestringLatest check-out time in ISO 8601 formatdescriptionstringA description of the propertyknowsLanguagestring | string[]Languages the host speaks (IETF BCP 47)reviewReview | Review[]User reviews of the listinggeoobjectAlternative way to specify coordinatesscriptIdstringCustom ID for the script tagscriptKeystringCustom data-seo attribute value
Accommodation Properties
PropertyTypeDescriptionadditionalTypestringType of room (EntirePlace, PrivateRoom, SharedRoom)bedBedDetails | BedDetails[]Information about bedsamenityFeatureLocationFeatureSpecification | arrayProperty amenitiesfloorSizeQuantitativeValueSize with unitCode (FTK/SQFT for sq ft, MTK/SQM for sq m)numberOfBathroomsTotalnumberTotal bathrooms (can be decimal, e.g., 2.5)numberOfBedroomsnumberTotal number of bedroomsnumberOfRoomsnumberTotal number of roomspetsAllowedbooleanWhether pets are allowedsmokingAllowedbooleanWhether smoking is allowed
Amenity Feature Values
Boolean amenities (use value: true/false):
ac,airportShuttle,balcony,beachAccess,childFriendly,crib,elevator,fireplace,freeBreakfast,gymFitnessEquipment,heating,hotTub,instantBookable,ironingBoard,kitchen,microwave,outdoorGrill,ovenStove,patio,petsAllowed,pool,privateBeachAccess,selfCheckinCheckout,smokingAllowed,tv,washerDryer,wheelchairAccessible,wifi
Non-boolean amenities (use value: "string"):
internetType: "Free", "Paid", "None"parkingType: "Free", "Paid", "None"poolType: "Indoor", "Outdoor", "None"licenseNum: License number with authority context
Best Practices
Minimum 8 images: Include at least one photo of bedroom, bathroom, and common areas
Precise coordinates: Use at least 5 decimal places for latitude/longitude
Complete address: Provide full physical address including unit numbers
Accurate occupancy: Specify the maximum number of guests allowed
Languages: List all languages the host can communicate in
Reviews: Include the
contentReferenceTimefor French listingsUnique identifier: Use a stable ID that won't change with content updates
Note: This feature requires integration with Google's Hotel Center and is limited to sites that meet eligibility criteria. Visit the vacation rental interest form to get started.
VideoJsonLd
The VideoJsonLd component helps you add structured data for videos to improve their appearance in Google Search results. This includes standard video results, video carousels, and rich video previews. You can also mark live videos with a LIVE badge, add key moments for video navigation, and specify viewing restrictions.
Basic Usage
import { VideoJsonLd } from "next-seo";
<VideoJsonLd
name="How to Make a Perfect Cake"
description="Learn how to make the perfect chocolate cake with this easy recipe"
thumbnailUrl="https://example.com/cake-video-thumbnail.jpg"
uploadDate="2024-01-15T08:00:00+00:00"
contentUrl="https://example.com/videos/cake-recipe.mp4"
embedUrl="https://example.com/embed/cake-recipe"
duration="PT10M30S"
/>;Advanced Usage with All Features
<VideoJsonLd
name="How to Make a Perfect Cake"
description="Learn how to make the perfect chocolate cake with this easy recipe"
thumbnailUrl={[
"https://example.com/thumbnails/1x1/cake.jpg",
"https://example.com/thumbnails/4x3/cake.jpg",
"https://example.com/thumbnails/16x9/cake.jpg",
]}
uploadDate="2024-01-15T08:00:00+00:00"
contentUrl="https://example.com/videos/cake-recipe.mp4"
embedUrl="https://example.com/embed/cake-recipe"
duration="PT10M30S"
expires="2025-01-15T00:00:00+00:00"
interactionStatistic={{
interactionType: "WatchAction",
userInteractionCount: 150000,
}}
regionsAllowed={["US", "CA", "GB"]}
author="Chef Julia"
publisher={{
name: "Cooking Channel",
logo: "https://example.com/cooking-channel-logo.png",
}}
/>Live Video with LIVE Badge
<VideoJsonLd
name="Live Cooking Show: Holiday Special"
description="Join us for a live cooking demonstration of holiday favorites"
thumbnailUrl="https://example.com/live-show-thumbnail.jpg"
uploadDate="2024-12-20T10:00:00+00:00"
embedUrl="https://example.com/live/holiday-special"
publication={{
isLiveBroadcast: true,
startDate: "2024-12-25T18:00:00+00:00",
endDate: "2024-12-25T20:00:00+00:00",
}}
/>Video with Key Moments (Clips)
<VideoJsonLd
name="Complete Cake Baking Tutorial"
description="A comprehensive guide to baking cakes from scratch"
thumbnailUrl="https://example.com/tutorial-thumbnail.jpg"
uploadDate="2024-01-15T08:00:00+00:00"
contentUrl="https://example.com/videos/complete-tutorial.mp4"
duration="PT30M"
hasPart={[
{
name: "Gathering Ingredients",
startOffset: 0,
endOffset: 120,
url: "https://example.com/videos/complete-tutorial?t=0",
},
{
name: "Mixing the Batter",
startOffset: 120,
endOffset: 480,
url: "https://example.com/videos/complete-tutorial?t=120",
},
{
name: "Baking and Decorating",
startOffset: 480,
endOffset: 1800,
url: "https://example.com/videos/complete-tutorial?t=480",
},
]}
/>Video with Automatic Key Moments (SeekToAction)
<VideoJsonLd
name="Recipe Collection Video"
description="Multiple recipes in one convenient video"
thumbnailUrl="https://example.com/collection-thumbnail.jpg"
uploadDate="2024-01-15T08:00:00+00:00"
embedUrl="https://example.com/embed/recipe-collection"
potentialAction={{
target: "https://example.com/videos/collection?t={seek_to_second_number}",
"startOffset-input": "required name=seek_to_second_number",
}}
/>Props
PropertyTypeDescriptionnamestringRequired. The title of the videodescriptionstringRequired. A description of the videothumbnailUrlstring | ImageObject | arrayRequired. URLs or ImageObjects for video thumbnails. Google recommends multiple aspect ratiosuploadDatestringRequired. The date and time the video was published in ISO 8601 formatcontentUrlstringDirect URL to the video file. Recommended if availableembedUrlstringURL to the embedded video player. Use if contentUrl isn't availabledurationstringVideo duration in ISO 8601 format (e.g., "PT30M" for 30 minutes)expiresstringDate after which the video is no longer availableinteractionStatisticInteractionCounter | arrayView counts, likes, or other interaction metricsregionsAllowedstring | string[]Countries where the video is viewable (ISO 3166 format)ineligibleRegionstring | string[]Countries where the video is blockedpublicationBroadcastEvent | arrayFor live videos - includes broadcast times and live statushasPartClip | Clip[]Video segments/chapters with timestamps and labelspotentialActionSeekToActionURL pattern for automatic key momentsauthorstring | Person | Organization | arrayVideo creator(s)publisherOrganizationOrganization that published the videotype"VideoObject"Schema type. Defaults to "VideoObject"scriptIdstringCustom ID for the script tagscriptKeystringCustom key for React
Best Practices
Thumbnail Images: Provide multiple thumbnail URLs in different aspect ratios (16:9, 4:3, 1:1) for optimal display
Duration Format: Use ISO 8601 duration format: PT[hours]H[minutes]M[seconds]S
Content vs Embed URL:
Use
contentUrlfor direct video files (mp4, webm, etc.)Use
embedUrlfor video player pagesProvide both when possible
Live Videos: For live streams, always include
publicationwithisLiveBroadcast: trueKey Moments:
Use
hasPartwithClipobjects when you want to specify exact timestampsUse
potentialActionwithSeekToActionto let Google automatically detect key moments
Timestamps: Always use ISO 8601 format with timezone information
Region Restrictions: Use two-letter ISO 3166-1 country codes
ProfilePageJsonLd
The ProfilePageJsonLd component helps you add structured data for profile pages where creators (either people or organizations) share first-hand perspectives. This helps Google Search understand the creators in an online community and show better content from that community in search results, including the Discussions and Forums feature.
Basic Usage
import { ProfilePageJsonLd } from "next-seo";
<ProfilePageJsonLd
mainEntity="Angelo Huff"
dateCreated="2024-12-23T12:34:00-05:00"
dateModified="2024-12-26T14:53:00-05:00"
/>;Advanced Usage
<ProfilePageJsonLd
mainEntity={{
name: "Angelo Huff",
alternateName: "ahuff23",
identifier: "123475623",
description: "Defender of Truth",
image: "https://example.com/avatars/ahuff23.jpg",
sameAs: [
"https://www.example.com/real-angelo",
"https://example.com/profile/therealangelohuff",
],
interactionStatistic: [
{
interactionType: "https://schema.org/FollowAction",
userInteractionCount: 1,
},
{
interactionType: "https://schema.org/LikeAction",
userInteractionCount: 5,
},
],
agentInteractionStatistic: {
interactionType: "https://schema.org/WriteAction",
userInteractionCount: 2346,
},
}}
dateCreated="2024-12-23T12:34:00-05:00"
dateModified="2024-12-26T14:53:00-05:00"
/>Organization Profile Example
<ProfilePageJsonLd
mainEntity={{
"@type": "Organization",
name: "ACME Corporation",
url: "https://acme.com",
logo: "https://acme.com/logo.png",
sameAs: ["https://twitter.com/acme", "https://linkedin.com/company/acme"],
interactionStatistic: {
interactionType: "https://schema.org/FollowAction",
userInteractionCount: 15000,
},
}}
/>Props
PropertyTypeDescriptionmainEntitystring | Person | Organization | Omit<Person, "@type"> | Omit<Organization, "@type">Required. The person or organization this profile page is aboutdateCreatedstringDate and time the profile was created (ISO 8601 format)dateModifiedstringDate and time the profile was last modified (ISO 8601 format)scriptIdstringCustom ID for the script tagscriptKeystringCustom key for React reconciliation
Person/Organization Properties
When providing an object for mainEntity, you can include these properties:
Common Properties:
name: The primary name (real name preferred)alternateName: Alternate identifier (e.g., username)identifier: Unique ID within your sitedescription: User's byline or credentialimage: Profile image URLsameAs: Array of external profile URLsinteractionStatistic: User statistics (followers, likes, etc.)agentInteractionStatistic: User's own activity statistics
Interaction Types:
https://schema.org/FollowAction: Number of followers/followinghttps://schema.org/LikeAction: Number of likeshttps://schema.org/WriteAction: Number of postshttps://schema.org/ShareAction: Number of reshareshttps://schema.org/BefriendAction: Bi-directional relationships
Best Practices
Profile focus: The page must primarily focus on a single person or organization
Real names: Use
namefor real names andalternateNamefor usernamesStable identifiers: Use IDs that won't change even if usernames change
Multiple images: Include profile images in multiple aspect ratios (16x9, 4x3, 1x1)
ISO 8601 dates: Always include timezone information in dates
Platform statistics: Only include stats from the current platform
Valid Use Cases
✅ User profile pages on forums or social media sites ✅ Author pages on news sites ✅ "About Me" pages on blog sites ✅ Employee pages on company websites
Invalid Use Cases
❌ Main home page of a store ❌ Organization review sites (where the org isn't affiliated with the site)
Note: ProfilePage markup is designed for sites where creators share first-hand perspectives. It can be linked from Article and Recipe structured data authors, and is often used in discussion forum and Q&A page structured data.
SoftwareApplicationJsonLd
The SoftwareApplicationJsonLd component helps you add structured data for software applications, including mobile apps, web apps, desktop software, and games. This can help your app appear in rich results and improve its visibility in app-related searches.
Basic Usage (Free App)
import { SoftwareApplicationJsonLd } from "next-seo";
<SoftwareApplicationJsonLd
name="My Awesome App"
offers={{
price: 0,
priceCurrency: "USD",
}}
aggregateRating={{
ratingValue: 4.5,
ratingCount: 100,
}}
/>;Paid App Example
<SoftwareApplicationJsonLd
name="Premium Photo Editor"
applicationCategory="DesignApplication"
operatingSystem="iOS 14.0+"
offers={{
price: 9.99,
priceCurrency: "USD",
}}
aggregateRating={{
ratingValue: 4.8,
reviewCount: 2500,
}}
description="Professional photo editing on the go"
screenshot={[
"https://example.com/screenshot1.jpg",
"https://example.com/screenshot2.jpg",
]}
/>Mobile Application
<SoftwareApplicationJsonLd
type="MobileApplication"
name="Fitness Tracker Pro"
applicationCategory="HealthApplication"
operatingSystem="Android 8.0+, iOS 12.0+"
offers={{
price: 0,
priceCurrency: "USD",
}}
review={[
{
author: "Jane Smith",
reviewRating: { ratingValue: 5 },
reviewBody: "Best fitness app I've ever used!",
},
]}
permissions={["location", "camera", "storage"]}
featureList={[
"GPS tracking",
"Heart rate monitoring",
"Social challenges",
"Meal planning",
]}
/>Web Application
<SoftwareApplicationJsonLd
type="WebApplication"
name="Project Management Suite"
url="https://app.example.com"
applicationCategory="BusinessApplication"
applicationSubCategory="ProjectManagement"
offers={[
{
price: 0,
priceCurrency: "USD",
availability: "https://schema.org/InStock",
},
{
price: 29.99,
priceCurrency: "USD",
availability: "https://schema.org/InStock",
},
]}
aggregateRating={{
ratingValue: 4.7,
ratingCount: 5000,
}}
screenshot={{
url: "https://example.com/app-dashboard.jpg",
caption: "Main dashboard view",
}}
/>Video Game (Co-typed)
For video games, Google requires co-typing with another application type:
<SoftwareApplicationJsonLd
type={["VideoGame", "MobileApplication"]}
name="Epic Adventure Quest"
applicationCategory="GameApplication"
operatingSystem="iOS 13.0+, Android 9.0+"
offers={{
price: 4.99,
priceCurrency: "USD",
}}
aggregateRating={{
ratingValue: 4.6,
ratingCount: 10000,
}}
contentRating="Everyone 10+"
screenshot={[
"https://example.com/gameplay1.jpg",
"https://example.com/gameplay2.jpg",
]}
featureList={[
"Multiplayer battles",
"50+ hours of gameplay",
"Cloud save support",
]}
/>Props
PropertyTypeDescriptionnamestringRequired. The name of the software applicationtypeApplicationType | VideoGameCoTypedType of application. Defaults to "SoftwareApplication"offersOffer | Offer[]Required. Pricing information. Set price to 0 for free appsaggregateRatingAggregateRatingRequired (or use review). Average rating informationreviewReview | Review[]Required (or use aggregateRating). Individual reviewsapplicationCategorystringRecommended. Category of the app (e.g., "GameApplication")operatingSystemstringRecommended. Required OS (e.g., "Windows 10, macOS 10.15+")descriptionstringDescription of the applicationurlstringURL of the app's webpageimagestring | ImageObject | (string | ImageObject)[]App icon or logoscreenshotstring | ImageObject | (string | ImageObject)[]Screenshots of the appfeatureListstring | string[]Key features of the apppermissionsstring | string[]Required permissionssoftwareVersionstringCurrent version numberdatePublishedstringInitial release datedateModifiedstringLast update dateauthorstring | Person | OrganizationDeveloper or development teampublisherOrganizationPublishing organizationdownloadUrlstringDirect download linkinstallUrlstringInstallation linkmemoryRequirementsstringRAM requirementsstorageRequirementsstringStorage space neededprocessorRequirementsstringCPU requirementscountriesSupportedstring | string[]Supported countriesapplicationSuitestringSuite the app belongs to
Application Types
The component supports all Google-recognized application types:
SoftwareApplication(default)MobileApplicationWebApplicationGameApplicationSocialNetworkingApplicationTravelApplicationShoppingApplicationSportsApplicationLifestyleApplicationBusinessApplicationDesignApplicationDeveloperApplicationDriverApplicationEducationalApplicationHealthApplicationFinanceApplicationSecurityApplicationBrowserApplicationCommunicationApplicationDesktopEnhancementApplicationEntertainmentApplicationMultimediaApplicationHomeApplicationUtilitiesApplicationReferenceApplication
Best Practices
Always include pricing: Use
offerswithprice: 0for free appsProvide ratings or reviews: Include either
aggregateRatingorreview(required by Google)Specify OS requirements: Use
operatingSystemfor better user experienceMultiple screenshots: Include various screenshots showing key features
Video games: Always co-type with another application type (e.g.,
["VideoGame", "MobileApplication"])Feature lists: Highlight key features that differentiate your app
Version information: Keep
softwareVersionanddateModifiedcurrentPermissions transparency: List all required permissions for mobile apps
ProductJsonLd
The ProductJsonLd component helps you add structured data for products to improve their appearance in search results. Products can appear as rich snippets with ratings, prices, and availability information.
Basic Usage
import { ProductJsonLd } from "next-seo";
<ProductJsonLd
name="Executive Anvil"
description="Sleeker than ACME's Classic Anvil, perfect for the business traveler"
image="https://example.com/products/anvil.jpg"
offers={{
price: 119.99,
priceCurrency: "USD",
availability: "InStock",
}}
/>;Product with Reviews
<ProductJsonLd
name="Executive Anvil"
sku="0446310786"
mpn="925872"
brand="ACME"
review={{
reviewRating: {
ratingValue: 4.5,
bestRating: 5,
},
author: "Fred Benson",
reviewBody:
"This anvil has held up well after many uses. Highly recommended!",
}}
aggregateRating={{
ratingValue: 4.4,
reviewCount: 89,
}}
offers={{
price: 119.99,
priceCurrency: "USD",
availability: "InStock",
priceValidUntil: "2024-12-31",
}}
/>Product with Pros and Cons
<ProductJsonLd
name="Cheese Grater Pro"
review={{
name: "Cheese Grater Pro Review",
author: "Pascal Van Cleeff",
reviewRating: {
ratingValue: 4,
bestRating: 5,
},
positiveNotes: {
itemListElement: [
{ name: "Consistent results" },
{ name: "Still sharp after many uses" },
{ name: "Easy to clean" },
],
},
negativeNotes: {
itemListElement: [
{ name: "No child protection" },
{ name: "Lacking advanced features" },
],
},
}}
offers={{
price: 29.99,
priceCurrency: "USD",
}}
/>Shopping Aggregator (Multiple Sellers)
<ProductJsonLd
name="Executive Anvil"
image={[
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg",
]}
description="Sleeker than ACME's Classic Anvil"
sku="0446310786"
mpn="925872"
brand="ACME"
offers={{
lowPrice: 119.99,
highPrice: 199.99,
priceCurrency: "USD",
offerCount: 5,
}}
aggregateRating={{
ratingValue: 4.4,
reviewCount: 89,
}}
/>Complete Example with All Features
<ProductJsonLd
name="Executive Anvil"
description="Sleeker than ACME's Classic Anvil"
url="https://example.com/products/anvil"
sku="0446310786"
mpn="925872"
gtin13="0614141999996"
brand="ACME"
category="Hardware"
color="Silver"
material="Steel"
model="EA-2024"
productID="anvil-001"
weight={{
value: 10,
unitCode: "KGM",
}}
width="30cm"
height="20cm"
depth="15cm"
manufacturer="ACME Manufacturing"
releaseDate="2024-01-01"
award="Best Anvil 2024"
image={[
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg",
]}
review={[
{
reviewRating: { ratingValue: 5, bestRating: 5 },
author: "Alice Johnson",
reviewBody: "Excellent quality!",
},
{
reviewRating: { ratingValue: 4, bestRating: 5 },
author: "Bob Smith",
reviewBody: "Good product, fast shipping.",
},
]}
aggregateRating={{
ratingValue: 4.4,
reviewCount: 89,
}}
offers={{
price: 119.99,
priceCurrency: "USD",
availability: "InStock",
priceValidUntil: "2024-12-31",
url: "https://example.com/buy/anvil",
seller: {
name: "ACME Store",
url: "https://example.com",
},
}}
/>Props
PropertyTypeDescriptionnamestringRequired. Product namedescriptionstringProduct descriptionimagestring | ImageObject | ArrayProduct imagesskustringStock Keeping UnitmpnstringManufacturer Part NumbergtinstringGlobal Trade Item Numbergtin8string8-digit GTINgtin12string12-digit GTIN (UPC)gtin13string13-digit GTIN (EAN)gtin14string14-digit GTINbrandstring | BrandProduct brandreviewProductReview | ProductReview[]Product reviewsaggregateRatingAggregateRatingAggregate rating from all reviewsoffersProductOffer | AggregateOffer | ProductOffer[]Price and availability (recommended)categorystringProduct categorycolorstringProduct colormaterialstringProduct materialmodelstringProduct modelproductIDstringProduct identifierurlstringProduct page URLweightstring | QuantitativeValueProduct weightwidthstring | QuantitativeValueProduct widthheightstring | QuantitativeValueProduct heightdepthstring | QuantitativeValueProduct depthadditionalPropertyPropertyValue[]Additional product propertiesmanufacturerstring | Organization | PersonProduct manufacturerreleaseDatestringProduct release dateproductionDatestringProduction datepurchaseDatestringPurchase dateexpirationDatestringExpiration dateawardstring | string[]Awards receivedisCarbooleanSet to true for car products
Important Requirements
Google requires at least one of the following properties for product snippets:
review- A nested review of the productaggregateRating- The overall rating based on multiple reviewsoffers- Price and availability information
Offer Properties
PropertyTypeDescriptionpricenumber | stringProduct pricepriceCurrencystringCurrency code (e.g., "USD")availabilityItemAvailabilityAvailability statuspriceValidUntilstringDate until price is validurlstringURL to purchase productsellerOrganization | PersonSeller informationitemConditionstringCondition (New, Used, Refurbished, etc.)
AggregateOffer Properties (Multiple Sellers)
PropertyTypeDescriptionlowPricenumber | stringRequired. Lowest pricepriceCurrencystringRequired. Currency codehighPricenumber | stringHighest priceofferCountnumberNumber of offers
Best Practices
Always include one of: review, aggregateRating, or offers (Google requirement)
Multiple images: Provide images in different aspect ratios (1x1, 4x3, 16x9)
Use specific identifiers: Include SKU, MPN, or GTIN when available
Pros and cons: Use positiveNotes and negativeNotes for editorial reviews
Price information: Always include priceCurrency with price
Availability: Use schema.org values (InStock, OutOfStock, PreOrder, etc.)
Multiple sellers: Use AggregateOffer for shopping comparison sites
Car products: Set
isCar={true}for automotive products to add Car type
ProductGroup (Product Variants)
The ProductJsonLd component now supports ProductGroup for representing product variants (different sizes, colors, materials, etc.) of the same product. This helps Google understand product variations and can enable variant displays in search results.
Single-Page Variant Example
Use this approach when all variants are selectable on a single product page:
import { ProductJsonLd } from "next-seo";
<ProductJsonLd
type="ProductGroup"
name="Wool Winter Coat"
description="Premium wool coat available in multiple colors and sizes"
productGroupID="WC2024"
brand="Nordic Style"
variesBy={["size", "color"]}
aggregateRating={{
ratingValue: 4.6,
reviewCount: 127,
}}
hasVariant={[
{
name: "Wool Winter Coat - Small Green",
sku: "WC2024-S-GRN",
size: "small",
color: "Green",
offers: {
price: 119.99,
priceCurrency: "USD",
availability: "InStock",
url: "https://example.com/coat?size=small&color=green",
},
},
{
name: "Wool Winter Coat - Large Blue",
sku: "WC2024-L-BLU",
size: "large",
color: "Blue",
offers: {
price: 139.99,
priceCurrency: "USD",
availability: "BackOrder",
url: "https://example.com/coat?size=large&color=blue",
},
},
// Reference to variants on other pages
{ url: "https://example.com/coat/medium-red" },
]}
/>;Multi-Page Variant Example
Use this approach when each variant has its own page:
// On a specific variant page
<ProductJsonLd
name="Premium Leather Wallet - Brown Classic"
sku="LW2024-BRN-CLS"
color="Brown"
pattern="Classic"
material="Genuine Leather"
isVariantOf={{ "@id": "#wallet_group" }}
inProductGroupWithID="LW2024"
offers={{
price: 79.99,
priceCurrency: "USD",
availability: "InStock",
}}
/>ProductGroup Properties
PropertyTypeDescriptiontype"ProductGroup"Specifies ProductGroup typeproductGroupIDstringRequired. Parent SKU or group identifiervariesBystring | string[]Properties that vary (size, color, material, etc.)hasVariantArray<Product | {url: string}>Array of product variantsaudiencePeopleAudienceTarget audience (age, gender)
Variant Properties
When defining variants in hasVariant, you can include:
PropertyTypeDescriptionnamestringVariant-specific nameskustringVariant SKUsizestringSize valuecolorstringColor valuepatternstringPattern typematerialstringMaterial compositionoffersProductOfferVariant-specific pricingurlstringFor referencing variants on other pages
Variant Reference Properties
For multi-page implementations, use these on individual product pages:
PropertyTypeDescriptionisVariantOf{@id: string} | ProductGroupReference to parent ProductGroupinProductGroupWithIDstringParent product group ID
VariesBy Values
Supported values for the variesBy property:
"size"or"https://schema.org/size""color"or"https://schema.org/color""material"or"https://schema.org/material""pattern"or"https://schema.org/pattern""suggestedAge"or"https://schema.org/suggestedAge""suggestedGender"or"https://schema.org/suggestedGender"
Best Practices for Product Variants
Use ProductGroup when you have multiple variants of the same product
Single-page approach: Best when variants are selectable via dropdowns/buttons on one page
Multi-page approach: Best when each variant needs its own SEO-optimized page
Always include productGroupID: This links all variants together
Specify variesBy: Clearly indicate which properties differentiate variants
Complete variant data: Include as much variant-specific data as possible
URL references: Use
{ url: "..." }for variants on separate pagesCommon properties: Place shared properties (brand, material) at ProductGroup level
Unique identifiers: Each variant should have unique SKU/GTIN
Consistent naming: Use clear naming patterns for variants (e.g., "Product - Size Color")
ReviewJsonLd
The ReviewJsonLd component helps you add structured data for reviews to improve their appearance in search results. Reviews can appear as rich snippets with star ratings and review excerpts.
Basic Usage
import { ReviewJsonLd } from "next-seo";
<ReviewJsonLd
author="Bob Smith"
reviewRating={{ ratingValue: 4 }}
itemReviewed="Legal Seafood"
/>;Full Review Example
<ReviewJsonLd
author={{ name: "Sarah Johnson", url: "https://example.com/sarah" }}
reviewRating={{
ratingValue: 5,
bestRating: 5,
worstRating: 1,
}}
itemReviewed={{
"@type": "Restaurant",
name: "Legal Seafood",
image: "https://example.com/seafood.jpg",
servesCuisine: "Seafood",
priceRange: "$$$",
telephone: "1234567",
address: {
streetAddress: "123 William St",
addressLocality: "New York",
addressRegion: "NY",
postalCode: "10038",
addressCountry: "US",
},
}}
reviewBody="Excellent seafood restaurant with fresh catches daily. The lobster was perfectly cooked and the service was outstanding."
datePublished="2024-01-15"
publisher="FoodCritic Magazine"
url="https://example.com/reviews/legal-seafood"
/>Props
PropertyTypeDescriptionauthorstring | Person | OrganizationRequired. The author of the reviewreviewRatingRatingRequired. The rating given in the reviewitemReviewedstring | ItemReviewedRequired. The item being revieweddatePublishedstringThe date the review was published (ISO 8601)reviewBodystringThe text of the reviewpublisherstring | Person | OrganizationThe publisher of the reviewurlstringURL of the reviewmainEntityOfPagestring | WebPageMain entity of the page
Supported Item Types
Reviews can be written about various types of items:
BookCourseCreativeWorkSeasonCreativeWorkSeriesEpisodeEventGameHowToLocalBusiness(including restaurants)MediaObjectMovieMusicPlaylistMusicRecordingOrganizationProductRecipeSoftwareApplication
Best Practices
Always include reviewBody: Provides context for the rating
Use specific item types: Specify
@typeforitemReviewedwhen possibleInclude datePublished: Helps establish review freshness
Author information: Provide as much author detail as possible
Avoid self-serving reviews: Don't mark up reviews on your own site about your organization
AggregateRatingJsonLd
The AggregateRatingJsonLd component helps you add structured data for aggregate ratings, showing the average rating from multiple reviews.
Basic Usage
import { AggregateRatingJsonLd } from "next-seo";
<AggregateRatingJsonLd
itemReviewed="Executive Anvil"
ratingValue={4.4}
ratingCount={89}
/>;Full Example
<AggregateRatingJsonLd
itemReviewed={{
"@type": "Product",
name: "Executive Anvil",
image: [
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg",
],
brand: "ACME",
}}
ratingValue={88}
bestRating={100}
worstRating={0}
ratingCount={20}
/>With Review Count
<AggregateRatingJsonLd
itemReviewed="Premium Coffee Maker"
ratingValue={4.5}
reviewCount={127} // Use reviewCount instead of ratingCount
bestRating={5}
/>Props
PropertyTypeDescriptionitemReviewedstring | ItemReviewedRequired. The item being ratedratingValuenumber | stringRequired. The average rating valueratingCountnumberThe total number of ratingsreviewCountnumberThe number of reviews (at least one of ratingCount or reviewCount is required)bestRatingnumberThe highest value in the rating system (default: 5)worstRatingnumberThe lowest value in the rating system (default: 1)
Rating Scale
Default scale: 1 to 5 stars
Custom scale: Use
bestRatingandworstRatingto define custom scalesPercentages: Use values like 88 with
bestRating: 100for percentage-based ratingsFractions: Supports decimal values like 4.4
Best Practices
Choose the right count: Use
ratingCountfor star ratings,reviewCountfor written reviewsSpecify scale for non-standard ratings: Always include
bestRatingandworstRatingfor non-5-star scalesCombine with Product/Organization data: Nest within or alongside main entity structured data
Minimum threshold: Only use when you have multiple genuine ratings
Keep it updated: Regularly update aggregate ratings as new reviews come in
Creating Custom Components
Next SEO now supports creating your own custom JSON-LD components using the same utilities and patterns as the built-in components. This allows you to implement any Schema.org type while maintaining the excellent developer experience of next-seo.
Quick Example
import { JsonLdScript, processors } from "next-seo";
export function PodcastEpisodeJsonLd({ name, author, duration, url }) {
const data = {
"@context": "https://schema.org",
"@type": "PodcastEpisode",
name,
...(url && { url }),
...(duration && { duration }),
...(author && { author: processors.processAuthor(author) }),
};
return <JsonLdScript data={data} scriptKey="podcast-episode" />;
}
// Usage - no @type needed for author!
<PodcastEpisodeJsonLd
name="Episode 1: Getting Started"
author="Jane Doe" // Simple string works!
duration="PT30M"
url="https://example.com/episode-1"
/>;Key Features
JsonLdScript Component: Core component for rendering structured data
60+ Processors: Transform flexible inputs into Schema.org compliant objects
@type Optional Pattern: Users never need to specify
@typemanuallyTypeScript Support: Full type safety with exported types
Available Utilities
See the processors export file for the complete list of available processors organized by category (People & Organizations, Media & Content, Locations & Places, Commerce & Offers, etc.).
Learn More
For comprehensive documentation on creating custom components, including:
Using built-in processors
Creating custom processors
Advanced patterns and best practices
Real-world examples
See the Custom Components Guide