Drupal 8 Custom Table of Contents Block for Book Content

November 9, 2017 - 10:15am
Art Williams

Recently we needed to build a knowledge base for a SaaS client to house support content in both text and video format. Since a lot of this type of content has a progression to it (first do this, then this) it made sense to dust off the Book Module.

The Book Module

The Book module has been in Drupal core since 2001, so it’s pretty much always been there. It provides a hierarchy to pages in Drupal that allow a next/previous functionality that is useful when you have content with an order to it. Additionally, the Book module comes with a table of contents block that outlines the hierarchy of the book for easy navigation.

The core table of contents block is fine for a basic outline, but in our case, we had some customization to this table of contents that necessitated a custom solution. We wanted to show a video icon next to any page that had a video on it. We also wanted to remove the topmost page of the book from the hierarchy. Lastly, we wanted to retain the functionality to display which page the user is on using an “active” class. After searching for a module that provided the customization we needed and coming up short, we realized that we could get Views to help us build this block without any custom code needed.

Custom Table of Contents Block

The Views Tree module provided the basic hierarchical structure. It only has a dev version for Drupal 8, but we haven’t run into any issues so far with it. The module displays the view results in a hierarchy once you set up hidden fields in the view for the node id of the page and it’s parent (setup documentation).

Video Icon

The video icon will be added via a css class. To accomplish this we added the video field we had defined on our content type to the view as a hidden field. Then we chose to rewrite the results of the field and just output the text ‘video’ when there was a value present in the field.  Next, under the “No Results Behavior” section of the view we check the boxes for “Hide if empty” and “Hide rewriting if empty”. Lastly, we added the token for the video field to one of the elements under “Style Settings” on the Title field (which is the only field we are allowing views to display). In our case, we made the wrapper HTML element a span and added the class for the video field there.

Hide Topmost Book Page

Add a contextual filter to the view for book depth and set it to “not equal to” 1. The topmost page in a book has a depth of 1 so this will not display that page on any book. Originally, we were just excluding the node id for the top page since we only have one book, but decided to change the filter to book depth so that it will work for any book on the site.

Add ‘Active’ Class

This is similar to the video class, but requires a little snippet of a twig conditional statement and a contextual filter to make it work. First add a contextual filter to your view called “Global: Null”.  The Global: Null filter allows you to get some information from the URL without actually impacting the query that Views is making. In this case “Provide default value” on the contextual filter should equal “Content ID from URL”

Next add an additional hidden (Exclude from display) Content: ID field to the view. Under the “Rewrite Results” section, check “Override the output of this field with custom text”. In the text box add a twig conditional that compares this nid value to the Global:Null (arguments.null) you just setup and returns a string for the ‘active’ class if they match. Here is what ours looks like:

{{ (nid == arguments.null) ? 'active' : '' }}

It’s important to make sure you are comparing the right nids because you should have 3 nid fields on this view right now: 1 for the page’s nid, 1 for the parents nid, and this one we are working on now. The first two were created during the Views Tree module setup instructions.
Now add the token for this hidden nid field to the same place we added the video class (see screenshot in Video Icon section above).


This View accomplished our needs for the table of contents, and can easily be added to if we ever need a New or Updated flag. The flexibility gained is worth the effort and we didn’t have to edit TWIG templates, write custom preprocess hook, or write a custom module.

TLDR; See the View’s yaml file and screenshot below. You will need to replace the field names and content types with your own.

Read our other Drupal-related blog posts.

Newsletter Signup

334 North Park Dr.
San Antonio, TX 78216