A Deep Dive into Flask Templates

    Kabaki Antony

    In this article, we’ll take a deep dive into Flask templates, exploring their importance and benefits, and learning how to create and render templates, use template inheritance and layouts, work with template variables and control structures, handle forms and user input, work with built-in filters and custom filters, manage static files and media, and deploy advanced template techniques.

    Table of Contents
    1. An Introduction to Flask Templates
    2. Creating and Rendering Templates
    3. Template Inheritance and Layouts
    4. Template Variables and Control Structures
    5. Template Context and Global Variables
    6. Template Forms and User Input
    7. Built-in Filters and Custom Filters
    8. Working with Static Files and Media
    9. Advanced Template Techniques
    10. Conclusion

    An Introduction to Flask Templates

    Flask is a popular Python micro web framework that provides powerful tools for building dynamic web applications. One of the key features of Flask is its powerful templating engine, which enables us to separate the presentation logic from the application logic in our web applications.

    Flask templates are built on the Jinja2 template engine. They enable us to create dynamic and interactive web pages by combining HTML with Python code.

    By the end of this article, you’ll have a comprehensive understanding of Flask templates, and you’ll be equipped with the knowledge to create dynamic and visually appealing web applications using Flask. Whether you’re a beginner or an experienced Flask developer, this deep dive into Flask templates will enhance your understanding and proficiency in building web interfaces.

    Note: this is an advanced article on Flask, so to get the most out of it you’ll need to have a basic understanding of how Flask works. To get up to speed with the basics of Flask, check out this introduction to Flask.

    Why are Templates Important in Flask?

    Templates are key in Flask, as they promote code organization, maintainability, and reusability. By separating the presentation logic from the business logic, templates make it easier to manage and update a user interface without modifying the underlying application code. This separation improves collaboration between developers and designers, allowing each team to work on different aspects of the application independently.

    Some of the benefits of using templates in Flask are laid out below.

    Code Reusability. Templates enable us to define reusable components, such as headers, footers, navigation bars, and other components that can be included on multiple pages. This promotes a consistent user interface across the application and saves us from duplication.

    Improved Readability. The separation of HTML code from the Python code leaves us with clean code, making it easy to read and understand.

    Ease of maintenance. Building on improved readability, having code separated makes it easy for us to maintain. We can update the business logic without touching the template or presentation code, or we can update the templates without touching the Python code.

    Flexibility. Using templates in Flask makes it easy to pass data to and from templates, allowing our application to create dynamic content. This gives our application the flexibility to handle different types of needs for different users.

    Creating and Rendering Templates

    Building on the philosophy of simplicity in Flask, working with templates is generally straightforward. One important requirement is that templates in Flask have to reside in a directory named templates, and this directory must be located in the same directory as our application file.

    Here’s an example of an application structure:

    ├── app.py
    └── templates/
      └── index.html

    The code block above is a plain-text representation of how our application could be structured. It shows a Flask project named my_app, which has an app.py file that will contain the application logic, and a templates directory that contains an index.html file. Structuring the application this way ensures that our business logic (Python code) is separated from the presentation logic (templates).

    Since Flask uses the Jinja2 templating engine, it can render files in various extensions such as .svg, .html, .csv. However, for this article, we’ll use the .html extension, since all of the files we’ll be working with and rendering are HTML documents.

    Let’s then create a template named index.html and place it in the templates directory:

    <!DOCTYPE html>
          <p>This is the index page.</p>

    This is a basic HTML template: it has a head and body section. To render templates in Flask, the flask module has the render_template() method, which takes in the name of the template file as its first argument and also takes in optional keyword arguments representing the data that we may want to pass to the template.

    Here’s an example:

    # sample.py
    from flask import Flask, render_template
    app = Flask(__name__)
    def index():
      return render_template('index.html')
    if __name__ == '__main__':

    In the Flask application above, we import Flask and render_template() from the flask module. The Flask class helps us create the Flask application instance. The render_template() will render and return the index.html template.

    index() is the view function that will handle requests from the root URL. In this example, this function will just return the template rendered by the render_template() function.

    So, in summary, we create a template, place it in the templates directory, and then use the render_template() function to return the template to the user.

    Template Inheritance and Layouts

    Inheritance in object-oriented programming is a concept that enables classes to inherit or acquire the properties and behaviors of another class. Inheritance in the context of templates in Flask is a feature provided by Flask and the Jinja2 templating engine that enables us to define a base template and child templates.

    Template inheritance works by creating a base template that contains the common elements and structure of our web pages. This base template serves as a blueprint that can be extended and customized by the individual child templates. The child templates inherit the structure and content of the base template and can override specific blocks to provide unique content for each page.

    Creating a base template

    To set up template inheritance, we start by creating a base template. It will contain the common HTML structure and elements. Typically, it will include the <head> section, the navigation menu, the footer, and any other elements that might be shared across the various pages of the web application.

    Here’s an example of a base template named base.html:

    <!DOCTYPE html>
            <title>{% block title %}{% endblock %}</title>
                <!-- navigation menu will go in here -->
          <div class="content">
                {% block content %}{% endblock %}
                <!-- the footer code will go in here -->

    This example shows an HTML document. It includes some tags such as {% block %}. The block tags are important in template inheritance, since they indicate all the code that the child template can override. A block tag will usually have a descriptive name that indicates the kind of content the block expects. It’s marked by an opening and a closing block. So the base.html template has two blocks: the title and the content block. Any child template that inherits this template is able to provide its own specific content for the two blocks.

    Extending the base template in child templates

    Extending (inheriting) a base template is generally straightforward. To show that a template is inheriting from a particular template, we use the {% extends %} tag, followed by the path to the template we’re inheriting. Once we’ve indicated the template we’re extending, we can go ahead and override the blocks defined in the parent template.

    Here’s an example of a child template named home.html that extends the base.html template:

    {% extends 'base.html' %}
    {% block title %}Home - My Website{% endblock %}
    {% block content %}
      <h1>Welcome to My Website</h1>
      <p>This is the home page content.</p>
    {% endblock %}

    In the example above, the first statement is the extends tag ({% extends ‘base.html’ %}). This statement tells Flask that the home.html template extends the base.html template. The {% block title %} and {% block content %} tags override the corresponding blocks in the base template, providing specific content for the home page.

    To override any of the blocks provided in the base.html template, the child template has to provide its own specific content. As we can see, the home.html template provides its own content for the two blocks, so when it’s rendered on the page it will show the content provided on its own pages.

    Overall, using template inheritance in our projects enables us to reuse code. It also enables us to define content that can be used across the different pages, giving our projects a consistent look. At the same time, it gives us the room to define page-specific content by overriding blocks in the child templates.

    Template Variables and Control Structures

    To be able to create dynamic applications, we need the ability to pass data to templates from our Python application. To pass variables from a Flask view function to a template, we can include them as arguments when calling the render_template() function as key–value pairs, or in a context dictionary. The key–value pairs represent the variable name in the template, while the value is the actual value of the variable.

    Here’s an example of passing variables as key=value pairs:

    from flask import Flask, render_template
    app = Flask(__name__)
    def index():
      name = "Antony"
      age = 30
      return render_template('index.html', name=name, age=age)

    In the example above, we have an index() function that declares two variables — name and age — which are passed to the template as optional arguments to the render_template() function.

    Here’s an example of how we could pass variables as context dictionaries to the template:

    from flask import Flask, render_template
    app = Flask(__name__)
    def index():
      name = "John"
      age = 25
      context = {
          'name': name,
          'age': age
      return render_template('index.html', **context)

    The example above is similar to the first one in terms of functionality. However, in this example, we create a context dictionary named context, which contains key–value pairs of our variables name and age. Then we pass the information to the render_template() function, with the template name index.html as the first argument and the context dictionary as the second argument. However, we’ll use **context notation to unpack the context dictionary.

    Once we pass variables to a template, we can access them using the {{ variable_name }} syntax on the template. Variables can represent any type of data, such as strings, numbers, lists, or even more complex objects.

    To access the variables in the template, we’ll use their names as specified in the context dictionary, or in the first case, where we pass them as key–value pairs.

    Here’s an example where we retrieve the name and age variables:

    <!DOCTYPE html>
      <title>My Website</title>
      <h1>Hello, {{ name }}!</h1>
      <p>You are {{ age }} years old.</p>

    In this example, we have the {{ name }} and {{ age }} placeholders, which are replaced by the actual values passed from the view function. Passing variables using a context dictionary lets us keep our render_template() function cleaner by not passing all the variables one by one.

    In the next section, let’s look at how we could use the various Python control structures in the templates to display and work with data.

    Using control structures in templates

    Control structures in programming are constructs that determine the order in which code blocks are executed based on certain conditions. They allow us to control the execution of a program by specifying the sequence and conditions under which different actions should be performed. In Flask templates, we can use Python control structures like conditionals (if, else, if) or iteration control structures (for, while).

    Using these control structures, we’re able to work with different kinds of data and therefore have pages that are more dynamic. We’ll next look at how we could use the various kinds of control structures in our templates.

    Conditionals (if, else, elif)

    Conditional statements enable our web application to take different paths or perform different actions depending on whether a condition evaluates to true or false. The {% if %}, {% else %}, and{% elif %} tags are used for conditionals. Using conditional statements, we can display certain data or work on it to give a different outcome depending on different conditions.

    Here’s an example:

    {% if temperature > 30 %}
      <p>It's a hot day!</p>
    {% elif temperature > 20 %}
      <p>It's a pleasant day.</p>
    {% else %}
      <p>It's a cold day!</p>
    {% endif %}

    In the example above, we have a temperature variable that holds the temperature value. The template will conditionally display a message depending on the value held in the temperature variable.

    We could combine the conditionals with logical operators (and, or, not) or comparison operators (==, <, >, etc.) to create more complex conditional statements in our templates, extending the capability of our application.

    Loops (for, while)

    Using loops, we can iterate over a sequence of elements or repeat a section of code until a condition is met.
    The {% for %} loop is used to iterate over a sequence such as a list or a dictionary.

    Here’s an example:

      {% for item in my_list %}
      <li>{{ item }}</li>
      {% endfor %}

    In the example above, we have a list named my_list. We use the for loop to iterate over it to get the individual items (item) and then display them using a list item.

    The {% while %} loop is used to repeat a section of code until a condition becomes false or true. Here’s an example where we iterate over a counter that’s set at zero until the value becomes five and then the while condition becomes false and the while loop exits:

    {% set counter = 0 %}
    {% while counter < 5 %}
      <p>Iteration {{ counter + 1 }}</p>
      {% set counter = counter + 1 %}
    {% endwhile %}

    Using variables and control structures in Flask templates, we’re able to handle dynamic data, apply conditional logic, and iterate over sequences. These features give us the flexibility to create dynamic and interactive web pages.

    Template Context and Global Variables

    Template context refers to the set of variables and values available to the template when it’s rendered. The template context provides the necessary data needed by the template to dynamically generate the template’s HTML content.

    Understanding template context

    The template context is available when rendering a template using the render_template() function. The function enables us to pass variables from our view functions to the template for display or processing.
    By default, Flask provides the following variables to the template context:

    • request: contains information about the current request so that we can access the incoming request data, though it will be unavailable if a template is rendered without an active request context

    • session: gives us session information, enabling us to remember information from one request to another

    • config: contains configuration information about our application

    • url_for(): a function (used to generate dynamic URLs) for routes within our application

    • g: enables us to set global variables that are accessible throughout the application

    Using global variables in templates

    There are times when we may want to pass the same information or some shared state around in our application and have it remain consistent throughout the lifespan of one active request. For this purpose, Flask provides the global namespace object (g), which can be used to store and access global variables.

    To use a global variable in a template, we need to assign the value to the g object in a view function.

    Here’s an example where we share the title of the site globally:

    from flask import Flask, render_template, g
    app = Flask(__name__)
    def set_global_variables():
      g.site_title = "My Website"
    def index():
      return render_template('index.html')

    To use the g object, we have to import it from the Flask module, and since we want the title variable to be accessible to every request, we use the before_request decorator. This means that the variable will always be set before every request comes into our application. We then create a custom function named set_global_variables(), where we set the variables that we want to have access to globally in our application.

    To access the variables in the templates, we have to use the {{ g.name_of_the_variable }} notation.

    Here’s an example:

    <!DOCTYPE html>
      <title>{{ g.site_title }}</title>

    By using global variables in templates, we can store and access data that’s shared across multiple views without the need to pass them explicitly from each view function.

    Template Forms and User Input

    Forms are important on the Web. They present a way of taking input from users that can be saved or processed. Flask provides features and tools for handling forms, building form interfaces in templates, validating and processing form data, and displaying form errors.

    Building forms in templates

    Flask provides two main ways of building forms: we can build them entirely using HTML, or we can use Flask-specific form helpers to generate form elements like buttons, labels, and form fields. For instance, Flask couples well with the WTForms library to provide more functionality and flexibility for building forms.
    When we combine any of the two methods with control structures and template variables, we can dynamically populate form fields, and set default values, creating very intuitive forms.

    Let’s see examples of how we can build forms in Flask using both methods.

    Building a form using HTML

    To create a form in templates, we can use HTML <form> tags along with various input elements such as <input>,<select>, and <textarea>. We can specify if the form is meant for getting data from the server or for posting data to the server using the GET or POST HTTP methods respectively, or via the action (the URL where the form is submitted to) in the opening <form> tag.

    Here’s an example:

    <form method="POST" action="{{ url_for('submit_form') }}">
      <input type="text" name="username">
      <input type="password" name="password">
      <button type="submit">Submit</button>

    This example shows a basic form that has two input fields — the username and password — and a submit button. The opening <form> tag denotes that this form will be posting data to the server using method="POST". The action="{{ url_for(submit_form) }}" part specifies that the form data will be submitted to the URL /submit_form in the server.

    Once the form has been built, we’ll render the form when a user visits the URL where they’re asked to enter their username and password. Let’s see how we can handle and process the form data in Flask:

    from flask import Flask, render_template, request
    app = Flask(__name__)
    @app.route('/', methods=['GET', 'POST'])
    def index():
      if request.method == 'POST':
          username = request.form['username']
          password = request.form['password']
          # Process the form data
          # Redirect or perform other actions
      return render_template('index.html')
    if __name__ == '__main__':

    This example is a basic Flask application that has one route for handling a user request when a user visits the root route. The route accepts two HTTP methods, GET and POST. This means that the form template will be rendered when a user visits the route, since it’s a GET request. When a user fills in the data required by the form and hits the submit button, it will be a POST request.

    In the POST request, when data is sent to the server we’ll be able to access the submitted data using request.form[‘username’] and request.form[‘password’]. By doing this, we have access to the data entered by the user, and once we have it, we can store it in a descriptive variable for further processing.

    Building a form using HTML directly in the template gives us control over its structure. It will require more effort than using a form class from the WTForms library. For instance, the form we created above doesn’t include any type of error handling and validation. We’ll need to handle that in our Flask application. This is done by creating custom functions that validate data before using it in the backend. This also requires us to write some JavaScript code that will validate user input on the frontend.

    Building a form using WTForms

    To build a form using the WTForms library in Flask, we first have to install the library, since it’s not part of the standard Flask library.

    Install it using the following command:

    pip install WTForms

    Once the library finishes installing, it’s available for use. It’s generally straightforward to use it in our application. The following are the general steps of using it:

    • create a form class
    • create an instance of the form class
    • render the form fields in the templates
    • handle the form submission

    Let’s see an example that demonstrates the usage:

    from flask import Flask, render_template, request
    from wtforms import Form, StringField, PasswordField, SubmitField, validators
    app = Flask(__name__)
    class MyForm(Form):
      username = StringField('Username', validators=[validators.DataRequired()])
      password = PasswordField('Password', validators=[validators.DataRequired()])
      submit = SubmitField('Submit')
    @app.route('/', methods=['GET', 'POST'])
    def index():
      form = MyForm(request.form)
      if request.method == 'POST' and form.validate():
          username = form.username.data
          password = form.password.data
          # Process the form data
          # Redirect or perform other actions
      return render_template('index.html', form=form)
    if __name__ == '__main__':

    In this example, we import the necessary classes from flask: the Flask class that creates the Flask application instance; render_template(), which will render our template; and request, through which we’ll have access to the request object and retrieve the form data. We also import Form, StringField, PasswordField, and SubmitField from wtforms, which we’ll use to define the form structure.
    validators will provide validation for form fields.

    After the imports, we create the Flask application using Flask(__name__) and store it in the app variable.
    We then define the MyForm class, extending the Form class from wtforms. This class will represent the form and contain fields such as username, password, and submit. Each of the fields is associated with an appropriate input type (StringField, PasswordField, SubmitField).

    The index() function is defined as the route handler for the root URL. It handles both GET and POST requests. Inside the function, we create an instance of MyForm. This takes an argument of request.form, which binds the form data to the form object. If the request method is POST and the form passes validation at form.validate(), the data is processed and appropriate actions can be taken. If the request method is GET, the index.html template is returned with the form object passed as a parameter. This is very important, since we’ll use the form object to create the form on the template.

    Here’s an example of how the form fields will be rendered using the form object:

    <form method="POST" action="">
      {{ form.csrf_token }}
      {{ form.username.label }} {{ form.username }}
      {{ form.password.label }} {{ form.password }}
      {{ form.submit }}

    The code above shows how a form will be rendered from a form object that was passed to the render_template() function. Let’s go over what this code does.

    The form object doesn’t build the opening and closing <form> tags; we have to provide those ourselves. The opening form tag shows that the form will use the POST method when submitting the form, and the action="" attribute shows that this form will be posted to the same URL that rendered it — which is why the action attribute has an empty string.

    {{ form.csrrf_token }} is a special field provided by WTForms to prevent cross-site request forgery (CSRF) attacks. It generates a hidden input field containing a CSRF token, which is required for form submission.

    {{ form.username.label }} will render the label associated with the username field of the form.

    {{ form.username }} will render the username field itself, which is going to be a text field.

    Similar to the above line, the {{ form.password.label }} template variable renders the label associated with the password field of the form.

    The {{ form.password }} template variable renders the password field itself.

    The {{ form.submit }} template variable renders the submit button associated with the form.

    By rendering the form fields and labels using the {{ }} syntax, we can dynamically generate the form HTML based on the form object passed from the Flask route. This way, the form fields and labels will match the fields defined in the MyForm class.

    Using WTForms makes working with forms easier by providing a convenient way to define form fields, apply validation rules, and handle form data. It abstracts the complexities of form handling and provides a clean and modular approach to building forms in Flask applications. The library has a lot more classes, validators, and methods for use. In order to get a sense of what it has to offer, visit the WTForms documentation.

    In this section, we’ve seen the two ways we can create a form in Flask — how a form is rendered in each case, and how to process and validate user input in both different cases. Overall, it shows that creating a form using WTForms is the better option, since it handles most of the heavy lifting for us.

    Built-in Filters and Custom Filters

    Filters are functions that enable us to modify or transform data before displaying it on the template. Using filters enables us to perform common data manipulations and formatting without writing the Python code in the templates. Jinja2 provides a set of built-in filters such as capitalize, lower, upper. Filters can be applied to variables or expressions on the templates. To apply a filter on a variable or an expression, we use the pipe (|) symbol after the variable or expression, followed by the name of the filter that we want to apply.

    Here’s an example using the upper filter on the name variable:

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

    In the example above, the upper filter will set the value of the string held in the name variable to uppercase. We can find more filters and their usage in the Jinja2 documentation.

    Creating custom filters

    Instead of using built-in filters, we can define our own custom filters. Custom filters allow us to define our own functionality and apply it to variables in our templates.

    To create a custom filter, we define a Python function and register it as a filter in our Flask application.

    Here’s an example of how we can create a custom filter:

    from flask import Flask, render_template
    from jinja2 import Markup
    app = Flask(__name__)
    def add_commas_filter(value):
      # Check if value is numeric
      if not isinstance(value, (int, float)):
            return value
      # Convert the value to string and add commas
      value_str = str(value)
      parts = []
      while value_str:
            value_str = value_str[:-3]
      return Markup(','.join(reversed(parts)))
    def index():
      number = 1000000
      return render_template('index.html', number=number)
    if __name__ == '__main__':

    In this example, we define an add_commas_filter custom filter function, which takes a value as input and adds commas to it if it’s a numeric value. We use the Markup class to mark the resulting string as safe for rendering in the template.

    The app.template_filter() decorator is then used to register the custom filter. The decorator takes the name of the filter as an argument, which is add_commas in this case.

    In the index() route function, we pass a number variable to the template, which will be processed by the add_commas filter.

    Finally, in the template, we can use the custom filter by invoking it on a variable using the pipe symbol. Here’s an example:

    <p>Number with commas: {{ number|add_commas }}</p>

    This will output the number variable with commas added if it’s a numeric value. By using filters, we’re able to modify variables that are output in the templates without needing to write all the Python code in the templates.

    Working with Static Files and Media

    As we continue to develop our web application, it’s crucial to go beyond HTML templates and explore additional elements to enhance the app’s functionality and visual appeal. Flask provides support for serving static files like CSS stylesheets, JavaScript files, images, videos, and other media files, enabling us to seamlessly integrate them into our web pages.

    By leveraging HTML templates, CSS stylesheets, static files, media handling, and JavaScript functionality, we can build a fully-fledged web application that delivers dynamic content and provides an engaging and visually appealing user experience. Flask’s features and flexibility empower us to seamlessly incorporate these elements, helping us create a polished and functional end product.

    Next, we’ll look at how we can work with static files and media in Flask.

    Serving static files in Flask

    Flask allows us to serve static files, such as CSS, JavaScript, images, and other media files directly from our application. By default, Flask looks for static files in a static directory, located in the same directory as our Flask application file.

    To serve a static file, we can use the url_for function in our templates to generate the URL for the file.

    Here’s an example:

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

    In this example, we have a file named style.css that resides in the css sub-directory of the static directory. For the file to be served, the url_for() function will generate the URL for the file. It’s particularly useful to use the url_for() function to generate URLs for resources rather than use static routes.

    Therefore, for Flask to serve static files, we should put them in a static directory. We could further place them in specific subdirectories to indicate their type. For instance, we could have a subdirectory for css, js, and images, indicating the type of resources each will hold.

    Linking CSS and JavaScript files in templates

    To link CSS and JavaScript files in our templates, we can use the appropriate HTML tags — such as <link> for CSS files and <script> for JavaScript files. We can include the file path or use the url_for() function to generate the URL, but the convention is to use the url_for() function.

    Here’s an example showing how to link CSS and JavaScript in our templates:

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

    Including these lines in our template will link the CSS and JavaScript files to the HTML page, allowing us to apply custom styles and add interactive behavior to our web application.

    By using the url_for() function with the 'static' endpoint, Flask automatically handles the URL generation, taking into account any blueprint or application configurations. This ensures that the correct URL is generated regardless of the application’s structure or deployment environment.

    Advanced Template Techniques

    In this section, we’ll explore some advanced techniques for working with templates in Flask. These techniques will help us build more modular and reusable code, test and debug our templates effectively, and make our development process more efficient.

    Including templates

    While building applications, components like the footer and navigation are usually uniform throughout the various pages of the application. This calls for us to keep repeating the code for these components, and in the spirit of programming, this is not good. To solve this, Flask provides a way of breaking down the common components, where we build it once and include it in the various templates where it may be needed. This promotes code reuse and makes it easier to maintain, since we only need to update the code in one location.

    To reuse template components, Jinja2 allows us to include other templates within one template using the {% include %} tag. This is useful for reusing common components or sections across multiple templates.

    Here’s an example of how we include components within other templates, and create a header.html component that can be included in the main.html template using template inclusion in Flask:

      <h1>Welcome to My Website</h1>
              <li><a href="/">Home</a></li>
              <li><a href="/about">About</a></li>
              <li><a href="/contact">Contact</a></li>
    <!DOCTYPE html>
      <title>My Website</title>
      <!-- CSS and other head elements -->
      {% include 'header.html' %}
      <div class="content">
          <!-- Main content of the page -->
      <!-- Other footer or scripts -->

    In the main.html template, we use the {% include ‘header.html’ %} statement to include the header.html component. Building components this way enables us to reuse the components across multiple pages without duplicating the code. This promotes code reuse and makes it easier to manage and update shared components on our website’s UI.

    Template macros and reusable code

    Template macros are similar to functions in normal programming. They enable us to define and reuse blocks of code in templates. The reason template macros are similar to functions is that they can contain arguments in certain cases, and we can call them in our templates whenever we need.

    Here’s an example of a macro that creates a card with a different header and content every time it’s used in our templates:

    {% macro render_card(title, content) %}
        <div class="card">
            <h2>{{ title }}</h2>
            <p>{{ content }}</p>
    {% endmacro %}

    In this example, we have a macro called render_card in card_macro.html that takes two parameters, title and content. Inside the macro is the structure of a card element with placeholders for the title and content. Note that, to define a macro, we use the {% macro %} tag, and we use the {% endmacro %} to close the definition of the macro.

    Let’s now see how we could use the macro in our template by creating a main.html template that will use the card_macro.html:

    <!-- main.html -->
    <!DOCTYPE html>
        <title>My Website</title>
        <!-- CSS and other head elements -->
        {% import  'card_macro.html'  as card %}
        <h1>Welcome to My Website</h1>
        {% card.render_card("Card 1", "This is the content of Card 1.") %}
        {% card.render_card("Card 2", "This is the content of Card 2.") %}
        <!-- Other content of the page -->

    In main.html, we import the card_macro.html template as card variable to make the render_card macro available. We then use the card.render_card attribute twice with different values for title and content, which allows us to reuse the same code block to generate multiple cards with different content.

    By using macros, we can define reusable blocks of code and easily incorporate them into our templates whenever needed. This promotes code modularity, reduces redundancy, and makes our templates more maintainable.

    Template testing and debugging

    When working with templates in Flask, we may encounter issues or want to test specific parts of our templates. Flask provides some helpful debugging techniques, such as the {% debug %} tag, which displays detailed information about the template context.

    In addition to the {% debug %} tag, Flask offers other debugging techniques that can help us identify and resolve issues effectively. Some techniques we can use are listed below.

    • Template syntax errors. Flask provides detailed error messages that point us to the specific line and column where the error occurred. This allows us to quickly fix syntax issues in our templates.
    • Interactive debugger. Flask ships with an interactive debugger called Werkzeug. If an unhandled exception occurs when a template is being rendered, the debugger is automatically activated in the browser and prints a stack trace pinpointing the cause of the issue.
    • Unit testing. Writing unit tests for our templates is an effective way to ensure their correctness and catch issues early on. We can use testing frameworks like unittest or pytest to automate the testing process.

    By utilizing these debugging techniques, we can effectively identify and resolve issues in our Flask templates, ensuring smooth and error-free rendering.


    Flask templates play a crucial role in building dynamic and interactive web applications. They provide a powerful mechanism for separating the presentation logic from the application logic, enabling us to create reusable and maintainable code.

    Throughout this article, we’ve explored various aspects of Flask templates. We discussed the importance of templates in Flask and the benefits they offer, such as code organization, reusability, and easier maintenance. We learned how to create and render templates using the render_template() function, as well as how to pass data to templates using context dictionaries.

    We delved into template inheritance, which enables us to create a base template and extend it in child templates. This enables us to define a consistent layout and structure for our application while customizing specific sections as needed. We also explored the usage of template variables and control structures, including conditionals, loops, and filters, to dynamically manipulate and display data in templates.

    Additionally, we discussed template context and global variables, understanding how to pass variables to templates, and make global variables accessible within the template context. We covered the url_for() function, which helps generate dynamic URLs for routes, enhancing the flexibility and maintainability of our application.

    Furthermore, we touched on template forms and user input, template extensions, working with static files and media, and advanced template techniques such as template inclusion, macros, testing, and debugging.

    By harnessing the power of Flask templates, we can create visually appealing and interactive web applications that are flexible, modular, and easy to maintain. Templates allow us to separate the concerns of our application, promote code reuse, and provide a clear separation between the frontend and backend components.

    Now that you have a solid understanding of Flask templates, you’re well-equipped to leverage this powerful feature in your Flask projects. Continue exploring the Flask documentation and experimenting with different template techniques to further enhance your web development skills. With Flask templates, you can deliver engaging and dynamic web experiences to your users.