Skip to content

Interface extensions

An interface is the editing widget for a field — what users see and interact with when entering or modifying a value on the item form. CairnCMS ships a long list of built-in interfaces (input, dropdown, datetime, tags, color, and so on); a custom interface adds a new one.

An interface extension has two parts: a configuration object that registers the interface with the app, and a Vue component that renders the editing UI. Both live inside a single npm package created by the extensions toolchain.

The package’s source entrypoint exports a configuration object built with defineInterface:

import { defineInterface } from '@cairncms/extensions-sdk';
import InterfaceComponent from './interface.vue';
export default defineInterface({
id: 'custom',
name: 'Custom',
icon: 'box',
description: 'A custom interface.',
component: InterfaceComponent,
types: ['string'],
options: null,
});

defineInterface is a no-op type wrapper; it returns the config unchanged but gives you full TypeScript inference on the shape.

The fields available on an interface configuration object:

  • id — unique key. Scope proprietary interfaces with an author or organization prefix to avoid collisions.
  • name — display name shown in the Create Field picker.
  • icon — icon name from the Material icon set or one of CairnCMS’s custom icons.
  • description — short description shown alongside the interface in the picker (under 80 characters).
  • component — the Vue component that renders the editing UI.
  • options — configuration fields exposed in the field detail drawer when an editor sets up this interface. Can be an array of field definitions, a { standard, advanced } object that splits options into two tabs, a function that returns either of those (passed an extension context), a Vue component for fully custom rendering, or null for no options.
  • types — array of supported storage types (string, text, integer, boolean, json, geometry, and so on). The interface only appears in the Create Field picker for fields with a matching type.
  • localTypes — array of supported local types: standard, file, files, m2o, o2m, m2m, m2a, presentation, translations, group. Defaults to ['standard'].
  • group — which category in the Create Field picker this interface appears under: standard, selection, relational, presentation, group, or other. Defaults to other.
  • order — sort order within the group.
  • relationaltrue if this interface displays related records.
  • hideLabel — hide the field label above the interface on the item page.
  • hideLoader — hide the loading skeleton while the field is initializing.
  • autoKey — auto-generate the field key from the field name during creation.
  • recommendedDisplays — array of display extension IDs that pair well with this interface. Surfaced when an editor configures the field’s Display section.
  • preview — an SVG string used as the preview image in the Create Field picker. Typically imported from a .svg file with ?raw.

The component renders the editing UI. It receives the current value as a prop and emits an event when the value changes.

A minimal SFC component:

<template>
<input :value="value" @input="emit('input', $event.target.value)" />
</template>
<script setup>
defineProps({
value: { type: String, default: null },
});
const emit = defineEmits(['input']);
</script>

The component receives the following props:

  • value — the current field value
  • type — the field’s storage type
  • collection — the collection key
  • field — the field key
  • fieldData — the full field configuration object
  • primaryKey — the current item’s primary key (or '+' for new items)
  • width — the field’s layout width (half, half-right, full, or fill)
  • length — the field’s max_length from the schema, when set
  • disabled — whether the field is currently locked
  • loading — whether the form is still loading
  • autofocus — whether this field should grab focus on mount
  • direction — text direction (ltr or rtl)

Any additional values configured under options are passed as props as well.

  • input — updates the value of this field. Emit the new value as the event payload.
  • set-field-value — sets the value of a different field on the same item. Emit { field: '<other-field-key>', value: <new-value> }.

The component is otherwise a blank canvas. Use any Vue 3 features and any third-party UI libraries that support Vue 3.

The SDK exports composables for reaching the API client, the platform stores, and other internal systems from inside an interface component:

import { useApi, useStores } from '@cairncms/extensions-sdk';
export default {
setup() {
const api = useApi();
const { useCollectionsStore } = useStores();
const collectionsStore = useCollectionsStore();
// ...
},
};

useApi() returns an axios instance pre-configured to talk to the CairnCMS API as the current user. useStores() returns the Pinia stores the app uses internally — useful when an interface needs to read collection metadata, current-user info, or other platform state.

Both composables work in any Vue 3 setup, including Options-API components that pair an options block with a small setup() function.

Putting it together: a single-line text input that uppercases its value on input.

src/index.js:

import { defineInterface } from '@cairncms/extensions-sdk';
import Component from './interface.vue';
export default defineInterface({
id: 'shouty-input',
name: 'Shouty Input',
icon: 'volume_up',
description: 'Uppercases as you type.',
component: Component,
types: ['string'],
group: 'standard',
options: null,
});

src/interface.vue:

<template>
<input
:value="value ?? ''"
@input="emit('input', $event.target.value.toUpperCase())"
/>
</template>
<script setup>
defineProps({
value: { type: String, default: null },
});
const emit = defineEmits(['input']);
</script>

Build with npm run build, then install the resulting package or symlink it into a CairnCMS instance. The new interface appears under Standard in the Create Field picker.

  • Displays cover the read-only counterpart — how a field’s value is rendered in non-edit contexts like list rows.
  • Creating extensions covers the toolchain in full, including how to install and reload extensions during development.