Zum Inhalt springen
Abstrakte Formen aus Mattglas: ein Raster aus quadratischen Fassungen, in das ein sechseckiges Modul geschoben wird

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.

6 min Lesezeit Statamic

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.

terminal bash
# 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.

app/Fieldtypes/ColorToken.php php
<?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.

resources/js/components/fieldtypes/ColorTokenFieldtype.vue vue
<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>
Auf einer Platine setzt sich der neue orange Chip in seinen freien Sockel, alles andere ist schon verbaut.

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.

Projekt anfragen

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.

resources/blueprints/collections/pages/page.yaml yaml
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.

Custom-CP für Ihr Team? Wir bauen mit

Workshops, Audit oder ein konkretes Addon, direkt im Shop.

Zum Shop