Plugins
A plugin adds something to Manablox that it cannot do out of the box: a new kind of field, a new step for workflows, some code that runs whenever a page is saved, or a set of content types you want to reuse. You write it in TypeScript inside your project, or you install one somebody else published on npm.
You already use a plugin without knowing it: all the built-in field types (text, rich text, images and so on) arrive as the plugin manabloxFields().
Where plugins go
Section titled “Where plugins go”Open content-model.ts in your project. It looks like this:
import type { ManabloxConfig } from '@manablox/core';import { manabloxFields } from '@manablox/fields';
export const plugins: NonNullable<ManabloxConfig['plugins']> = [manabloxFields()];
export const contentTypes: NonNullable<ManabloxConfig['contentTypes']> = [];The plugins list is where every plugin is switched on. Both config files (manablox.config.ts for the admin and manablox.public.config.ts for the public API) import this file, so a plugin you add here is loaded by every Manablox process of your project. Keep manabloxFields() in the list: without it, no content type can use the built-in fields.
Your first plugin
Section titled “Your first plugin”This plugin does nothing useful yet. It only writes a line into the log, so you can see that plugins work.
- In your project folder, next to
content-model.ts, create a file calledhello-plugin.tswith this content:
import { definePlugin } from '@manablox/core';
export const helloPlugin = definePlugin({ name: 'hello', setup(manablox) { manablox.logger.info('Hello from my first plugin'); },});- Open
content-model.ts, import the plugin and add it to the list:
import type { ManabloxConfig } from '@manablox/core';import { manabloxFields } from '@manablox/fields';import { helloPlugin } from './hello-plugin.ts';
export const plugins: NonNullable<ManabloxConfig['plugins']> = [manabloxFields(), helloPlugin];
export const contentTypes: NonNullable<ManabloxConfig['contentTypes']> = [];- Save both files. If
pnpm devis running, it restarts by itself. Otherwise start it withpnpm dev.
You should see “Hello from my first plugin” in the terminal. Right below it, the start-up line manablox initialised lists the names of all loaded plugins: @manablox/fields and hello.
If the CMS stops with an error instead, check that the import ends in .ts ('./hello-plugin.ts') and that the path matches where the file is.
definePlugin does nothing except check the shape of your plugin while you type. name is required and must be unique: if two plugins share a name, only the first one is loaded.
What a plugin can contain
Section titled “What a plugin can contain”| Key | What it adds | More |
|---|---|---|
name | The plugin’s unique name. Required | |
fieldTypes | New kinds of fields for the admin’s Add field menu | Custom field types |
workflowActions | New steps for the workflow editor | Workflow actions |
setup(manablox) | Code that runs once when the CMS starts, for example to register hooks | Hooks |
hooks | Hooks as a list of { hook, handler, priority }. setup does the same with better type checking | Hooks |
contentTypes | Content types written in code, the same shape as in content-model.ts | Content types in code |
extend | Extra fields for a content type that is written in code | below |
workflows, webhooks, credentials, templates | Workflows, webhook endpoints, credential slots and content templates, written into your spaces by manablox sync | Workflows and webhooks in code |
version, description | Optional information about the plugin |
A plugin may also name an admin entry for extra admin screens. That needs a custom build of the admin, which a project made with manablox create cannot do; see Admin screens below.
Adding fields to a content type
Section titled “Adding fields to a content type”With extend, a plugin adds fields to a content type that is declared somewhere else in code: in contentTypes of content-model.ts or in another plugin. This is handy for a group of fields you want on several types, like search engine fields.
Say content-model.ts declares an article type in code:
export const contentTypes: NonNullable<ManabloxConfig['contentTypes']> = [ { name: 'article', label: 'Article', fields: [{ name: 'body', type: 'richtext' }], },];This plugin (in seo-plugin.ts) adds two text fields to it, shown in the side column of the editor:
import { definePlugin } from '@manablox/core';
export const seoPlugin = definePlugin({ name: 'seo', extend: [ { name: 'article', fields: [ { name: 'meta_title', type: 'string', settings: { max: 60 }, admin: { zone: 'sidebar' } }, { name: 'meta_description', type: 'string', settings: { editor: 'textarea', max: 160 }, admin: { zone: 'sidebar' }, }, ], }, ],});Add seoPlugin to the plugins list as before. When you open an article in the admin, you see the two new fields.
extend only reaches content types written in code. A type you built in the admin lives in the database, and the plugin cannot see it when the CMS starts. If the type does not exist in code, the CMS refuses to start with plugin.extend.contentType.notFound.
A plugin from npm
Section titled “A plugin from npm”A plugin somebody else published is an npm package. Its README tells you its package name and what to import. The steps are always the same:
- Install the package in your project folder (replace the name with the real one):
pnpm add some-manablox-plugin- Import it in
content-model.tsand add it to thepluginslist, exactly as with your own plugin. - Restart
pnpm devif it does not restart by itself.
If pnpm add stops with (or warns about) Ignored build scripts, the package (or one of its dependencies) wants to run an install script, and pnpm blocks that unless you allow it. Add the package name under allowBuilds: in pnpm-workspace.yaml (for example some-package: true) and run pnpm install again. Only allow packages you trust.
Plugins on the server
Section titled “Plugins on the server”In a docker project, the image is built from your project folder, so a plugin reaches the server only when you rebuild:
- If you changed
package.json(for example withpnpm add), write the lockfile again:
./scripts/lockfile.sh- Build the image and start the new version:
docker compose builddocker compose up -dRemember that your plugin is loaded by the public API process too. A setup function runs in both processes, so with pnpm dev and pnpm dev:public running you see the hello message twice. Hooks on content changes only fire in the admin’s process, because the public API never changes content.
Admin screens
Section titled “Admin screens”The admin that comes with your project (the @manablox/admin package) is already built: it is a finished bundle of files that the CMS hands to the browser. Extra admin pages, menu entries or your own field editors would have to be compiled into that bundle. The tools for that exist (@manablox/admin-plugin, and the server.admin: { dir } option that serves a different build), but the admin’s source code is not part of the published packages, so a project made with manablox create cannot build its own admin. Leave admin: true in manablox.config.ts.
What you can do without a new admin build:
- A custom field type can reuse one of the admin’s built-in editors. See Custom field types.
- A workflow action draws its settings form from its own description, so it appears in the workflow editor without any admin build. See Workflow actions.
When something goes wrong
Section titled “When something goes wrong”The CMS checks all plugins when it starts and stops with a short message if something does not fit. The message starts with manablox: and names the problem:
| Message | Cause |
|---|---|
plugin.extend.contentType.notFound | extend names a content type that is not declared in code |
contentType.name.duplicate | A plugin declares a content type whose name is already taken |
fieldType.name.duplicate | Two field types use the same name |
fieldType.name.invalid | A field type’s name is not lowercase letters, digits and -, starting with a letter |
codeResource.duplicate | Two workflows, webhooks, credentials or templates share a slug |
contentType.field.type.notFound | A content type uses a field type that no plugin provides any more, for example after you removed a plugin |
More problems and their fixes are in Common problems.