top of page

Learn through our Blogs, Get Expert Help, Mentorship & Freelance Support!

Welcome to Colabcodes, where innovation drives technology forward. Explore the latest trends, practical programming tutorials, and in-depth insights across software development, AI, ML, NLP and more. Connect with our experienced freelancers and mentors for personalised guidance and support tailored to your needs.

Coding expert help blog - colabcodes

Jinja Templates in Python: Complete Guide with Examples

  • Writer: Samul Black
    Samul Black
  • 2 days ago
  • 7 min read

Jinja Templates in Python make it simple to build dynamic and reusable web pages without mixing business logic directly into HTML. Jinja (often referred to as Jinja2) is a powerful templating engine widely used in frameworks like Flask and Django to render HTML content dynamically.

In this complete guide, we will explore the core features of Jinja templates, walk through practical examples, and show how they integrate seamlessly with Python applications—especially in web development. Whether we are building a small Flask project, creating dashboards, or designing email templates, Jinja provides the flexibility and efficiency needed to separate logic from presentation.

Jinja in python - Colabcodes

What Are Jinja Templates in Python?

Jinja is a widely used templating engine for Python that allows developers to create dynamic and reusable web pages. Instead of hardcoding values into HTML, Jinja makes it possible to embed variables, apply logic, and reuse template components. This approach not only makes code cleaner but also ensures a clear separation between application logic and presentation.

The engine is commonly known as Jinja2, which was its official name for many years. In recent versions, the “2” has been dropped and the project is now simply called Jinja. Despite this change, the term “Jinja2” is still used extensively across tutorials, documentation, and projects, particularly with Flask applications.


Some of the main advantages of Jinja templates include:


  1. Separation of concerns: Application logic and design remain independent, reducing complexity.

  2. Dynamic rendering: Templates can display different content based on runtime data.

  3. Reusable components: Features like template inheritance and includes help avoid repeating code.

  4. Flexibility: Jinja supports conditionals, loops, filters, and macros directly inside templates.

  5. Integration: It comes pre-integrated with Flask and can also be used as a standalone library.


By combining Python code with Jinja templates, developers can efficiently build web applications that are easy to maintain and scale. Jinja effectively acts as a bridge between backend logic and frontend presentation, ensuring that web pages remain dynamic without cluttering HTML files with Python code.


Important Terminologies in Jinja Templates

Before diving into examples and advanced concepts, it is essential to understand the core terminologies used in Jinja. These terms form the foundation of how Jinja templates work and will make it easier to follow along when working with real-world projects.


Variables

Variables are placeholders that get replaced with actual values at runtime. In Jinja, variables are written inside double curly braces. For example:

<p>Hello, {{ name }}!</p>

When rendered with name="Samuel", the output will be:

<p>Hello, Samuel!</p>

Expressions

Expressions allow simple operations or function calls inside templates. They can be used for calculations, string manipulation, or applying filters. For example:

<p>Price with tax: {{ price * 1.18 }}</p>

Jinja supports inline expressions for concise conditional rendering.

<p>Status: {{ "Active" if user.is_active else "Inactive" }}</p>

This avoids writing long {% if %} blocks for small conditions.


Control Structures

Control structures provide logic inside templates, such as loops and conditionals. These are enclosed in {% ... %} tags.


1. If statement:

{% if user %}
  <p>Welcome, {{ user }}</p>
{% else %}
  <p>Please log in</p>
{% endif %}

2. For loop:

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

Filters

Filters modify variables before displaying them. They are applied using the pipe (|) symbol. For example:

<p>{{ name|upper }}</p>

If name="samuel", the output will be SAMUEL.


Template Inheritance

Jinja supports reusing layouts through inheritance. A base template defines a common structure, and child templates extend it by overriding specific sections.

<!-- base.html -->
<html>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>
<!-- child.html -->
{% extends "base.html" %}
{% block content %}
  <h1>Home Page</h1>
{% endblock %}

Includes

Includes allow inserting smaller template files, such as headers or footers, into other templates.

