Drupal 8 Theming Essential Guide

Drupal 8 will soon become the new norm. Enter the “Drupal 8" query in Google Trends, and you will see how D8 has risen with time.

Compared to the previous version, Drupal 8 has made big improvements. There are so many changes in front-end, the theming system in this version. This means some challenges, and awesome new world to try for Drupal themers. Yet, have you got familiar with it? And the fact is there are not many tutorials about Drupal 8 front-end. Struck is what we sometimes feel.

So in this guide, I'll cover basic Drupal 8 theming, including:

  • What's new with Drupal 8 theming?
  • Theme folder structure.
  • Defining a theme with the .info.yml file
  • Libraries - how to add stylesheets & scripts to the theme.
  • Introducing Stable & Classy
  • Theme engine (introducing Twig).
  • Breakpoint module
  • Lastly, you'll create a drupal 8 theme from scratch by following our step by step tutorial.

And by the time you finish the tutorial, you will have a good understanding of the Drupal 8 theming system, and how to create a new theme in Drupal 8, and also a starter theme you'll be able to use in your future projects.

I’ll list some changes with the theming system of Drupal 8 compared to Drupal 7. You may have got familiar with these in the Drupal 7 version. The bases are still remained, but they have changed in a new form. There may be some points you don’t understand, but don’t worry. I’ll explain them later. Now just take these as bullet points to remember.

  • Your custom theme now goes into a "theme" directory in the root and not in the "/sites/all/themes"
  • .info files now become .info.yml files that use the YAML syntax
  • PHP template engine is replaced by Twig.
  • All theme files are now html.twig instead of .tpl.php.
  • template.php becomes theme_name.theme.
  • Stylesheets (css) and scripts (js) are defined in libraries.
  • Introducing the 2 new base themes - Stable & Classy.
  • CSS coding standards are based upon two very popular conventions SMACSS and BEM.
  • Drupal 8 is responsive out of the box.
  • Breakpoints can be set and used across modules and themes.
  • And much more

You used to place all of themes, modules, and third-party library assets like Font-Awesome, jquery,... at the sites/all/themes directory in Drupal 7. So in the past, if you want to create a custom theme, you would place it in /sites/all/themes/{custom/}.

File structure in Drupal 8 has changed. Now, the core folder contains all the modules and themes that are used in Drupal core, and other custom or contributed modules and themes will live in the /modules, and /themes respectively.

To create a custom theme, you will need to place it at /themes/{custom}.

A folder structure for a simple theme would look like this

  • theme_name
    • css
    • images
    • js

Let’s give you an example of a complete theme folder.

Drupal will scan the theme directory and search for the theme_name.info.yml file to install your theme. Drupal 8 will look at the .info.yml the same way Drupal 7 looks at .info file. D8 has adopted the Symfony YAML (.yml) format. This is also the format of many programming languages. The pros is that YML uses specific standard which is supported by many libraries.

This is a complete info.yml file of our zircon D8 theme.

      name: Zircon
      type: theme
      base theme: classy
      description: 'A flexible, colorable theme with many regions and a responsive, mobile-first layout.'
      package: Core
      core: 8.x
      - zircon/global-styling
        header: Header
        primary_menu: 'Primary menu'
        secondary_menu: 'Secondary menu'
        main_menu: 'Main menu'
        slideshow: Slideshow
        help: Help
        page_top: 'Page top'
        page_bottom: 'Page bottom'
        messages: Messages
        featured: Featured
        breadcrumb: Breadcrumb
        content: Content # the content region is required
        sidebar_first: 'Sidebar first'
        sidebar_second: 'Sidebar second'
        panel_first_1: 'Panel first col 1'
        panel_second_1: 'Panel second col 1'
        panel_second_2: 'Panel second col 2'
        panel_second_3: 'Panel second col 3'
        panel_second_4: 'Panel second col 4'
        footer: Footer

Fields you can find in a *.info.yml file

The following keys are what you might find in a .info.yml file. Note that some are required, some are optional. They will provide meta-data about your theme, and its basic functionality.

  1. name (required)

    This is the name of your theme. The name will show up in the administration/appearance D8 admin
  2. description (optional yet recommended)

    What you want to describe about your theme should be put here. This will also appear in Administration/Appearance
  3. version (optional )

    The version of your theme. It will show behind the theme name.
  4. type (required)

    Let Drupal know the type of extension. E.g. Theme, Module, or Profile.
  5. core (required)

    Show the major version of Drupal core that is supported
  6. base theme (optional yet recommended)

    Indicate what base theme your custom theme will inherit. If not include this field, Drupal will use Stable as your base theme.
  7. region (optional)

    Define the regions of the blocks that can be placed. If you do not declare any regions in the .info.yaml file, Drupal will take the regions enabled by core. Keep in mind that if you define the regions even just one, the default regions are not longer applied. And content region is required to exist if you define regions in the info file.

