45

I'm developing a site using Drupal 8 beta-14. I've created a view block of different terms and now I want to display it using code. How can I display it programatically? I used to do it in Drupal 7 using this code but I'm confused about Drupal 8.

$block = module_invoke('block', 'block_view', '4');
$text_block = render($block['content']);
1

10 Answers 10

103

There are two types of blocks, and the method for rendering the two is a bit different:

Content Blocks

Content blocks are blocks that you create in the interface. They are much like nodes configurable data structures, with fields etc. If you want to render one of these, you can do what you would normally do with entities, load them and render them with the view builder:

$bid = ??? // Get the block id through config, SQL or some other means
$block = \Drupal\block_content\Entity\BlockContent::load($bid);
$render = \Drupal::entityTypeManager()->
  getViewBuilder('block_content')->view($block);
return $render;

Plugin blocks

Blocks can also be plugin, defined in various modules. An example could be the breadcrumb block. If you want to render these, you will need to use the block plugin manager.

$block_manager = \Drupal::service('plugin.manager.block');
// You can hard code configuration or you load from settings.
$config = [];
$plugin_block = $block_manager->createInstance('system_breadcrumb_block', $config);
// Some blocks might implement access check.
$access_result = $plugin_block->access(\Drupal::currentUser());
// Return empty render array if user doesn't have access.
// $access_result can be boolean or an AccessResult class
if (is_object($access_result) && $access_result->isForbidden() || is_bool($access_result) && !$access_result) {
  // You might need to add some cache tags/contexts.
  return [];
}
$render = $plugin_block->build();
// Add the cache tags/contexts.
\Drupal::service('renderer')->addCacheableDependency($render, $plugin_block);
return $render;

Config entities

Shared for the two types are blocks, are that once you insert them into a region, you will create a config entity that has all of the settings for the block. In some cases it will be more useful handling config entities. Since the same block can be place in multiple regions with and with different configuration, it can get more tricky using the block config entities. The nice thing is that you might want to render a block with specific configuration, the bad thing is that config ids can change by messing with the interface, so the code might end up not working after letting users use the block interface.

$block = \Drupal\block\Entity\Block::load('config.id');
$render = \Drupal::entityTypeManager()
  ->getViewBuilder('block')
  ->view($block);
return $render;
12
  • 2
    The question doesn't specify if it is about rendering a block config entity ( a pre-configured block placement) or a block plugin with hardcoded configuration. Which makes sense because that difference doesn't exist in 7.x. This is more flexible because the other actually a requires a specific block that needs to be placed in a given theme and region. However, you should never create them by hand. Use the block plugin manager's createInstance() method for that with the plugin ID, where you can also provide a $configuration array...
    – Berdir
    Commented Sep 5, 2015 at 12:52
  • 2
    Also, you might want to consider calling the access() method first in case block access is restricted to e.g. a certain permission by that block. Can you improve your answer a bit on that? Can become a useful resource then :)
    – Berdir
    Commented Sep 5, 2015 at 12:53
  • 1
    @Berdir It's been a while, but I finally got around to improving the answer. With all the various caching going on, using the plugin directly is probably only useful in limited situations.
    – googletorp
    Commented Jan 30, 2016 at 10:55
  • 7
    The downside with using the block plugin manager createInstance() is that the resulting render array isn't run through block theming, so you can't use the block--blockname.twig.html, for instance. The alternative is to create a block for your theme, but leave it disabled, and then in your code do: ``` $block = \Drupal\block\Entity\Block::load('myblock'); $build = \Drupal::entityManager()->getViewBuilder('block')->view($block); ```
    – joachim
    Commented Apr 26, 2016 at 8:21
  • 1
    Nope - Another rabbit hole. The code for the content block white screens (with the infamous "The website encountered an unexpected error. Please try again later.") The second gets closer - But displays a cryptic message about the block being missing or something... (which isn't true because I'm attempting a system block - the powered by drupal thing).
    – sea26.2
    Commented Feb 9, 2019 at 21:47
20

For display only your block in your templates with preprocess the best way is

$block = \Drupal\block\Entity\Block::load('my_block_id');
$variables['My_region'] = \Drupal::entityTypeManager()
          ->getViewBuilder('block')
          ->view($block); // Works in Drupal 9

