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

@kon10/seo

v1.5.0

Published

Kon10 SEO plugin — an injectable search & social metadata field with backend derivation and a Studio preview.

Readme

@kon10/seo

Injectable search & social metadata for Kon10. It provides seo() and socialGraph() field builders, server-side field registration, backend derivation hooks, and Studio renderers with live Google-result and social-card previews — each rendered as its own tab.

Install

pnpm add @kon10/seo

When to use this package

Use @kon10/seo when pages, posts, or any content need editor-friendly search and social metadata — a meta title/description, canonical URL, robots directives, OpenGraph, and Twitter cards — without hand-rolling the same field group on every entity.

Public API

  • seoPlugin(options) to register the field types, inject fields, and wire the derivation hooks.
  • seo() (search) and socialGraph() (Open Graph / Twitter) field builders for explicit, per-entity opt-in.
  • @kon10/seo/studio for the Studio renderers.
  • seoDataSchema / socialDataSchema and their types, plus the template + derivation helpers, for custom workflows.

Two surfaces, two tabs

The search surface (seo) and the social surface (socialGraph) are separate fields so each renders in its own Studio tab (tabs are grouped by field meta.group). They stay linked: the social previews fall back to the search title/description, and seoPlugin injects/wires both together.

Two ways to opt in

Both styles work together — pick per entity.

Explicit fields — add seo() and socialGraph() to the entity's fields, one per tab:

import { defineConfig, text } from '@kon10/core'
import { Collection, ContentModule } from '@kon10/content'
import { seo, socialGraph, seoPlugin } from '@kon10/seo'

export default defineConfig({
  plugins: [seoPlugin({ titleTemplate: '%s · Acme' })],
  modules: [
    ContentModule({
      entities: [
        Collection({
          slug: 'posts',
          fields: {
            title: text({ required: true }),
            excerpt: text(),
            // description auto-derives from `excerpt`; title from `title`.
            seo: seo({ meta: { group: 'SEO' } }),
            social: socialGraph({ meta: { group: 'Social Graph' } }),
          },
        }),
      ],
    }),
  ],
})

Config injection — add both fields to entities that don't declare them, by slug list or predicate:

seoPlugin({
  inject: ['pages', 'posts'],            // or: (entity) => entity.kind === 'document'
  titleTemplate: '%s · Acme',
  // social: false,                      // inject the SEO field only
})

How it works

  • Field types. seo (search: title, description, canonical, robots) and socialGraph (Open Graph + Twitter) are Zod-first field types, each storing its own object in its own column. Neither is ever required, so a fresh payload validates before the hooks fill it.
  • Backend derivation. At onInit — after every module, before migrate(), so the storage columns are created for free — the plugin resolves each seo field's from map (falling back to a value inferred from the entity's own fields: title ← the title field, description ← an excerpt/summary field) and appends beforeCreate/beforeUpdate hooks. The hooks backfill only blank sub-fields, so a newly created record already carries sensible metadata and re-saving never clobbers an editor's own text. The social block stays empty and falls back to the SEO values at render time.
  • Studio. A renderer per field (shipped via Plugin.studio.ui) composes the Studio's existing renderers for each sub-field — so an OG image reuses the media picker — and adds live search-result and social-card previews. The socialGraph renderer reads its cross-linked seo sibling so its previews fall back to the search copy.

Options

| Option | Purpose | |---|---| | inject | Entity slugs (or a predicate) to auto-add the seo + socialGraph fields to. | | titleTemplate | Site-wide template applied to a derived title, e.g. '%s · Acme'. | | social | Inject the socialGraph field alongside seo. Default true. | | robots | Default for whether injected seo fields show the robots switches. | | from | Plugin-wide derivation map merged under each seo field's own from. |

Operational notes

  • Register seoPlugin() once in the Kon10 config.
  • Detection is by field type ('seo' / 'socialGraph'), never by name — a hand-rolled seo: group(...) is left untouched.
  • Length limits are preview thresholds, not hard validation: a slightly long title still saves.

Related documentation