npm package: @flightdev/seo
Description: Native SEO and head management for Flight Framework. Meta tags, Open Graph, Twitter Cards, JSON-LD, sitemap, and robots.txt generation.
Author: Flight Contributors
Latest version: 0.2.1

# @flightdev/seo

Native SEO and head management for Flight Framework. Zero dependencies, framework-agnostic, SSR-first.

## Features

- **Head Management** - Tag deduplication, ordering, and efficient rendering
- **Meta Tags** - Basic meta, robots, verification, viewport
- **Open Graph** - Full OG protocol support including article, product, profile
- **Twitter Cards** - Summary, large image, app, and player cards
- **JSON-LD** - Type-safe structured data for rich results
- **Sitemap Generation** - XML sitemaps with image, video, and news support
- **robots.txt** - Configurable crawler directives
- **Framework Integrations** - React, Vue, Solid, Svelte

## Installation

```bash
npm install @flightdev/seo
```

## Quick Start

### Core API

```typescript
import { createSEO, defineMeta } from '@flightdev/seo';

const seo = createSEO({
    baseUrl: 'https://example.com',
    titleTemplate: '%s | My Site',
    defaultTitle: 'My Site',
});

const metadata = defineMeta({
    title: 'About Us',
    description: 'Learn more about our company',
    canonical: '/about',
    openGraph: {
        type: 'website',
        images: [{ url: '/og.jpg', width: 1200, height: 630 }],
    },
    twitter: {
        card: 'summary_large_image',
    },
});

const { html, tags } = seo.render(metadata);
// Insert `html` into your document head
```

### React

```tsx
import { SEO, JsonLd } from '@flightdev/seo/react';

export default function ProductPage({ product }) {
    return (
        <>
            <SEO
                title={product.name}
                description={product.description}
                openGraph={{
                    type: 'product',
                    images: [{ url: product.image }],
                }}
            />
            <JsonLd
                data={{
                    '@type': 'Product',
                    name: product.name,
                    offers: {
                        '@type': 'Offer',
                        price: product.price,
                        priceCurrency: 'USD',
                    },
                }}
            />
            <main>{/* Page content */}</main>
        </>
    );
}
```

### Vue

```vue
<script setup>
import { useSEO, useJsonLd } from '@flightdev/seo/vue';

const product = defineProps(['product']);

useSEO({
    title: product.name,
    description: product.description,
    openGraph: {
        type: 'product',
        images: [{ url: product.image }],
    },
});

useJsonLd({
    '@type': 'Product',
    name: product.name,
    offers: {
        '@type': 'Offer',
        price: product.price,
        priceCurrency: 'USD',
    },
});
</script>
```

### Solid

```tsx
import { SEO, JsonLd } from '@flightdev/seo/solid';

export default function ProductPage(props) {
    return (
        <>
            <SEO
                title={props.product.name}
                description={props.product.description}
            />
            <JsonLd data={{ '@type': 'Product', name: props.product.name }} />
            <main>...</main>
        </>
    );
}
```

### Svelte

```svelte
<script>
    import { generateMetadataHtml, createJsonLdScript } from '@flightdev/seo/svelte';

    export let product;
</script>

<svelte:head>
    {@html generateMetadataHtml({
        title: product.name,
        description: product.description,
        openGraph: { type: 'product' }
    })}
    {@html createJsonLdScript({
        '@type': 'Product',
        name: product.name
    })}
</svelte:head>
```

## API Reference

### createSEO(config?)

Create an SEO service instance.

```typescript
const seo = createSEO({
    baseUrl: 'https://example.com',
    titleTemplate: '%s | My Site',
    defaultTitle: 'My Site',
    defaultOpenGraph: {
        siteName: 'My Site',
        locale: 'en_US',
    },
    defaultTwitter: {
        site: '@mysite',
    },
});
```

### defineMeta(metadata)

Type-safe metadata definition.

```typescript
const metadata = defineMeta({
    title: 'Page Title',
    description: 'Page description',
    keywords: ['keyword1', 'keyword2'],
    author: 'Author Name',
    canonical: 'https://example.com/page',
    robots: { index: true, follow: true },
    openGraph: { /* ... */ },
    twitter: { /* ... */ },
});
```

### Metadata Types

