A lightweight, type-safe HTML <head> builder for modern web applications.
Build SEO-friendly metadata with a fluent API, full TypeScript support, and framework adapters for React, TanStack Router/Start, and more.
Warning
Early Development: This library is in active development (v0.x.x). Expect breaking changes between minor versions until v1.0. We recommend pinning to exact versions in production.
- Fluent Builder API β Chain methods naturally for readable, maintainable metadata configuration
- Full TypeScript Support β Autocomplete, type checking, and inline documentation in your IDE
- Zero Dependencies β Lightweight core with optional framework adapters
- SEO Essentials β Title, description, canonical URLs, robots directives.
- Social Media β Open Graph and Twitter Card meta tags for rich previews.
- Mobile Optimization β Viewport configuration, color schemes, PWA icons.
- Advanced Tags β Alternates, manifests, stylesheets, scripts, and custom meta tags.
- Simplified URL Management β Most metadata (Open Graph, canonical, alternates) requires absolute URLs. Set
metadataBaseonce and use convenient relative paths everywhere. - Continuously Expanding β Actively adding more metadata types based on community feedback.
- React Adapter β Generate React components directly from your metadata
- TanStack Router β First-class support for route-level metadata
- Framework Agnostic β Works with vanilla JS, SSR, SSG, or any rendering strategy
# npm
npm install @devsantara/head
# pnpm
pnpm add @devsantara/head
# yarn
yarn add @devsantara/head
# bun
bun add @devsantara/headNote: Framework adapters are included in the core package. No additional installations needed.
import { HeadBuilder } from '@devsantara/head';
const head = new HeadBuilder()
.addTitle('My Awesome Website')
.addDescription('A comprehensive guide to web development')
.addViewport({ width: 'device-width', initialScale: 1 })
.build();// Output (HeadElement[]):
[
{
type: 'title',
attributes: { children: 'My Awesome Website' },
},
{
type: 'meta',
attributes: {
name: 'description',
content: 'A comprehensive guide to web development',
},
},
{
type: 'meta',
attributes: {
name: 'viewport',
content: 'width=device-width, initial-scale=1',
},
},
];Use metadataBase to automatically resolve relative URLs to absolute URLs:
import { HeadBuilder } from '@devsantara/head';
const head = new HeadBuilder({
metadataBase: new URL('https://devsantara.com'), // <- Add metadata base URL
})
.addTitle('My Blog Post')
.addOpenGraph((helper) => ({
title: 'My Blog Post',
url: helper.resolveUrl('/blog/my-post'),
image: {
url: helper.resolveUrl('/images/og-image.jpg'),
},
}))
.build();// Output (HeadElement[]):
[
{
type: 'title',
attributes: { children: 'My Blog Post' },
},
{
type: 'meta',
attributes: {
property: 'og:title',
content: 'My Blog Post',
},
},
{
type: 'meta',
attributes: {
property: 'og:url',
content: 'https://devsantara.com/blog/my-post',
},
},
{
type: 'meta',
attributes: {
property: 'og:image',
content: 'https://devsantara.com/images/og-image.jpg',
},
},
];import { HeadBuilder } from '@devsantara/head';
import { HeadReactAdapter } from '@devsantara/head/adapters';
const head = new HeadBuilder({
adapter: new HeadReactAdapter(), // <- Add React adapter
})
.addTitle('My Awesome Website')
.addDescription('A comprehensive guide to web development')
.build();
// Use in your document
export function Document() {
return (
<html>
<head>{head}</head>
{/* ... */}
</html>
);
}// Output (React.ReactNode[]):
[
<title>My Awesome Website</title>,
<meta
name="description"
content="A comprehensive guide to web development"
/>,
];import { HeadBuilder } from '@devsantara/head';
import { HeadTanstackRouterAdapter } from '@devsantara/head/adapters';
import { createRootRoute } from '@tanstack/react-router';
export const Route = createRootRoute({
head: () => {
return new HeadBuilder({
adapter: new HeadTanstackRouterAdapter(), // <- Add Tanstack router adapter
})
.addTitle('About Us')
.addDescription('Learn more about our company')
.build();
},
});// Output (Tanstack Router Head[]):
[
meta: [
{ title: 'About Us' },
{
name: 'description',
content: 'Learn more about our company',
},
],
links:[],
scripts:[],
styles: []
]| Method | Description |
|---|---|
build() |
Builds and returns the final head elements (or adapted output) |
For advanced use cases not covered by the essential methods below, use these basic methods to add any custom element directly.
| Method | Description |
|---|---|
addTitle(title: string) |
Adds a <title> element |
addMeta(attributes: HeadAttributeTypeMap['meta']) |
Adds a <meta> element with custom attributes |
addLink(attributes: HeadAttributeTypeMap['link']) |
Adds a <link> element with custom attributes |
addScript(attributes: HeadAttributeTypeMap['script']) |
Adds a <script> element with custom attributes |
addStyle(attributes: HeadAttributeTypeMap['style']) |
Adds a <style> element with custom attributes |
High-level convenience methods for common metadata patterns. These methods handle the complexity of creating properly structured head.
| Method | Description |
|---|---|
addDescription(description: string) |
Adds a meta description tag |
addCanonical(valueOrFn: BuilderOption<string | URL>) |
Adds a canonical URL link |
addRobots(options: RobotsOptions) |
Adds robots meta tag for search engine directives |
addCharSet(charset: CharSet) |
Adds character encoding declaration |
addViewport(options: ViewportOptions) |
Adds viewport configuration for responsive design |
addColorScheme(colorScheme: ColorScheme) |
Adds color scheme preference (light/dark mode) |
addOpenGraph(valueOrFn: BuilderOption<OpenGraphOptions>) |
Adds Open Graph meta tags for social media previews |
addTwitter(valueOrFn: BuilderOption<TwitterOptions>) |
Adds Twitter Card meta tags |
addIcon(preset: IconPreset, valueOrFn: BuilderOption<IconOptions>) |
Adds favicon or app icons (favicon, apple-touch-icon, etc.) |
addStylesheet(href: string | URL, options?: StylesheetOptions) |
Adds an external stylesheet link |
addManifest(valueOrFn: BuilderOption<string | URL>) |
Adds a web app manifest link |
addAlternateLocale(valueOrFn: BuilderOption<AlternateLocaleOptions>) |
Adds alternate language/locale links |
π‘ Tip: Most methods support either direct values or callback functions that receive a helper object with
resolveUrl()for dynamic URL resolution.
Adapters transform the raw HeadElement[] output into framework-specific formats. The library includes built-in adapters for popular frameworks, and you can create custom adapters for your specific needs.
| Framework | Adapter | Output Type |
|---|---|---|
| React | HeadReactAdapter |
React.ReactNode[] |
| Tanstack Router/Start | HeadTanstackRouterAdapter |
TanStack Router Head object |
You can create your own adapter by implementing the HeadAdapter<T> interface:
import type { HeadAdapter, HeadElement } from '@devsantara/head';
// Create a custom adapter that outputs HTML strings
class HeadHTMLAdapter implements HeadAdapter<string> {
transform(elements: HeadElement[]): string {
return elements
.map((element) => {
const { type, attributes } = element;
const attrs = Object.entries(attributes)
.filter(([key]) => key !== 'children')
.map(([key, value]) => `${key}="${value}"`)
.join(' ');
const children = attributes.children || '';
if (type === 'meta' || type === 'link') {
return `<${type} ${attrs}>`;
}
return `<${type} ${attrs}>${children}</${type}>`;
})
.join('\n');
}
}// Use your custom adapter
const html = new HeadBuilder({
adapter: new HeadHTMLAdapter(), // <- Add custom HTML adapter
})
.addTitle('My Site')
.addDescription('My description')
.build();<!-- Output (HTML string) -->
<title>My Site</title>
<meta name="description" content="My description" />interface HeadAdapter<T> {
transform(elements: HeadElement[]): T;
}Parameters:
elements- Array of head elements withtypeandattributes
Returns:
T- Your custom output format (string, object, framework components, etc.)
Licensed under the MIT license.