Skip to main content

Your First Flask App (Part 2): Templates and Jinja2

In the last article, we created our first Flask application, but our view function returned a simple string of HTML. This is not practical for building real websites. A real website has complex HTML, and we need a way to keep our Python logic separate from the presentation markup.

Flask solves this problem by integrating the powerful Jinja2 templating engine. A template is a file (usually HTML) that contains placeholders for dynamic data. Flask can take a template, fill in the placeholders with actual data, and return the final, rendered HTML to the browser.


📚 Prerequisites

You should have a minimal Flask application running from the previous article, with the recommended app.py, templates/, and static/ folder structure.


🎯 Article Outline: What You'll Master

In this article, you will learn:

  • Rendering Templates: How to use the render_template() function to serve an HTML file.
  • Passing Variables: How to send data from your Python code to your HTML template.
  • Jinja2 Delimiters: Learn the basic syntax for embedding expressions ({{ ... }}) and statements ({% ... %}) in your HTML.
  • Using Control Structures: How to use if statements and for loops directly inside your HTML to make it dynamic.

🧠 Section 1: Rendering Your First Template

First, let's create a simple HTML file. By default, Flask looks for templates in a folder named templates in your project's root directory.

Create templates/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My First Template</title>
</head>
<body>
<h1>Hello from a Template!</h1>
<p>This is a real HTML file.</p>
</body>
</html>

Now, let's modify our app.py to render this file instead of returning a string. To do this, we import and use the render_template() function from Flask.

Modify app.py:

# We need to import render_template
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def index():
# Instead of returning a string, we render a file.
# Flask will look for this file in the 'templates' folder.
return render_template("index.html")

Run your app.py and visit http://127.0.0.1:5000/. You should see your rendered HTML page.


💻 Section 2: Passing Variables to Templates

This is where the magic happens. We can pass data from our Python view function to the template. We do this by passing keyword arguments to the render_template() function.

Modify app.py:

from flask import Flask, render_template
import datetime

app = Flask(__name__)

@app.route("/")
def index():
# Let's define some variables to pass to the template
my_name = "Alice"
current_time = datetime.datetime.now()

# Pass the variables as keyword arguments
return render_template("index.html", name=my_name, time=current_time)

Now, we need to modify our template to display these variables. Jinja2 uses special delimiters for this:

  • {{ ... }}: For expressions or variables. This is where the content will be printed.
  • {% ... %}: For statements, like if conditions or for loops.

Modify templates/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<title>Dynamic Page</title>
</head>
<body>
<!-- Use {{ ... }} to display the variable passed from Flask -->
<h1>Hello, {{ name }}!</h1>
<p>The current time is: {{ time.strftime('%Y-%m-%d %H:%M:%S') }}</p>
</body>
</html>

Now, when you refresh the page, you'll see "Hello, Alice!" and the current time. Notice that you can even call methods (like .strftime()) on the objects you pass to the template.


🛠️ Section 3: Using Control Structures in Jinja2

Jinja2 also supports control structures like loops and conditionals, which use the {% ... %} syntax.

for Loops

Let's pass a list from our Python code and loop through it in the template.

Modify app.py:

@app.route("/items")
def item_list():
# A list of items to display
items = ["Apple", "Banana", "Cherry"]
return render_template("items.html", items=items)

Create templates/items.html:

<!DOCTYPE html>
<html>
<body>
<h2>My Shopping List</h2>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</body>
</html>

Visiting /items will now display an unordered list of the fruits.

if Statements

We can also conditionally show parts of the template.

Modify app.py:

@app.route("/user/<username>")
def user_profile(username):
# Let's pretend we have a list of logged-in users
logged_in_users = ["Alice", "Bob"]
return render_template("user.html", user=username, logged_in=username in logged_in_users)

Create templates/user.html:

<!DOCTYPE html>
<html>
<body>
<h1>Profile for {{ user }}</h1>

{% if logged_in %}
<p>Welcome back, you are logged in!</p>
{% else %}
<p>Welcome, guest! Please log in.</p>
{% endif %}
</body>
</html>

Now, visiting /user/Alice will show the logged-in message, while visiting /user/Charlie will show the guest message.


✨ Conclusion & Key Takeaways

Templates are the key to creating dynamic, multi-page web applications. By separating your Python logic from your HTML presentation, you create code that is far more organized and maintainable.

Let's summarize the key takeaways:

  • Use render_template() to serve HTML files from the templates folder.
  • Pass variables from your view function to the template as keyword arguments.
  • Use {{ variable }} in your HTML to display the value of a variable.
  • Use {% ... %} to embed control structures like for loops and if statements directly into your HTML.

➡️ Next Steps

We've seen how to create different pages using different routes. In the next article, we'll dive deeper into "Routing in Flask: Variable rules and URL building," learning how to create dynamic URLs and link between our pages effectively.

Happy templating!