Build a Fieldtype
Fieldtypes determine the user interface and storage format for your fields. Statamic includes 40+ fieldtypes to help you tailor the perfect intuitive experience for your authors, but there's always room for one more.
Creating#
Fieldtypes have a PHP component and JS component. You can use a command to generate both pieces:
php please make:fieldtype Uppercase
app/
Fieldtypes/
Uppercase.php
resources/
js/
components/
fieldtypes/
Uppercase.vue
If you haven't already set up Vite for the Control Panel, the command will do it for you.
Registering#
Statamic will automatically register any fieldtype classes in the App\Fieldtypes namespace.
However, if you wish to store them elsewhere, you'll need to manually register it by calling the static register method on your fieldtype's class:
public function boot()
{
\App\SomewhereElse\Uppercase::register();
}
PHP Class#
The PHP class can be very barebones. At the most basic level, it just needs to exist in order to let Statamic know about it.
<?php
namespace App\Fieldtypes;
use Statamic\Fields\Fieldtype;
class Uppercase extends Fieldtype
{
//
}
Of course, you may add functionality to it, outlined below.
Icon#
You can either specify the name of icon included in Statamic or an SVG string containing a custom icon (be sure to use fill="currentColor") via the $icon property.
Alternatively, you may return an SVG string from the icon() method:
<?php
class Uppercase extends Fieldtype
{
protected $icon = 'tags';
// or
protected $icon = '<svg> ... </svg>';
// or
function icon()
{
return file_get_contents(resource_path('svg/left_shark.svg'));
}
}
Categories#
When using the blueprint builder inside the Control Panel, your fieldtype will be listed under the special category by default.
To move your fieldtype into a different category, define the $categories property on your class:
<?php
class Uppercase extends Fieldtype
{
public $categories = ['text'];
}
You can select from any of the keys available in the FieldtypeSelector:
textcontrolsmedianumberrelationshipstructuredspecial
Keywords#
You may specify keywords to be used when searching in the fieldtype selector.
For example: if you search for "rich text" or "gutenberg", the Bard fieldtype will be returned as a result.
<?php
class CustomFieldtype extends Fieldtype
{
protected $keywords = ['file', 'files', 'image', 'images', 'video', 'videos', 'audio', 'upload'];
}
Config fields#
You can make your fieldtype configurable with configuration fields. These fields are defined by adding a configFieldItems() method on your PHP class that returns an array of fields.
protected function configFieldItems(): array
{
return [
'mode' => [
'display' => 'Mode',
'instructions' => 'Choose which mode you want to use',
'type' => 'select',
'default' => 'regular',
'options' => [
'regular' => __('Regular'),
'enhanced' => __('Enhanced'),
],
'width' => 50
],
'secret_agent_features' => [
'display' => 'Enable super secret agent features',
'instructions' => 'Can you even handle these features?',
'type' => 'toggle',
'default' => false,
'width' => 50
],
];
}
The configuration values can be accessed in the Vue component using the config property.
return this.config.mode; // regular
Options#
| Key | Definition |
|---|---|
| display | The field's display label |
| instructions | Text shown underneath the display label. Supports Markdown. |
| type | Name of the fieldtype used to manage the config option. |
| default | An optional default value. |
| width | The field's width. |
| other | Some fieldtypes have additional configuration options available. |
A little code diving will reveal all the possible config options for each field type. Look for the configFieldItems() method in each class here: https://github.com/statamic/cms/tree/6.x/src/Fieldtypes
Vue Component#
The Vue component is responsible for the view and data binding. It's what your user will be interacting with.
The make:fieldtype command would have generated a Vue component into resources/js/components/fieldtypes/Uppercase.vue.
You'll need to register this Vue component in your JS entry file (resources/js/cp.js):
import UppercaseFieldtype from './components/fieldtypes/Uppercase.vue';
Statamic.booting(() => {
// Should be named [snake_case_handle]-fieldtype
Statamic.$components.register('uppercase-fieldtype', UppercaseFieldtype);
});
Your component should use our Fieldtype composable for defining props & emits, updating the field value and accessing meta.
<script setup>
import { Fieldtype } from '@statamic/cms';
const emit = defineEmits(Fieldtype.emits);
const props = defineProps(Fieldtype.props);
const { expose, ... } = Fieldtype.use(emit, props);
defineExpose(expose);
</script>
<template>
<!-- -->
</template>
Other than that, your component can do whatever you like!
Example#
For this example we will create an input field with a button to make the text uppercase:
<script setup>
import { Fieldtype } from '@statamic/cms';
import { Input, Button } from '@statamic/cms/ui';
const emit = defineEmits(Fieldtype.emits);
const props = defineProps(Fieldtype.props);
const { expose, update } = Fieldtype.use(emit, props);
defineExpose(expose);
function makeItUppercase() {
update(props.value.toUpperCase());
}
</script>
<template>
<div>
<Input :model-value="value" @update:model-value="update" />
<Button @click="makeItUppercase">Make it upper case!</Button>
</div>
</template>
What's happening?#
- The
Fieldtypecomposable is providing theemitsandpropswe need to define, as well as theexpose, updateandupdateDebouncedmethods. - When you type into the text field, an
updatemethod is called which emits an event. Statamic listens to that event and updates thevalueprop.
Those are the two requirements satisfied. β
In addition to that, when the button is clicked, we're converting the string to uppercase and calling update in our function.
Accessing other fields#
If you find yourself needing to access other form field values, configs, etc., you can reach into the publish form store from within your Vue component:
import { injectPublishContext } from '@statamic/cms/ui';
const { values } = injectPublishContext();
// Do what you need to with values
console.log(values.value.title)
Processing#
You may need to modify the data going to and from the browser.
- The
preProcessmethod allows you to modify the original value into what the Vue component requires. - The
processmethod does the opposite. It takes the Vue component's value and allows you to modify it for what gets saved.
For example, the YAML fieldtype stores its value in content as an array but the field needs it as a string in order for it to be editable:
public function preProcess($value)
{
return YAML::dump($value); // dump a yaml string from an array
}
In the other direction, it takes the YAML string and needs to convert it back to an array when saving:
public function process($value)
{
return YAML::parse($value); // parses a yaml string into an array
}
(These snippets are simplified for example purposes.)
Meta Data#
Fieldtypes can preload additional "meta" data from PHP into JavaScript. This can be anything you want, from settings to eager loaded data.
public function preload()
{
return ['foo' => 'bar'];
}
This can be accessed in the Vue component using the meta prop.
return props.meta; // { foo: bar }
If you have a need to update this meta data on the JavaScript side, use the updateMeta method. This will persist the value back to Vuex store and communicate the update to the appropriate places.
const props = defineProps(Fieldtype.props);
const { updateMeta } = Fieldtype.use(emit, props);
updateMeta({ foo: 'baz' });
props.meta; // { foo: 'baz' }
Example use cases#
Here are some reasons why you might want to use this feature:
- The assets and relationship fieldtypes only store IDs, so they will fetch item data using AJAX requests. If you have many of these fields in one form, you'd have a bunch of AJAX requests fire off when the page loads. Preload the item data to avoid the initial AJAX requests.
- Grid, Bard, and Replicator fields all preload values for what a new row/set contains, plus the recursive meta values of any nested fields.
Replicator Preview#
When Replicator (or Bard) sets are collapsed, Statamic will display a preview of the values within it.
By default, Statamic will do its best to display your fields value. However, if you have a value more complex than a simple string or array, you may want to customize it.
You may customize the preview text by calling defineReplicatorPreview from your Vue component. For example:
const { defineReplicatorPreview } = Fieldtype.use(emit, props);
defineReplicatorPreview(() => props.value.join('+'));
This does support returning an HTML string so you could display image tags for a thumbnail, etc. Just be aware of the limited space.
Index fieldtypes#
In listings (collection indexes in the Control Panel, for example), string values will be displayed as a truncated string and arrays will be displayed as JSON.
You can adjust the value before it gets sent to the listing with the preProcessIndex method:
public function preProcessIndex($value)
{
return str_repeat('*', strlen($value));
}
If you need extra control or functionality, fieldtypes may have an additional "index" Vue component.
import UppercaseIndexFieldtype from './UppercaseIndexFieldtype.vue';
// Should be named [snake_case_handle]-fieldtype-index
Statamic.$components.register('toggle_password-fieldtype-index', UppercaseIndexFieldtype);
<script setup>
import { IndexFieldtype } from '@statamic/cms';
const props = defineProps(IndexFieldtype.props);
const numberOfUppercaseCharacters = computed(() => {
return [...props.value].filter(char => char >= 'A' && char <= 'Z').length;
});
</script>
<template>
<div>String contains {{ numberOfUppercaseCharacters }} uppercase characters.</div>
</template>
The IndexFieldtype composable will provide you with a value prop so you can display it however you'd like. Continuing our example above, we will calculate how many characters of the given string are uppercase.
Augmentation#
By default, a fieldtype will not perform any augmentation. It will just return the value as-is.
You can customize how it gets augmented with an augment method:
public function augment($value)
{
return strtoupper($value);
}
Adding config fields to existing fieldtypes#
Sometimes you may want to add a config field to another fieldtype rather than creating a completely new one.
You can do this using the appendConfigField or appendConfigFields methods on the respective fieldtype.
use Statamic\Fieldtypes\Text;
// One field...
Text::appendConfigField('group', [
'type' => 'text',
'display' => 'Group',
]);
// Multiple fields...
Text::appendConfigFields([
'group' => ['type' => 'text', 'display' => '...',],
'another' => ['type' => 'text', 'display' => '...',],
]);
You can also append a config field to all fieldtypes via the Fieldtype class:
use Statamic\Fields\Fieldtype;
Fieldtype::appendConfigField('group', [
'type' => 'text',
'display' => 'A new group',
]);