#! code: Drupal 11: How To Alter Entity View Builder Configuration Before Rendering

I should also note here that the callback type #post_render exists. The post-render callback accepts a markup object that is basically the fully rendered output of the render pipelines. They also need to be registered in the same way with the trustedCallbacks() method.For example, assuming that we have a Media entity of some sort, we get that entity ready for rendering using the following.<img loading="lazy" width="500" height="250" src="/sites/default/files/styles/wide/public/2025-12/20241119_093540.jpg?h=101e5a8c&amp;itok=ZC8g1gD4" alt="An example image.">

namespace Drupalmy_modulePluginBlock;

use DrupalCoreBlockAttributeBlock;
use DrupalCoreBlockBlockBase;
use DrupalCoreFormFormStateInterface;
use DrupalCorePluginContainerFactoryPluginInterface;
use DrupalCoreSecurityTrustedCallbackInterface;
use DrupalCoreStringTranslationTranslatableMarkup;
use SymfonyComponentDependencyInjectionContainerInterface;

/**
* Provides a footer information block.
*/
#[Block(
id: "render_entity",
admin_label: new TranslatableMarkup("Render entity"),
category: new TranslatableMarkup("Custom Components")
)]
class RenderEntity extends BlockBase implements ContainerFactoryPluginInterface, TrustedCallbackInterface {
// ...

$viewBuilder = Drupal::service('entity_type.manager')->getViewBuilder('media');
$mediaView = $viewBuilder->view($media, 'default');

As it wasn’t that simple I took some notes and decided to convert them into an article to show how to do the same. In this article we will look at using the view builder to generate a renderable view of an entity and then look at how to alter the attributes of the view mode without using a preprocess hook.Here is the heading of the block class, with the TrustedCallbackInterface added to the class signature.Whilst this works well, the issue here is that we are stuck with this view mode. If we want to inject custom attributes into the media then we could create another view mode with more templates, but that quickly becomes difficult to manage if we have lots of custom properties to inject.Since a block is a prett self contained sort of class we will use this for the rest of the examples in this article. To get our media item rendered we could use something like the following to generate a render array for a media item in a block build() method.The setDimentions() callback just needs to make sure that the media image item exists before injecting height and width dimentions into the item attributes.$viewBuilder = Drupal::service('entity_type.manager')->getViewBuilder('media');
$mediaView = $viewBuilder->view($media, 'default');
$mediaView['#pre_render'][] = static::class . '::setDimensions';

An example of this method in use can be seen in Drupal the class DrupalmediaPluginFilterMediaEmbed, which uses a callback method called disableContextualLinks to remove the contextual links section of the render array when the media item is embedded in CKEditor windows.In itself, this array doesn’t contain enough information yet to allow us to alter certain things about the final render. For example, if we wanted to inject attributes into a image as then we would need to inject them into an attributes array, but this array doesn’t exist at this level.The resulting HTML will be the standard rendered <img> tag from the media item, but it will also include the attributes that we want to inject.The solution to this might be to use a preprocess hook, but the issue with that is that we lose the context of what we were doing. The preprocess hook will intercept the entity rendering process, but we won’t be sure where the entity came from so it might be difficult to alter it in the correct way.In the below example we are setting the #pre_render attribute to be a method called setDimensions, which exists in the same class as we are setting up our render array.Using #pre_render is pretty simple to add to our render array, but it doesn’t work in isolation. The following code will show how to add height and width attributes to an image Media entity item using this method.Instead of hard coding the dimentions into the setDimensions method we can instead add them to the generated render array so that it’s passed to the callback. Anything we add to the initial render array (as long as it’s prefixed with a #) will eventually be passed to the callback method. The attributes we add shouldn’t interfere with the rendering process (unless you’ve named them to clash with something Drupal uses internally).If we return this as a render array (or even part of one) then the media item will be rendered fully through the render pipeline.I encountered an issue on a Drupal 11 site recently where I had a block that was rendering an entity to display on a page. /**
* Injects dimensions into the set image.
*
* @param array<mixed> $build
* The render array for the embedded media.
*
* @return array<mixed>
* The updated render array.
*/
public static function setDimensions(array $build):array {
if (isset($build['field_media_image'][0])) {
$build['field_media_svg'][0]['#item_attributes']['width'] = 500;
$build['field_media_svg'][0]['#item_attributes']['height'] = 250;
}
return $build;
}

I found this technique useful when I was rendering SVG images that needed to be a very specific size in a block. This meant that now matter how large the original SVG image was, the resulting image was always contrained to the correct dimensions. Performing this method with SVG images made a lot of sense since it isn’t possible to pass an SVG image through a image formatter. It is possible to restrict the size of an SVG in the display mode, but that would have meant having multiple different view modes for different images dotted around the site.$view_builder = $this->entityTypeManager->getViewBuilder('media');
$mediaView = $view_builder->view($media, 'default');
$mediaView['#pre_render'][] = static::class . '::setDimensions';

// Use the image.factory service to get an image
$imageFactory = Drupal::service('image.factory');
$imageService = $imageFactory->get($img['#media']->get('field_media_image')->referencedEntities()[0]->get('uri')->getValue()[0]['value']);

// Extrac the height and weight of the image.
$height = $imageService->getHeight();
$width = $imageService->getWidth();

// Perform some manipulation on them. Here we are making the image 10% of the original size.
$height = $height * 0.1;
$width = $width * 0.1;

// Store these values in the render array for later use.
$img['#height'] = $height;
$img['#width'] = $width;

I tend to avoid using post-render callbacks unless I really need to as they require string manipulations to alter the content. As general rule, it is easier to manipulate the content before it is rendered, rather than after.

Making Things More Dynamic

To get started we just need to inject the #pre_render render attribute into the $mediaView render array, giving it a callback function that it can call to perform the action.public function build():array {
$mediaId = 123;
$media = $this->entityTypeManager->getStorage('media')
->load(mediaId);
if ($media) {
$view_builder = $this->entityTypeManager->getViewBuilder('media');
$mediaView = $view_builder->view($media, 'default');
$mediaView['#pre_render'][] = static::class . '::setDimensions';
return $mediaView;
}

return [];
}

To follow on from the previous examples, we can expand on the image example by extracting the dimentions of the image from the original source. This is possible using the image.factory service, which can create a Image object using the location of the image itself. Once we have this Image object we can extract all sorts of information about the image, including our image dimentions.  /**
* {@inheritdoc}
*/
public static function trustedCallbacks() {
return ['setDimensions'];
}

public static function setDimensions(array $build):array {
if (isset($build['field_media_image'][0])) {
$build['field_media_image'][0]['#item_attributes']['width'] = $build['#width'];
$build['field_media_image'][0]['#item_attributes']['height'] = $build['#height'];
}
return $build;
}

The solution to this is to use the #pre_render attribute to change the output of the rendering process, which gives us greater control over the final product.There was nothing unusual about what was going on in the rendering process, but in this case I needed to add some attributes to the entity markup and I found that this process wasn’t that simple. The solution was to intercept the rendering process half way through using a pre-rendering callback method.To get a content entity ready for rendering we need to use the entity_type.manager service to get the correct view builder for the entity in question. Once we have the view builder object we can use the view() method to get our renderable array.

Similar Posts