I've got default images located in a custom module directory and I'd like to load them using hook_preprocess_node(). I then will pass the images to the template file.

In the screenshot, I'm loading an image into $default_image, which renders correctly when passed to the template file.

Instead of loading the image from an entity field, I want to read the image directly from the directory in the custom module. Clearly, the path I manually built into $premier_tech_image is incorrect.


The problem is NOT how to access the file. For example, we use code like this for JSON feeds.

$module_handler = $this->moduleHandler;
$module_path = $module_handler->getModule('my_custom_module')->getPath();
$resource = $module_path . '/resources/countries.json';

How do I correctly pass the reference to an image located in the directory of a custom module to the template file?

  • 1
    Does this answer your question? Get raw and relative path of a file uri – leymannx May 29 at 16:50
  • 1
    For file objects it's either $file->createFileUrl() or file_url_transform_relative(file_create_url($file_uri)). – leymannx May 29 at 16:52
  • @leymannx that might help. I think the issue is that I need to create a consumable URI to pass to the template. The code that I used above in the example only results in a relative path to the file like "web/modules/custom/custom_module/images/premier-technology.png – sea26.2 May 29 at 17:00
  • Yeah, then the duplicate won't help. As you have no file object. But the answers below should give you what you want. – leymannx May 29 at 17:04

A Drupal URI starts with a stream wrapper like "public://" that indicates to Drupal how to handle that resource. Typically, the "public" stream wrapper is defined as "treat this resource as a local file within sites/default/files."

Your module assets are not located within sites/default/files, so they cannot use the "public" stream wrapper.

Drupal doesn't actually come out-of-the-box with a registered stream wrapper for module or theme assets. You might try installing https://www.drupal.org/project/system_stream_wrapper, which would then allow you to make a URI like module://tableau_partners_sync/images/premier-technology.png.

  • @bingo! That is exactly the issue. Do I need system_stream_wrapper to do that? I'm considering a pivot and creating an admin UI in configuration to store the files. Probably a better approach, Just a PITA to write all the code needed for that – sea26.2 May 29 at 17:18
  • 1
    Lots of file handling stuff in Drupal and Twig functions assume you're passing a URI parameter. Notably, image style creation requires a URI, for example. If you need to use those functions and you want to pass a module/theme asset, you'll need system_stream_wrapper. But if your use case is simple enough that you just need a plain-old URL to a static asset, you should be able to refactor your Twig to use just a relative path. – Les Lim May 29 at 17:29
  • Although the issue is not solved, your answer addresses the problem with viable options. In my original question, it was clear that what I was passing to Twig is not consumable in the way that "normal" assets are. The key to this is the need to have a system_stream_wrapper. Or - punt and find a way to refactor Twig to use a relative path (I'm leaning that way in the interest of simplicity and time invested). Thanks for feedback – sea26.2 May 29 at 18:29

Try letting Drupal generate the module path:

$modulePath = \Drupal::service('module_handler')->getModule('MYMODULE')->getPath();
$imagePath = $modulePath . "/images/my-image.jpg";
  • That was my first thought... and I tried it. ` $module_handler = \Drupal::moduleHandler(); $module_path = $module_handler->getModule('company_partners_sync')->getPath(); $premier_tech_image = $module_path . '/images/premier-technology.png'; $variables['default_partner_logo'] = $premier_tech_image;` But - when it gets to twig - the file path is somehow interpreted as files\... – sea26.2 May 29 at 4:07
  • To confirm, if you print_r($premier_tech_image); or kint($premier_tech_image); in preprocess_node(), is the path files/...? Did you clear cache after updating the module path to ->getModule? – Prestosaurus May 29 at 4:13

Drupal 8 still has drupal_get_path(), which is used from Drupal core modules and third-party modules for the path of files contained in modules directories. (See the code for ckeditor_ckeditor_css_alter() or the example code given in hook_js_alter().

function hook_js_alter(&$javascript, \Drupal\Core\Asset\AttachedAssetsInterface $assets) {
  $javascript['core/assets/vendor/jquery/jquery.min.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js';

Deprecate drupal_get_path() and drupal_get_filename() and replace with ExtensionList::getPath() and ExtensionList::getPathname() aims to deprecate the function.
At the moment I am writing this answer, its status is still Needs work (for Drupal 9.3.x). When it's marked as fixed, instead of drupal_get_path('module', $module_name), modules will use \Drupal::service('extension.list.module')->getPath($module_name).

For example, the previous example hook could be rewritten in this way.

function hook_js_alter(&$javascript, \Drupal\Core\Asset\AttachedAssetsInterface $assets) {
  $javascript['core/assets/vendor/jquery/jquery.min.js']['data'] = \Drupal::service('extension.list.module')->getPath('jquery_update') . '/jquery.js';

Alternatively, \Drupal::service('extension.path.resolver')->getPath('module', $module_name) could be used, which is what drupal_get_path() and drupal_get_filename() have been deprecated in favor of extension listing services suggests to use in the case the extension type is dynamically specified.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.