<p>You need to define the template root (<code>password-global</code>) 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.</p>
<p>Use <a href="https://docs.craftcms.com/api/v3/craft-web-view.html#event-register-cp-template-roots" rel="nofollow noreferrer">View::EVENT_REGISTER_CP_TEMPLATE_ROOTS</a> to define your template root:</p>
<pre class="lang-php prettyprint-override"><code>use yii\base\Event;
use craft\web\View;
use craft\events\RegisterTemplateRootsEvent;

// put this inside the init() method
Event::on(
    View::class,
    View::EVENT_REGISTER_CP_TEMPLATE_ROOTS,
    function (RegisterTemplateRootsEvent $e) {
        $e-&gt;roots[$this-&gt;id] = __DIR__ . '/src/templates';
    }
);
</code></pre>
<p>See <a href="https://craftcms.com/docs/3.x/extend/template-roots.html" rel="nofollow noreferrer">Template Roots</a> for more information.</p>
<hr />
<p>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 <code>View::renderTemplate</code>, it only applies to this function call:</p>
<pre class="lang-php prettyprint-override"><code>return \Craft::$app-&gt;view-&gt;renderTemplate(
    'password-global/_formTemplate',
    [],
    \craft\web\View::TEMPLATE_MODE_CP,
);
</code></pre>

CC BY-SA 4.0
2021-10-21T14:33:24.403Z
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
CC BY-SA 4.0
2021-10-21T14:35:15.613Z
@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
CC BY-SA 4.0
2021-10-21T14:36:50.173Z
@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
CC BY-SA 4.0
2021-10-21T14:48:35.377Z
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
CC BY-SA 4.0
2021-10-21T17:05:06.103Z
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
CC BY-SA 4.0
2021-10-21T12:56:02.313Z
2021-10-21T14:35:42.300Z
2021-10-21T14:35:42.300Z
MoritzLost
<p>For future reference, this is what solved my issue:
On main class' init() function -- note the <strong>DIRECTORY_SEPARATOR</strong> part:</p>
<pre><code>Event::on(
            View::class, 
            View::EVENT_REGISTER_SITE_TEMPLATE_ROOTS, 
            function (RegisterTemplateRootsEvent $e) {
            if (is_dir($baseDir = $this-&gt;getBasePath() . DIRECTORY_SEPARATOR . 'templates')) {
                $e-&gt;roots[$this-&gt;id] = $baseDir;
                }
            }
        );
</code></pre>
<p>This is registering this path:</p>
<pre><code>/app/modules/abc/passwordglobal/src/templates
</code></pre>
<p>And in my twig extension function, there's no need to add the TEMPLATE_MODE_SITE parameter:</p>
<pre><code> $html = \Craft::$app-&gt;view-&gt;renderTemplate(
                'password-global/_formTemplate');
</code></pre>

CC BY-SA 4.0
2021-10-21T17:11:59.573Z
2021-10-21T17:11:59.573Z
mauricio
<p>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!</p>
<p>I'm using Craft 3.7, and I am trying to render a template file, which is called from my module's twig extension.</p>
<p>I have this:</p>
<p>The template file is in</p>
<p><code>modules/abc/passwordglobal/src/templates/_formTemplate.twig</code></p>
<p><code>In app.php:</code></p>
<pre><code>'modules' =&gt; [
        'password-global' =&gt; abc\passwordglobal\PasswordGlobal::class,
    ],
    'bootstrap' =&gt; ['password-global']
</code></pre>
<p>And in my <em>twig extension</em> file, I have a function with this code:</p>
<pre><code>// ... returns html template;
$oldMode = \Craft::$app-&gt;view-&gt;getTemplateMode();
\Craft::$app-&gt;view-&gt;setTemplateMode(View::TEMPLATE_MODE_CP);
$html = \Craft::$app-&gt;view-&gt;renderTemplate('password-global/templates/_formTemplate');
\Craft::$app-&gt;view-&gt;setTemplateMode($oldMode);

return $html;
</code></pre>
<p>And the error I see:</p>
<p><strong>Twig Template Loading Error – craft\web\twig\TemplateLoaderException</strong></p>
<p><strong>Unable to find the template “password-global/templates/_formTemplate”.</strong></p>
<p>Thank you so much.</p>

<templating><modules>
Unable to find the template
# Unable to find the template

(Asked by: username_0 on 2021-10-21)

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
modules/abc/passwordglobal/src/templates/_formTemplate.twig
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();
\Craft::$app->view->setTemplateMode(View::TEMPLATE_MODE_CP);
$html = \Craft::$app->view->renderTemplate('password-global/templates/_formTemplate');
\Craft::$app->view->setTemplateMode($oldMode);

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.

**Tags:** templating, modules

---

## 2 answers

### Answer by username_1 on 2021-10-21. Score: 3 | Accepted answer

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
Event::on(
    View::class,
    View::EVENT_REGISTER_CP_TEMPLATE_ROOTS,
    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(
    'password-global/_formTemplate',
    [],
    \craft\web\View::TEMPLATE_MODE_CP,
);

> **Comments:**
> **username_0**: 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?  
> **username_1**: @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 :)  
> **username_1**: @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.  
> **username_0**: 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()?  
> **username_0**: 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.  

---

### Answer by username_0 on 2021-10-21. Score: 3

For future reference, this is what solved my issue:
On main class' init() function -- note the DIRECTORY_SEPARATOR part:
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;
                }
            }
        );

This is registering this path:
/app/modules/abc/passwordglobal/src/templates

And in my twig extension function, there's no need to add the TEMPLATE_MODE_SITE parameter:
 $html = \Craft::$app->view->renderTemplate(
                'password-global/_formTemplate');


---

