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.
Anatomy
Section titled “Anatomy”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.
Configuration
Section titled “Configuration”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, ornullfor 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, orother. Defaults toother.order— sort order within the group.relational—trueif 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.svgfile with?raw.
The interface component
Section titled “The interface component”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 valuetype— the field’s storage typecollection— the collection keyfield— the field keyfieldData— the full field configuration objectprimaryKey— the current item’s primary key (or'+'for new items)width— the field’s layout width (half,half-right,full, orfill)length— the field’smax_lengthfrom the schema, when setdisabled— whether the field is currently lockedloading— whether the form is still loadingautofocus— whether this field should grab focus on mountdirection— text direction (ltrorrtl)
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.
Accessing internal systems
Section titled “Accessing internal systems”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.
A complete minimal example
Section titled “A complete minimal example”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.
Where to go next
Section titled “Where to go next”- 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.