And in your page.html.twig or node.html.twig or xxx.html.twig use your variable My_region like this :

{% if page.My_region %}
    {{ page.My_region }}
{% endif %}

And in renderable array (custom module) by example into an controller custom in content() :

public function content() {
    $block = \Drupal\block\Entity\Block::load('my_block_id');
    $block_content = \Drupal::entityManager()
      ->getViewBuilder('block')
      ->view($block);

          return array(
        '#type' => 'container',
        '#attributes' => array(
          'class' => array("Myclass"),
        ),
        "element-content" => $block_content,
        '#weight' => 0,
      );
}

Using drupal_render is not useful as Drupal already assume the render in D8 and this is deprecated. You should use \Drupal::service('renderer')->renderRoot() instead.

It's a bit heavy, it is better to use the maximum area system and does not add load block from the preprocess. In the case of using a controller in your modules this seems a justified use.

4
  • This controller implementation is exactly what I was looking for. Thanks!
    – Mrweiner
    Commented May 24, 2018 at 0:46
  • I'm interested in this controller implementation for a similar use case I'm dealing with. But I can't find any documentation about element-content property in a render array. Do you know where it's documented ?
    – Eria
    Commented Jun 18, 2018 at 9:52
  • I don't know why, but \Drupal\block\Entity\Block::load does not return a block all the time. It only returns something if the block I load is placed within the view in block layout. If it's not placed, it returns null. Commented Oct 8, 2018 at 14:18
  • 4
    This answer should be updated to use \Drupal::entityTypeManager()->getViewBuilder('block')->view($block); Commented Aug 9, 2019 at 15:08
11

I needed to get the HTML of a custom block and got it using:

$con = \Drupal\block\BlockViewBuilder::lazyBuilder('bartik_search', 'full');
$d   = \Drupal::service('renderer')->renderPlain($con);

print $d->__toString();
2
  • 1
    I needed to add it to a render array and there it worked without __toString().
    – leymannx
    Commented Nov 15, 2016 at 12:32
  • 1
    Important to mention that a block at least needs to be placed in "Disabled blocks" region. Or any other active region.
    – leymannx
    Commented Dec 21, 2016 at 9:07
9

In addition to the top answer... If you want to render a block from a view, you may have to do things a little differently.

$view = views_embed_view('my_view_name', 'my_display_name');

(display name eg -> block_1)

Since we're going to pass it to twig, we don't need to render (using render service).

So you can just pass it as a variable to twig (for this example, its the return of a Controller):

