Need some help with your project? Contact me

Creating a custom Views field in Drupal 8

In this article I am going to show you how to create a custom Views field in Drupal 8. At the end of this tutorial, you will be able to add a new field to any node based View which will flag (by displaying a specific message) the nodes of a particular type (configurable in the field configuration). Although I will use nodes, you can use this example to create custom fields for other entities as well.

So let's get started by creating a small module called d8views (which you can also find in this repository):

name: Drupal 8 Views Demo
description: 'Demo module that illustrates working with the Drupal 8 Views API'
type: module
core: 8.x

In Drupal 7, whenever we want to create a custom field, filter, relationship, etc for Views, we need to implement hook_views_api() and declare the version of Views we are using. That is no longer necessary in Drupal 8. What we do now is create a file called in the root of our module and implement the views related hooks there.

To create a custom field for the node entity, we need to implement hook_views_data_alter():

 * Implements hook_views_data_alter().
function d8views_views_data_alter(array &$data) {
  $data['node']['node_type_flagger'] = array(
    'title' => t('Node type flagger'),
    'field' => array(
      'title' => t('Node type flagger'),
      'help' => t('Flags a specific node type.'),
      'id' => 'node_type_flagger',

In this implementation we extend the node table definition by adding a new field called node_type_flagger. Although there are many more options you can specify here, these will be enough for our purpose. The most important thing to remember is the id key (under field) which marks the id of the views plugin that will be used to handle this field. In Drupal 7 we have instead a handler key in which we specify the class name.

In Drupal 8 we have something called plugins and many things have now been converted to plugins, including views handlers. So let's define ours inside the src/Plugin/views/field folder of our module:



 * @file
 * Definition of Drupal\d8views\Plugin\views\field\NodeTypeFlagger

namespace Drupal\d8views\Plugin\views\field;

use Drupal\Core\Form\FormStateInterface;
use Drupal\node\Entity\NodeType;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;

 * Field handler to flag the node type.
 * @ingroup views_field_handlers
 * @ViewsField("node_type_flagger")
class NodeTypeFlagger extends FieldPluginBase {

   * @{inheritdoc}
  public function query() {
    // Leave empty to avoid a query on this field.

   * Define the available options
   * @return array
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['node_type'] = array('default' => 'article');

    return $options;

   * Provide the options form.
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    $types = NodeType::loadMultiple();
    $options = [];
    foreach ($types as $key => $type) {
      $options[$key] = $type->label();
    $form['node_type'] = array(
      '#title' => $this->t('Which node type should be flagged?'),
      '#type' => 'select',
      '#default_value' => $this->options['node_type'],
      '#options' => $options,

    parent::buildOptionsForm($form, $form_state);

   * @{inheritdoc}
  public function render(ResultRow $values) {
    $node = $values->_entity;
    if ($node->bundle() == $this->options['node_type']) {
      return $this->t('Hey, I\'m of the type: @type', array('@type' => $this->options['node_type']));
    else {
      return $this->t('Hey, I\'m something else.');

We are defining our NodeTypeFlagger class that extends FieldPluginBase (which is the base plugin abstract class for the views field many plugins extend from). Just above the class declaration we use the @ViewsField annotation to specify the id of this plugin (the same one we declared in the hook_views_data_alter() implementation). We also use the @ingroup annotation to mark that this is a views field handler.

In our example class, we have 4 methods (all overriding the parent class ones).


First, we override the query() method but leave it empty. This is so that views does not try to include this field in the regular node table query (since the field is not backed by a table column).


The second method is the defineOptions() method through which we specify what configuration options we need for this field. In our case one is enough: we need to specify the node type which we want flagged in the Views results. We set a sensible default as the article node type.


The third method, buildOptionsForm() is responsible for creating the form for the configuration options we declared earlier. In our case we just have a select list with which we can choose from the existing node types.


Lastly, the render() method which is the most important and which deals with output. We use it to actually render the content of the field for each result. Here is where we perform some business logic based on the currently set node type option and flag with a message whether or not the current result is in fact of the type specified in the configuration.

The $resultRow object is an instance of Drupal\views\ResultRow which contains data returned for the current row by Views and the entity object at the base of the query (in our case the node). Based on this information we can perform our logic.

Keep in mind you can use depedency injection to inject all sorts of services into this class and make use of them in your logic. Additionally, you can override various other methods of the parent class in order to further customize your field.


There you have it. A small custom module that demonstrates how to create a custom Views field (plugin). Relationships, filters, sorters and others work in similar way. I will be covering those in later articles. Stay tuned.



First of all, thank you for all the tutorial you have done on Drupal 8.

I followed the guide but I can't figure out where the field should be added. Could you give me more precision about this.

Thank you !

How to add "Node Type Flagger" field to View

  1. Once all files have been created in custom module based on article, clear site cache
  2. Edit a View, add "Node Type Flagger" to view "fields"
  3. When adding "Node Type Flagger" field, select node type in settings of a node type that will be listing in the view output based on other "FILTER CRITERIA".
  4. Review view output. A new column in the view output will be listed called "NODE TYPE FLAGGER".
  5. Each row will have the "Hey, I'm of the type: " or "Hey, I\'m something else." text listed based on the settings for the field.

Content: Node type flagger

I got the field in View but its not getting added, after i check the field in views and click add and configure fields nothing happens. The field adding page doesn't opens.

In case anyone else hits this, it happens if you put the code in a differently named module and forget to change the namespace. This results in an ajax error which causes the settings form to not show.

Hi Danny,

I found your article in a search to solve a simple (maybe) issue, not sure if I can achieve what I want with your code, but I believe it.

I want to display or filter based on comment_status field from node__comment table.

Meanwhile, yesterday I open this issue in Drupal,

( Sorry on my none drupal way of saying things, but my experience on Drupal is less then an year)

Thanks in advance.

Hi Danny,
I am new in drupal 8. I am doing theming in d8.I created a view and also did theming.In that view there is an image filed. I want to to add a class with in .In d7 i rendered views using views pre render.please help me for d8 .

thank's for your tutorial. it's helpfull
i have a problem with the render function because i want disply a list of uri.
so i have the html code in the result instead the html list.
thank you

the group property is now mandatory in hook_views_data_alter(), so use fe. Content:

function d8views_views_data_alter(array &amp;$data) {
  $data['node']['node_type_flagger'] = array(
    'title' =&gt; t('Node type flagger'),
    'group' =&gt; t('Content'),
    'field' =&gt; array(
      'title' =&gt; t('Node type flagger'),
      'help' =&gt; t('Flags a specific node type.'),
      'id' =&gt; 'node_type_flagger',



instead of


to get the current entity because your views field might appear in a relation, in which case $values->_entity is actually the entity the view is for and not the entity your views field is for ...

new to drupal 8, in node i created two custom fields.. dates start and end can i call date fields values

it works like a charm. Thanks for writing this post.

How can I filter the view by this custom field? Because usually filter use where statements for filtering, but our custom field don`t store in the table.


Great information! I could create the custom field however I could not use it in Contextual filter. How could I acheive that?

Okay so I spend hours trying to figure out how to get this to work for an entity instead of a node.
Then someone pointed out that node is actually just an entity type.

So all you gotta do is replace node with your entity type name.
My entity was drd_domain so I did:

function d8views_views_data_alter(array &amp;$data) {
  $data['drd_domain']['node_type_flagger'] = array(

Also make sure to change d8views to your own module name and edit the namespace in 2 locations to your own module's namespace, both at the top of NodeTypeFlagger.php

For some reason (NodeType API change), I had to change some code

public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    $types = NodeType::loadMultiple();
    $options = [];
    foreach ($types as $key => $NodeType) {
      $options[$key] = $NodeType->get('name');

Hm, i don't think so.

The label() method is on the EntityInterface so it should still return the title no?


Thanks for the article.

I created the files manually but couldn't get it to work. One problem I believe is the lack of a <?php tag at the start of the file. However the repo version worked fine so it might just be me ......



Hi, thanks for this tutorial. I'm trying to add a field on File entities and don't succeed.
I guess the issue resides in the array declaration $data['file']['myfield'] as if I replace file by node it works. Do ou have an idea?

If your output is based on a dynamic data, don't forget to add cache metadata, eg :

    return [
      '#markup' =&gt; $my_string,
      '#cache' =&gt; ['tags' =&gt; ['node_list',...]],

In the 3rd paragraph - you state "in Drupal 8. What we do now is create a file called in the root of our module and implement the views related hooks there."

I'm confused... since when do modules have roots? As far as I know, there is no such thing as the root of a module... Maybe you meant the root of a certain directory tree?

Hey. It's a pretty common way of saying the root folder of the module.


Add new comment

You must have Javascript enabled to use this form.