HTML tags being removed after theme_preprocess_node I am using theme_preprocess_node to modify an HTML tag, but the tag is being stripped somewhere in the theme layer.

mytemplate.twig

{% if node.field_embedded_media.value %}
  {{ content }}


mytheme.theme

    function mytheme_preprocess_node(&amp;$variables) {
      if ($variables['view_mode'] == 'ebook_export') {
        $contentBody = $variables['node']-&gt;get('body')-&gt;value;

        $pattern = '/&lt;drupal-media/';
        $replace = '&lt;drupal-media data-view-mode="markdown_link"';
        $contentBody = preg_replace($pattern, $replace, $contentBody);

        $variables['content']['body'] = array(
            '#markup' =&gt; $contentBody
          );
        }
     }


Original input (node body value):

&lt;drupal-media data-align="center" data-entity-type="media" data-entity-uuid="269d4491-9866-403c-90e3-c1298e9857de"&gt;&lt;/drupal-media&gt;

This is some text.

&lt;drupal-media data-align="center" data-entity-type="media" data-entity-uuid="269d4491-9866-403c-90e3-c1298e9857de"&gt;&lt;/drupal-media&gt;&lt;pre&gt;

This is some more text.


dpm($contentBody) in mytheme_preprocess_node (this is what I want to output, and it's correct here)

&lt;drupal-media data-view-mode="markdown_link" data-align="center" data-entity-type="media" data-entity-uuid="269d4491-9866-403c-90e3-c1298e9857de"&gt;&lt;/drupal-media&gt;

This is some text.

&lt;drupal-media data-view-mode="markdown_link" data-align="center" data-entity-type="media" data-entity-uuid="269d4491-9866-403c-90e3-c1298e9857de"&gt;&lt;/drupal-media&gt;

This is some more text.


Actual HTML output on page render (this is what I get, and the &lt;drupal-media&gt; tags are gone)

This is some text.

This is some more text.


The &lt;drupal-media&gt; tags got stripped.  How do I prevent them from being stripped?  I don't understand enough about the theme layer to determine where this is happening.
As described in Render API overview, the value passed in #markup is sanitized via \Drupal\Component\Utility\Xss::filterAdmin(). You can eventually pass the tags not to filter via the #allowed_tags properties, but the tag attributes could still be sanited.

To have full control on what it is sanitized, you should use #theme or #type instead of #markup. (The first is my choice when the markup is rendered only in a single place/page. Alternatively, you could also use #inline_template as in the following example code taken from the documentation page.

$build['hello'] = [
  '#type' =&gt; 'inline_template',
  '#template' =&gt; "{% trans %} Hello {% endtrans %} &lt;strong&gt;{{name}}&lt;/strong&gt;",
  '#context' =&gt; [
    'name' =&gt; $name,
  ],
];


It allows to use any Twig filter Drupal core can use and to have full control over the rendered tags.
It's the easier alternative to #theme which doesn't require to implement hook_theme() nor putting the template content in a separated file. (If you need to reuse the same inline template in different pages, or the template content is rather complex, using #theme is still preferable.)