Creating a Custom Field Formatter Plugin in Drupal
Creating a custom Field Formatter in Drupal is a great way to control exactly how your data is displayed to the end-user. To include a settings form, you’ll need to work with the Plugin API, Form API, and Configuration Schema.
Here is the step-by-step breakdown.
1. Directory Structure
Your plugin must be located in a specific directory within your custom module (e.g., my_module):
src/Plugin/Field/FieldFormatter/MyCustomFormatter.php
2. The Formatter Class
Your class should extend FormatterBase. You will need to override four main methods to handle the settings: defaultSettings(), settingsForm(), settingsSummary(), and viewElements().
PHP
namespace Drupal\my_module\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* @FieldFormatter(
* id = "my_custom_formatter",
* label = @Translation("My Custom Formatter"),
* field_types = {
* "string",
* "text"
* }
* )
*/
class MyCustomFormatter extends FormatterBase {
/**
* Define default values for your settings.
*/
public static function defaultSettings() {
return [
'text_color' => '#000000',
] + parent::defaultSettings();
}
/**
* Create the UI form for the settings.
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements['text_color'] = [
'#type' => 'textfield',
'#title' => $this->t('Text Color'),
'#default_value' => $this->getSetting('text_color'),
'#description' => $this->t('Enter a hex code (e.g., #FF0000).'),
];
return $elements;
}
/**
* Display a summary of the settings in the Manage Display UI.
*/
public function settingsSummary() {
$summary = [];
$summary[] = $this->t('Color: @color', ['@color' => $this->getSetting('text_color')]);
return $summary;
}
/**
* Render the field content.
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$color = $this->getSetting('text_color');
foreach ($items as $delta => $item) {
$elements[$delta] = [
'#type' => 'inline_template',
'#template' => '<span style="color: {{ color }}">{{ value }}</span>',
'#context' => [
'color' => $color,
'value' => $item->value,
],
];
}
return $elements;
}
}
3. Configuration Schema
To make your settings "translatable" and "deployable," Drupal needs to know the data types of your settings. Create a schema file here:
config/schema/my_module.schema.yml
YAML
# The key follows the pattern: field.formatter.settings.[PLUGIN_ID]
field.formatter.settings.my_custom_formatter:
type: mapping
label: 'My Custom Formatter settings'
mapping:
text_color:
type: string
label: 'Text Color'
4. Key Implementation Details
Method | Purpose |
Annotation | Defines which field types (e.g., |
| Ensures the plugin has fallback values before a user saves the form. |
| Build this using standard Drupal Form API elements. |
| This returns a render array. You can use |
5. Clear Caches
Once you have created these files, you must clear the Drupal cache for the plugin to be discovered. Navigate to your Content Type's Manage Display tab, and you should see "My Custom Formatter" in the dropdown for the compatible fields.
Next, here is how to attach a CSS library to this formatter so you don't have to use inline styles.
To attach a CSS library, you need to define the library in your module's .libraries.yml file and then "attach" it within the viewElements() method of your plugin. This is much cleaner than using inline styles.
1. Define the Library
Create or edit my_module.libraries.yml in your module's root directory:
YAML
formatter-styles:
version: 1.x
css:
theme:
css/my-formatter.css: {}
2. Create the CSS File
Place your CSS in css/my-formatter.css:
CSS
.my-custom-text {
font-weight: bold;
padding: 5px;
border-radius: 4px;
display: inline-block;
}
3. Attach the Library in the Formatter
Update your viewElements() method in MyCustomFormatter.php. You can attach the library to the main render array so it only loads when that specific field is displayed.
PHP
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
$color = $this->getSetting('text_color');
foreach ($items as $delta => $item) {
$elements[$delta] = [
'#type' => 'html_tag',
'#tag' => 'span',
'#attributes' => [
'style' => 'color: ' . $color . ';',
'class' => ['my-custom-text'],
],
'#value' => $item->value,
];
}
// Attach the library to the entire element array
$elements['#attached']['library'][] = 'my_module/formatter-styles';
return $elements;
}
Why use #attached?
Using the #attached property is the Drupal-standard way to manage assets. It ensures:
Performance: The CSS is only loaded if the field is actually visible on the page.
Aggregation: Drupal can aggregate this CSS file with others to reduce HTTP requests.
Cacheability: The library dependency is tracked by Drupal's render cache.
Pro-Tip: Adding External Assets
If you wanted to use a Google Font or an external color picker library instead of a text field, you would define the external URL in your libraries.yml and attach it the exact same way.
Next, we'll look at how to add a validation step to the settingsForm to ensure users enter a valid Hex color code.
Adding validation is a crucial step to prevent broken styles or invalid configuration from being saved. In Drupal, you add validation to a plugin form by using the #element_validate property on specific form elements.
1. Update settingsForm
Modify your settingsForm method to include the validation callback. Note that since this is a plugin, the callback is usually a method within the same class.
PHP
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements['text_color'] = [
'#type' => 'textfield',
'#title' => $this->t('Text Color'),
'#default_value' => $this->getSetting('text_color'),
'#description' => $this->t('Enter a hex code (e.g., #FF0000).'),
'#element_validate' => [
[$this, 'validateHexColor'],
],
];
return $elements;
}
2. Create the Validation Method
Add this method to your MyCustomFormatter class. It uses a regular expression to check if the input matches a standard hex color format.
PHP
/**
* Validation callback for the text_color setting.
*/
public function validateHexColor($element, FormStateInterface $form_state) {
$value = $element['#value'];
// Hex color regex: Starts with #, then 3 or 6 hex characters.
if (!preg_php_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $value)) {
$form_state->setError($element, $this->t('The color must be a valid hex code (e.g., #FFFFFF).'));
}
}
How the Data Flows
It's helpful to visualize how Drupal processes these settings from the UI to the database:
UI (
settingsForm): The user enters a value in the "Manage Display" tab.Validation (
#element_validate): Drupal checks the input before saving.Schema (
.schema.yml): Drupal ensures the data type matches the expected configuration structure.Storage: The setting is saved into the entity's display configuration (usually in the
core.entity_view_display.*config files).
Alternative: Using the HTML5 Color Picker
If you want to avoid manual hex entry altogether, you can change the #type from textfield to color.
PHP
$elements['text_color'] = [
'#type' => 'color', // This renders a native browser color picker
'#title' => $this->t('Text Color'),
'#default_value' => $this->getSetting('text_color'),
];
Note: Using #type' => 'color' handles its own internal validation, so you wouldn't need the custom regex method!
Recent content
-
2 hours 34 minutes ago
-
1 week 1 day ago
-
1 week 1 day ago
-
1 week 1 day ago
-
1 week 1 day ago
-
1 week 6 days ago
-
2 weeks 1 day ago
-
2 weeks 1 day ago
-
2 weeks 1 day ago
-
2 weeks 2 days ago