Skip to Content
CustomizeRepository UIJinja templates

Template Rendering with Jinja easy

Jinja is the server-side templating engine used throughout InvenioRDM for rendering HTML pages.
NRP-based repositories build on top of this system and use Jinja extensively for page layout, custom UI views, and template overrides.

If you are more interested in how to change built-in templates with your custom ones, the topic is covered in great detail undder the Branding -> Templating docs

In this page we will focus on creation and usage of custom Jinja templates including how to use them, how templates interact with UI Resources (the NRP Invenio mechanism for generating HTML responses for front-end pages).

Referencing templates in UI Resources

UI Resources in NRP repositories define view methods tied to specific templates.
Which view renders which template is determined by a UIResourceConfig configuration.

class DatasetUIResourceConfig(UIResourceConfig): blueprint_name = "datasets" url_prefix = "/datasets" template_folder = "templates" templates = { "record_detail": "datasets/detail.html", "record_preview": "datasets/preview.html", }

This means:

  • Templates are looked up inside your registered module’s templates/ directory
  • datasets/detail.html resolves to ui/datasets/templates/datasets/detail.html
  • record_detail view method of DatasetUIResource uses this exact template to render a detail of a record.

Using templates inside UI Resource methods

When implementing a custom view, the following method is typically used to render a template while including any context variables. This method supports both plain Jinja and enhanced JinjaX templates, while pulling the configured template from the templates map of UIResourceConfig.

from oarepo_ui.resources import UIResource from oarepo_ui.proxies import current_oarepo_ui class DatasetUIResource(UIResource): def detail(self, identity, pid, record=None, **kwargs): context_kwargs = { "title": "Demo" } return current_oarepo_ui.catalog.render( self.get_jinjax_macro( "record_detail", ), **context_kwargs, )

This keeps view logic clean: UI Resources provide the context driven by business logic, and templates handle the rendering.

Creating new templates

To create and use a new template, follow this elementary steps:

Create a file under your /templates folder, such as:

ui/example/templates/example/hello.html
Hello from your Jinja template: <h1>Hello, {{ name }}!</h1> This will render at any route pointing to the hello view.

Add it to your templates mapping in the ResourceConfig:

templates = { "hello": "example/hello.html" }

Make sure to render it from your UI Resource’s hello view:

return current_oarepo_ui.catalog.render( self.get_jinjax_macro( "hello", ), **{"name": "Mirek"}, )

Basic Jinja syntax refresher

Here we cover just the most common syntax examples you might come across in a Jinja templates. Please refer to the official Jinja Template Designer  docs for a full reference on Jinja syntax.

Variables

Prints a value of a context variable.

<p>{{ user.email }}</p>

Filters

One or more chained filters processing/transforming the value of a context variable.

{{ title | upper }}

Loops

Iterate over iterable context variable values (like lists or tuples).

<ul> {% for item in items %} <li>{{ item }}</li> {% endfor %} </ul>

Conditionals

The if statement in Jinja is comparable with the Python if statement. Used to test for a result of a boolean expression.

{% if record.access == "public" %} <p>Public record</p> {% endif %}

Macros

Include other snippet of Jinja code into a template.

{% macro input(name, value='', type='text', size=20) -%} <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}"> {%- endmacro %} <p>{{ input('username') }}</p>

Template inclusion & composition

Reusable shared templates (navbars, footers, panels) could be placed in shared dirs & then included where needed:

{% include "branding/header.html" %}

Blocks

Defines extension points of a template or overrides block contents from inherited template (see Template Inheritance)

{% extends "invenio_theme/page.html" %} {% block page_body %} <div class="ui container"> <h1>{{ record.title }}</h1> </div> {% endblock %}

Here a page_body block is defined, overriding content from the same block in invenio_theme/page.html.

Template Inheritance

Provide or extend content of blocks defined in inherited template.

For example, InvenioRDM uses a central base template for almost all repository pages:

invenio_theme/page.html

You typically extend it with extends keyword and provide your own content for the blocks defined by extended template:

{% extends "invenio_theme/page.html" %} {% block page_body %} <div class="ui container"> <h1>{{ record.title }}</h1> </div> {% endblock %}

For cases where you just want to append to the beginning/end of a block, there is the super() keyword.

{% extends "invenio_theme/page.html" %} {% block page_footer %} <p>I'm before the standard footer content.</p> {{ super() }} <p>I folow after the standard footer content.</p> {% endblock %}

This will bring up original block content from the extended template.

Best practices

  1. Keep templates light & simple.
  2. Use templates only for presentational logic.
  3. Use UIResourceConfig to map templates to views.
  4. Never hard-code template filenames inside view methods.
  5. Prefer macros for repeated UI fragments.
  6. Templates should do minimal assumptions and rely mainly on the context passed by UIResource views.
Last updated on