I would like to render a full html page(with html and body tags) with content provided by a render array.

What do I need to do in order to NOT render attachments(CSS, JS, drupalSettings) from active theme or modules but solely from attachments defined in the provided render array?

So far this is quite close but I still see attachments from base theme:

        'Page label',

Using my own, empty, theme hasn't helped.

Ok, I had to duplicate what bare_html_page_renderer did since it attached "default" libraries form the system module. So now with my own theme all looks good but the structure is not right. Seems like the placeholders are not working properly.

My html.html.twig source code looks like:

<!DOCTYPE html>
<html{{ html_attributes }}>
  <head-placeholder token="{{ placeholder_token }}">
  <title>{{ head_title|safe_join(' | ') }}</title>
  <css-placeholder token="{{ placeholder_token }}">
  <js-placeholder token="{{ placeholder_token }}">
<body{{ attributes }}>
{{ page }}
<js-bottom-placeholder token="{{ placeholder_token }}">

but the rendered HTML will looks like:

<html dir="ltr" lang="en">
<head> </head>
<head-placeholder token="...">
<title>Page title</title>
<link rel="stylesheet" href="..." media="all">
<div>the rendered content</div>
<script src="...">

So the issue was the there has to be at least one attribute in the header, otherwise the 'head' placeholder won't be replaced. So adding a favicon or generator or any metatag will do the trick.

Unfortunately this works only for route response with theme negotiator but not if I just want to print the pure html code since the active theme will be applied.


1 Answer 1


So this is a working solution. The response has getContent() method so it can be used in code and not only as route response.

function renderHtml($some_content) {
    $html = [
      '#type' => 'html',
      'page' => [
        '#type' => 'page',
        '#title' => 'Some page',
        'content' => $some_content,
        '#attached' => [
          // At least one of these have to have value,
          // otherwise the head placeholder won't get replaced.
          'html_head_link' => [],
          'html_head' => []

    $html['page']['#attached']['html_head'][] = [[
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => array(
        'name' => 'Sometag',
        'content' => 'somevalue',
    ], 'someid'];

    $active_theme = \Drupal::theme()->getActiveTheme();
    $render_theme_extension = \Drupal::service('theme_handler')->getTheme('rendering_theme');
    $new_active_theme = \Drupal::service('theme.initialization')->getActiveTheme($render_theme_extension);

    $response = new HtmlResponse();
    $response = \Drupal::service('html_response.attachments_processor')->processAttachments($response);


    return $response;

Note: when you are using this via ajax callback, you have to unset the ajax_page_state from the post request, otherwise you'll be most likely be missing some libraries.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.