Adding regions to your theme

A region is basically a section on the page of your theme. You can define as many regions as you wish on your .info.yml file. And the next step you have to update your page.twig file to inform the new regions.

Regions are rendered in the page template file, which will be covered in the next recipe, Twig templates.

You may have asked where can I add stylesheets (CSS) and script (Javascript) to my theme? In D8, the answer is the libraries file.

In Drupal 7, you would include all of your stylesheets & scripts of your theme in the .info file as follows:


      name = zircon
      description = 'A flexible, recolorable theme with many regions and a responsive, mobile-first layout.'
      core = "8.x"
      ; Stylesheets
      stylesheets[all][] = css/style.css
      stylesheets[print][] = css/print.css
      ; Scripts
      scripts[] = js/scripts.js

Now, in Drupal 8 you have to include in both the theme_name.info.yml & theme_name.libraries.yml to enable the stylesheets, and scripts. Here are what they look like.


      name: Zircon
      type: theme
      base theme: classy
      description: 'A flexible, recolorable theme with many regions and a responsive, mobile-first layout.'
      package: Core
      core: 8.x
      - zircon/global-styling


              css/style.css: {}
              css/print.css: { media: print }
              js/scripts.js: {}
          - core/drupal
          - core/jquery

In other words, in D8, if you define a library in the .libraries.yml file, you have to declare it in the .info.yml file respectively. Drupal 8 takes this approach to create the new library file in order to improve website performance. Rather than loading all CSS, JS and other assets, only those that are specified in the library are loaded.

With the example, in the .info file, we define a library called global-styling. Global-styling means that this library will be included on every page. And in the library file, we indicate the css, and js file that will load with the global-styling library. The css and js that loads globally will live in root/theme/css/style.css and root/theme/js/scripts.js respectively.


Libraries have the ability to choose other libraries as dependencies. This is to help Drupal know what is necessary to load.

You notice that we have dependencies: core/jquery. By default, Drupal 8 does not load any scripts. Jquery is not included sidewide like in Drupal 7. So we have to inform to include the Jquery version of Drupal core. And we also define core/drupal dependencies to take advantage of Drupal.behaviors

How to add Libraries to selected pages

What if you only want your libraries to be enabled to specific pages? Let's say you want to add the style to your maintenance page. So the first step is to define the css for it in your .libraries.yml file:

        version: VERSION
            css/maintenance-page.css: {}

After that, instead of attaching it globally in your .info.yml file, we'll have to add some codes to the THEME_NAME.theme in your theme folder (if you don't have one, you need to create one and place it in the root of your theme folder)