```typescript
interface Metadata {
    // Basic
    title?: string;
    titleTemplate?: string;
    description?: string;
    keywords?: string | string[];
    author?: string;
    canonical?: string;

    // Robots
    robots?: RobotsMeta | string;
    googleBot?: RobotsMeta | string;

    // Open Graph
    openGraph?: {
        type?: 'website' | 'article' | 'product' | ...;
        title?: string;
        description?: string;
        url?: string;
        siteName?: string;
        locale?: string;
        images?: OpenGraphImage[];
        article?: OpenGraphArticle;
        product?: OpenGraphProduct;
    };

    // Twitter
    twitter?: {
        card?: 'summary' | 'summary_large_image' | 'app' | 'player';
        title?: string;
        description?: string;
        site?: string;
        creator?: string;
        image?: TwitterCardImage | string;
    };
}
```

## JSON-LD Structured Data

```typescript
import {
    createJsonLd,
    articleJsonLd,
    productJsonLd,
    breadcrumbJsonLd,
    faqJsonLd,
} from '@flightdev/seo/json-ld';

// Generic
const jsonLd = createJsonLd({
    '@type': 'Organization',
    name: 'My Company',
    url: 'https://example.com',
});

// Article
const article = articleJsonLd({
    headline: 'Article Title',
    datePublished: '2026-01-10',
    author: { '@type': 'Person', name: 'Author' },
    publisher: { '@type': 'Organization', name: 'Publisher' },
});

// Product
const product = productJsonLd({
    name: 'Widget',
    description: 'A great widget',
    offers: {
        '@type': 'Offer',
        price: 9.99,
        priceCurrency: 'USD',
        availability: 'InStock',
    },
});

// Breadcrumb
const breadcrumb = breadcrumbJsonLd([
    { name: 'Home', url: '/' },
    { name: 'Products', url: '/products' },
    { name: 'Widget' },
]);

// FAQ
const faq = faqJsonLd([
    { question: 'What is this?', answer: 'A great product.' },
    { question: 'How much?', answer: '$9.99' },
]);
```

## Sitemap Generation

```typescript
import { createSitemap, createSitemapIndex } from '@flightdev/seo/sitemap';

const sitemap = createSitemap({
    baseUrl: 'https://example.com',
    pretty: true,
});

// Add URLs
sitemap.add('/', { priority: 1.0, changefreq: 'daily' });
sitemap.add('/about', { priority: 0.8 });
sitemap.add('/products/widget', {
    lastmod: new Date(),
    priority: 0.7,
    images: [{ loc: '/images/widget.jpg', title: 'Widget' }],
});

// Generate XML
const xml = sitemap.generate();

// For large sites, use sitemap index
const index = createSitemapIndex();
index.add('https://example.com/sitemap-1.xml');
index.add('https://example.com/sitemap-2.xml');
const indexXml = index.generate();
```

## robots.txt Generation

```typescript
import { createRobots, standardRobots } from '@flightdev/seo/robots';

// Custom
const robots = createRobots();
robots.allow('*', '/');
robots.disallow('*', '/admin');
robots.disallow('*', '/api');
robots.addSitemap('https://example.com/sitemap.xml');
const txt = robots.generate();

// Standard preset
const standard = standardRobots({
    sitemapUrl: 'https://example.com/sitemap.xml',
    blockPaths: ['/private'],
});
```

## SSR Integration

```typescript
// In your server entry
import { createSEO, createHeadManager } from '@flightdev/seo';

const seo = createSEO({ baseUrl: 'https://example.com' });

export function render(url: string, metadata: Metadata): string {
    const { html: headHtml } = seo.render(metadata);

    return `
<!DOCTYPE html>
<html>
<head>
    ${headHtml}
</head>
<body>
    <!-- app content -->
</body>
</html>
    `;
}
```

## Dynamic Metadata

```typescript
// src/routes/products/[id].page.tsx
import { defineMeta } from '@flightdev/seo';

export async function generateMetadata({ params }) {
    const product = await getProduct(params.id);

    return defineMeta({
        title: product.name,
        description: product.description,
        openGraph: {
            type: 'product',
            images: [{ url: product.image }],
        },
    });
}

export default function ProductPage({ product }) {
    return <main>{/* content */}</main>;
}
```

## Best Practices

1. **Always set title and description** - These are essential for SEO
2. **Use canonical URLs** - Prevent duplicate content issues
3. **Provide Open Graph images** - 1200x630px recommended for social sharing
4. **Add structured data** - Improves rich results in search
5. **Generate sitemaps** - Help search engines discover your pages
6. **Configure robots.txt** - Control crawler access

## Related

- [Flight Framework](https://github.com/flight-framework/flight)
- [Open Graph Protocol](https://ogp.me/)
- [Twitter Cards](https://developer.twitter.com/en/docs/twitter-for-websites/cards)
- [Schema.org](https://schema.org/)
- [Google Rich Results](https://search.google.com/test/rich-results)

## License

MIT License