Plugins

Create a Plugin

Scaffold a VitNode plugin, register its package in your host app, and serve its first TanStack Start page.

A plugin is the starting point for a VitNode feature. It keeps routes, API modules, data, translations, and AdminCP extensions together in one installable package. Nice boundaries; fewer archaeological digs later.

Plugins need a workspace

Run the generator from a repository with turbo.json. When creating an app, turn on Turborepo first; a plain single-folder app has nowhere for plugins/*.

Generate the package

Run this at the workspace root and enter a package name such as @acme/site-notes when prompted:

Create a VitNode plugin
bun create vitnode-app@canary --plugin

The CLI creates plugins/site-notes, adds it as a workspace dependency, and gives it a route, locale, and config skeleton. It does not enable the feature for the host—that explicit switch is next.

Keep the route in the plugin

The generated routes.ts is the public contract. Add another page() here when the plugin needs another URL; never copy its page into apps/web/src/routes.

plugins/site-notes/src/routes.ts
import { definePluginRoutes, lazy, page } from '@vitnode/core/routing'

export const routes = definePluginRoutes([
  page('/site-notes', {
    component: lazy(() => import('./pages/home-page')),
  }),
])

Register the plugin with the host

Import the plugin factory in the host config and add it to plugins:

apps/web/src/vitnode.config.ts
import { siteNotesPlugin } from '@acme/site-notes/config'
import { buildConfig } from '@vitnode/core/vitnode.config'

export const vitNodeConfig = buildConfig({
  plugins: [
    siteNotesPlugin(), 
  ],
})

That is the only composition step - the factory carries the plugin's routes, content types and AdminCP navigation, and the feature stays in its package. Your build reads this list and generates one literal import per plugin for each of those, so a page or an editing screen loads with the route that needs it rather than with the config. See Configuration.

Its translations need one more line, in src/locales/packages.ts - see Languages & Localization.

Run it and visit the route

Start the plugin in development
bun dev

Open http://localhost:3000/site-notes. The page comes from the plugin, gets its own chunk, and never moves house. Tiny victory dance optional.

Add the next capability