Close Menu
GeekBlog

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    Marvel Made a Show About an AI Mind. The Crew Hand Built Every Glitch.

    October 10, 2026

    Garmin Approach S72 Is Here: Titanium, ECG and a $799.99 Price Tag

    October 10, 2026

    Someone Rebuilt Elizabeth Holmes’s Desk From 1,169 Court Exhibits. You Can Read Her Email.

    October 10, 2026
    Facebook
    GeekBlog
    • Home
    • Mobile
    • Tech News
    • Blog
    • Gaming
    • Smartwatch
    • How-To Guides
    • AI & Software
    Facebook
    GeekBlog
    Home»Blog»How to Wrap a Block in Jinja2 (super, call Blocks, Filters and Conditional Wrappers)
    Blog

    How to Wrap a Block in Jinja2 (super, call Blocks, Filters and Conditional Wrappers)

    Ethan CaldwellBy Ethan CaldwellSeptember 9, 202612 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Email Copy Link
    Programming code with template blocks on a laptop screen
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link

    To wrap a block in Jinja2, override the block in a child template and call {{ super() }} inside your wrapper markup, or define a macro that takes a body through {% call %} and renders {{ caller() }} where the wrapped content should go. Both approaches let you put a container, a card, a conditional div or a filter around existing content without copying that content. Which one you pick depends on whether the thing you are wrapping is a template inheritance block, a reusable snippet, or a piece of text that just needs transforming.

    Quick answer: For inheritance, override the block and wrap {{ super() }}: {% block content %}<div class="card">{{ super() }}</div>{% endblock %}. For reusable wrappers, write a macro and invoke it with {% call wrapper() %} ... {% endcall %}, rendering the body with {{ caller() }}. For text transforms, use {% filter upper %} ... {% endfilter %}. For an optional wrapper, capture the body once with {% set body %} ... {% endset %} and print it inside or outside the wrapper.

    The rest of this guide walks through each pattern with working templates you can drop into Flask, Django (with the Jinja2 backend), Ansible or any other project that renders Jinja2. It also covers include with context, how wrapping interacts with multiple levels of inheritance, and a troubleshooting section for the errors people hit most often, like super() being undefined or caller missing.

    Wrapping an inherited block with super()

    Template inheritance is the most common place people need a wrapper. You have a base template with a content block, and a child that fills it. Later you want a specific page to put the whole content area inside a container, but you do not want to duplicate what the parent already renders. That is exactly what super() is for. When you call it inside an overriding block, Jinja2 renders the parent’s version of that block and returns it as markup.

    {# base.html #}
    <main>
      {% block content %}
        <p>Default content from the base template.</p>
      {% endblock %}
    </main>
    
    {# page.html #}
    {% extends "base.html" %}
    {% block content %}
      <div class="card">
        <div class="card-body">
          {{ super() }}
        </div>
      </div>
    {% endblock %}

    The output contains the card markup with the parent’s paragraph inside it. You can wrap in either direction: put your own markup before and after super(), or call super() first and append. The parent block never changes, and other children that extend the same base are unaffected.

    This works across more than one level. If page.html extends section.html, which extends base.html, then super() in page.html renders the block as defined in section.html. If section.html itself calls super(), you get all three layers nested. Jinja2 also supports super.super() when you need to skip one level, though that usually signals the hierarchy could be simpler.

    Tip: Give blocks that you expect children to wrap a clean, single purpose. A content block that also contains the sidebar is hard to wrap cleanly. Split it into content and sidebar blocks and children can wrap either one independently.

    Wrapping arbitrary content with a macro and call blocks

    When the wrapper is reusable and not tied to inheritance, a macro is the right tool. A regular macro takes arguments and returns markup. A macro invoked with {% call %} also receives a block of template content, which it can render anywhere by calling caller(). This is how you build a card, modal, alert or panel component that accepts any body.

    {# macros.html #}
    {% macro panel(title, kind="info") -%}
    <section class="panel panel-{{ kind }}">
      <h3>{{ title }}</h3>
      <div class="panel-body">
        {{ caller() }}
      </div>
    </section>
    {%- endmacro %}
    
    {# page.html #}
    {% from "macros.html" import panel %}
    {% call panel("Deployment status", kind="warning") %}
      <p>The last deploy finished with {{ warnings|length }} warnings.</p>
      <ul>
      {% for w in warnings %}
        <li>{{ w }}</li>
      {% endfor %}
      </ul>
    {% endcall %}

    The body between {% call %} and {% endcall %} is compiled as a nested macro and passed into panel as caller. Everything inside the body has access to the calling template’s variables, so warnings resolves normally. The macro can render caller() once, several times, or not at all, which is useful for things like tabs or collapsible sections.

    Recommended for you:

    How to Embed TikTok Videos on a Website or App
    Blog·Sep 9, 2026

    How to Embed TikTok Videos on a Website or App

    The macro can also hand values back to the body. Declare parameters on the call block with {% call(item) render_list(items) %} and invoke caller(item) inside the macro. The official Jinja2 template documentation on call blocks shows this pattern for rendering a dialog and a list where the macro owns the loop and the caller owns the per item markup.

    {% macro render_list(items) -%}
    <ul class="list">
    {% for item in items %}
      <li>{{ caller(item) }}</li>
    {% endfor %}
    </ul>
    {%- endmacro %}
    
    {% call(user) render_list(users) %}
      <strong>{{ user.name }}</strong> ({{ user.email }})
    {% endcall %}

    Conditional wrappers without duplicating content

    A very common request is “wrap this in a link only if a URL exists” or “wrap this in a div only on mobile”. The naive approach writes the body twice, once inside the wrapper and once outside, and the two copies drift apart. The clean approach captures the body into a variable once using a block set, then decides where to print it.

    {% set body %}
      <img src="{{ product.image }}" alt="{{ product.name }}">
      <span class="name">{{ product.name }}</span>
    {% endset %}
    
    {% if product.url %}
      <a href="{{ product.url }}" class="product">{{ body }}</a>
    {% else %}
      <div class="product">{{ body }}</div>
    {% endif %}

    Block assignments were added in Jinja 2.8, so this works on any modern install. With autoescaping on, the captured value is already marked safe because the template engine rendered it, so printing {{ body }} does not double escape it.

    The same idea works with the super() pattern from earlier. If a page should wrap the inherited content only when a flag is set, capture super() into a variable and branch on the flag, rather than writing two overriding blocks.

    {% block content %}
      {% set inner = super() %}
      {% if boxed %}
        <div class="box">{{ inner }}</div>
      {% else %}
        {{ inner }}
      {% endif %}
    {% endblock %}

    Before you branch on a variable like boxed, make sure it is actually defined in the rendering context. Our guide on checking whether a variable exists in Jinja2 covers is defined and the difference between undefined and falsy values.

    Filter blocks for text transforms

    Sometimes the wrapper is not markup at all. You want everything inside a region uppercased, stripped of whitespace, indented, or passed through a custom filter. Jinja2’s {% filter %} tag applies a filter to the rendered output of everything between the opening and closing tags, and you can chain filters the same way you would on an expression.

    {% filter upper %}
      Deployment {{ env }} finished at {{ finished_at }}
    {% endfilter %}
    
    {% filter indent(width=4) | trim %}
    {% include "snippets/config.yaml" %}
    {% endfilter %}

    Filter blocks are a good fit for generated configuration files, email bodies and anything where indentation or casing has to be exact. If you need to match or replace text inside the wrapped region, see how to use regular expressions in Jinja2, which covers the regex_replace and regex_search filters available in Ansible and how to add equivalents in plain Jinja2.

    Include with context inside a wrapper

    You can wrap an included template exactly like any other content, because {% include %} just renders another template in place. The question people usually have is about variables. By default an included template sees the current context, including variables you set with {% set %} in the including template. The with context and without context modifiers make that explicit, and they matter most for imports, where the default is the opposite.

    {% set panel_title = "Recent orders" %}
    <div class="wrapper">
      {% include "partials/orders_table.html" with context %}
    </div>
    
    {# The partial can read panel_title and any loop or block variables #}
    
    {# Imports are cached without context by default; opt in when the macro needs request data #}
    {% from "macros.html" import panel with context %}

    One more include option is useful for wrappers: ignore missing skips silently if the file does not exist, which gives you optional per page overrides inside a fixed wrapper without any Python code.

    Choosing the right wrapping technique

    Each pattern solves a slightly different problem, and mixing them up leads to templates that are harder to maintain than they need to be. This table summarizes when to use which.

    TechniqueUse whenKey syntaxLimits
    Block override with super()Wrapping content a parent template already renders{{ super() }} inside {% block %}Only works in a child template with extends
    Macro with call blockReusable component that accepts any body{% call m() %} and {{ caller() }}Macro must be imported; context is not shared unless with context
    Block set captureConditional or repeated wrapper around one body{% set x %} ... {% endset %}Captured value is a string, not a live block
    Filter blockTransforming text: casing, indent, trim, custom filters{% filter f %} ... {% endfilter %}Applies to text output, not structure
    Include with contextWrapping a partial that needs page variables{% include "x.html" with context %}Included files cannot override the includer’s blocks

    A useful rule of thumb: if the wrapper belongs to a layout, use inheritance; if it belongs to a component, use a macro; if it belongs to a single decision on a single page, use a block set. Macros that need to unpack several values from the caller are covered in how to unpack more than one variable in Jinja2.

    Troubleshooting

    UndefinedError: ‘super’ is undefined

    super() is only available inside a block in a template that extends another template, and only when the parent defines a block with the same name. If the child is rendered directly without {% extends %}, or the block name has a typo, Jinja2 raises this error. Check the block names match exactly, including case, and that the extends tag is the first tag in the child template.

    UndefinedError: ‘caller’ is undefined

    You invoked a macro that uses {{ caller() }} with a plain {{ panel("Title") }} expression instead of a {% call %} block. Either switch to {% call panel("Title") %} ... {% endcall %}, or guard the macro with {% if caller %}{{ caller() }}{% endif %} so it works both ways.

    The wrapped content is escaped and shows raw HTML

    This happens when the body was built as a Python string and passed in with autoescaping on. Content rendered by the template engine, including super(), caller() and block set captures, is already marked safe. Content assembled in Python needs to be wrapped in markupsafe.Markup or printed with the safe filter, and only if you trust it.

    Macro cannot see request, session or loop variables

    Imported macros do not receive the calling context by default, for caching reasons. Add with context to the import line, or pass what the macro needs as explicit arguments. Explicit arguments are the more maintainable choice because the macro’s dependencies are visible at the call site.

    Extra blank lines around the wrapper

    Recommended for you:

    How to Use the TikTok for Developers Documentation
    Blog·Sep 9, 2026

    How to Use the TikTok for Developers Documentation

    Block tags leave newlines behind. Use the whitespace control dash on tags ({%- and -%}) at the edges of your macro, or enable trim_blocks and lstrip_blocks on the environment. For generated config files, pair that with a {% filter trim %} block around the whole output.

    Frequently asked questions

    Can I wrap a block from a parent template without overriding it?

    No. The only way to change how an inherited block renders is to override it in a child template. Wrapping is done by overriding the block and calling super() inside your wrapper markup. If you want the wrapper applied everywhere, put it in the base template instead and keep the inner block for children.

    Does super() work across multiple levels of inheritance?

    Yes. super() renders the block as defined in the immediate parent. If that parent also calls super(), the grandparent’s version is included too, so wrappers nest naturally. Jinja2 also allows super.super() to reach the grandparent directly, but a simpler hierarchy is usually the better fix.

    Can a call block pass variables back to the caller?

    Yes. Declare parameters on the call tag, for example {% call(item, index) render(items) %}, and inside the macro invoke caller(item, loop.index). The body then uses those names. This lets the macro own the loop or structure while the calling template controls the markup for each item.

    What is the difference between include and import for wrapping?

    include renders another template in place and shares the current context by default. import loads macros and variables from a template without rendering it, and does not share context by default. Use include to drop a partial inside a wrapper, and import to bring in a reusable wrapper macro.

    Can I wrap content in a filter and a macro at the same time?

    Yes. Filter blocks and call blocks nest freely. You can place a {% filter indent(4) %} block inside a {% call %} body, or wrap an entire call block in a filter. The filter applies to the final rendered text of everything inside it, including whatever the macro produced.

    The bottom line

    Jinja2 gives you four solid ways to wrap content: super() for inherited blocks, {% call %} with caller() for reusable components, block set for conditional wrappers, and {% filter %} for text transforms. Each one avoids duplicating the wrapped content, which is the whole point.

    Pick the technique that matches where the wrapper lives: layout, component or a single page decision. Keep block names unique and descriptive, pass macros explicit arguments instead of leaning on with context, and use whitespace control at the edges so your wrappers do not leak blank lines into the output.

    About this article: GeekBlog covers U.S. technology news, AI, phones, smartwatches and gaming. Every story is written and checked under our Editorial Policy. Spotted a mistake or have a story tip? Contact our editors.

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Telegram Email Copy Link
    Previous ArticleWashington vs Arizona: Which State Is Better to Live In?
    Next Article How to Compare Float Numbers in Jinja2 (Without Getting Bitten by Strings)
    Ethan Caldwell

      Ethan Caldwell is GeekBlog's resident Apple specialist, covering the entire Apple ecosystem: iPhone, iPad, Mac, Apple Watch, AirPods and the software that ties them together. A longtime iOS user and gadget collector, Ethan tracks Cupertino's every move, breaking down Apple keynotes, A- and M-series chip benchmarks, iOS feature updates and the rumor mill into clear, practical takes that help readers decide whether the latest Apple hardware is worth the upgrade.

      Related Posts

      10 Mins Read

      Google Ads Keyword Planner: How It Shows the Most Relevant Keywords

      11 Mins Read

      How to Use Remarketing Techniques for Better Conversions

      11 Mins Read

      How to Conduct A/B Testing for Marketing Campaigns

      10 Mins Read

      AMP for WP Plugin Vulnerability: What Was Fixed and What to Do

      11 Mins Read

      How to Create a Facebook Business Page in 2026

      11 Mins Read

      How to Convert GMT Time to Other Time Zones in C++

      Top Posts

      Every iPhone Camera Ranked in 2026 (Best to Worst)

      July 6, 2026223 Views

      Best Stores for Buying MP3 and Digital Music You Can Keep Forever (2026)

      August 2, 2025131 Views

      Windows 11 vs Windows 10: Should You Upgrade in 2026?

      July 7, 202692 Views
      Stay In Touch
      • Facebook

      Subscribe to Updates

      Get the latest tech news from FooBar about tech, design and biz.

      Most Popular

      iPhone Battery Replacement Cost: Every Model, Apple vs Repair Shop

      October 6, 202659 Views

      Best Offline Music App in 2026: Free and Paid Picks

      October 7, 202648 Views

      One UI 9 Watch Beta Now Covers Three Galaxy Watch Generations: What You Get and How to Join

      October 4, 202645 Views
      Our Picks

      Marvel Made a Show About an AI Mind. The Crew Hand Built Every Glitch.

      October 10, 2026

      Garmin Approach S72 Is Here: Titanium, ECG and a $799.99 Price Tag

      October 10, 2026

      Someone Rebuilt Elizabeth Holmes’s Desk From 1,169 Court Exhibits. You Can Read Her Email.

      October 10, 2026

      Subscribe to Updates

      Get the latest creative news from FooBar about art, design and business.

      HEICJPG.online - Convert HEIC to JPG online
      Facebook
      • About Us
      • Contact us
      • Privacy Policy
      • Disclaimer
      • Terms and Conditions
      • Editorial Policy
      • Cookie Policy
      • Your Privacy Choices
      © 2026 GeekBlog

      Type above and press Enter to search. Press Esc to cancel.