I'm trying to find what is the proper way to override the template_preprocess_book_navigation function from the Drupal 8's core Book module. I want to add only two lines (bolded), but don't know how this should be done in "Drupal 8 way". Should I create a custom module or add something in the mytheme.theme file?

Thank you and be healthy!

function template_preprocess_book_navigation(&$variables) {
  $book_link = $variables['book_link'];

  // Provide extra variables for themers. Not needed by default.
  $variables['book_id'] = $book_link['bid'];
  $variables['book_title'] = $book_link['link_title'];
  $variables['book_url'] = Url::fromRoute('entity.node.canonical', ['node' => $book_link['bid']])->toString();
  $variables['current_depth'] = $book_link['depth'];
  $variables['tree'] = '';

  /** @var \Drupal\book\BookOutline $book_outline */
  $book_outline = \Drupal::service('book.outline');

  if ($book_link['nid']) {
    $variables['tree'] = $book_outline->childrenLinks($book_link);

    $build = [];

    if ($prev = $book_outline->prevLink($book_link)) {
      $prev_href = Url::fromRoute('entity.node.canonical', ['node' => $prev['nid']])->toString();
      $build['#attached']['html_head_link'][][] = [
        'rel' => 'prev',
        'href' => $prev_href,
      $variables['prev_url'] = $prev_href;
      $variables['prev_title'] = $prev['title'];

    /** @var \Drupal\book\BookManagerInterface $book_manager */
    $book_manager = \Drupal::service('book.manager');
    if ($book_link['pid'] && $parent = $book_manager->loadBookLink($book_link['pid'])) {
      $parent_href = Url::fromRoute('entity.node.canonical', ['node' => $book_link['pid']])->toString();
      $build['#attached']['html_head_link'][][] = [
        'rel' => 'up',
        'href' => $parent_href,
      $variables['parent_url'] = $parent_href;
      $variables['parent_title'] = $parent['title'];
      $variables['prev_depth'] = $prev['depth'];

    if ($next = $book_outline->nextLink($book_link)) {
      $next_href = Url::fromRoute('entity.node.canonical', ['node' => $next['nid']])->toString();
      $build['#attached']['html_head_link'][][] = [
        'rel' => 'next',
        'href' => $next_href,
      $variables['next_url'] = $next_href;
      $variables['next_title'] = $next['title'];
      $variables['next_depth'] = $next['depth'];

  if (!empty($build)) {

  $variables['has_links'] = FALSE;
  // Link variables to filter for values and set state of the flag variable.
  $links = ['prev_url', 'prev_title', 'parent_url', 'parent_title', 'next_url', 'next_title'];
  foreach ($links as $link) {
    if (isset($variables[$link])) {
      // Flag when there is a value.
      $variables['has_links'] = TRUE;
    else {
      // Set empty to prevent notices.
      $variables[$link] = '';

1 Answer 1


You can put a template_preprocess() function in a custom module or your theme mytheme.theme file.

It depends on what you want to achieve.

If this preprocess function is specific to the theme (for example, the results are used in a theme template), then you should put it in mytheme.theme.

But, if you put the code in mytheme.theme and then change themes, your book navigation will no longer behave as expected.

So, if the preprocess function does general processing that you might want to use in multiple themes, and it doesn't do anything theme-specific, then it's better to put it in a custom module. (This is probably your use case.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.