Create & Publish a Plugin
Scaffold, build, test, and publish a Frontguard plugin to the npm registry.
Create & Publish a Plugin
Frontguard plugins extend the pipeline via lifecycle hooks. Anyone can publish a plugin to npm and have others install it with a single command. This guide walks through scaffolding, building, testing, and publishing one.
Already know the hook system? Jump to Publishing. For the full hook reference, see Custom Plugins.
Naming convention
Frontguard discovers and installs plugins by a naming convention:
frontguard-plugin-<name> # unscoped
@your-scope/frontguard-plugin-<name> # scopedThe CLI resolves short names automatically — frontguard plugin install slack
installs frontguard-plugin-slack.
Scaffold a new plugin
The fastest way to start is the official scaffolder:
npm create frontguard-plugin@latest my-plugin
# or
npx create-frontguard-plugin slack --description "Post results to Slack"This generates a complete, tested project:
frontguard-plugin-slack/
├── package.json # correct name, keywords, peerDeps
├── tsconfig.json
├── vitest.config.ts
├── src/index.ts # plugin factory with setup + afterRun hooks
├── test/index.test.ts # passing tests
└── README.mdThen:
cd frontguard-plugin-slack
npm install
npm test # tests pass out of the box
npm run buildAnatomy of a plugin
A plugin is an object with a unique name and one or more optional hooks. The
scaffold exports a factory so users can pass options:
import type { FrontguardPlugin } from '@frontguard/cli';
export interface SlackOptions {
webhookUrl: string;
}
export function createSlackPlugin(options: SlackOptions): FrontguardPlugin {
return {
name: 'slack',
setup() {
if (!options.webhookUrl) throw new Error('slack: webhookUrl is required');
},
async afterRun(result) {
const { regressions } = result.summary;
if (regressions > 0) {
await fetch(options.webhookUrl, {
method: 'POST',
body: JSON.stringify({ text: `⚠️ ${regressions} visual regression(s) detected` }),
});
}
},
};
}
export default createSlackPlugin;Supported export shapes
The loader accepts any of these (in priority order):
export defaulta factory function(config) => pluginexport defaulta plugin object- a named
export const plugin(object or factory) - the module itself being a plugin object
Using a plugin
Users install and reference it in their config:
frontguard plugin install slack// frontguard.config.ts
import createSlackPlugin from 'frontguard-plugin-slack';
export default {
baseUrl: 'http://localhost:3000',
plugins: [createSlackPlugin({ webhookUrl: process.env.SLACK_WEBHOOK })],
};Managing installed plugins
frontguard plugin list # show installed frontguard-plugin-* packages
frontguard plugin install slack # add one
frontguard plugin uninstall slackplugin list scans node_modules for packages matching the naming convention,
including scoped packages, and prints their version + description.
Publishing
-
Make sure
package.jsonnamefollows the convention andkeywordsincludefrontguard-plugin. -
Build and test:
npm run build && npm test -
Publish:
npm publish --access public
Add the frontguard-plugin keyword so your plugin is discoverable via
npm search.
Version compatibility
Declare @frontguard/cli as a peer dependency (the scaffold does this) so
your plugin shares the host's Frontguard version rather than bundling its own.
The loader surfaces a clear error if a plugin can't be imported, pointing users
to frontguard plugin install.