I've created a simple filter that allows webforms to be rendered anywhere in a node with a tag like this: [webform:machine_name]

The code to render the form is as follows:

$webform = \Drupal::entityTypeManager()->getStorage('webform')->load($machineName);
$viewBuilder = \Drupal::service('entity_type.manager')->getViewBuilder('webform');
$formBuild = $viewBuilder->view($webform);
return render($formBuild);

It works insofar as the form is displayed, client-side validation is working.

BUT none of the webform-specific JS is attached (e.g. input masks don't work). I assume this is because Drupal doesn't know it needs to attach the libraries.

How do I attach those libraries? Preferably in a method that can be invoked from the code that renders the filter, as if it's in a separate place things would start to get more complicated.


Following 4k4's feedback, here's the code that I'm now using. It works as above, without the libraries being attached:

foreach ($renderArrays as $tag => $renderArray) {
  $renderContext = new RenderContext();         
  $renderedOutput = $this->renderer->executeInRenderContext($renderContext, function () use (&$renderArray) {
    return $this->renderer->render($renderArray);
  $metaData = BubbleableMetadata::createFromRenderArray($renderArray);
  if (!$renderContext->isEmpty()) {
    $metaData = $metaData->merge($renderContext->pop());
  $result = $result->merge($metaData);
  $text = str_replace($tag, $renderedOutput, $text);

For completeness, $renderArrays contains:

['[webform:machine_name]' => [
  '#type' => 'webform',
  '#webform' => 'machine_name',

2 Answers 2


Now you return bare HTML without any attached meta data for caching, libraries and so on.

Also Webforms provides a render element, so this is all you need to build a webform:

$build = [
  '#type' => 'webform',
  '#webform' => $machineName,

The important part though is that the filter you put the token in handles the attached metadata correctly. It needs to do it similar to Entity Embed:

    $result = new FilterProcessResult($text);
    // We need to render the embedded entity:
    // - without replacing placeholders, so that the placeholders are
    //   only replaced at the last possible moment. Hence we cannot use
    //   either renderPlain() or renderRoot(), so we must use render().
    // - without bubbling beyond this filter, because filters must
    //   ensure that the bubbleable metadata for the changes they make
    //   when filtering text makes it onto the FilterProcessResult
    //   object that they return ($result). To prevent that bubbling, we
    //   must wrap the call to render() in a render context.
    $entity_output = $this->renderer->executeInRenderContext(new RenderContext(), function () use (&$build) {
      return $this->renderer->render($build);
    $result = $result->merge(BubbleableMetadata::createFromRenderArray($build));


Edit: Improved bubbling of metadata:

    $render_context = new RenderContext();
    $entity_output = $this->renderer->executeInRenderContext($render_context, function () use (&$build) {
      return $this->renderer->render($build);
    $meta_data = BubbleableMetadata::createFromRenderArray($build);
    if (!$render_context->isEmpty()) {
      $meta_data = $meta_data->merge($render_context->pop());
    $result = $result->merge($meta_data);

If you are looking for an easier solution you could for example add the webform to $variables in preprocess to render it in a node template. Then the attached libraries bubble up in the right place automatically.

  • Thanks for the information, both parts of your response are useful. I can't go with the node template solution as it needs to be more generic and reusable just based on a tag in a field. I've rewritten the code to use the anonymous function as described above, but I can't work out how to actually replace the tag. If I use a standard str_replace, despite the extra code, the form is rendered without any Javascript. Do you have any idea?
    – Geat
    Commented Dec 12, 2018 at 18:36
  • After you've replaced the tag, do you put the resulting text in $result->setProcessedText($text) and return $result?
    – 4uk4
    Commented Dec 12, 2018 at 19:25
  • I do. I've added the code I'm using as an edit to the question.
    – Geat
    Commented Dec 12, 2018 at 19:33
  • I don't know why the code example doesn't do it, but normally you would merge the bubbled up metadata from the render context. I edit the answer.
    – 4uk4
    Commented Dec 12, 2018 at 19:59
  • Thanks for your continued help. I updated the code as suggested but it didn't change anything unfortunately.
    – Geat
    Commented Dec 12, 2018 at 20:12

Even after adding the META data bubbling, which is the correct approach for processing a filter, the libraries were still not being included.

It turns out that came down to the way I was sending the output from my custom field widget to the template.

I was originally sending this to the template:

  '#markup' => check_markup($field_content, 'full_html')

When in order to correctly run the filters and attach the libraries, I should have been sending:

  '#type' => 'processed_text',
  '#text' => $field_content,
  '#format' => 'full_html',

Funnily enough it was this post from 4k4 (who also helped me above) that pointed me in the right direction:


  • 1
    This has still to do with the filter. Although you get away with sending the field as render array to the template and leak metadata by simply render() in the filter, this is not good practice, because the filter might be used in places where it is supposed to return the metadata in FilterProcessResult as defined in the interface.
    – 4uk4
    Commented Dec 14, 2018 at 16:42
  • Can you suggest a proper solution, keeping in mind nothing else worked?
    – Geat
    Commented Dec 14, 2018 at 17:51
  • It might be worth noting that I do still use the meta bubbling code that you provided above.
    – Geat
    Commented Dec 14, 2018 at 17:52
  • 1
    Then it's OK. I had the impression this was meant to be an answer to the question and you wanted to ignore the changes to the filter.
    – 4uk4
    Commented Dec 14, 2018 at 18:59
  • Fair comment, I should edit in just in case people find this post useful.
    – Geat
    Commented Dec 14, 2018 at 19:29

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.