npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coldbydefault/next-seo-lite

v2.0.2

Published

A zero-dependency SEO helper for Next.js — 100% Lighthouse SEO score out of the box.

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 "|" via titleSeparator.
  • OpenGraph — fills og:title, og:description, og:image (with width, height, alt), og:locale, and og:type in one call.
  • Twitter Cards — automatically uses summary_large_image when an image is provided, falls back to summary otherwise. Supports twitter:creator and twitter:site.
  • Canonical URLs — combine a baseUrl with a page path and you're done.
  • hreflang alternates — multi-language support via alternateLocales config or per-page alternates.
  • 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], and meta[name=publisher].
  • metadataBase — automatically set from baseUrl for proper URL resolution.
  • Comprehensive robots — public pages get full googleBot directives (max-image-preview: large, max-snippet: -1, max-video-preview: -1).
  • noIndex support — mark private pages (/dashboard, /checkout) with a single flag.
  • Structured data (JSON-LD) — built-in helpers for Person, Article/BlogPosting, and Organization schemas.
  • <JsonLd> component — renders a <script type="application/ld+json"> tag directly — no wrapper <div>, no dangerouslySetInnerHTML in your code, no hydration mismatches in Next.js.
  • Zero runtime dependencies — only next and react as peer deps (for types / JSX).

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@latest

No 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.com

2. Authenticate once with a GitHub Personal Access Token that has read:packages scope:

npm login --registry=https://npm.pkg.github.com --scope=@coldbydefault

3. Install (same command as npm):

npm install @coldbydefault/next-seo-lite@latest

Quick 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 and the <JsonLd> component to embed valid JSON-LD for Google Rich Results — no wrapper <div>, no dangerouslySetInnerHTML in your code, and no hydration mismatches in Next.js.

// app/layout.tsx
import { personSchema, JsonLd } 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: "[email protected]",
  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 lang="en">
      <body>
        <JsonLd data={schema} />
        {children}
      </body>
    </html>
  );
}
// app/blog/[slug]/page.tsx
import { articleSchema, JsonLd } 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 (
    <>
      <JsonLd data={schema} />
      <article>{/* ... */}</article>
    </>
  );
}

Migrating from jsonLdScript()? The old string-based helper still works but is deprecated. Replace <div dangerouslySetInnerHTML={{ __html: jsonLdScript(data) }} /> with <JsonLd data={data} />.


---

## 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.

#### `JsonLd({ data }: JsonLdProps): ReactElement`

Renders a `<script type="application/ld+json">` tag directly. Accepts a single schema object or an array. No wrapper elements, no hydration issues.

```tsx
import { JsonLd, personSchema } from "@coldbydefault/next-seo-lite";
<JsonLd data={personSchema({ name: "Jane", url: "https://jane.dev" })} />

jsonLdScript(data): string (deprecated)

Serialises JSON-LD into a <script type="application/ld+json"> tag string for use with dangerouslySetInnerHTML. Prefer <JsonLd> instead.


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