NNO Docs
Concepts

Features

Feature packages are pluggable React modules that extend your NNO console with new UI and backend capabilities.

Features

A feature package is the unit of extensibility in NNO. Each feature is an npm package that contributes routes, sidebar navigation, and optionally a backend Cloudflare Worker to your console shell. Features are activated per platform and can be grouped into Stacks to share infrastructure.

What a feature package contains

Every feature package exports two things:

  • FeatureDefinition — describes what the feature does at runtime: its routes, navigation items, required permissions, and lifecycle hooks.
  • featureManifest — describes how the shell should load it at build time: lazy-loading priority, domain grouping, default enabled state.
// Minimal feature definition
const featureDefinition: FeatureDefinition = {
  id: 'analytics',
  version: '1.0.0',
  displayName: 'Analytics',
  routes: [{ path: '/', component: 'AnalyticsDashboard' }],
  navigation: [{ label: 'Analytics', path: '/', icon: 'BarChart2', order: 50 }],
  permissions: [{ key: 'analytics:read', label: 'View Analytics', required: true }],
  requiresService: true,
  serviceEnvKey: 'VITE_ANALYTICS_API_URL',
}

Auto-discovery via Vite plugin

Feature packages do not need to be manually registered. The console shell's Vite plugin scans all installed packages at build time, finds any package with "neutrino": { "type": "feature" } in its package.json, and automatically wires up its routes and navigation.

This means adding a feature to your platform is as simple as:

  1. Install the feature package
  2. Rebuild the console shell
  3. Activate the feature via the NNO console or CLI

The Vite plugin exposes discovered features through a virtual module (virtual:feature-registry) that the shell imports at startup.

The neutrino field in package.json

The auto-discovery contract lives in package.json:

{
  "name": "@your-scope/feature-analytics",
  "neutrino": {
    "type": "feature"
  }
}

Only packages with "type": "feature" are picked up by the scanner. This prevents accidental inclusion of utility packages.

Shell integration

Once loaded, a feature integrates with the console shell through the ShellContext. The shell provides each feature with:

  • platform — the current platform ID and name
  • tenant — the active tenant context
  • user — the authenticated user with their permissions
  • features — which other features are active
  • navigate — programmatic navigation
  • envVITE_* environment variables

Features access this context through the useShell() hook from @neutrino-io/sdk/feature. This keeps features decoupled from the shell implementation — they only depend on the SDK contract, not on shell internals.

Backend Workers

If a feature declares requiresService: true, NNO provisions a Cloudflare Worker for it when the feature is activated on a platform. The Worker URL is injected into the console shell as the environment variable named in serviceEnvKey. Features in a Stack receive additional bindings for shared D1, R2, and KV resources.

Building your own feature

Use the CLI to scaffold a new feature package:

nno feature init my-feature
cd my-feature
nno feature dev        # start local dev with mock shell
nno feature validate   # check compatibility before submitting

See the Feature Development guide for the full workflow.

On this page