D11 Pipeline: VIEW Event & Main Content Renderers
Phase ③
VIEW Event & Main Content Renderers
When a controller returns a render array, the VIEW event is fired and MainContentViewSubscriber negotiates the response format and delegates to the appropriate renderer.
← Phase ② Controller returned a render array
Kernel Event
KernelEvents::VIEW dispatched by HttpKernel
Symfony's
not a
(the render array) and the current
HttpKernel fires this event whenever the controller result isnot a
Response object. The event payload carries the controller result(the render array) and the current
Request.VIEW Event Subscriber
MainContentViewSubscriber::onViewRenderArray()
The highest-priority subscriber for the VIEW event in Drupal core.
Guards that the controller result is actually a render array, then negotiates format.
Guards that the controller result is actually a render array, then negotiates format.
1
Verify controller result is a render array
If not an array → subscriber does nothing; Symfony may throw a LogicException.
2
Negotiate request format
Reads
$request->getRequestFormat() (set earlier by NegotiationMiddleware from Accept header / _format route param / ?_format= query string).3
Look up renderer service for that format
Consults the map of
main_content_renderer-tagged services, keyed by MIME type / format string.Format supported?
✗ No matching renderer
406 Not Acceptable
A JSON response is generated listing the supported formats in machine-readable form, per RFC 2616 §10.4.7. Execution ends here.
✓ Renderer found
MainContentRendererInterface::renderResponse()
The matched renderer service is initialized and its
renderResponse($main_content, $request, $route_match) method is called. It must return a Response.Built-in MainContent Renderer implementations (Drupal 11 core)
Which renderer is selected?
Determined by request format. Each is a tagged service.
HtmlRenderer
format: html · MIME: text/html
The default path for normal page requests. Decorates content with blocks, invokes page hooks, renders Twig. → Phase ④
AjaxRenderer
format: drupal_ajax · MIME: application/vnd.drupal-ajax
Returns an AJAX command array (JSON). Used by Drupal's built-in AJAX framework (
$.ajax callbacks).DialogRenderer
format: drupal_dialog · MIME: application/vnd.drupal-dialog
Opens content in a jQuery UI dialog. Returns JSON with dialog options and rendered HTML.
ModalRenderer
format: drupal_modal · MIME: application/vnd.drupal-modal
Variant of DialogRenderer with
modal: true. Locks background interaction.Extending — custom renderers
Any module can register its own format renderer by implementing MainContentRendererInterface and tagging the service with main_content_renderer and a format attribute. The format is then available via ?_format=myformat or the _format route parameter.
Special case — BareHtmlPageRenderer
For install, update, maintenance mode, and error pages, the full pipeline above is bypassed. BareHtmlPageRenderer is used instead — a simplified renderer with no block decoration, no BigPipe, and minimal asset libraries. It renders #type html directly via the Renderer service.
→ Phase ④ HtmlRenderer (HTML path) or Response returned