Inertia + Vue 3 + Statamic CP: Custom Fieldtype in 30 Minuten
Das Control Panel von Statamic basiert auf Inertia und Vue 3. So bauen Sie einen eigenen Fieldtype mit Vue-Component, Validierung und sauberer Speicherung, Schritt für Schritt.
Worum es geht
Warum ein eigener Fieldtype, nicht ein Composer-Add-on
Use-Case
Redakteure sollen im Control Panel einen „Color-Token-Picker“ haben, der nur Brand-Farben aus dem Design-System anbietet. Kein freier HEX-Picker, keine Magic-Numbers in Entries. Die Auswahl wird als String-Slug gespeichert (etwa „accent“ oder „surface-strong“) und im Frontend zu einer CSS-Variable aufgelöst.
Warum nicht einfach ein Select?
Ein Select zeigt nur Text. Wir wollen Farb-Swatches, Tooltips mit OKLCH-Werten und einen Kontrast-Hinweis gegen den aktuellen Hintergrund. Sobald die UI mehr kann als Text und Icon, lohnt sich ein eigener Fieldtype.
Stack-Voraussetzungen
Statamic 6 mit PHP 8.5 und Vite. Das Control Panel rendert mit Vue 3 und Inertia, Composition API ist Standard. Wir bauen direkt in der App, nicht als wiederverwendbares Composer-Package. Die Extraktion in ein Addon ist ein eigener Schritt.
Schritt 1
Skeleton erzeugen und Vite aufsetzen
Was make:fieldtype generiert
Die PHP-Klasse unter App\Fieldtypes\ColorToken bestimmt Defaults, Validierungs-Regeln und das Augmentation-Verhalten beim Auslesen. Das Vue-SFC im Frontend-Pfad rendert die Eingabe-UI. Beide werden über den Klassen-Namen automatisch verdrahtet.
CP-Vite verstehen
setup-cp-vite legt eine eigene Vite-Konfig für das Control Panel an und ergänzt npm-Scripts cp:dev und cp:build. Die Frontend-Vite-Pipeline bleibt davon unangetastet, Sie haben also zwei getrennte Build-Targets.
# Fieldtype-Skeleton erzeugen
php please make:fieldtype ColorToken
# Erstellt App\Fieldtypes\ColorToken.php
# und resources/js/components/fieldtypes/ColorTokenFieldtype.vue
# Vite für das CP einrichten (einmalig pro Projekt)
php please setup-cp-vite
# CP-Build watchen
npm run cp:dev
Schritt 2
Die PHP-Klasse: Config, Processing, Augmentation
configFieldItems als Mini-Blueprint
Was hier definiert wird, sehen Redakteure später im Blueprint-Builder als zusätzliche Felder. So lässt sich pro Verwendung konfigurieren, welche Token-Slugs erlaubt sind und ob ein leerer Wert möglich ist.
Process als Server-Side-Schutz
Wir vertrauen dem Vue-Component nicht blind. Selbst wenn jemand mit DevTools einen anderen Wert in den Payload schmuggelt, prüft process() gegen die konfigurierte Whitelist. Validate-Layer und Process-Layer ergänzen sich, sie ersetzen sich nicht.
augment für komfortable Templates
Templates können {{ color_token:slug }} und {{ color_token:var }} verwenden, ohne dass Redakteure die CSS-Variable manuell zusammenbauen müssen. Das ist der Vertrag zwischen Fieldtype und Template-Layer.
<?php
namespace App\Fieldtypes;
use Statamic\Fields\Fieldtype;
class ColorToken extends Fieldtype
{
protected static $title = 'Color Token';
protected $icon = 'color';
protected $categories = ['special'];
/**
* Config-Felder, die im Blueprint-Builder erscheinen.
*/
protected function configFieldItems(): array
{
return [
'tokens' => [
'type' => 'array',
'display' => 'Available tokens',
'instructions' => 'Slugs aus Ihrem Design-System.',
'default' => ['accent', 'surface', 'surface-strong'],
],
'allow_empty' => [
'type' => 'toggle',
'display' => 'Allow empty selection',
'default' => false,
],
];
}
/**
* Preprocess vor Übergabe an Vue.
*/
public function preProcess($data): ?string
{
return $data ?: null;
}
/**
* Process vor dem Speichern auf Disk.
*/
public function process($data): ?string
{
$allowed = (array) $this->config('tokens', []);
if (! in_array($data, $allowed, true)) {
return null;
}
return $data;
}
/**
* Augmentation: Was Templates erhalten.
*/
public function augment($value): array
{
return [
'slug' => $value,
'var' => $value ? "var(--color-{$value})" : null,
];
}
public function rules(): array
{
return $this->config('allow_empty') ? [] : ['required'];
}
}
Schritt 3
Die Vue-Component: Composition API und v-model
Das value/input-Pattern
Statamic-Fieldtypes folgen nicht dem üblichen v-model:modelValue Pattern, sondern erwarten value als Prop und input als Event. Das ist Convention im CP-Ökosystem und sorgt dafür, dass Ihre Component in Replicators, Repeater-Sets und Bard-Bards funktioniert.
Config als Live-Prop
Die config-Prop enthält alles, was Redakteure im Blueprint pro Feld gesetzt haben. Über computed-Werte können Sie darauf reagieren, ohne erneut zu rendern. Im Beispiel ist das die Token-Liste, die pro Verwendung verschieden sein kann.
CP-UI-Komponenten wiederverwenden
Für produktionsnahe Fieldtypes lohnt es sich, Statamic-eigene UI-Komponenten aus @statamic/cms/ui einzubinden, etwa SButton oder SInput. So bleibt das visuelle Verhalten konsistent mit dem restlichen CP.
<template>
<div class="grid grid-cols-4 gap-2">
<button
v-for="token in tokens"
:key="token"
type="button"
class="flex items-center gap-2 rounded border p-2"
:class="token === value
? 'border-blue-500 ring-1 ring-blue-500'
: 'border-neutral-200 hover:border-neutral-400'"
@click="select(token)"
>
<span
class="h-5 w-5 rounded-full border border-black/10"
:style="{ background: `var(--color-${token})` }"
/>
<span class="text-sm">{{ token }}</span>
</button>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
value: { type: String, default: null },
config: { type: Object, required: true },
})
const emit = defineEmits(['input'])
const tokens = computed(() => props.config.tokens ?? [])
function select(token) {
emit('input', token === props.value && props.config.allow_empty
? null
: token)
}
</script>
Schritt 4
Registrierung und Vite-Hook
Auto-Discovery in app/Fieldtypes
PHP-Klassen unter App\Fieldtypes werden von Statamic automatisch geladen, Sie müssen nichts manuell registrieren. Der Klassen-Name ColorToken ergibt den Handle color_token. Mehr Magie ist nicht nötig.
Vue-Component im CP einbinden
In resources/js/cp.js registrieren Sie die Component mit Statamic.$components.register("color_token-fieldtype", ColorTokenFieldtype). Der Handle plus Suffix -fieldtype ist Konvention. Vite übernimmt das beim nächsten cp:dev sofort.
Blueprint-Integration
In einem Blueprint nutzen Sie type: color_token und übergeben die tokens-Liste. Redakteure sehen sofort den Picker. Falsche Werte werden serverseitig zurückgewiesen, das Frontend bekommt sauber augmentierte Daten.
Eigenes CP-Modul gewünscht?
Wir bauen Fieldtypes, Widgets und ganze Control-Panel-Bereiche als wiederverwendbare Addons.
Blueprint
Im Blueprint verwenden
Pro Stelle eigene Token-Whitelist
Beachten Sie, dass die tokens-Liste pro Verwendung anders sein darf. In einem Blog-Post-Blueprint erlauben Sie alle Brand-Farben, in einem Footer-Block vielleicht nur zwei. Die Whitelist gehört bewusst nicht in die PHP-Klasse, sondern in den Blueprint.
Listing-Spalten und Sortierung
Setzen Sie listable: true, und der ausgewählte Token wird in der Entry-Listing-Tabelle als Spalte sichtbar. Für Sortierung und Filterung müssen Sie in der PHP-Klasse zusätzlich preProcessIndex implementieren, das ist ein Folge-Schritt.
tabs:
main:
sections:
-
display: General
fields:
-
handle: accent_token
field:
type: color_token
display: 'Accent color'
instructions: 'Wählen Sie eine Brand-Farbe für diesen Eintrag.'
tokens:
- accent
- surface-strong
- ink-900
allow_empty: true
listable: true
width: 50
Testen und Veröffentlichen
Vom App-Code zum Addon
Feature-Tests gegen die PHP-Klasse
Den process()-Pfad können Sie ohne Browser unit-testen. Instanziieren Sie die Fieldtype-Klasse mit einer simulierten Config, rufen Sie process("accent") sowie process("hex-ffffff") auf und prüfen Sie, dass der zweite Fall null liefert. Das ist die Sicherheits-Garantie auf Code-Ebene.
CP-Tests mit Dusk oder Playwright
Für die Vue-Component lohnt sich ein E2E-Test, der das CP öffnet, eine Entry erstellt und den Picker bedient. Bei MARCODES verwenden wir Laravel Dusk für Statamic-CP-Pfade, weil es sich nahtlos mit der Auth-Session integriert.
Extraktion in ein Composer-Package
Wenn der Fieldtype in mehreren Projekten eingesetzt werden soll, extrahiert ihn in ein Addon. Dazu gehören ein ServiceProvider, der die Klasse registriert, ein eigenes Vite-Setup und ein composer.json mit dem statamic-addon Type. Das ist ein eigener Wochen-Sprint, nicht Teil der ersten 30 Minuten.
Artikel teilen