Unmanaged Files in Drupal (Part 4): Rendering via a Twig Template

Precision Over Placement: Output Files Exactly Where You Want with Theme Hooks
Unmanaged Files in Drupal: Rendering via a Twig Template (Part 4)

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/test to 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() }}.

Note: The vision of this web portal is to help promote news and stories around the Drupal community and promote and celebrate the people and organizations in the community. We strive to create and distribute our content based on these content policy. If you see any omission/variation on this please reach out to us at #thedroptimes channel on Drupal Slack and we will try to address the issue as best we can.

Related Organizations

Upcoming Events

Latest Opportunities