Skip to content

Extensions

CairnCMS is built to be extended. The same APIs and components that power the platform are available to you, so a custom extension can add new capabilities without forking the codebase.

This page covers the extension types CairnCMS ships with and how to choose between them. The next page, Creating extensions, covers the toolchain — scaffolding, building, installing, and publishing.

CairnCMS supports nine extension types, grouped into four categories.

App extensions extend the admin app. They are written as Vue components and run in the browser.

  • Interface — a custom field editing widget. Use this to add new ways to enter or edit data on the item form.
  • Display — a custom read-only renderer for a field. Use this when you need a different way to show a value in lists, tables, and detail views without changing how it is edited.
  • Layout — a custom collection page layout, alongside the built-in Table, Cards, Calendar, and Map.
  • Module — a brand-new top-level area in the module bar. Use this when you need an entire workspace that does not fit into the existing modules.
  • Panel — a custom panel type for Insights dashboards.

API extensions extend the server. They are written in JavaScript or TypeScript and run in Node.

  • Hook — a way to react to or modify platform events. Hooks come in four flavours: filter (blocking, can transform or veto), action (non-blocking, runs after), init (runs once at startup), and schedule (runs on a cron schedule).
  • Endpoint — a custom HTTP route mounted alongside the built-in API. Use this when you need to expose logic that does not map to a collection’s CRUD endpoints.

Hybrid extensions have both an app component and a server component.

  • Operation — a custom flow operation. The app side renders the operation’s configuration form in the flow editor; the API side runs the logic when the flow executes.
  • Bundle — a wrapper that ships several extensions of any type as a single package. Use this when several extensions share dependencies, are released together, or implement a single coherent feature across the app and API.

A short decision rubric:

  • The user needs a new way to edit a field’s value → Interface.
  • The user needs a new way to display a field’s value in non-edit contexts → Display.
  • The user needs a new way to browse a whole collection → Layout.
  • The user needs an entirely new workspace unrelated to existing modules → Module.
  • A dashboard needs a new visualization or interaction → Panel.
  • The server needs to react to or modify a platform event → Hook.
  • The server needs to expose a custom HTTP route → Endpoint.
  • A flow needs a new step → Operation.
  • Several extensions ship together → Bundle.

If you find yourself wanting to ship app and server logic that should be released together, reach for a Bundle rather than separate top-level extensions.

Convention-based customization (not extensions)

Section titled “Convention-based customization (not extensions)”

A couple of developer-facing customization paths exist outside the extension system. They use simple file-folder conventions rather than the SDK’s define* API:

  • Custom migrations — drop migration .js files into EXTENSIONS_PATH/migrations and they run alongside built-in migrations.
  • Email templates — drop Liquid templates into EXTENSIONS_PATH/templates and reference them from the Send Email flow operation or by name from any code that sends mail.

These are not extension types and do not require the SDK or a build step. They are documented separately for that reason.

CairnCMS discovers extensions from three sources:

  • Package extensions — installed from npm into the project’s node_modules. Any package whose name matches cairncms-extension-<name>, @<scope>/cairncms-extension-<name>, or @cairncms/extension-<name> is auto-discovered.
  • Local package extensions — full package directories (each with its own package.json) placed inside EXTENSIONS_PATH. Bundles are installed this way.
  • Local file extensions — pre-built extension files placed in type subfolders inside EXTENSIONS_PATH (for example, EXTENSIONS_PATH/interfaces/<name>/index.js). Used for non-bundle extension types when you do not need a separate package.

The Creating extensions page walks through all three.

  • Creating extensions covers the toolchain end to end: scaffold, build, install, hot reload, publish.
  • The individual type pages above cover each extension type’s API, file structure, and minimum example.