In-Page Navigation with Drupal 8 and Paragraphs

February 2017

There is a trend to make web sites with fewer, longer pages. In other words, the trend is towards less clicking and more scrolling. There are plenty of studies showing that this is the right direction, and personally I do not miss the days of clicking through nested “table of contents” (ToC) pages before reaching something worth reading. (See the references at the bottom of the page.)

One challenge with longer pages is how to give readers an easy way to navigate within the page. While I do not like a page that is only a ToC for other pages, I do want a ToC as part of a long page. Here I will describe one way I came up with to generated such a ToC on a recent project. I think this method “has legs.”

Drupal and Paragraphs

At Isovera, we build web sites with the Drupal content management system (CMS). Drupal encourages us to think of our sites in terms of small, reusable pieces of content, so that any page on the site is built from some of these pieces. The main navigation menu is one such piece. Another is the footer, with its copyright notice and “Contact us” link.

The Paragraphs module for Drupal takes this approach one step further. Even the content that is particular to one page (like this particular blog post) can be broken up into smaller pieces. My colleague Aaron Manire talked about how we used Paragraphs in Making WGBH News: A Responsive, Reusable Redesign. (See the “Flexible Layout” section.)


Here is a screenshot of the top of a long, complex page with a ToC at the top to provide navigation.

Boxed example

Here is another example, where I have added some CSS to put a box around each paragraph. You would not want a real site to look like this, but it makes the structure easy to see.


This section is for readers who have some experience in building web sites with Drupal.

The main section of the page is a multi-valued Paragraphs field. There are several types of paragraph that can be used: text, image, list, etc. Some sites may include a type that contains sub-paragraphs, in order to create sub-sections.

My first thought for building the ToC was to use Views. It is easy to build a view that lists all the paragraphs on the page. The problem is how to get them into the right order. Think about what happens if the page is edited and a paragraph is inserted between two existing ones, or if the order is explicitly rearranged. If we allow sub-paragraphs, can we get the ToC to reflect the page structure?

Then I realized that what I wanted was a stripped-down version of the whole page, including only one line for each section … and that I could build this stripped-down version by using custom view modes.

Earlier versions of Drupal require a bit of custom code or an extra module in order to create custom view modes, but that is one of the things we get “out of the box” with Drupal 8. I created a “Table of contents” view mode for paragraphs and one for nodes. The view mode for nodes shows just the main Paragraphs field, rendered using the “Table of contents” view mode. The paragraph view mode shows just a single text field: title or caption.

(One exception: if we want to allow sub-sections, then there is a “bundle” paragraph type with a title and its own Paragraphs field. The “Table of contents” view mode for the bundle type will display the title and the Paragraphs field. Of course, the nested Paragraphs field will be displayed using the same view mode.)

The custom view modes get us 90% of the way there without writing any code. The missing part is how to get the lines in the ToC to be links to the corresponding sections on the main page. First, I wrote a simple preprocess function to add an id attribute to the paragraphs in the main section, so that they can be targeted by links.

 * Implements hook_preprocess_HOOK().
 * Add an id attribute to each paragraph.
function tufts_utilities_preprocess_paragraph(&$variables) {
  $variables['attributes']['id'] = 'paragraph-' . $variables['paragraph']->id();


Then I created a Twig template for the custom paragraph view mode, in order to create the link.

set classes = [
'paragraph--type--' ~ paragraph.bundle|clean_class,
view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class,
  This will be used as an in-page navigation link, so add the <A> element.
  Remove the id attribute or else the link will go to itself.
<div{{ attributes.addClass(classes).removeAttribute('id') }}>
  <a href="#paragraph-{{ }}">{{ content }}</a>


That is all it takes! With some careful configuration and a few lines of code in your theme, you can use this technique on your own sites. If you do, please leave a comment on this page: we love to get feedback.

I have also written a little module that makes it even easier: Paragraphs Table of Contents. As I said, you can do this all yourself, so you do not really need the module, but having it there on makes it even easier to see how it works. Just visit, click the “Launch sandbox” button, and click through the installer. In a few minutes you will have a working Drupal site that includes the module. Enable the module (and its dependencies) and you can see exactly how the view modes are configured. Build a page (of type “Page with sections”) and see the ToC magically appear.

Filed under: