Skip to content
How-to guides

Publish a plugin

Build a Kubb plugin as an npm package with a kubb-extension.json manifest and an OVERVIEW.md, publish it, and submit it to the Kubb registry.

Package a plugin so other projects can install it from npm, then list it in the Kubb registry so users can run kubb add and see it in Studio. Start from a working plugin with tests, such as the one from Create and extend a plugin. Adapters and parsers follow the same steps.

Build the package

Export the factory from a package entrypoint. Use a .js extension for the relative import so the compiled ESM works in Node:

src/index.ts
export { pluginExample } from './plugin.js'

Create a TypeScript build config:

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "declaration": true,
    "skipLibCheck": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["src/index.ts", "src/plugin.ts"]
}

Add the following fields to your existing package.json, keeping the development dependencies installed earlier. Replace the package name with an available name you own before publishing:

package.json fields
{
  "name": "kubb-plugin-example",
  "version": "1.0.0",
  "type": "module",
  "files": ["dist", "kubb-extension.json", "OVERVIEW.md"],
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "test": "vitest run src/plugin.test.ts",
    "prepack": "npm run build && npm test"
  },
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    },
    "./kubb-extension.json": "./kubb-extension.json",
    "./package.json": "./package.json"
  },
  "peerDependencies": { "kubb": "^5.0.0" }
}

files ships the manifest and the overview you add next. The exports entries keep kubb-extension.json and package.json importable, because an exports map hides every path it does not list.

kubb remains a development dependency so you can build and test locally. The peer dependency asks consumers to supply a compatible Kubb version.

Add the manifest

Create kubb-extension.json at the package root. It tells the registry, kubb add, and Studio what the package is and which factory to call:

kubb-extension.json
{
  "$schema": "https://kubb.dev/schemas/kubb-extension.json",
  "kind": "plugin",
  "name": "plugin-example",
  "export": "pluginExample",
  "displayName": "Example",
  "description": "Writes a comment with the HTTP method and path of every operation.",
  "compatibility": { "kubb": ">=5.8.2", "node": ">=22" },
  "options": [
    {
      "key": "prefix",
      "path": ["prefix"],
      "label": "Prefix",
      "description": "Text written at the start of every comment.",
      "type": "text"
    }
  ]
}

Set name to the plugin's meta.name and export to the factory that src/index.ts exports. compatibility takes only kubb and node. Add an options entry for each option users should edit in Studio.

Give meta.compatibility the same ranges, so Kubb checks them at runtime too:

src/plugin.ts
export const pluginExample = defineExtension<ExamplePlugin>({
  kind: 'plugin',
  meta: { name: 'plugin-example', version: '1.0.0', compatibility: { kubb: '>=5.8.2', node: '>=22' } },
  // ...
})

When you build with a bundler, import compatibility from the manifest instead. See Extension manifest for the pattern and every field.

Write the overview

Create OVERVIEW.md next to the manifest. It becomes the extension's page on kubb.dev and its summary in Studio. Cover these points in order, and add headings only when they help:

  1. What the extension is and does, in one or two short paragraphs.
  2. How it works, only when that is not obvious.
  3. The setup it needs, such as other plugins or peer packages.
  4. Options worth explaining and known limits.

Use sentence case, plain language, and no em dashes. Leave out install commands, badges, a changelog, license text, and marketing. The page already shows how to install, and the registry rejects an overview that contains kubb add, npm install, npm i, pnpm add, or yarn add.

Keep your README for npm and GitHub. Show installation and pluginExample({ prefix: 'API: ' }) in a Kubb config there, choose a license, and include its file.

Check the tarball

Build, inspect the files npm would include, and create a local tarball:

Terminal
npm run build
npm pack --dry-run
npm pack

The dry run should list dist/index.js, dist/index.d.ts, dist/plugin.js, dist/plugin.d.ts, kubb-extension.json, OVERVIEW.md, and package.json. The tarball should contain compiled output rather than the fixture, tests, or generated client files. prepack runs the build and both tests before packaging.

Publish to npm

Review the tarball and confirm the package name and version before publishing to npm.

When you are ready, sign in to your npm account and publish:

Terminal
npm login
npm publish --access public

Consumers install your package as a development dependency and import its factory from the package name, rather than from ./src/plugin.

Submit to the registry

Open a submission issue on kubb-labs/registry:

  • In Source, paste the npm package name, optionally with a version ([email protected]), its npmjs.com page, or the GitHub URL of the repository or package folder. Without a version, the npm latest version is used.
  • In Categories, tick every category that fits. The first tick is the main one.

A workflow opens a review pull request with the record and comments the link on your issue. Your id is <owner>/<slug>: the GitHub owner of the repository and the package name without its scope, such as acme/kubb-plugin-example. Both must be lowercase letters and digits joined by single hyphens.

Until the review pull request exists, editing the issue runs intake again. After that, the reviewer reads later edits from the issue.

After a maintainer merges the pull request, the registry publishes it within minutes, and the kubb.dev catalog lists it after its next deploy. Users install it with:

Terminal
kubb add acme/kubb-plugin-example

Review

A maintainer reviews the pull request against REVIEW.md. The review checks:

  • The submitter owns the repository or belongs to the owning organization, and the npm package names the same repository.
  • The published code matches the repository, and the package declares kubb or @kubb/core as a peer dependency.
  • The manifest is valid, export is a factory the package exports, and each option edits a real option.
  • The overview follows the content contract.
  • Install scripts, network access, and file access are explained in the overview, and the generator writes only inside its output path.

Validation never installs, imports, or runs your extension. It reads kubb-extension.json and OVERVIEW.md from the npm tarball of the pinned version, after checking the tarball against the SHA-512 integrity npm publishes. A community version must ship a manifest.

When the package ships no OVERVIEW.md, the reviewer writes one in the registry. A later version that ships its own replaces it.

Release a new version

A record pins one npm version, and merging it approves that exact version. Publishing a new version to npm does not change what kubb add installs.

Every Monday, a workflow opens one pull request that moves each pin to the npm latest version. A version that fails validation keeps the previous pin and is listed as not bumped. Each new version needs the same review. Once it merges, users get it with kubb update.

See also