I know this question has been asked multiple times. I have read all the post on this topic, and craft's docs. But still, I haven't figure out what I'm doing wrong. I would really appreciate a hand here!

I'm using Craft 3.7, and I am trying to render a template file, which is called from my module's twig extension.

I have this:

The template file is in


In app.php:

'modules' => [
        'password-global' => abc\passwordglobal\PasswordGlobal::class,
    'bootstrap' => ['password-global']

And in my twig extension file, I have a function with this code:

// ... returns html template;
$oldMode = \Craft::$app->view->getTemplateMode();
$html = \Craft::$app->view->renderTemplate('password-global/templates/_formTemplate');

return $html;

And the error I see:

Twig Template Loading Error – craft\web\twig\TemplateLoaderException

Unable to find the template “password-global/templates/_formTemplate”.

Thank you so much.

2 Answers 2


You need to define the template root (password-global) in your main module file. This is a gotcha when working on site-specific modules instead of plugins – plugins get a template root based on their ID automatically, but modules don't.

Use View::EVENT_REGISTER_CP_TEMPLATE_ROOTS to define your template root:

use yii\base\Event;
use craft\web\View;
use craft\events\RegisterTemplateRootsEvent;

// put this inside the init() method
    function (RegisterTemplateRootsEvent $e) {
        $e->roots[$this->id] = __DIR__ . '/src/templates';

See Template Roots for more information.

Small sidenote, you don't need to manually keep track of and restore the old template mode afterwards. If you pass a template mode as the third argument to View::renderTemplate, it only applies to this function call:

return \Craft::$app->view->renderTemplate(
  • thank you so much for your help with this. It clarifies quite a few things. I did as you indicated: added the template root in the main module's file init(). And in my twig extension file, in the function that should render the temlate I have now \Craft::$app->view->renderTemplate('password-global/_formTemplate'); However, I still get the same error. Not sure what's wrong. Side question: I'm using this on the site's front end. Should I use EVENT_REGISTER_SITE_TEMPLATE_ROOTS instead?
    – mauricio
    Oct 21, 2021 at 14:33
  • @mauricio Yes, use EVENT_REGISTER_SITE_TEMPLATE_ROOTS if you need the templates in the frontend. Otherwise they won't be available. Or just use both :)
    – MoritzLost
    Oct 21, 2021 at 14:35
  • @mauricio If it's still not working, try rendering the template with TEMPLATE_MODE_SITE. Not quite sure how custom template roots interact with template modes.
    – MoritzLost
    Oct 21, 2021 at 14:36
  • 1
    hmm, Ok, using now EVENT_REGISTER_SITE_TEMPLATE_ROOTS, and rendering with TEMPLATE_MODE_SITE. Do you know if there's a way to see what is the root url that got registered in the module's init()?
    – mauricio
    Oct 21, 2021 at 14:48
  • 1
    wow, finally, problem solved(!!)... By chance I saw another post using this registration of the template root: ``` Event::on( View::class, View::EVENT_REGISTER_SITE_TEMPLATE_ROOTS, function (RegisterTemplateRootsEvent $e) { if (is_dir($baseDir = $this->getBasePath() . DIRECTORY_SEPARATOR . 'templates')) { $e->roots[$this->id] = $baseDir; } } ); ``` The little change in the init made the difference. Thanks for all the input.
    – mauricio
    Oct 21, 2021 at 17:05

For future reference, this is what solved my issue: On main class' init() function -- note the DIRECTORY_SEPARATOR part:

            function (RegisterTemplateRootsEvent $e) {
            if (is_dir($baseDir = $this->getBasePath() . DIRECTORY_SEPARATOR . 'templates')) {
                $e->roots[$this->id] = $baseDir;

This is registering this path:


And in my twig extension function, there's no need to add the TEMPLATE_MODE_SITE parameter:

 $html = \Craft::$app->view->renderTemplate(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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