Skip to main content

The Template Resolution Process

Flask uses a specialized template loading system to support its modular architecture. Instead of a simple file-system lookup, Flask employs a "dispatching" mechanism that coordinates template discovery across the main application and all registered blueprints. This process is managed by the DispatchingJinjaLoader class in flask.templating.

The Dispatching Mechanism​

The DispatchingJinjaLoader does not load templates directly from the disk. Instead, it acts as a coordinator that iterates through the individual loaders of the application and its blueprints. When a template is requested via render_template, the loader searches for the first match according to a strict priority order.

Resolution Priority​

The search order is determined by the _iter_loaders method. Flask prioritizes the application's own templates over those provided by blueprints:

  1. The Application Loader: Flask first checks the template_folder configured on the Flask application instance.
  2. Blueprint Loaders: If the template is not found in the application folder, Flask iterates through all registered blueprints in the order they were registered.
def _iter_loaders(self, template: str) -> t.Iterator[tuple[Scaffold, BaseLoader]]:
loader = self.app.jinja_loader
if loader is not None:
yield self.app, loader

for blueprint in self.app.iter_blueprints():
loader = blueprint.jinja_loader
if loader is not None:
yield blueprint, loader

This design allows application developers to override templates provided by blueprints. For example, if a blueprint named auth provides a template at auth/login.html, the application can provide its own version by placing a file at the same path in its own templates directory.

Fast vs. Explained Loading​

The DispatchingJinjaLoader implements two modes for finding template sources: a "fast" mode for production performance and an "explained" mode for debugging.

Fast Resolution​

In standard operation, _get_source_fast returns the first template source it finds. It silently ignores TemplateNotFound exceptions from individual loaders until it has exhausted all possibilities.

def _get_source_fast(
self, environment: BaseEnvironment, template: str
) -> tuple[str, str | None, t.Callable[[], bool] | None]:
for _srcobj, loader in self._iter_loaders(template):
try:
return loader.get_source(environment, template)
except TemplateNotFound:
continue
raise TemplateNotFound(template)

Debugging with Template Explanation​

When the configuration option EXPLAIN_TEMPLATE_LOADING is set to True, Flask switches to _get_source_explained. This method tracks every attempt made by the loader, recording which loaders were queried and whether they successfully found the template.

This information is then passed to flask.debughelpers.explain_template_loading_attempts, which logs the resolution process. This is particularly useful for diagnosing why a template is being shadowed by another or why a template cannot be found despite existing on the filesystem.

Customizing Template Discovery​

While Flask automatically configures the DispatchingJinjaLoader, developers can customize how the global loader is created by overriding the create_global_jinja_loader method in a Flask subclass.

class MyFlask(Flask):
def create_global_jinja_loader(self):
from jinja2 import DictLoader
# Return a custom loader that might include additional sources
return DictLoader({"index.html": "Hello Custom World!"})

However, Flask's internal documentation in src/flask/sansio/app.py notes that overriding this method is generally discouraged. In most cases, customizing the template_folder on the application or individual blueprints is sufficient to achieve the desired template structure.

Constraints and Requirements​

For a blueprint or application to participate in template resolution, it must have a template_folder defined. If the template_folder attribute is None (the default for blueprints), the jinja_loader property will return None, and the DispatchingJinjaLoader will skip that component during its search.

Additionally, because blueprints are searched in registration order, if two blueprints provide a template with the same path, the one registered first on the Flask application will always take precedence.