The Drupal 8 render pipeline
Or "How Drupal renders pages"
First, you need to know the general routing concepts: please read Route controllers for simple routes first.
This is explained both textually (below) and in the form of a diagram — see the files attached to this page (available in PNG, SVG and PDF — print the PDF to have a handy offline reference!).
High level: controllers, the VIEW
event and main content renderers
Takeaway
- Routes whose controllers return a
Response
object bypass the pipeline below. They rely directly on the Symfony render pipeline. - Routes whose controllers return the "main content" as a render array automatically have the ability to be requested in multiple ways: it can be rendered in a certain format (HTML, JSON …) and/or in a certain decorated manner (e.g. with blocks around the main content).
The pipeline
This can be considered the Drupal render pipeline, but it really is just embedded within the Symfony render pipeline.
- After the controller returns a render array, the
VIEW
event will be triggered by theHttpKernel
, because the controller result is not aResponse
, but a render array. MainContentViewSubscriber
is subscribed to theVIEW
event. It checks whether the controller result is a render array, and if so, it guarantees to generate aResponse
.- Next,
MainContentViewSubscriber
checks whether the negotiated request format is supported:- Any format for which a main content renderer service exists (an implementation of
MainContentRendererInterface
is supported. - If the negotiated request format is not supported, a 406 JSON response is generated, which lists the supported formats in a machine-readable way (as per RFC 2616, section 10.4.7).
- Any format for which a main content renderer service exists (an implementation of
- Otherwise, when the negotiated request format is supported, the corresponding main content renderer service is initialized. A response is generated by calling
MainContentRendererInterface::renderResponse()
on the service. That's it!
Main content renderers
Each main content renderer service can choose how to implement its renderResponse()
method. It may, of course choose to add protected helper methods to provide more structure if it's a complex main content renderer.
Drupal 8 ships with the following main content renders (and thus supports rendering any render array in one of the following formats/MIME types):
- HTML:
HtmlRenderer
(text/html
) - AJAX:
AjaxRenderer
(application/vnd.drupal-ajax
) - Dialog:
DialogRenderer
(application/vnd.drupal-dialog
) - Modal:
ModalRenderer
(application/vnd.drupal-modal
)
The HTML main content renderer
This can be considered the HTML main content render pipeline, but it really is just embedded within the Drupal render pipeline (see above).
The above is the high-level flow. But let's take a look at the HTML main content renderer (HtmlRenderer
), because that will be used most often.
HtmlRenderer::prepare()
receives the main content render and ensures we have a'#type' => 'page'
render array (which corresponds to<body>
):- if the main content render array already is
'#type' => 'page'
, then we're done - otherwise, we need to build that
'#type' => 'page'
render array. TheRenderEvents::SELECT_PAGE_DISPLAY_VARIANT
event is dispatched, to select a page display variant.- By default,
SimplePageVariant
is used, which doesn't apply any decorations. - But, when the Block module is enabled,
BlockPageVariant
is used, which allows the site builder to place blocks in any of the page regions, and hence "decorate" the main content. - Drupal 8 modules can subscribe to this event as well, and use a different page variant, even on a per-route basis. (Panels, Page Manager etc. can be cleanly implemented thanks to this event.)
- By default,
- if the main content render array already is
HtmlRenderer::prepare()
now is guaranteed to be working on a'#type' => 'page'
render array.hook_page_attachments()
andhook_page_attachments_alter()
are invoked. (For attaching page-level assets, that aren't bound to a specific page.)HtmlRenderer::renderResponse()
uses the'#type' => 'page'
render array returned by the previous step and wraps it in'#type' => 'html'
.hook_page_top()
andhook_page_bottom()
are invoked.Renderer
(formerlydrupal_render()
) is called on the'#type' => 'html'
render array, which uses thehtml.html.twig
template and the return value is a HTML document as a string.- This HTML string is returned as the
Response
(specifically, aHtmlResponse
object, which is a more specialized subclass ofResponse
).
Steps 1–2 generate the <body>
(page.html.twig
). Steps 3–4 generate the <html>
(html.html.twig
). Step 5 sends a Response
object containing the generated <html>
.
Note: render arrays remain arrays and thus aren't rendered until step 4 above!
Rendering HTML in limited environments
For HTML pages to be rendered in limited environments, such as when you are installing or updating Drupal, or when you put it in maintenance mode, or when an error occurs, a simpler HTML page renderer is used for rendering these bare pages: BareHtmlPageRenderer
.
Resources
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion