Change record status: 
Introduced in branch: 

Overview: Removed or deprecated methods and their replacements

When Twig's autoescape functionality was enabled in Drupal 8 core, the SafeMarkup class was added in order to integrate Drupal core's own filtering and escaping APIs with Twig's. Following extensive critical work on Drupal 8's sanitization APIs, most of the public API for the SafeMarkup class has been removed. Replacements for the removed methods are listed below.

Deprecated method Replacement(s)




  1. Twig autoescape
  2. #plain_text render element. See Support for #plain_text has been added for the render arrays.
  3. @placeholder with t() or SafeMarkup::format()
  4. Html::escape()

See below for details.

SafeMarkup::format() SafeMarkup::format() returns a FormattableMarkup object instead of a string
SafeMarkup::isSafe() Use $variable instanceof \Drupal\Component\Render\MarkupInterface instead.
Removed method Replacement(s)



Same as for SafeMarkup::checkPlain(). Note that SafeMarkup::escape() was only used to escape strings that were marked as unsafe, so Html::escape() is not a direct equivalent. If you want to check for safeness before escaping, use $variable instanceof \Drupal\Component\Render\MarkupInterface.




See Twig autoescape enabled and text sanitization APIs updated




See Twig autoescape enabled and text sanitization APIs updated




No direct replacement. There is no global list of safe strings, but any mixed collection of strings and MarkupInterface objects can be serialized and restored without loss of "safeness".
See Twig autoescape enabled and text sanitization APIs updated




No direct replacement. Code that needs to do something like this must handle the problem itself. See views_pre_render_views_form_views_form(). (Note however, that uses the deprecated SafeMarkup::isSafe.)


SafeMarkup::xssFilter() and SafeMarkup::xssFilterAdmin()



  1. Xss::filter() and Xss::filterAdmin()
  2. #markup render array element

See Xss::filter() and Xss::filterAdmin() no longer mark their result safe; SafeMarkup::checkAdminXss() and SafeMarkup::xssFilter() are removed; #allowed_tags added for #markup.




No direct replacement. Use SafeMarkup::format() with $args = ['%placeholder' => 'thing to be placeholdered'].

Escaping markup (or, how to check_plain() in Drupal 8) (#)

The most direct replacement for SafeMarkup::checkPlain($text) is return new HtmlEscapedText($text). But, better best practices are explained below. When replacing usages of SafeMarkup::checkPlain(), care needs to be taken to identify where the escaped string is being used. There are four possibilities: a Twig template, a render array, or some other type of response (most often non-HTML).

  1. Twig

    If the string is used in a Twig template, rely on Twig's auto-escaping feature. Simply remove the call to SafeMarkup::checkPlain(). For example, most SafeMarkup::checkPlain() calls in template preprocess functions can be removed. Another common case is the construction of a render array that uses a Twig template. For example, data that is being themed using an item list or table does not need to be escaped as Twig will do this for you. If text that has already been escaped using Html::escape() ends up in a Twig template variable, it will be double-escaped.

  2. Render array #plain_text element

    Render arrays can automatically escape text using the #plain_text key:

    $escaped_render_array = ['#plain_text' => 'foo <strong>bar</strong>'];

    See Support for #plain_text has been added for the render arrays.

  3. Html::escape() and SafeMarkup::format() for render arrays and HTML output

    Alternatively you might be joining escaped markup with markup you do not want escaped. Html::escape() or SafeMarkup::format() with a '@placeholder' can be used in places where explicit escaping is needed:

    $escaped = t('Some text') . '<span> foo' . Html::escape('<strong>bar</strong>') . '</span>';
    // or:
    $escaped = SafeMarkup::format('foo <span>@input</span>', ['@input' => '<strong>bar</strong>']);
    // Using #markup will automatically XSS admin filter the markup but it won't affect the previously escaped content.
    $render_array = ['#markup' => $escaped];

    Just like SafeMarkup::checkPlain() used to, all of these will escape the input string and turn it into: "&lt;strong&gt;bar&lt;/strong&gt;" (unescaped this would have been "<strong>bar</strong>").

  4. Non-HTML responses

    Output that bypasses the theme system (for example, a JSON response) must perform its own sanitization. In these cases, Twig's autoescape functionality will never be invoked.

    One strategy is to use Html::escape() to prepare output for a property value within a JSON response that JavaScript will insert into the DOM as HTML. For example, an autocomplete response should escape its output:

      foreach ($entities as $entity) {
          $matches[$entity->id()] = Html::escape($entity->label());
      return new JsonResponse($matches);

    Alternatively, it is possible to escape output in JavaScript instead. This was the approach taken on #2503963: XSS in Quick Edit: entity title is not safely encoded.

    Drupal.checkPlain() is defined in core/misc/drupal.js
    and is used in /core/modules/quickedit/js/theme.js for example

       Drupal.theme.quickeditEntityToolbarLabel = function (settings) {
        return '<span class="field">' + Drupal.checkPlain(settings.fieldLabel) + '</span>' + Drupal.checkPlain(settings.entityLabel);

Related change records

See Twig autoescape enabled and text sanitization APIs updated for a full list of related change records.

Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other updates done