npm package: @opensourceframework/next-seo
Description: SEO plugin for Next.js projects
Latest version: 7.3.5

## 🚀 Quick Start

#### Attribution

- **Original Author**: Unknown
- **Original Repository**: https://github.com/garmeeh/next-seo
- **Original License**: MIT



```bash
npm install @opensourceframework/next-seo
# or
yarn add @opensourceframework/next-seo
# or
pnpm add @opensourceframework/next-seo
# or
bun add @opensourceframework/next-seo
```

### Basic Usage

```tsx
import { ArticleJsonLd } from "@opensourceframework/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-in [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata) function.

> **Pages Router Support**: If you're using Next.js Pages Router, import components from `@opensourceframework/next-seo/pages`. See the [Pages Router documentation](./src/pages/README.md) for details.

## 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

```tsx
import { ArticleJsonLd } from "@opensourceframework/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

```tsx
<ArticleJsonLd
  type="NewsArticle"
  headline="Breaking: Next SEO v7 Released"
  url="https://example.com/news/@opensourceframework/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

```tsx
<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

| Property              | Type                                                    | Description                                              |
| --------------------- | ------------------------------------------------------- | -------------------------------------------------------- |
| `type`                | `"Article" \| "NewsArticle" \| "BlogPosting" \| "Blog"` | The type of article. Defaults to "Article"               |
| `headline`            | `string`                                                | **Required.** The headline of the article                |
| `url`                 | `string`                                                | The canonical URL of the article                         |
| `author`              | `string \| Person \| Organization \| Author[]`          | The author(s) of the article                             |
| `datePublished`       | `string`                                                | ISO 8601 date when the article was published             |
| `dateModified`        | `string`                                                | ISO 8601 date when the article was last modified         |
| `image`               | `string \| ImageObject \| (string \| ImageObject)[]`    | Article images. Google recommends multiple aspect ratios |
| `publisher`           | `Organization`                                          | The publisher of the article                             |
| `description`         | `string`                                                | A short description of the article                       |
| `isAccessibleForFree` | `boolean`                                               | Whether the article is accessible for free               |
| `mainEntityOfPage`    | `string \| WebPage`                                     | Indicates the article is the primary content of the page |
| `scriptId`            | `string`                                                | Custom ID for the script tag                             |
| `scriptKey`           | `string`                                                | Custom key prop for React                                |

#### Best Practices

1. **Always include images**: Google strongly recommends including high-resolution images with multiple aspect ratios (16x9, 4x3, 1x1)
2. **Use ISO 8601 dates**: Include timezone information for accuracy
3. **Multiple authors**: List all authors when applicable
4. **Publisher logo**: Include a logo for NewsArticle type
5. **Update dateModified**: Keep this current when updating content