return [
  ['description' => [
    '#theme' => 'your_theme_hook',
    '#your_variable => $view
  ]
]

in your module you need a hook_theme() for your variable:

function hook_theme($existing, $type, $theme, $path) {
  return array(
    'your_theme_hook' => array(
      'variables' => [
        'your_variable' => NULL,
      ]
    )
  )
}

And finally in your twig template:

{{ your_variable }}
5

Basically, there are two types of renders.

  1. When there is an existing instance of the block in the layout. the block can be rendered in twig using preprocess as

    $block = Block::load('BLOCK_ID');
    $variables['social_links'] = \Drupal::entityTypeManager()
      ->getViewBuilder('block')
      ->view($block);
    
  2. There is no instance or configurations for the block. Then in the preprocessor, we need to create the instance, build the block and then render it

    $block_manager = \Drupal::service('plugin.manager.block');
    $config = [];
    $plugin_block = $block_manager->createInstance('farmjournal_social_sharing', $config);
    $render = $plugin_block->build();
    $variables['farmjournal_social_sharing'] = render($render);
    
5
  • 1
    +1 upvote - in Drupal 9 - 9.0.7 option 2 definitely works - I have found. Crucial is the render function at the end, which actually generates the html. Commented Nov 5, 2020 at 23:24
  • 1
    And code snippet 2. works, in Drupal 9, without having to have a (disabled) block instance in admin/structure/block . The reason is that 2. creates and instance from the Block "class" (in this example, that is farmjournal_social_sharing). Commented Nov 5, 2020 at 23:47
  • 1
    Thank you so much Kashmiri! A tip - I was checking solution code snippet 2. using xdebug with Visual Studio Code and ddev. In my code I had a variable assigned to the value of what render( (line 5 in the code above) returned. The variable list, with that variable in it only, shows the first few characters. So it did look like that the variable had nothing in it which misled me to believe solution 2 didn't work - but it did actually! When I copied the value from that variable in the debugger to another text file I could see the whole html. Spent hours on this before the "penny dropped". Commented Nov 5, 2020 at 23:51
  • Hope that helps others. By the way Kashmiri, interesting to note your block farmjournal_social_sharing Have you heard of FarmOS Drupal Distribution. A real success story. drupal.org/project/farm Commented Nov 5, 2020 at 23:52
  • If your block relies on a block template to generate markup (the block class does not return #markup), or you prefer your block to render using the block template for consistency (which IMO should be how all blocks render by default), option 2 will not work. It feels very hacky, but apparently the only way is option 1. You can make a theme region called Disabled, place your block there and do not render the theme region anywhere. Only then will your block be available to Block::load, but the ID will be the one configured in block layout, not on your block class. Commented Feb 14, 2022 at 20:24
4

The following code works for blocks.

$block = \Drupal\block\Entity\Block::load('some_block_id_3');
$plugin = $block->getPlugin();
$build = $plugin->build();
$build['#weight'] = 4;
$form['block'] = $build;
0
2
// You need a block_id! to get it just click configure in the desire block and you'll get url like this /admin/structure/block/manage/bartik_search   the last part of the parameter is the block id
$block = \Drupal\block\Entity\Block::load('bartik_search');
$block_content = \Drupal::entityManager()
  ->getViewBuilder('block')
  ->view($block);

return array('#markup' => \Drupal::service('renderer')->renderRoot($block_content));
2
  • If possible, you should avoid using drupal_render or the render service. drupal_render is depricated but returing a render a array with the rendered content is pretty bad, you should return $block_content instead, the render array can be altered before the actual render and you should let Drupal do the rendering as much as posisble instead or doing it your self.
    – googletorp
    Commented Jan 30, 2016 at 10:59
  • This is going to work only if the block is already placed on the page through block layout. Commented Sep 9, 2017 at 14:41
1

You get the block output and then you may return it.

$block = \Drupal\block\Entity\Block::load ('my_bock_id');
block_content = \Drupal::entityManager()->getViewBuilder('block')->view($block);

return [
  '#type' => 'container',
  'element-content' => $block_content
];
1
  • If possible, you should avoid using drupal_render or the render service. drupal_render is depricated but returing a render a array with the rendered content is pretty bad, you should return $block_content instead, the render array can be altered before the actual render and you should let Drupal do the rendering as much as posisble instead or doing it your self. What you returns needs to be rendered again, which makes the actual rendering pointless
    – googletorp
    Commented Jan 30, 2016 at 11:01
0

Adding an additional answer here for better visitbility:

There is now a module that allows you to render a plugin block called Block plugin view builder: https://www.drupal.org/project/block_plugin_view_builder.

You can of course still use Drupals render service, however that returns a \Drupal\Component\Render\MarkupInterface object. In some cases, you need a render array. For example if wanting to render the block in the lazy builder.

Example of using that modules service once that module is installed:

$block_view_builder = \Drupal::service('block_plugin.view_builder');
$block = $block_view_builder->view('bid', []);
return $block;
-1

Based on my research, you could base the code from How to render a block programmatically in drupal 8. You could also change

return array('#markup' => \Drupal::service('renderer')->renderRoot($block_content));

into something as simple as:

$output .= \Drupal::service('renderer')->renderRoot($block_content);

to attach it for example into a page's return variable.

3
  • If possible, you should avoid using drupal_render or the render service. drupal_render is depricated but returing a render a array with the rendered content is pretty bad, you should return $block_content instead, the render array can be altered before the actual render and you should let Drupal do the rendering as much as posisble instead or doing it your self.
    – googletorp
    Commented Jan 30, 2016 at 10:59
  • You're right. This isn't the recommended and most flexible solution. Commented Jan 30, 2016 at 21:09
  • Your link is dead "How to render a block..."
    – sea26.2
    Commented Feb 9, 2019 at 21:50

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.