Package Exports
- @coldbydefault/next-seo-lite
Readme
next-seo-lite
Stop writing 50 lines of Meta tags. Do it in 5.
A tiny, zero-runtime-dependency SEO helper for Next.js App Router.
It turns simple props into the full Metadata object Next.js expects — including openGraph, twitter, canonical URLs, structured data (JSON-LD), and everything you need for a 100% Google Lighthouse SEO score.
Features
- Title suffixing —
"Home"becomes"Home | MyBrand"automatically. - Configurable separator — use
" – "or" · "instead of the default"|"viatitleSeparator. - OpenGraph — fills
og:title,og:description,og:image(withwidth,height,alt),og:locale, andog:typein one call. - Twitter Cards — automatically uses
summary_large_imagewhen an image is provided, falls back tosummaryotherwise. Supportstwitter:creatorandtwitter:site. - Canonical URLs — combine a
baseUrlwith a pagepathand you're done. - hreflang alternates — multi-language support via
alternateLocalesconfig or per-pagealternates. - Default fallback image — set once at the root, used everywhere you don't override it.
- Keywords — global + page-level keywords, merged and de-duplicated.
- Author / Publisher — sets
meta[name=author],meta[name=creator], andmeta[name=publisher]. metadataBase— automatically set frombaseUrlfor proper URL resolution.- Comprehensive robots — public pages get full
googleBotdirectives (max-image-preview: large,max-snippet: -1,max-video-preview: -1). noIndexsupport — mark private pages (/dashboard,/checkout) with a single flag.- Structured data (JSON-LD) — built-in helpers for
Person,Article/BlogPosting, andOrganizationschemas. - Zero runtime dependencies — only
nextas a peer dep (for types).
Installation
This package is published to both npm and GitHub Packages — use whichever fits your workflow.
Option A — npm (recommended, zero friction)
npm install @coldbydefault/next-seo-lite@latest
# or
yarn add @coldbydefault/next-seo-lite@latest
# or
pnpm add @coldbydefault/next-seo-lite@latestNo auth, no .npmrc changes needed. ✅
Option B — GitHub Packages
Useful if you're already inside a GitHub org/Actions workflow.
1. Add to your project's .npmrc:
@coldbydefault:registry=https://npm.pkg.github.com2. Authenticate once with a GitHub Personal Access Token that has read:packages scope:
npm login --registry=https://npm.pkg.github.com --scope=@coldbydefault3. Install (same command as npm):
npm install @coldbydefault/next-seo-lite@latestQuick Start
1. One-liner per page (standalone)
// app/about/page.tsx
import { defineSEO } from "@coldbydefault/next-seo-lite";
import type { Metadata } from "next";
export const metadata: Metadata = defineSEO({
title: "About Us",
description: "Learn more about our team.",
siteName: "MyBrand",
image: "https://mysite.com/about-og.png",
baseUrl: "https://mysite.com",
path: "/about",
keywords: ["about", "team", "company"],
author: "Jane Doe",
});2. Global defaults with createSEOConfig (recommended)
Define your globals once, then call the returned function on every page.
// lib/seo.ts
import { createSEOConfig } from "@coldbydefault/next-seo-lite";
export const defineSEO = createSEOConfig({
siteName: "MyBrand",
baseUrl: "https://mysite.com",
defaultImage: "https://mysite.com/og-default.png",
keywords: ["MyBrand", "web development"],
author: "Jane Doe",
publisher: "MyBrand Inc.",
twitterHandle: "@mybrand",
locale: "en_US",
alternateLocales: {
en: "",
de: "/de",
},
});// app/layout.tsx
import { defineSEO } from "@/lib/seo";
import type { Metadata } from "next";
export const metadata: Metadata = defineSEO({
title: "Home",
description: "Welcome to MyBrand.",
path: "/",
});// app/blog/[slug]/page.tsx
import { defineSEO } from "@/lib/seo";
export async function generateMetadata({
params,
}: {
params: { slug: string };
}) {
const post = await getPost(params.slug);
return defineSEO({
title: post.title,
description: post.summary,
image: post.coverImage,
path: `/blog/${params.slug}`,
keywords: post.tags,
type: "article",
});
}3. Structured Data (JSON-LD)
Use the built-in schema helpers to generate valid JSON-LD for Google Rich Results.
// app/layout.tsx
import { personSchema, jsonLdScript } from "@coldbydefault/next-seo-lite";
const schema = personSchema({
name: "Jane Doe",
url: "https://janedoe.dev",
jobTitle: "Full Stack Developer",
description: "Building modern web applications.",
email: "jane@example.com",
sameAs: ["https://github.com/janedoe", "https://linkedin.com/in/janedoe"],
knowsAbout: ["React", "Next.js", "TypeScript"],
location: "Berlin, Germany",
worksFor: { name: "Acme Corp", url: "https://acme.com" },
});
export default function RootLayout({ children }) {
return (
<html>
<head>
<div dangerouslySetInnerHTML={{ __html: jsonLdScript(schema) }} />
</head>
<body>{children}</body>
</html>
);
}// app/blog/[slug]/page.tsx
import { articleSchema, jsonLdScript } from "@coldbydefault/next-seo-lite";
export default async function BlogPost({ params }) {
const post = await getPost(params.slug);
const schema = articleSchema({
headline: post.title,
description: post.excerpt,
url: `https://mysite.com/blog/${post.slug}`,
image: post.coverImage,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
authorName: "Jane Doe",
authorUrl: "https://janedoe.dev",
publisherName: "MyBrand",
publisherLogo: "https://mysite.com/logo.png",
wordCount: post.wordCount,
readingTime: post.readingTime,
keywords: post.tags,
articleSection: post.category,
});
return (
<>
<div dangerouslySetInnerHTML={{ __html: jsonLdScript(schema) }} />
<article>{/* ... */}</article>
</>
);
}API Reference
defineSEO(props: SEOProps): Metadata
Standalone helper — no global config needed.
createSEOConfig(config: SEOConfig): (props: SEOProps) => Metadata
Returns a defineSEO function pre-loaded with your global defaults.
Page-level props always win over the global config.
SEOConfig
| Prop | Type | Description |
|---|---|---|
siteName |
string |
Appended to every title: "Page | SiteName". |
baseUrl |
string |
Absolute base URL for canonical links, e.g. "https://example.com". Also sets metadataBase. |
defaultImage |
string |
Fallback OG / Twitter image URL. |
titleSeparator |
string |
Separator between title and site name. Defaults to "|". |
locale |
string |
Default og:locale, e.g. "en_US". |
keywords |
string[] |
Global keywords merged into every page. |
author |
string |
Author name for meta[name=author] and meta[name=creator]. |
publisher |
string |
Publisher name. Falls back to siteName. |
twitterHandle |
string |
@handle for twitter:creator and twitter:site. |
alternateLocales |
Record<string, string> |
Locale → path prefix map for hreflang ({ en: "", de: "/de" }). |
SEOProps
| Prop | Type | Required | Description |
|---|---|---|---|
title |
string |
✅ | Page title (without suffix). |
description |
string |
✅ | Short page description. |
image |
string |
— | Overrides defaultImage. |
imageAlt |
string |
— | Alt text for OG image. Defaults to "<title> - <siteName>". |
imageWidth |
number |
— | OG image width in px. Defaults to 1200. |
imageHeight |
number |
— | OG image height in px. Defaults to 630. |
path |
string |
— | Slug appended to baseUrl for the canonical URL, e.g. "/about". |
siteName |
string |
— | Overrides global siteName. Pass "" to suppress the suffix. |
baseUrl |
string |
— | Overrides global baseUrl. |
noIndex |
boolean |
— | Sets robots: { index: false, follow: false } when true. |
locale |
string |
— | Overrides global locale for og:locale. |
keywords |
string[] |
— | Merged with global keywords (de-duplicated). |
author |
string |
— | Overrides global author. |
type |
string |
— | Override og:type. Defaults to "website". |
alternates |
Record<string, string> |
— | Per-page language alternates (locale → URL/path). |
structuredData |
Record | Record[] |
— | JSON-LD structured data object(s). |
Structured Data Helpers
personSchema(props: PersonSchemaProps): Record<string, unknown>
Generates a Schema.org Person JSON-LD object.
articleSchema(props: ArticleSchemaProps): Record<string, unknown>
Generates a Schema.org Article / BlogPosting JSON-LD object.
organizationSchema(props: OrganizationSchemaProps): Record<string, unknown>
Generates a Schema.org Organization JSON-LD object.
jsonLdScript(data): string
Serialises JSON-LD into a <script type="application/ld+json"> tag string for use with dangerouslySetInnerHTML.
What This Gets You (Lighthouse SEO)
| Check | Status |
|---|---|
<title> element |
✅ |
| Meta description | ✅ |
| Valid canonical | ✅ |
| hreflang alternates | ✅ |
| Open Graph tags | ✅ |
| Twitter Card tags | ✅ |
| robots / googleBot directives | ✅ |
| Structured data (JSON-LD) | ✅ |
metadataBase for URL resolution |
✅ |
| Keywords meta tag | ✅ |
| Author / Publisher | ✅ |
License
MIT