[↑ Back to Components](#-components-by-category)

### 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

```tsx
import { ClaimReviewJsonLd } from "@opensourceframework/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

| Property        | Type                               | Description                                                                           |
| --------------- | ---------------------------------- | ------------------------------------------------------------------------------------- |
| `claimReviewed` | `string`                           | **Required.** A short summary of the claim being evaluated (keep under 75 characters) |
| `reviewRating`  | `object`                           | **Required.** The assessment of the claim with rating value and textual rating        |
| `url`           | `string`                           | **Required.** Link to the page hosting the full fact check article                    |
| `author`        | `string \| Organization \| Person` | The publisher of the fact check article                                               |
| `itemReviewed`  | `Claim`                            | Detailed information about the claim being reviewed                                   |
| `scriptId`      | `string`                           | Custom ID for the script tag                                                          |
| `scriptKey`     | `string`                           | Custom key for script identification                                                  |

#### Review Rating Properties

| Property        | Type     | Description                                                                                 |
| --------------- | -------- | ------------------------------------------------------------------------------------------- |
| `alternateName` | `string` | **Required.** The truthfulness rating as human-readable text (e.g., "False", "Mostly true") |
| `ratingValue`   | `number` | **Required.** Numeric rating (closer to bestRating = more true)                             |
| `bestRating`    | `number` | Best value in the rating scale (must be greater than worstRating)                           |
| `worstRating`   | `number` | Worst value in the rating scale (minimum value of 1)                                        |
| `name`          | `string` | Alternative to alternateName (use alternateName instead)                                    |

#### Advanced Example with Claim Details

```tsx
<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

1. **Clear ratings**: Use descriptive alternateName values that clearly indicate the verdict
2. **Claim summary**: Keep claimReviewed concise (under 75 characters) to prevent wrapping
3. **Full context**: Include itemReviewed when possible to provide claim origin details
4. **Consistent scale**: Use a consistent rating scale across all your fact checks
5. **Author credibility**: Clearly identify your fact-checking organization

[↑ Back to Components](#-components-by-category)

### 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

```tsx
import { CreativeWorkJsonLd } from "@opensourceframework/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

| Property              | Type                                                                                          | Description                                                  |
| --------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
| `type`                | `"CreativeWork" \| "Article" \| "NewsArticle" \| "Blog" \| "BlogPosting" \| "Comment" \| ...` | The type of creative work. Defaults to "CreativeWork"        |
| `headline`            | `string`                                                                                      | The headline of the content (used for Article types)         |
| `name`                | `string`                                                                                      | The name of the content (alternative to headline)            |
| `url`                 | `string`                                                                                      | URL of the content                                           |
| `author`              | `string \| Person \| Organization \| Array`                                                   | Author(s) of the content                                     |
| `datePublished`       | `string`                                                                                      | ISO 8601 publication date                                    |
| `dateModified`        | `string`                                                                                      | ISO 8601 modification date                                   |
| `image`               | `string \| ImageObject \| Array`                                                              | Image(s) associated with the content                         |
| `publisher`           | `string \| Organization \| Person`                                                            | Publisher of the content                                     |
| `description`         | `string`                                                                                      | Description of the content                                   |
| `isAccessibleForFree` | `boolean`                                                                                     | Whether the content is free or requires payment/subscription |
| `hasPart`             | `WebPageElement \| WebPageElement[]`                                                          | Marks specific sections as paywalled                         |
| `mainEntityOfPage`    | `string \| WebPage`                                                                           | The main page for this content                               |
| `scriptId`            | `string`                                                                                      | Custom ID for the script tag                                 |
| `scriptKey`           | `string`                                                                                      | Custom key for script identification                         |

#### WebPageElement Properties (for hasPart)

| Property              | Type      | Description                                         |
| --------------------- | --------- | --------------------------------------------------- |
| `isAccessibleForFree` | `boolean` | **Required.** Whether this section is free (false)  |
| `cssSelector`         | `string`  | **Required.** CSS class selector (e.g., ".paywall") |

#### Marking Paywalled Content

```tsx
<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

```tsx
<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

```tsx
// 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

1. **Use specific types**: Choose the most specific CreativeWork type (Article, NewsArticle, etc.) when applicable
2. **Mark paywalled sections**: Use `hasPart` with `cssSelector` to identify paywalled content sections
3. **Class selectors only**: Only use class selectors (e.g., ".paywall") for `cssSelector`, not IDs or other selectors
4. **Consistent selectors**: Ensure your HTML classes match the `cssSelector` values exactly
5. **Complete metadata**: Include as much metadata as possible (author, dates, images) for better search results

[↑ Back to Components](#-components-by-category)

### 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

```tsx
import { RecipeJsonLd } from "@opensourceframework/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

```tsx
<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

| Property             | Type                                                                             | Description                                                                                   |
| -------------------- | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `name`               | `string`                                                                         | **Required.** The name of the dish                                                            |
| `image`              | `string \| ImageObject \| (string \| ImageObject)[]`                             | **Required.** Images of the completed dish. Google recommends multiple high-resolution images |
| `description`        | `string`                                                                         | A short summary describing the dish                                                           |
| `author`             | `string \| Person \| Organization`                                               | The creator of the recipe                                                                     |
| `datePublished`      | `string`                                                                         | ISO 8601 date when the recipe was published                                                   |
| `url`                | `string`                                                                         | The canonical URL of the recipe page                                                          |
| `prepTime`           | `string`                                                                         | ISO 8601 duration for preparation time (e.g., "PT30M" for 30 minutes)                         |
| `cookTime`           | `string`                                                                         | ISO 8601 duration for cooking time                                                            |
| `totalTime`          | `string`                                                                         | ISO 8601 duration for total time (prep + cook)                                                |
| `recipeYield`        | `string \| number`                                                               | The quantity produced (e.g., "4 servings", "1 loaf", or just 6)                               |
| `recipeCategory`     | `string`                                                                         | The type of meal or course (e.g., "dessert", "main course")                                   |
| `recipeCuisine`      | `string`                                                                         | The cuisine of the recipe (e.g., "French", "Mexican")                                         |
| `recipeIngredient`   | `string[]`                                                                       | List of ingredients with quantities                                                           |
| `recipeInstructions` | `string \| HowToStep \| HowToSection \| (string \| HowToStep \| HowToSection)[]` | Step-by-step instructions                                                                     |
| `nutrition`          | `NutritionInformation`                                                           | Nutritional information per serving                                                           |
| `aggregateRating`    | `AggregateRating`                                                                | The aggregate rating from users                                                               |
| `video`              | `VideoObject`                                                                    | A video showing how to make the recipe                                                        |
| `keywords`           | `string`                                                                         | Keywords about the recipe, separated by commas                                                |
| `scriptId`           | `string`                                                                         | Custom ID for the script tag                                                                  |
| `scriptKey`          | `string`                                                                         | Custom key prop for React                                                                     |

#### Duration Format (ISO 8601)

Use these formats for time durations:

- `PT15M` - 15 minutes
- `PT1H` - 1 hour
- `PT1H30M` - 1 hour 30 minutes
- `PT2H15M` - 2 hours 15 minutes

#### Best Practices

1. **High-quality images**: Include multiple high-resolution images (16x9, 4x3, 1x1 aspect ratios)
2. **Detailed instructions**: Use HowToStep objects for better structured data
3. **Complete nutrition info**: Include nutrition data when possible for better search visibility
4. **Accurate times**: Always provide prepTime and cookTime together
5. **Ratings**: Include aggregateRating when you have user reviews
6. **Video content**: Adding a video significantly improves search appearance

[↑ Back to Components](#-components-by-category)

### HowToJsonLd

The `HowToJsonLd` component helps you add structured data for how-to guides and tutorials. This can help your content appear as rich results with step-by-step instructions in search results.

#### Basic Usage

```tsx
import { HowToJsonLd } from "@opensourceframework/next-seo";

export default function HowToPage() {
  return (
    <>
      <HowToJsonLd
        name="How to Change a Flat Tire"
        description="Step-by-step instructions for safely changing a flat tire"
        image="https://example.com/tire-change.jpg"
        totalTime="PT30M"
        estimatedCost="$20"
        supply={["Spare tire", "Wheel wedges"]}
        tool={["Lug wrench", "Jack"]}
        step={[
          "Turn on hazard lights and apply wheel wedges",
          "Remove the hubcap and loosen lug nuts",
          "Position jack and raise the vehicle",
          "Remove flat tire and mount spare",
          "Lower vehicle and tighten lug nuts",
        ]}
      />
      <article>
        <h1>How to Change a Flat Tire</h1>
        {/* Guide content */}
      </article>
    </>
  );
}
```

#### Advanced Example with Sections and Detailed Steps

```tsx
<HowToJsonLd
  name="How to Change a Flat Tire"
  description="Complete guide to safely changing a flat tire on the roadside"
  image={{
    url: "https://example.com/tire-change-guide.jpg",
    width: 1200,
    height: 800,
  }}
  estimatedCost={{
    currency: "USD",
    value: 20,
  }}
  prepTime="PT5M"
  performTime="PT25M"
  totalTime="PT30M"
  yield="1 changed tire"
  tool={[
    {
      name: "Spare tire",
    },
    {
      name: "Lug wrench",
      image: "https://example.com/lug-wrench.jpg",
    },
    {
      name: "Jack",
    },
    {
      name: "Wheel wedges",
      image: "https://example.com/wheel-wedges.jpg",
    },
  ]}
  supply={[
    {
      name: "Flares",
      image: "https://example.com/flares.jpg",
    },
  ]}
  step={[
    {
      "@type": "HowToSection",
      name: "Preparation",
      position: 1,
      itemListElement: [
        {
          "@type": "HowToStep",
          position: 1,
          itemListElement: [
            {
              "@type": "HowToDirection",
              position: 1,
              text: "Turn on your hazard lights and set the flares.",
            },
            {
              "@type": "HowToTip",
              position: 2,
              text: "You're going to need space and want to be visible.",
            },
          ],
        },
        {
          "@type": "HowToStep",
          position: 2,
          itemListElement: [
            {
              "@type": "HowToDirection",
              position: 1,
              text: "Position wheel wedges in front of front tires if rear tire is flat, or behind rear tires if front tire is flat.",
            },
            {
              "@type": "HowToTip",
              position: 2,
              text: "You don't want the car to move while you're working on it.",
            },
          ],
        },
      ],
    },
    {
      "@type": "HowToSection",
      name: "Raise the Car",
      position: 2,
      itemListElement: [
        {
          "@type": "HowToStep",
          position: 1,
          text: "Position the jack underneath the car, next to the flat tire.",
          image: "https://example.com/position-jack.jpg",
        },
        {
          "@type": "HowToStep",
          position: 2,
          text: "Raise the jack until the flat tire is just barely off of the ground.",
        },
        {
          "@type": "HowToStep",
          position: 3,
          text: "Remove the hubcap and loosen the lug nuts.",
        },
      ],
    },
    {
      "@type": "HowToSection",
      name: "Finishing Up",
      position: 3,
      itemListElement: [
        {
          "@type": "HowToStep",
          position: 1,
          text: "Lower the jack and tighten the lug nuts with the wrench.",
        },
        {
          "@type": "HowToStep",
          position: 2,
          text: "Replace the hubcap.",
        },
        {
          "@type": "HowToStep",
          position: 3,
          text: "Put the equipment and the flat tire away.",
        },
      ],
    },
  ]}
  video={{
    name: "How to Change a Tire Video Tutorial",
    description: "Watch our mechanic demonstrate the proper technique",
    thumbnailUrl: "https://example.com/video-thumb.jpg",
    contentUrl: "https://example.com/tire-change-video.mp4",
    uploadDate: "2024-01-15T08:00:00+00:00",
    duration: "PT8M",
  }}
/>
```

#### Props

| Property        | Type                                                 | Description                                                               |
| --------------- | ---------------------------------------------------- | ------------------------------------------------------------------------- |
| `name`          | `string`                                             | **Required.** The title of the how-to guide                               |
| `description`   | `string`                                             | A brief description of the guide                                          |
| `image`         | `string \| ImageObject`                              | An image of the completed task or project                                 |
| `estimatedCost` | `string \| MonetaryAmount`                           | The estimated cost of supplies (e.g., "$20" or MonetaryAmount object)     |
| `prepTime`      | `string`                                             | ISO 8601 duration for preparation time                                    |
| `performTime`   | `string`                                             | ISO 8601 duration for the time to perform the instructions                |
| `totalTime`     | `string`                                             | ISO 8601 duration for total time (prep + perform)                         |
| `yield`         | `string \| QuantitativeValue`                        | The result of performing the instructions (e.g., "1 birdhouse")           |
| `supply`        | `string \| HowToSupply \| (string \| HowToSupply)[]` | Supplies consumed when performing the task                                |
| `tool`          | `string \| HowToTool \| (string \| HowToTool)[]`     | Tools used but not consumed                                               |
| `step`          | `string \| HowToStep \| HowToSection \| (Step)[]`    | The steps to complete the task. Can be simple strings, steps, or sections |
| `video`         | `VideoObject`                                        | A video showing how to complete the task                                  |
| `scriptId`      | `string`                                             | Custom ID for the script tag                                              |
| `scriptKey`     | `string`                                             | Custom key prop for React                                                 |

#### Step Types

**HowToStep** - A single step in the guide:

```tsx
{
  "@type": "HowToStep",
  name: "Step Name",           // Optional step title
  text: "Step instructions",   // The instruction text
  url: "https://...",         // Optional URL for more details
  image: "https://...",       // Optional step image
}
```

**HowToSection** - A group of related steps:

```tsx
{
  "@type": "HowToSection",
  name: "Section Name",
  position: 1,
  itemListElement: [
    { "@type": "HowToStep", text: "First step" },
    { "@type": "HowToStep", text: "Second step" },
  ]
}
```

**HowToDirection** and **HowToTip** - For detailed step content:

```tsx
{
  "@type": "HowToStep",
  itemListElement: [
    {
      "@type": "HowToDirection",
      text: "Do this specific action",
      beforeMedia: "https://example.com/before.jpg",
      afterMedia: "https://example.com/after.jpg",
    },
    {
      "@type": "HowToTip",
      text: "Here's a helpful tip",
    }
  ]
}
```

#### Duration Format (ISO 8601)

Use these formats for time durations:

- `PT15M` - 15 minutes
- `PT1H` - 1 hour
- `PT1H30M` - 1 hour 30 minutes
- `PT2H15M` - 2 hours 15 minutes

#### Best Practices

1. **Clear steps**: Write concise, actionable step instructions
2. **Include images**: Add images for complex steps to improve clarity
3. **Separate sections**: Use HowToSection to group related steps logically
4. **Accurate timing**: Provide realistic time estimates for each phase
5. **List all materials**: Include all supplies and tools needed upfront
6. **Add video**: Video content significantly improves search appearance

[↑ Back to Components](#-components-by-category)

### 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

```tsx
import { OrganizationJsonLd } from "@opensourceframework/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

```tsx
<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: "support@example.com",
  }}
  telephone="+1-999-999-9999"
  email="contact@example.com"
  foundingDate="2010-01-01"
  vatID="FR12345678901"
  iso6523Code="0199:724500PMK2A2M1SQQ228"
  numberOfEmployees={{
    minValue: 100,
    maxValue: 999,
  }}
/>
```

#### OnlineStore Example with Return Policy

```tsx
<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: "support@example.com",
    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

| Property                  | Type                                                     | Description                                             |
| ------------------------- | -------------------------------------------------------- | ------------------------------------------------------- |
| `type`                    | `"Organization" \| "OnlineStore"`                        | The type of organization. Defaults to "Organization"    |
| `name`                    | `string`                                                 | The name of your organization                           |
| `url`                     | `string`                                                 | The URL of your organization's website                  |
| `logo`                    | `string \| ImageObject`                                  | Your organization's logo (112x112px minimum)            |
| `description`             | `string`                                                 | A detailed description of your organization             |
| `sameAs`                  | `string \| string[]`                                     | URLs of your organization's profiles on other sites     |
| `address`                 | `string \| PostalAddress \| (string \| PostalAddress)[]` | Physical or mailing address(es)                         |
| `contactPoint`            | `ContactPoint \| ContactPoint[]`                         | Contact information for your organization               |
| `telephone`               | `string`                                                 | Primary phone number (include country code)             |
| `email`                   | `string`                                                 | Primary email address                                   |
| `alternateName`           | `string`                                                 | Alternative name your organization goes by              |
| `foundingDate`            | `string`                                                 | ISO 8601 date when the organization was founded         |
| `legalName`               | `string`                                                 | Registered legal name if different from name            |
| `taxID`                   | `string`                                                 | Tax ID associated with your organization                |
| `vatID`                   | `string`                                                 | VAT code (important trust signal)                       |
| `duns`                    | `string`                                                 | Dun & Bradstreet DUNS number                            |
| `leiCode`                 | `string`                                                 | Legal Entity Identifier (ISO 17442)                     |
| `naics`                   | `string`                                                 | North American Industry Classification System code      |
| `globalLocationNumber`    | `string`                                                 | GS1 Global Location Number                              |
| `iso6523Code`             | `string`                                                 | ISO 6523 identifier (e.g., "0199:724500PMK2A2M1SQQ228") |
| `numberOfEmployees`       | `number \| QuantitativeValue`                            | Number of employees or range                            |
| `review`                  | `Review \| Review[]`                                     | A review or array of reviews of the organization        |
| `aggregateRating`         | `AggregateRating`                                        | The overall rating based on a collection of reviews     |
| `hasMerchantReturnPolicy` | `MerchantReturnPolicy \| MerchantReturnPolicy[]`         | Return policy details (OnlineStore only)                |
| `hasMemberProgram`        | `MemberProgram \| MemberProgram[]`                       | Loyalty/membership program details (OnlineStore only)   |
| `scriptId`                | `string`                                                 | Custom ID for the script tag                            |
| `scriptKey`               | `string`                                                 | Custom key prop for React                               |

#### Organization with Reviews and Ratings

```tsx
<OrganizationJsonLd
  name="Acme Software Inc."
  url="https://www.acmesoftware.com"
  logo="https://www.acmesoftware.com/logo.png"
  review={[
    {
      author: "Sarah Johnson",
      reviewBody: "Excellent company to work with!",
      reviewRating: {
        ratingValue: 5,
        bestRating: 5,
      },
      datePublished: "2025-06-15",
    },
    {
      author: "Michael Chen",
      reviewBody: "Great software solutions with excellent customer service.",
      reviewRating: {
        ratingValue: 4,
        bestRating: 5,
      },
      datePublished: "2025-08-22",
    },
  ]}
  aggregateRating={{
    ratingValue: 4.6,
    ratingCount: 312,
    reviewCount: 245,
    bestRating: 5,
    worstRating: 1,
  }}
/>
```

#### OnlineStore with Loyalty Program Example

```tsx
<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

```tsx
<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

| Property      | Type                                       | Description                                   |
| ------------- | ------------------------------------------ | --------------------------------------------- |
| `name`        | `string`                                   | **Required**. Name of the loyalty program     |
| `description` | `string`                                   | **Required**. Description of program benefits |
| `url`         | `string`                                   | URL where customers can sign up               |
| `hasTiers`    | `MemberProgramTier \| MemberProgramTier[]` | **Required**. Tier(s) of the loyalty program  |

#### MemberProgramTier Properties

| Property                 | Type                          | Description                          |
| ------------------------ | ----------------------------- | ------------------------------------ |
| `name`                   | `string`                      | **Required**. Name of the tier       |
| `hasTierBenefit`         | `string \| string[]`          | **Required**. Benefits for this tier |
| `hasTierRequirement`     | `various` (see below)         | Requirements to join this tier       |
| `membershipPointsEarned` | `number \| QuantitativeValue` | Points earned per unit spent         |
| `url`                    | `string`                      | URL for tier-specific signup         |
| `@id`                    | `string`                      | Unique 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:**

```tsx
hasTierRequirement: {
  name: "Store Premium Credit Card";
}
```

**Minimum Spending Requirement (MonetaryAmount):**

```tsx
hasTierRequirement: {
  value: 1000,
  currency: "USD"
}
```

**Subscription Fee (UnitPriceSpecification):**

```tsx
hasTierRequirement: {
  price: 9.99,
  priceCurrency: "EUR",
  billingDuration: 12,      // Total duration
  billingIncrement: 1,      // Billing frequency
  unitCode: "MON"          // Unit (MON = monthly)
}
```

**Text Description:**

```tsx
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:**

```tsx
membershipPointsEarned: 5;
```

**Detailed:**

```tsx
membershipPointsEarned: {
  value: 10,
  minValue: 10,
  maxValue: 20,
  unitText: "points per dollar (double on special events)"
}
```

#### Best Practices

1. **Place on homepage or about page**: Add this markup to your homepage or a dedicated "about us" page
2. **Use specific subtypes**: Use "OnlineStore" for e-commerce sites rather than generic "Organization"
3. **Include identifiers**: Add VAT ID, ISO codes, and other identifiers for better trust signals
4. **Complete address information**: Provide full address details including country code
5. **Multiple locations**: Use array format for addresses if you have multiple locations
6. **High-quality logo**: Use a logo that looks good on white background, minimum 112x112px

[↑ Back to Components](#-components-by-category)

### 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

```tsx
import { LocalBusinessJsonLd } from "@opensourceframework/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

```tsx
<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

```tsx
<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

| Property                    | Type                                                       | Description                                                                |
| --------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------- |
| `type`                      | `string \| string[]`                                       | Business type (e.g., "Restaurant", "Store", or ["Restaurant", "BarOrPub"]) |
| `name`                      | `string`                                                   | **Required.** The name of the business                                     |
| `address`                   | `string \| PostalAddress \| (string \| PostalAddress)[]`   | **Required.** Physical location(s) of the business                         |
| `url`                       | `string`                                                   | The fully-qualified URL of the business location page                      |
| `telephone`                 | `string`                                                   | Primary contact phone number (include country code)                        |
| `image`                     | `string \| ImageObject \| (string \| ImageObject)[]`       | Images of the business (multiple aspect ratios recommended)                |
| `priceRange`                | `string`                                                   | Relative price range (e.g., "$", "$$", "$$$", or "$10-15")                 |
| `geo`                       | `GeoCoordinates`                                           | Geographic coordinates (min 5 decimal places precision)                    |
| `openingHoursSpecification` | `OpeningHoursSpecification \| OpeningHoursSpecification[]` | Business hours including special/seasonal hours                            |
| `review`                    | `Review \| Review[]`                                       | Customer reviews (for review sites only)                                   |
| `aggregateRating`           | `AggregateRating`                                          | Average rating based on multiple reviews                                   |
| `department`                | `LocalBusinessBase \| LocalBusinessBase[]`                 | Departments within the business                                            |
| `menu`                      | `string`                                                   | URL of the menu (for food establishments)                                  |
| `servesCuisine`             | `string \| string[]`                                       | Type of cuisine served (for restaurants)                                   |
| `sameAs`                    | `string \| string[]`                                       | URLs of business profiles on other sites                                   |
| `branchOf`                  | `Organization`                                             | Parent organization if this is a branch                                    |
| `currenciesAccepted`        | `string`                                                   | Currencies accepted (e.g., "USD")                                          |
| `paymentAccepted`           | `string`                                                   | Payment methods accepted                                                   |
| `areaServed`                | `string \| string[]`                                       | Geographic areas served                                                    |
| `email`                     | `string`                                                   | Business email address                                                     |
| `faxNumber`                 | `string`                                                   | Business fax number                                                        |
| `slogan`                    | `string`                                                   | Business slogan or tagline                                                 |
| `description`               | `string`                                                   | Detailed description of the business                                       |
| `publicAccess`              | `boolean`                                                  | Whether the business location is accessible to the public                  |
| `smokingAllowed`            | `boolean`                                                  | Whether smoking is allowed at the location                                 |
| `isAccessibleForFree`       | `boolean`                                                  | Whether access is free                                                     |
| `scriptId`                  | `string`                                                   | Custom ID for the script tag                                               |
| `scriptKey`                 | `string`                                                   | Custom key prop for React                                                  |

#### Opening Hours Examples

**Standard Business Hours:**

```tsx
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:**

```tsx
openingHoursSpecification={{
  "@type": "OpeningHoursSpecification",
  dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
  opens: "00:00",
  closes: "23:59",
}}
```

**Closed on Specific Days:**

```tsx
openingHoursSpecification={{
  "@type": "OpeningHoursSpecification",
  dayOfWeek: "Sunday",
  opens: "00:00",
  closes: "00:00",
}}
```

**Seasonal Hours:**

```tsx
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

1. **Use specific business types**: Use the most specific LocalBusiness subtype (e.g., "Restaurant" instead of "LocalBusiness")
2. **Multiple types**: For businesses that fit multiple categories, use an array (e.g., `["Restaurant", "BarOrPub"]`)
3. **Complete address**: Provide as many address fields as possible for better local SEO
4. **High-quality images**: Include multiple images with different aspect ratios (16:9, 4:3, 1:1)
5. **Accurate coordinates**: Use at least 5 decimal places for latitude and longitude
6. **Opening hours**: Be precise with opening hours and include seasonal variations
7. **Department naming**: Include the main store name with department name (e.g., "Store Name - Pharmacy")
8. **Price range**: Keep under 100 characters; use standard symbols ($, $$, $$$) or ranges

[↑ Back to Components](#-components-by-category)

### 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:

```tsx
import { MerchantReturnPolicyJsonLd } from "@opensourceframework/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:

```tsx
import { MerchantReturnPolicyJsonLd } from "@opensourceframework/next-seo";

<MerchantReturnPolicyJsonLd merchantReturnLink="https://www.example.com/returns" />;
```

#### Advanced Usage with All Features

```tsx
import { MerchantReturnPolicyJsonLd } from "@opensourceframework/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:

```tsx
import { ProductJsonLd } from "@opensourceframework/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:

```tsx
import { OrganizationJsonLd } from "@opensourceframework/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

| Property                                  | Type                                     | Description                                                |
| ----------------------------------------- | ---------------------------------------- | ---------------------------------------------------------- |
| **Option A Properties**                   |
| `applicableCountry`                       | `string \| string[]`                     | **Required** (Option A). Countries where products are sold |
| `returnPolicyCategory`                    | `string`                                 | **Required** (Option A). Type of return policy             |
| `merchantReturnDays`                      | `number`                                 | Days for returns (required if finite window)               |
| `returnPolicyCountry`                     | `string \| string[]`                     | Countries where returns are processed                      |
| `returnMethod`                            | `string \| string[]`                     | How items can be returned                                  |
| `returnFees`                              | `string`                                 | Type of return fees                                        |
| `returnShippingFeesAmount`                | `MonetaryAmount`                         | Shipping fee for returns                                   |
| `refundType`                              | `string \| string[]`                     | Types of refunds available                                 |
| `restockingFee`                           | `number \| MonetaryAmount`               | Restocking fee (percentage or fixed)                       |
| `returnLabelSource`                       | `string`                                 | How customers get return labels                            |
| `itemCondition`                           | `string \| string[]`                     | Acceptable return conditions                               |
| **Customer Remorse Properties**           |
| `customerRemorseReturnFees`               | `string`                                 | Fees for change-of-mind returns                            |
| `customerRemorseReturnShippingFeesAmount` | `MonetaryAmount`                         | Shipping fee for remorse returns                           |
| `customerRemorseReturnLabelSource`        | `string`                                 | Label source for remorse returns                           |
| **Item Defect Properties**                |
| `itemDefectReturnFees`                    | `string`                                 | Fees for defective item returns                            |
| `itemDefectReturnShippingFeesAmount`      | `MonetaryAmount`                         | Shippin