Here’s how it would look like:

      function zircon_preprocess_maintenance_page(&$variables) {
        if ($variables['is_front']) {
        $variables['#attached']['library'][] = ’zircon/maintenance_page';

If you have other library with other name, change the correct respective name as shown in the screenshot above.

Remember to clear your cache to see it works.

Base themes are said to be used as a backwards compatibility layer for Drupal 8's core markup, CSS and JS.

The reason is if you don’t use a base theme ( by setting base theme : false in the .info.yml file), Drupal will use the markup in core. If there are changes in core, you need to check the core markup or you run the risk of losing your style. That’s terrible. Meanwhile, if you use base theme, markup will reside in the markup folder of your base themes. Change anything if you want, and never fear risk.

So the use of base themes is to ensure the stability of your theme.

Besides Bartik, Seven, and Stark, which you may have seen in Drupal 7, 2 new base themes are introduced in D8 - Stable & Classy.

The chart below will show you the relationship among the core themes


Core ⇒ Stable

Classy ⇒ Seven

⇒ Bartik

As indicated Seven & Bartik themes will use Classy as its base theme.

When no base theme is informed in the .info.yml file, Stable will automatically become one.

With the coming of Classy, and Stable, this is really helpful as Drupal gives you 2 flavors to choose as base theme.

Stable: minimal markup and very few classes
Classy: provide default markup with sensible classes for styling

If you want to start with some markups and a few defined classes , go with Classy. This base theme provides a solid markup and css combinations built on modern BEM conventions that you will find easier to work with.

If you want to start with more clean theme markups, and want to add your own classes, you can choose Stable. And in fact, it’s the default base theme in D8.

Let’s show the differences in markup of the 2 themes when viewing source-code.


      <div class="region field-name-field-cars field--type-string field--label-above">
        <div class="field__label">Cars</div>
        <div class='field__items'>
          <div class="field__item">BMW</div>
          <div class="field__item">Mercedes</div>
          <div class="field__item">Honda</div>



See how minimal the Stable theme is in comparison with Classy.

Twig is the template engine for PHP. It is a component of the Symfony framework. In D8, Twig becomes the default templating engine (which was PHP engine in Drupal 7). As a result, all of the theme_* functions and PHPTemplate *.tpl.php files are no longer used. The template files now have a new Twig extension:


With Twig, people say that Drupal 8 has moved away from an isolated island. Twig is meant to be more modern,

more secure, easier to use ( No more PHP codes inside your template files. PHP engine requires users to have a rudimentary knowledge of PHP to use. Now non technical users will find Twig much more easier to use) and well-documented.


Twig syntax is similar Django and Jinja templates, and it’s quite simple:

{{ These }} are for printing content, either explicitly or via functions

{% These %} are for executing statements

{# These #} are for comments

Twig Filter

It's possible to let the variable go through a filter before printing it. This can be done using
{{ variable|filter }}.

A complete list of default Twig filters can be found here.

Adding theme regions with Twig

Theme developers don’t want to be limited with the default regions of Drupal 8. So defining regions is an inevitable task for any themers.

The first step is to declare the regions you want to add in the .info.yml file. Say that I want to add header, main content, footer. and sidebar. I will go back to the info file, and add these.

      #Define regions
        header: 'Header'
        content: 'Main Content'
        sidebar: ‘Sidebar’
        footer: 'Footer'

But this is still not complete. It is because Drupal’s standard html template doesn’t know about your custom regions. So the next step is to copy the page.html.twig file from the core templates folder and place it in a folder named templates within your theme (if you don’t have this folder you have to create one)

Now provided that we want to add the sidebar to your page first, here are the codes:

      {% if page.sidebar %}
        {{ page.sidebar }}
      {% endif %}

The above codes creates a conditional to check that the header region has something in it, then prints out the content. This is a good practice to ensure that empty markup will not be printed to the page.

The content section is an exception. It does not need a conditional statement because there will always be something in the content region.

Repeat the codes for other regions of your page.

      <div class="page">
        {% if page.header %}
            {{ page.header}}
        {% endif %}
          {{ page.content }}
        {% if page.sidebar %}
        <div id="sidebar">
           {{ page.sidebar }} 
        {% endif %}


{% if page.header %} <footer> {{ page.header}} </footer> {% endif %} </div>

How do we know what template file that we need to override, how to know which template file that renders a page or a markup when you view source?

To do that, Twig in Drupal 8 has come with a very useful function - Twig Debug.
Here how to enable it.

Under the twig.config section, you need to change debug: false to debug: true (If there is not services.yml here; make a copy of the default.services.yml file and rename it to services.yml). And when inspecting the page, you see that Drupal will print out the source code comments that indicate the template information

Debug variables

You can print a variable, or all variables in the template file with the dump() function. The syntax is really simple.

// Print out all variables on the page.

{{ dump() }}

// Print content of the foo variable.

{{ dump(foo) }}

This is a new cool feature in Drupal 8. Breakpoints are used to separate the resolutions (height & width) of devices so that you will have a responsive design that will change corresponding to each device being used.

To create new Breakpoints, you need to add a [theme-name] or [module-name] .breakpoints.yml in the root folder of your theme or module

We’ll take the bartik.breakpoints.yml to show you an example of the content of the breakpoint file.

        label: mobile
        mediaQuery: '(min-width: 0px)'
        weight: 0
        - 1x
        label: narrow
        mediaQuery: 'all and (min-width: 560px) and (max-width: 850px)'
        weight: 1
        - 1x
        label: wide
        mediaQuery: 'all and (min-width: 851px)'
        weight: 2
        - 1x

Here’s the explanation.

  • label - A human readable label for the breakpoint. It should be unique within the file
  • mediaQuery - Media query for the breakpoint
  • weight - Weight used for ordering breakpoint
  • multipliers - the ratio between the physical pixel size of the active device and the device-independent pixel size. This is used to support pixel resolution multipliers. E.g. 1x is normal; 1.5x supports Android; 2x supports Mac retina device.

Let’s go back to the breakpoint file. we have divided 3 different breakpoints for the bartik theme (mobile, narrow, and wide) with different widths. And from mobile to wide (small width to larger width), the weight increases (0 - 2). This is also how Breakpoints order the weight based on width or height.

If you use SASS for styling, you will find that Breakpoints help your styling much easier and simpler.


That’s it for theming. I hope that until now you have got fundamental knowledge about Drupal 8 theming. Next, you can create your own base theme by following our next post - Step to step to create a Drupal 8 theme from scratch.

If you have a question regarding about themes, theming, or anything you read here, feel free to leave a comment below, or do it via our contact page. We are always free to help.