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.xalpha. Build against@nextlyhq/plugin-sdk— the stability boundary — and pin yournextly/@nextlyhq/plugin-sdkversions.
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
| Plugin | Package | Description |
|---|---|---|
| Form Builder | @nextlyhq/plugin-form-builder | Visual 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 seamsself-- 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
- Plugin Author Guide -- the overview: lifecycle,
ctx, and the scaffold → dev → test → publish loop - Lifecycle, dependencies & order --
setup/init/destroy, load order,dependsOn, version compatibility,enabled - Data access --
ctx.services, queries, bulk ops,{ as: "system" } - Permissions -- declare-but-never-grant, route gating,
useCan/<Can> - HTTP routes --
contributes.routes, secure-by-default, middleware - Admin UI -- menu, pages, view overrides, placement
- Schema & data lifecycle -- collections,
extend, relations, provenance, uninstall - Testing --
createTestNextly+ real-database tests
Ship & maintain
- API stability -- what's
@publicvs@experimental, semver + deprecation policy - Publishing & distribution -- publish, discovery, the trust model
- Contributing a plugin -- the quality checklist + how to get listed
Reference
- Plugin API Reference -- the public
@nextlyhq/plugin-sdksurface - Plugin Error Reference -- fail-fast boot errors and how to fix them
- Configuration Reference -- full
defineConfigoptions including thepluginsarray