There's a lot to like about Drupal 8's entity API. It allows developers total control over how entities are managed both in the database and in the application. Together with the Drupal console project, which allows you to generate entity models with scaffolding, and development gets quicker, cleaner and easier.

But there are a few things that the console generation leaves out. One of the most important is providing a hook to add the new content entity to the theme registry, and providing a definition of the default template. Adding the code below to your custom content module will take care of this.


The example code in this article follows these assumptions:

  • The custom content entity id used in this example is called "artwork"
  • The name of the module that contains the entity definition, and the namespace, is "artwork_provider"
  • We want our entity available for other theme hooks
  • We want a base twig theme template for our entity
  • We want to extend theming options with a theme suggestion hook

The Theme registry hook

You need to first implement the hook for your custom content entity which will be entered into the theme registry. This not only allows you to create a theme template your entity, but it is required in order for your entity to be recognized as themeable in other structures like views and blocks, and to make it available to other hooks.

/** @file modules/custom/artwork_provider/artwork_provider.module
 * Provides a theme definition for custom content entity
 * {@inheritdoc}
function artwork_provider_theme($existing, $type, $theme, $path) {
  $theme = [
     'artwork' => [                            // the entity id
      'path' => $path . '/templates',          // template directory in module or theme
      'template'  => 'artwork',                // defines base template as /artwork.html.twig
      'render element' => 'elements',          // Theme API call to define the container of the entity in the render array
                                               // The entity will be prefixed with a hash sign as ['elements']['#artwork']
  return $theme;

The theme template data provider

This provides a template preprocess method to expose the content entity's data to twig, implementing hook_preprocess_HOOK()

/** @file modules/custom/artwork_provider/templates/artwork.html.twig
 * Prepares variables for templates.
 * implements hook_preprocess_HOOK()
function template_preprocess_artwork(&$variables) {
  // Helpful $content variable for templates.
  $variables['content'] = [];
  foreach (\Drupal\Core\Render\Element::children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  /** @var ArtworkInterface $entity */
  $entity = $variables['elements']['#artwork'];
  //provide the label
  $variables['label'] = $entity->label();
  //provide the alias
  $variables['url'] = $entity->toUrl()->toString();

Now when rendering the artwork entity, the content variable contains the entity's field data. Here's the default theme template

{# ** @file modules/custom/artwork_provider/templates/artwork.html.twig
 * Default theme implementation to present Artwork data.
 * Available variables:
 * - content: A list of content items. Use 'content' to print all content, or
 *          content.field_name to access public fields
 * - attributes: HTML attributes for the container element.
 * @see template_preprocess_artwork()
 * @ingroup themeable
<div{{ attributes.addClass('artwork') }}>
  {% if content %}
    {{- content -}}
  {% endif %}

This can now be moved into your theme using the standard workflow of copying the template.

Here's an example of how the formatted theme template may look

{# ** @file themes/custom/mytheme/templates/artwork.html.twig #}

{% if content %}
<div{{ attributes.addClass('artwork') }}>
  <a href="{{ url }}">
    {{ label }}
      {{ content.field_image }}
      {{ content.body }}
{% endif %}

Adding theme suggestions

Since the entity is now in the theme registry, it is available for other hooks.

The following could provide suggestions for artwork--full.html.twig, artwork--teaser.html.twig, etc, assuming you have defined and associated these display modes with your entity

/** @file modules/custom/artwork_provider/artwork_provider.module */

function artwork_provider_theme_suggestions_artwork_alter( array &$suggestions, array $vars, $hook ) {
    /** @var \Drupal\artwork_provider\Entity\Artwork $artwork */
    if ( $artwork = $vars['elements']['#artwork'] ) {
       if (isset($vars['view_mode'])) {
            $suggestions[] = 'artwork__' . $vars['view_mode'];
Last Updated October 14th, 2019