You're reading docs for Nextly Alpha. APIs may change between releases.

Plugins

Plugins

Extend Nextly with official and custom plugins that add collections, admin views, hooks, and API endpoints.

Alpha (0.x) — pin your versions.

The plugin API surface is now stable and semver-protected (see API stability); the packages themselves are still 0.x alpha. Build against @nextlyhq/plugin-sdk — the stability boundary — and pin your nextly / @nextlyhq/plugin-sdk versions.

Plugins extend Nextly with new functionality without modifying core code. A plugin can add collections, register lifecycle hooks, inject custom admin views, and transform configuration -- all through a single plugins array in your config.

Official Plugins

PluginPackageDescription
Form Builder@nextlyhq/plugin-form-builderVisual drag-and-drop form builder with submission management, email notifications, spam protection, and export

Community Plugins

Community plugins are distributed on npm — search the nextly-plugin keyword. To get yours listed here, see Contributing a plugin.

No community plugins listed yet — be the first.

Adding a Plugin

Install the plugin package, then add it to your defineConfig call.

// nextly.config.ts
import { defineConfig } from 'nextly';
import { formBuilder } from '@nextlyhq/plugin-form-builder';

const fb = formBuilder();

export default defineConfig({
  plugins: [fb.plugin],
  collections: [Posts, Users, Media],
});

Plugin collections are merged automatically -- you do not need to spread them into the collections array yourself. The plugin's config() transformer handles this during initialization.

For simpler setups where you do not need to customize plugin options, use the pre-configured default instance:

// nextly.config.ts
import { defineConfig } from 'nextly';
import { formBuilderPlugin } from '@nextlyhq/plugin-form-builder';

export default defineConfig({
  plugins: [formBuilderPlugin],
  collections: [Posts, Users, Media],
});

How Plugins Work

Every plugin implements the PluginDefinition interface from nextly. The lifecycle has two phases:

1. Declarative + setup phase

Plugins declare schema, permissions, routes, and admin UI in contributes (read by the host without running the plugin). Before services start, Nextly also runs each plugin's optional setup(config) escape hatch — all setups run before any init. Don't mutate the config; spread and return a new object.

import { definePlugin } from '@nextlyhq/plugin-sdk';

export const myPlugin = definePlugin({
  name: '@acme/nextly-plugin-example',
  version: '1.0.0',
  nextly: '^1.0.0',
  contributes: {
    collections: [MyCollection], // merged automatically by the pipeline
  },
  setup(config) {
    return { ...config }; // escape hatch for advanced config transforms
  },
});

2. Initialization Phase

After all services are registered, Nextly calls each plugin's init(ctx) with a PluginContext. It provides:

  • services -- managed, secure-by-default data access ({ as: 'system' } to elevate)
  • db -- raw Drizzle escape hatch (unmanaged)
  • hooks / events / filters / actions -- in-transaction hooks, post-commit events, typed seams
  • self -- your entities' resolved slugs after any host .rename()
  • logger, config, nextlyVersion
import { definePlugin } from '@nextlyhq/plugin-sdk';

export const auditPlugin = definePlugin({
  name: 'audit-log',
  version: '1.0.0',
  nextly: '^1.0.0',

  async init(ctx) {
    ctx.hooks.on('afterCreate', '*', async (context) => {
      ctx.logger.info('Created entry', {
        collection: context.collection,
        id: context.data?.id,
      });
    });
  },
});

See the Plugin Author Guide for the full workflow.

Plugin Admin Integration

Plugins can control where their collections appear in the admin sidebar and customize their visual appearance.

const plugin: PluginDefinition = {
  name: 'my-plugin',
  collections: [MyCollection],

  admin: {
    order: 50,
    description: 'What this plugin does',
    appearance: {
      icon: 'BarChart',
      label: 'Analytics',
      badge: 'Beta',
      badgeVariant: 'secondary',
    },
  },
};

Plugins can also register custom admin view components. The Form Builder plugin, for example, replaces the default collection edit view with a visual drag-and-drop builder. Components are referenced by path strings (e.g., @nextlyhq/plugin-form-builder/admin#FormBuilderView) and auto-registered when the admin panel loads.

Overriding Plugin Behavior

Host applications can override a plugin's sidebar placement and appearance through the admin.pluginOverrides section of defineConfig:

// nextly.config.ts
export default defineConfig({
  plugins: [fb.plugin],
  admin: {
    pluginOverrides: {
      '@nextlyhq/plugin-form-builder': {
        placement: AdminPlacement.COLLECTIONS,
        order: 10,
        appearance: {
          label: 'Contact Forms',
        },
      },
    },
  },
});

Next Steps

Build a plugin

Ship & maintain

Reference