Plugins

SEO

Global meta tags, Open Graph, Twitter Cards, robots.txt, and per-page metadata.

SEO

Global SEO defaults (meta tags, Open Graph, Twitter Cards, robots.txt) plus a <Metadata> component for per-page overrides.

Installation

bun add @manicjs/seo

Quick Start

1. Configure Global Defaults

// manic.config.ts
import {  } from 'manicjs/config';
import {  } from '@manicjs/seo';

export default ({
  : [
    ({
      : 'https://example.com',
      : 'My App',
      : 'Build amazing apps with Manic',
    }),
  ],
});

2. Override Per Page

// app/routes/about.tsx
import {  } from '@manicjs/seo/metadata';

export default function () {
  return (
    <>
      <
        ="About Us"
        ="Learn more about our company"
        ="/images/about-og.png"
      />
      <>About Us</>
    </>
  );
}

The <Metadata> component overrides the global defaults for that page. When the user navigates away, the previous values are restored automatically.


Global Options

Prop

Type

TwitterConfig

Prop

Type

OpenGraphConfig

Prop

Type

RobotRule

Prop

Type

ContentSignals

Prop

Type


Full Example

// manic.config.ts
import {  } from 'manicjs/config';
import {  } from '@manicjs/seo';

export default ({
  : [
    ({
      : 'https://example.com',
      : 'My App',
      : 'Build amazing apps with Manic',
      : 'John Doe',
      : {
        : 'summary_large_image',
        : '@myapp',
        : '@johndoe',
      },
      : {
        : 'website',
        : 'en_US',
        : 'https://example.com/og.png',
        : 'My App',
      },
      : [
        { : '*', : ['/'], : ['/admin'] },
      ],
      : {
        'ai-train': 'no',
        : 'yes',
      },
    }),
  ],
});

Generated Output

The plugin generates:

Meta Tags (injected into <head>)

<meta name="title" content="My App">
<meta name="description" content="Build amazing apps with Manic">
<meta name="author" content="John Doe">

<!-- Open Graph -->
<meta property="og:title" content="My App">
<meta property="og:description" content="Build amazing apps with Manic">
<meta property="og:url" content="https://example.com">
<meta property="og:type" content="website">
<meta property="og:image" content="https://example.com/og.png">
<meta property="og:site_name" content="My App">

<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@myapp">
<meta name="twitter:title" content="My App">
<meta name="twitter:description" content="Build amazing apps with Manic">

robots.txt

User-agent: *
Allow: /
Disallow: /admin

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

Content-Signal: ai-train=no, search=yes

Per-Page Metadata

The <Metadata> component lets you override global SEO defaults on individual pages. It renders nothing visible — it manages <head> tags via side effects.

import {  } from '@manicjs/seo/metadata';

Props

Prop

Type

Usage Examples

Static Metadata

// app/routes/about.tsx
import {  } from '@manicjs/seo/metadata';

export default function () {
  return (
    <>
      <
        ="About Us"
        ="Learn more about our company"
        ="https://example.com/about"
      />
      <>About Us</>
    </>
  );
}

Dynamic Metadata

Use variables and state to set metadata dynamically:

// app/routes/blog/[slug].tsx
import {  } from '@manicjs/seo/metadata';
import {  } from 'manicjs';

const : <string, { : string; : string; : string }> = {
  'hello-world': { : 'Hello World', : 'My first post', : '/images/hello.png' },
  'react-tips': { : 'React Tips', : 'Useful React patterns', : '/images/react.png' },
};

export default function () {
  const {  } = ();
  const  = [.];

  if (!) return <>Post not found</>;

  return (
    <>
      <
        ={.}
        ={.}
        ={.}
        ="article"
      />
      <>
        <>{.}</>
        <>{.}</>
      </>
    </>
  );
}

With Fetched Data

import {  } from '@manicjs/seo/metadata';
import { ,  } from 'react';

export default function () {
  const [, ] = <{ : string; : string; : string } | null>(null);

  (() => {
    ('/api/product/1')
      .( => .())
      .(() => ( as { : string; : string; : string }));
  }, []);

  return (
    <>
      { && (
        <
          ={.}
          ={.}
          ={.}
        />
      )}
      <>{?. ?? 'Loading...'}</>
    </>
  );
}

How It Works

  1. On mount, <Metadata> updates existing <meta> / <link> tags or creates new ones in <head>
  2. It saves the previous values of any tags it modifies
  3. On unmount (when navigating away), it restores the original values — so global SEO defaults come back automatically
  4. Tags created by <Metadata> that didn't exist before are removed on cleanup

Integration with Sitemap

Works with @manicjs/sitemap for complete SEO:

import {  } from 'manicjs/config';
import {  } from '@manicjs/seo';
import {  } from '@manicjs/sitemap';

export default ({
  : [
    ({
      : 'https://example.com',
      : 'My App',
    }),
    ({
      : 'https://example.com',
    }),
  ],
});

When autoSitemap is true (the default), the SEO plugin automatically adds /sitemap.xml to robots.txt and emits a Link header for sitemap discovery.

See Also

On this page