Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

Overview: Creating a Custom Field

12/04/2025, by Ivan
This tutorial was originally published on Web Wash. However, Berdir asked if I could post the tutorial here, so here it is.

The module in Drupal 7 allows you to store code samples/snippets in a field. It comes with a customizable field called “Snippets field” and displays three form elements: description, source code, and syntax highlighting mode (which programming language).

But now it's time to upgrade the module to Drupal 8.

In this tutorial, I’ll show you how I created a “basic” custom field in Drupal 8. I won’t go into detail about PSR–4, annotations, or plugins, or else this tutorial will become enormous.

Instead, I’ll include links to other sites that explain these concepts further.

That said, if you’re looking for detailed documentation on the Field API in Drupal 8, check out the following series:

 

In Drupal 8, fields are not implemented using hooks like in Drupal 7. Instead, they are created using the new Drupal 8 plugin API. This means that instead of implementing hooks, we define a class for the widget, formatter, and field item. Most Drupal 7 hooks such as hook_field_schema, hook_field_is_empty, etc., are now methods in classes.

Step 1: Implement the Field Item

The first thing we need to do is define a field item class named SnippetsItem, which extends FieldItemBase.

1. In Drupal 8, classes are loaded using PSR-4.

So, to define the SnippetsItem class, we need to create a file SnippetsItem.php and place it in "module" /src/Plugin/Field/FieldType/SnippetsItem.php

/**
 * @file
 * Contains \Drupal\snippets\Plugin\Field\FieldType\SnippetsItem.
 */

namespace Drupal\snippets\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;

Then, in the file we add the namespace Drupal\snippets\Plugin\Field\FieldType and three use statements: Drupal\Core\Field\FieldItemBase, Drupal\Core\Field\FieldStorageDefinitionInterface, and Drupal\Core\TypedData\DataDefinition.

2. Now we need to define the actual field details such as field ID, label, default widget, formatter, etc. This is equivalent to implementing hook_field_info in Drupal 7.

In Drupal 8, many, if not all, informational hooks have been replaced with annotations.

/**
 * Plugin implementation of the 'snippets' field type.
 *
 * @FieldType(
 *   id = "snippets_code",
 *   label = @Translation("Snippets field"),
 *   description = @Translation("This field stores code snippets in the database."),
 *   default_widget = "snippets_default",
 *   default_formatter = "snippets_default"
 * )
 */
class SnippetsItem extends FieldItemBase { }

So instead of implementing hook_field_info, we define the field as an annotation inside the comment above the class.

The annotation attributes are self-explanatory. Just make sure the default_widget and default_formatter refer to the widget and formatter annotation IDs, not the class names.

If you want to learn more about annotations, visit the Plugin Annotations Documentation page on drupal.org.

3. Now that we have the field item class, we need to define a few methods. First, we'll look at schema()

In Drupal 7, when creating a custom field you define schema using hook_field_schema. In Drupal 8, we define schema by adding the schema() method in the SnippetsItem class.

The Schema API documentation describes the schema array structure and possible values.
/**
 * {@inheritdoc}
 */
public static function schema(FieldStorageDefinitionInterface $field) {
  return array(
    'columns' => array(
      'source_description' => array(
        'type' => 'varchar',
        'length' => 256,
        'not null' => FALSE,
      ),
      'source_code' => array(
        'type' => 'text',
        'size' => 'big',
        'not null' => FALSE,
      ),
      'source_lang' => array(
        'type' => 'varchar',
        'length' => 256,
        'not null' => FALSE,
      ),
    ),
  );
}

4. Now we need to add the isEmpty() method to define what constitutes an empty field item. This method is similar to hook_field_is_empty in Drupal 7.

/**
 * {@inheritdoc}
 */
public function isEmpty() {
  $value = $this->get('source_code')->getValue();
  return $value === NULL || $value === '';
}

5. The last method we add to the class is propertyDefinitions().

/**
 * {@inheritdoc}
 */
static $propertyDefinitions;

/**
 * {@inheritdoc}
 */
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['source_description'] = DataDefinition::create('string')
      ->setLabel(t('Snippet description'));

    $properties['source_code'] = DataDefinition::create('string')
      ->setLabel(t('Snippet code'));

    $properties['source_lang'] = DataDefinition::create('string')
      ->setLabel(t('Programming Language'))
      ->setDescription(t('Snippet code language'));

    return $properties;
}

This method is used to define the type of data that exists in the field values. The "Snippets field" only has three values: description, code, and language. So I just added these values to the method as strings.

To learn more about this, go to the How the Entity API Implements the Typed Data API documentation on drupal.org.

Click here to see the full file. Note: it needs to be updated to the PSR-4 specification, see https://www.drupal.org/node/2128865 for more info.

This tutorial was originally published on Web Wash. However, Berdir asked if I could post the tutorial here, so here it is.

The module in Drupal 7 allows you to store code samples/snippets in a field. It comes with a customizable field called “Snippets field” and displays three form elements: description, source code, and syntax highlighting mode (which programming language).

But now it's time to upgrade the module to Drupal 8.

In this tutorial, I’ll show you how I created a “basic” custom field in Drupal 8. I won’t go into detail about PSR–4, annotations, or plugins, or else this tutorial will become enormous.

Instead, I’ll include links to other sites that explain these concepts further.