{% include "header.html" %}

Understanding these terminologies is crucial for working effectively with Jinja. They provide the building blocks for rendering dynamic content, reusing template structures, and keeping code organized across a project.


Hands-On Setup with Jinja Templates in Python

To begin using Jinja in real projects, we will walk through a series of examples. Starting from a simple template rendered with pure Python, we will then expand into loops, conditionals, and finally a complete Flask application that demonstrates template inheritance and dynamic rendering.


Step 1: Install Jinja2

Before writing any code, we need to install Jinja. Although it is often bundled with Flask, it can also be used independently. Installation is done through pip:

pip install Jinja2

This command will add the Jinja templating engine to the environment, making it available for both standalone use and within frameworks like Flask.


Step 2: A Simple Jinja Template Example

To understand how Jinja works, let’s start with the simplest case—rendering a string with placeholders.

from jinja2 import Template

# Define a template with a placeholder
template = Template("Hello, {{ name }}! Welcome to {{ site }}.")

# Render the template with data
output = template.render(name="Samuel", site="ColabCodes")
print(output)

Output:
Hello, Samuel! Welcome to ColabCodes.

Step 3: Using Jinja with Loops and Conditionals

Jinja supports logic inside templates, allowing developers to display content conditionally or iterate over lists.

# Define a template with loop and condition
template = Template("""
{% if users %}
  <h2>User List</h2>
  <ul>
  {% for user in users %}
    <li>{{ user }}</li>
  {% endfor %}
  </ul>
{% else %}
  <p>No users found.</p>
{% endif %}
""")

# Render with data
output = template.render(users=["Alice", "Bob", "Charlie"])
print(output)

Output:
  <h2>User List</h2>
  <ul>
  
    <li>Alice</li>
  
    <li>Bob</li>
  
    <li>Charlie</li>
  
  </ul>

Here, we use {% if %} to check if users exist and {% for %} to loop through the list. This separation of data and presentation makes the template cleaner and easier to maintain. Instead of hardcoding multiple <li> tags, we let Jinja generate them dynamically.


Step 4: Jinja in a Flask Application

While standalone templates are useful for learning, Jinja is most powerful when integrated into a web framework. Flask uses Jinja as its default templating engine, allowing developers to keep HTML files separate while still injecting Python data into them.


Project Structure:

flask_jinja_demo/
│
├── app.py
└── templates/
    ├── base.html
    ├── index.html
    └── users.html

app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def home():
    return render_template("index.html", title="Home", name="Samuel")

@app.route("/users")
def users():
    user_list = ["Alice", "Bob", "Charlie"]
    return render_template("users.html", title="Users", users=user_list)

if __name__ == "__main__":
    app.run(debug=True)

In this application, Flask uses render_template() to connect Python variables with HTML templates. When users visit /, the index.html template is rendered, and when they visit /users, the users.html template is used with a list of names passed from Python.


templates/base.html

<!DOCTYPE html>
<html>
<head>
    <title>{{ title }} - Jinja Demo</title>
</head>
<body>
    <header>
        <h1>My Flask + Jinja App</h1>
        <nav>
            <a href="/">Home</a> |
            <a href="/users">Users</a>
        </nav>
    </header>
    <hr>
    <main>
        {% block content %}{% endblock %}
    </main>
</body>
</html>

This base template defines the layout that other templates will inherit. It contains the HTML structure, navigation bar, and a content block that child templates can override. This prevents repeating the same HTML code in multiple files.


templates/users.html

{% extends "base.html" %}
{% block content %}
    <h2>User List</h2>
    <ul>
        {% for user in users %}
            <li>{{ user }}</li>
        {% endfor %}
    </ul>
{% endblock %}

The users.html template also extends base.html but displays a list of users. By looping over the users list, Jinja generates <li> elements dynamically. This shows how Jinja simplifies working with collections of data inside web pages.


Step 5: Running the Application

Finally, run the application:

python app.py

Output:
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 132-445-278

