Making everything buttons in Drupal 8 with Bootstrap 4

There are a bevy of links in Drupal 8 that would feel really good if they looked like buttons.

Things like ‘read more’ and ‘add new comment’ links, like ‘add child page’ and ‘printer-friendly version’ in the built-in Drupal 8 Book module.

Many CSS frameworks, like Bootstrap and, require classes directly on the <a> elements to style buttons. But there isn’t a way to get classes directly on there in Views! Ouch! Futhermore, Drupal 8 builds these links in a few different ways, so there’s no single way to get this done everywhere.

Best practice is to keep as much customization in template files, but I can’t see a way to get that done. Instead I went into the .theme file.

I’ve built Bootstrap 4 into my theme, so I’ll be using that in my example.

.theme file

Every Drupal 8 theme has a mytheme.theme file in that theme’s root directory (where mytheme is the name of your theme).

This is where preprocess functions live. Preprocess functions in Drupal 8 can make changes to elements before they get pulled into your theme.

For example, most of these links I want to change show up in template files as {{ link }}. This object doesn’t just pull in the url (like I expected), but the entire <a> element complete with attributes. Preprocess functions let us into that object before it arrives in the template.

The preprocess functions

So I should say I’m not an expert in how/why this all actually works. I figured most of this out through trial and error, asking in Drupal support chats, and the Googling.


This will cover you for most things you’ll find tucked into <ul class="links"> where 'links' is a class that Drupal 8 throws in by default. If you have Twig debugging on, you’ll see HTML comments in your DOM stating this list of links is coming from links.html.twig. (Twig debugging is a good place to start when asking “where does that come from?”.)

So this includes things like ‘Read more’, ‘Add comments’, etc. Without intervention, this is what they looked like in my theme.

‘Read more’ and ‘Add new comment’ appear as unstyled links by default

Here is how you can get classes on them. This goes in your .theme file.

/* Add Bootstrap 4 classes to Read More and Comments links */
function MYTHEME_preprocess_links(&$variables) {
$variables['links']['node-readmore']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
$variables['links']['comment-comments']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
$variables['links']['comment-add']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
$variables['links']['comment-delete']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
$variables['links']['comment-edit']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
$variables['links']['comment-reply']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
/* Book */
$variables['links']['book_printer']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
$variables['links']['book_add_child']['link']['#options']['attributes']['class'][] = 'btn btn-sm btn-secondary';
  • Replace MYTHEME with the name of your theme (in lowercase)
  • $variables['links']['LINKNAME']['link']['#options']['attributes']['class'][] = 'YOUR CLASS NAMES';

The link name should be the class name Drupal 8 adds automatically to the <li> of that link.

The ‘node-readmore’ class on the <li> matches the link in the preprocess function

EXCEPT BOOK. Check out the Book links in my code snipped above. Look at those underscores. They haunt me. (I ended up being able to hunt these down in core/modules/book/book.module, but it was like, the end of me.)

A clue to where preprocess_links comes from is in the template file that creates these links, links.html.twig. In the comment block at the top of the file it gives you a lil hint.

* @see template_preprocess_links()

Moving on.


This covers me for ‘more’ links created by Views.

See, I got the ‘more’ link to be a button

Here’s my code:

/* Add Bootstrap 4 classes to views buttons */
function MYTHEME_preprocess_views_view(&$variables) {
$variables['more']['#options']['attributes']['class'] = array('YOUR', 'CLASSES', 'HERE');

You can see where you need to replace MYTHEME with your theme name, and where to add your classes.

I do not know why this format is different. ¯\_(ツ)_/¯


This will cover things like the ‘Log in to post new content in the forum.’ button. Again, not sure why this is different, but it’s working for me.

/* Add Bootstrap 4 classes to local actions */
function MYTHEME_preprocess_menu_local_action(&$variables) {
$variables['link']['#options']['attributes']['class'][] = 'YOUR';
$variables['link']['#options']['attributes']['class'][] = 'CLASSES';
$variables['link']['#options']['attributes']['class'][] = 'HERE';

So that should do it.

My placeholder text comes from and my placeholder images are from Have fun with your Drupal.