@flightdev/seo
v0.2.1
Published
Native SEO and head management for Flight Framework. Meta tags, Open Graph, Twitter Cards, JSON-LD, sitemap, and robots.txt generation.
Maintainers
Readme
@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
npm install @flightdev/seoQuick Start
Core API
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 headReact
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
<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
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
<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.
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.
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
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
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
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
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
// 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
// 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
- Always set title and description - These are essential for SEO
- Use canonical URLs - Prevent duplicate content issues
- Provide Open Graph images - 1200x630px recommended for social sharing
- Add structured data - Improves rich results in search
- Generate sitemaps - Help search engines discover your pages
- Configure robots.txt - Control crawler access
Related
License
MIT License