That said, if you’re looking for detailed documentation on the Field API in Drupal 8, check out the following series:

 

In Drupal 8, fields are not implemented using hooks like in Drupal 7. Instead, they are created using the new Drupal 8 plugin API. This means that instead of implementing hooks, we define a class for the widget, formatter, and field item. Most Drupal 7 hooks such as hook_field_schema, hook_field_is_empty, etc., are now methods in classes.

Step 1: Implement the Field Item

Step 2: Implement the Field Widget

Now that we have defined the field item, let's create the field widget. We need to create a class named SnippetsDefaultWidget that extends the WidgetBase class.

1. So, create the file SnippetsDefaultWidget.php and add it to "module" /src/Plugin/Field/FieldWidget/SnippetsDefaultWidget.php.

/**
 * @file
 * Contains \Drupal\snippets\Plugin\Field\FieldWidget\SnippetsDefaultWidget.
 */

namespace Drupal\snippets\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;

Make sure the file's namespace is Drupal\snippets\Plugin\Field\FieldWidget and add the following three use statements: Drupal\Core\Field\FieldItemListInterface, Drupal\Core\Field\WidgetBase, and Drupal\Core\Form\FormStateInterface.

2. Next, we need to define the widget using an annotation. This is equivalent to using hook_field_widget_info in Drupal 7.

/**
 * Plugin implementation of the 'snippets_default' widget.
 *
 * @FieldWidget(
 *   id = "snippets_default",
 *   label = @Translation("Snippets default"),
 *   field_types = {
 *     "snippets_code"
 *   }
 * )
 */
class SnippetsDefaultWidget extends WidgetBase { }

Just as a note, make sure the field_types attribute in the annotation refers to the field type using its ID. For this module, that’s snippets_code, because we added id = "snippets_code" in the @FieldType annotation.

3. And finally, we need to define the actual widget form. We do this by adding the formElement() method to the SnippetsDefaultWidget class. This method is similar to hook_field_widget_form in Drupal 7.

/**
 * {@inheritdoc}
 */
public function formElement(FieldItemListInterface $items, $delta, array $element, array & $form, FormStateInterface $form_state) {

  $element['source_description'] = array(
        '#title' => $this->t('Description'),
        '#type' => 'textfield',
        '#default_value' => isset($items[$delta]->source_description) ? $items[$delta]->source_description : NULL,
      );
  $element['source_code'] = array(
        '#title' => $this->t('Code'),
        '#type' => 'textarea',
        '#default_value' => isset($items[$delta]->source_code) ? $items[$delta]->source_code : NULL,
      );
  $element['source_lang'] = array(
        '#title' => $this->t('Source language'),
        '#type' => 'textfield',
        '#default_value' => isset($items[$delta]->source_lang) ? $items[$delta]->source_lang : NULL,
      );
  return $element;
}

Click here to see the full file. Note: it needs to be updated to PSR-4 specification, see https://www.drupal.org/node/2128865 for more info.

Step 3: Implement the Field Formatter

The last piece of the puzzle is the field formatter, and we create it by defining a class named SnippetsDefaultFormatter, which extends FormatterBase.

1. Create the file SnippetsDefaultFormatter.php and add it to "module" /src/Plugin/Field/FieldFormatter/SnippetsDefaultFormatter.php.

/**
 * @file
 * Contains \Drupal\snippets\Plugin\field\formatter\SnippetsDefaultFormatter.
 */

namespace Drupal\snippets\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Field\FieldItemListInterface;

Make sure the file's namespace is Drupal\snippets\Plugin\Field\FieldFormatter and add the following use statements: Drupal\Core\Field\FieldItemListInterface and Drupal\Core\Field\FormatterBase.

2. Next, we need to define the formatter as an annotation. Just as we did for the widget and field type, this is equivalent to using hook_field_formatter_info.

/**
 * Plugin implementation of the 'snippets_default' formatter.
 *
 * @FieldFormatter(
 *   id = "snippets_default",
 *   label = @Translation("Snippets default"),
 *   field_types = {
 *     "snippets_code"
 *   }
 * )
 */
class SnippetsDefaultFormatter extends FormatterBase { }

3. Now we just need to add the viewElements() method and define the actual formatting logic for the field. Again, this method is similar to hook_field_formatter_view in Drupal 7.

/**
 * {@inheritdoc}
 */
public function viewElements(FieldItemListInterface $items, $langcode) {
  $elements = array();
  foreach ($items as $delta => $item) {
    // Render output using snippets_default theme.
    $source = array(
      '#theme' => 'snippets_default',
      '#source_description' => $item->source_description,
      '#source_code' => $item->source_code,
    );

    $elements[$delta] = array('#markup' => drupal_render($source));
  }

  return $elements;
}

Note that I use a custom template named snippets_default to render the snippets before they are displayed by the formatter.

The reason for this is that I didn’t want to put a lot of logic or HTML code inside the viewElements() method.

Click here to see the full file. Note: it needs to be updated to the PSR-4 specification, see https://www.drupal.org/node/2128865 for more info.

Conclusion

As mentioned earlier, the biggest change in Drupal 8 is that fields are created using the plugin API instead of hooks. Once you understand that, the concept of creating a field is very similar to Drupal 7. Many methods in Drupal 8 correspond to hooks in Drupal 7.

If you want to test the code snippets, download the 8.x-dev release and give it a try.

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.