Adding a bit Style to Jinja Templates

While Jinja handles the dynamic rendering of content, design and user experience come from styling. Flask automatically supports a static directory where CSS, JavaScript, and images can be stored, making it straightforward to integrate styling into Jinja templates. By creating a style.css file inside the static folder and linking it in the base template, we ensure that every page inherits the same design. A single stylesheet can define consistent fonts, background colors, navigation styles, and card-like layouts for lists or tables. The base template then includes this CSS file using {{ url_for('static', filename='style.css') }}, so that all child templates automatically gain the same styling without repeating code. This separation of content, logic, and style makes applications easier to maintain and ensures that the dynamic templates generated by Jinja are also visually appealing.

body {
    font-family: Arial, sans-serif;
    background-color: #f4f6f8;
    margin: 0;
    padding: 0;
}

header {
    background-color: #0f9d58;
    color: white;
    padding: 15px;
    text-align: center;
}

nav a {
    color: white;
    text-decoration: none;
    margin: 0 10px;
    font-weight: bold;
}

nav a:hover {
    text-decoration: underline;
}

main {
    padding: 20px;
}

h2 {
    color: #0f9d58;
}

ul {
    list-style-type: none;
    padding: 0;
}

li {
    background: white;
    margin: 5px 0;
    padding: 10px;
    border-radius: 6px;
    box-shadow: 0px 1px 3px rgba(0,0,0,0.1);
}

In your base.html template, you can link the stylesheet in header like this:

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

Now, when we move to the url http://127.0.0.1:5000/users, we will have a page like this:

Jinja in python - colabcodes

Advanced Features of Jinja Templates

Once the basics of variables, loops, and conditionals are clear, Jinja provides a rich set of advanced features that make templates far more powerful and reusable. These features allow developers to cleanly organize code, avoid duplication, and build dynamic layouts with less effort. The most important ones include filters, macros, and includes.


Macros

Macros work like reusable functions inside templates. They allow blocks of code to be written once and reused multiple times, reducing redundancy.

{% macro render_item(item) %}
    <li>{{ item }}</li>
{% endmacro %}

<ul>
    {% for user in users %}
        {{ render_item(user) }}
    {% endfor %}
</ul>

Here, the macro render_item is defined once and then used repeatedly inside the loop. This makes templates more modular and easier to maintain.

Macros can also be split into separate files and imported, making them reusable across multiple templates.

{% import "forms.html" as forms %}
{{ forms.render_input("username") }}

This is excellent for larger applications where forms, buttons, or table renderers are used in multiple places.


Template Globals

Jinja supports global variables and functions that can be accessed in any template without explicitly passing them from Python.

from flask import Flask
app = Flask(__name__)

@app.context_processor
def inject_globals():
    return dict(company_name="ColabCodes", year=2025)
<footer>
  <p>&copy; {{ year }} {{ company_name }}. All rights reserved.</p>
</footer>

Custom Filters

Beyond built-in filters, developers can define their own to handle domain-specific formatting.

@app.template_filter('reverse')
def reverse_filter(s):
    return s[::-1]
<p>{{ "Hello"|reverse }}</p> <!-- Output: olleH -->

Control Structures with set

Sometimes, you may need to store intermediate results inside a template. The {% set %} statement makes this possible.

{% set total = price * quantity %}
<p>Total: ${{ total }}</p>

This avoids recalculating expressions multiple times.


Conclusion

Jinja templates make it simple to connect Python logic with HTML, enabling clean, dynamic, and reusable web pages. From variables and loops to advanced features like filters, macros, includes, and inheritance, Jinja helps developers keep applications structured and maintainable. By separating presentation from logic, it ensures code stays organized while designs remain consistent.

Whether used in Flask, Django, or standalone projects, Jinja provides a powerful foundation for building scalable and elegant web applications. Mastering its features is a valuable step for any Python developer aiming to create efficient, professional, and user-friendly solutions.

Get in touch for customized mentorship, research and freelance solutions tailored to your needs.

bottom of page