Unmanaged Files in Drupal (Part 4): Rendering via a Twig Template
In Part 3 we exposed the file handler through a Block Plugin, letting site builders drop the output into regions via the UI. That works great for flexibility, but sometimes you want tighter control: a fixed piece of markup directly in a page template, not dependent on the block system at all.
Register the template
This approach registers a module-provided Twig template using Drupal’s theme system (hook_theme()). This allows custom templates to be discoverable and used with defined variables passed directly from a controller.
/**
* Implements hook_theme().
*
* Registers a module-provided template so we don't need a custom theme.
*/
function unmanaged_files_theme() {
return [
'unmanaged_files_test' => [
'variables' => [
'image_url' => NULL,
'uri' => NULL,
'message' => NULL,
],
'template' => 'unmanaged-files-test',
],
];
}
The controller
The controller renders content using the new theme hook rather than manually constructing markup in PHP.
/**
* Renders the unmanaged files test page using the module template.
*/
public function view(): array {
$uri = $this->handler->getRandomFile();
if (!$uri) {
return [
'#theme' => 'unmanaged_files_test',
'#message' => $this->t('No files found under public://segregated_maps'),
'#cache' => ['max-age' => 1],
];
}
$url = $this->urlGen->generateAbsoluteString($uri);
return [
'#theme' => 'unmanaged_files_test',
'#image_url' => $url,
'#uri' => $uri,
'#cache' => ['max-age' => 1],
];
}
The template
The Twig template is saved as modules/custom/unmanaged_files/templates/unmanaged-files-test.html.twig and renders variables output by the controller.
{#
/**
* @file
* Renders a random unmanaged file picked by the service.
*/
#}
Unmanaged files test
{% if image_url %}
Picked: {{ uri }}
{% else %}
{{ message }}
{% endif %}
- Disable the block from Part 3 to avoid duplication
- Clear caches using
ddev drush cr - Visit
/unmanaged-files/testto test the new template output
Why this approach?
Part 3 (Block Plugin): Enables UI-driven placement and visibility control, ideal for editorial use.
Part 4 (Twig Template): Offers tighter control for themers and developers who need precise, fixed markup in templates.
Both methods rely on the same service, differing only in the integration layer—block vs. theme.
In Part 5, this series will explore exposing the handler via a custom Twig function, enabling inline usage such as {{ random_unmanaged_file() }}.


