Django’s 'include' Template Tag

This article introduces Django’s {% include %} template tag and explains how to reuse small template fragments across pages. It covers context sharing, with, only, loops, reusable partials, template organization, inheritance differences, performance considerations, testing, and common mistakes..

Django’s include Template Tag

Django templates often contain pieces of HTML that appear in more than one place.

Examples include:

  • navigation menus
  • article cards
  • user profile blocks
  • pagination controls
  • alert messages
  • form field layouts
  • sidebars
  • reusable buttons
  • table rows

Copying the same markup into several templates creates duplication.

Django’s {% include %} template tag helps solve this by rendering another template inside the current template.

A simple example looks like:

django

1
{% include "articles/article_card.html" %}

Django loads articles/article_card.html, renders it using the current template context, and inserts the result at that position.

Conceptually:

text

1
2
3
4
5
6
7
Main template
    ↓
{% include "article_card.html" %}
    ↓
Render included template
    ↓
Insert rendered HTML

The include tag is useful for small reusable template fragments that belong inside a larger page.

What Does {% include %} Do?

The include tag renders another template and inserts its output into the current template.

Suppose the main template contains:

django

1
2
3
<h1>Articles</h1>

{% include "articles/article_card.html" %}

And articles/article_card.html contains:

django

1
2
3
4
<article>
    <h2>{{ article.title }}</h2>
    <p>{{ article.summary }}</p>
</article>

If the current context contains:

python

1
2
3
{
    "article": article,
}

the included template can access that variable.

The final HTML might resemble:

html

1
2
3
4
5
6
<h1>Articles</h1>

<article>
    <h2>Django Templates</h2>
    <p>An introduction to reusable templates.</p>
</article>

The included template is rendered before its output becomes part of the parent template.

Basic Syntax

The basic syntax is:

django

1
{% include "template_name.html" %}

For example:

django

1
{% include "shared/header.html" %}

or:

django

1
{% include "accounts/profile_summary.html" %}

The path follows the same template lookup rules used by other Django template operations.

Why Use include?

The main reason is to avoid repeating markup.

Without include, several templates might contain the same card:

django

1
2
3
4
<article class="article-card">
    <h2>{{ article.title }}</h2>
    <p>{{ article.summary }}</p>
</article>

If the design changes, every copy must be updated.

With an included template:

text

1
2
articles/
└── article_card.html

the markup exists in one place.

Other templates can reuse it:

django

1
{% include "articles/article_card.html" %}

This provides:

  • less duplication
  • easier maintenance
  • more consistent markup
  • smaller parent templates
  • clearer separation of reusable UI pieces

A Simple Reusable Partial

Suppose an application displays user information in several places.

Create:

text

1
2
3
templates/
└── accounts/
    └── user_summary.html

Contents:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<div class="user-summary">
    <strong>
        {{ user.username }}
    </strong>

    {% if user.email %}
        <span>
            {{ user.email }}
        </span>
    {% endif %}
</div>

Include it:

django

1
{% include "accounts/user_summary.html" %}

The included template receives the current context by default.

Included Templates Receive the Current Context

Consider:

django

1
2
3
{% for article in articles %}
    {% include "articles/article_card.html" %}
{% endfor %}

Inside article_card.html:

django

1
2
3
4
5
<article>
    <h2>
        {{ article.title }}
    </h2>
</article>

This works because the loop variable:

text

1
article

is part of the current context when the include tag runs.

The included template can access it.

Using with

You can explicitly pass values into the included template using with.

Example:

django

1
{% include "articles/article_card.html" with article=featured_article %}

Inside the included template:

django

1
2
3
<h2>
    {{ article.title }}
</h2>

Even though the parent variable is called:

text

1
featured_article

the partial receives it as:

text

1
article

This can make reusable fragments easier to understand.

Passing Multiple Variables

You can pass several values:

django

1
{% include "shared/button.html" with label="Save" button_type="submit" %}

Included template:

django

1
2
3
<button type="{{ button_type }}">
    {{ label }}
</button>

This allows one fragment to be used with different data.

For example:

django

1
{% include "shared/button.html" with label="Save" button_type="submit" %}

and:

django

1
{% include "shared/button.html" with label="Cancel" button_type="button" %}

Variables Can Be Expressions

Values passed with with do not have to be literal strings.

Example:

django

1
{% include "articles/article_card.html" with article=featured_article %}

Or:

django

1
{% include "users/avatar.html" with user=comment.author %}

Or:

django

1
{% include "shared/status.html" with status=order.status %}

This makes included templates useful inside loops and nested object relationships.

Using only

By default, an included template receives the current context.

Sometimes that gives the partial access to more variables than it needs.

Use:

django

1
{% include "articles/article_card.html" with article=article only %}

The only option restricts the included template to the variables explicitly provided.

Conceptually:

text

1
2
3
4
5
6
7
Without only
    Parent context
    +
    Explicit variables

With only
    Explicit variables only

This can make a reusable template fragment more predictable.

Why only Can Be Useful

Suppose the parent template contains:

text

1
2
3
4
5
6
7
article
user
request
categories
page_obj
featured_articles
settings

but the partial only needs:

text

1
article

You can write:

django

1
{% include "articles/article_card.html" with article=article only %}

Now the partial has a smaller and clearer dependency.

This is useful when you want the fragment to behave more like a small component.

Include Inside a Loop

One of the most common patterns is including a partial for each object in a collection.

Parent template:

django

1
2
3
4
5
6
7
8
9
<h1>Articles</h1>

<div class="article-list">
    {% for article in articles %}
        {% include "articles/article_card.html" with article=article only %}
    {% empty %}
        <p>No articles found.</p>
    {% endfor %}
</div>

Partial:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<article class="article-card">
    <h2>
        <a href="{{ article.get_absolute_url }}">
            {{ article.title }}
        </a>
    </h2>

    <p>
        {{ article.summary }}
    </p>
</article>

This keeps the list template focused on the list and the card template focused on one article.

Include Inside Conditional Logic

The include tag can be placed inside an if block.

Example:

django

1
2
3
4
5
{% if user.is_authenticated %}
    {% include "accounts/account_menu.html" %}
{% else %}
    {% include "accounts/login_links.html" %}
{% endif %}

Django includes only the fragment for the branch that executes.

Dynamic Template Names

The template name can come from a variable.

Example:

django

1
{% include template_name %}

If:

python

1
template_name = "articles/article_card.html"

Django renders that template.

This can be useful when the application chooses a partial dynamically.

For example:

django

1
{% include item.template_name %}

Use dynamic includes carefully because they can make template flow harder to follow.

Static template paths are usually easier to understand.

Included Templates Are Independent Render Operations

An important detail is that the included template is rendered as a separate template fragment.

The parent template does not simply paste the source code into itself before rendering.

Conceptually:

text

1
2
3
4
5
6
7
Parent context
    ↓
Render included template
    ↓
Receive rendered result
    ↓
Insert result into parent output

This affects how template blocks behave.

include Is Not Template Inheritance

The include tag and template inheritance solve different problems.

Template inheritance uses:

django

1
{% extends "base.html" %}

and:

django

1
2
{% block content %}
{% endblock %}

It defines the overall page structure.

The include tag inserts a small fragment inside that structure.

A useful distinction is:

text

1
2
3
4
5
Template inheritance
    Page layout and structure

include
    Reusable template fragment

For example:

text

1
2
3
4
5
6
7
8
base.html
    Overall site layout

articles/list.html
    Page-specific content

articles/article_card.html
    Reusable article fragment

Example with Template Inheritance

base.html:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<!doctype html>
<html lang="en">
<head>
    <title>
        {% block title %}
            Example Site
        {% endblock %}
    </title>
</head>
<body>
    {% include "shared/header.html" %}

    <main>
        {% block content %}
        {% endblock %}
    </main>

    {% include "shared/footer.html" %}
</body>
</html>

articles/list.html:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{% extends "base.html" %}

{% block title %}
    Articles
{% endblock %}

{% block content %}
    <h1>Articles</h1>

    {% for article in articles %}
        {% include "articles/article_card.html" with article=article only %}
    {% endfor %}
{% endblock %}

Inheritance defines the page hierarchy.

Includes provide reusable sections within that hierarchy.

Blocks Inside Included Templates

Suppose an included template contains:

django

1
2
3
{% block title %}
    Example
{% endblock %}

Do not expect a child template to override that block in the same way it overrides blocks from its parent template.

Included templates are rendered separately.

Use {% extends %} and {% block %} when you need inheritance and overriding behavior.

Use {% include %} when you need to render a fragment.

Reusable Navigation

A navigation bar is a common include.

Create:

text

1
2
3
templates/
└── shared/
    └── navigation.html

Contents:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<nav>
    <a href="{% url 'home' %}">
        Home
    </a>

    <a href="{% url 'article-list' %}">
        Articles
    </a>

    {% if user.is_authenticated %}
        <a href="{% url 'dashboard' %}">
            Dashboard
        </a>
    {% endif %}
</nav>

Include it in a base template:

django

1
{% include "shared/navigation.html" %}

Because the normal template context is inherited, the fragment can access user when that context variable is available.

Reusable Message Fragment

Suppose several layouts need to display Django messages.

Create:

text

1
2
3
templates/
└── shared/
    └── messages.html
django

1
2
3
4
5
6
7
8
9
{% if messages %}
    <ul class="messages">
        {% for message in messages %}
            <li>
                {{ message }}
            </li>
        {% endfor %}
    </ul>
{% endif %}

Then:

django

1
{% include "shared/messages.html" %}

This prevents the same message-rendering markup from being repeated.

Reusable Form Field

A project may use a partial for manually rendered form fields.

Create:

text

1
2
3
templates/
└── forms/
    └── field.html
django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<div class="form-field">
    {{ field.label_tag }}

    {{ field }}

    {% if field.help_text %}
        <small>
            {{ field.help_text }}
        </small>
    {% endif %}

    {{ field.errors }}
</div>

Use it:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<form method="post">
    {% csrf_token %}

    {% include "forms/field.html" with field=form.title only %}
    {% include "forms/field.html" with field=form.content only %}

    <button type="submit">
        Save
    </button>
</form>

This can help standardize form markup.

Reusable Button Fragment

A simple button partial:

django

1
2
3
4
5
6
<button
    type="{{ type|default:'button' }}"
    class="{{ css_class|default:'button' }}"
>
    {{ label }}
</button>

Usage:

django

1
{% include "shared/button.html" with label="Save" type="submit" only %}

Another use:

django

1
{% include "shared/button.html" with label="Delete" css_class="button danger" only %}

This works well for small repeated markup.

For highly configurable UI components, however, many template arguments can eventually make a partial difficult to understand.

Naming Included Templates

There is no Django requirement that included templates use a special filename convention.

Common approaches include:

text

1
2
3
article_card.html
user_summary.html
navigation.html

Some projects use a leading underscore:

text

1
2
_article_card.html
_navigation.html

This indicates that the file is intended to be used as a partial rather than as a complete page.

For example:

text

1
2
3
4
5
templates/
└── articles/
    ├── article_list.html
    ├── article_detail.html
    └── _article_card.html

Then:

django

1
{% include "articles/_article_card.html" %}

The underscore is only a project convention.

Django does not treat it specially.

Organizing Template Partials

A simple project may use:

text

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
templates/
├── base.html
├── shared/
│   ├── header.html
│   ├── footer.html
│   └── messages.html
└── articles/
    ├── article_list.html
    ├── article_detail.html
    └── article_card.html

Another structure might use:

text

1
2
3
4
5
templates/
└── components/
    ├── button.html
    ├── alert.html
    └── pagination.html

Both approaches are reasonable.

The important goal is that developers can easily find the fragment.

App-Level Includes

Reusable fragments that belong to one Django app can stay inside that app's template namespace.

For example:

text

1
2
3
4
5
6
articles/
└── templates/
    └── articles/
        ├── article_list.html
        ├── article_detail.html
        └── article_card.html

Include:

django

1
{% include "articles/article_card.html" %}

This namespacing helps avoid collisions with similarly named templates from other apps.

Project-Level Includes

Fragments shared across several apps can live in a project-level template directory.

Example:

text

1
2
3
4
5
6
templates/
└── shared/
    ├── navigation.html
    ├── footer.html
    ├── pagination.html
    └── messages.html

Then:

django

1
{% include "shared/pagination.html" %}

Passing Boolean-Like Values

Suppose a card can optionally show an image.

You might write:

django

1
{% include "articles/article_card.html" with article=article show_image=True only %}

Inside:

django

1
2
3
4
5
6
{% if show_image and article.image %}
    <img
        src="{{ article.image.url }}"
        alt="{{ article.title }}"
    >
{% endif %}

This can make one partial useful in several contexts.

However, if the partial collects too many configuration flags, it may be doing too much.

Avoid Too Many Include Arguments

This can become difficult to understand:

django

1
{% include "article_card.html" with article=article compact=True show_image=False show_author=True show_date=True show_tags=False clickable=True only %}

A developer now has to understand many configuration options.

Sometimes separate fragments are clearer:

text

1
2
article_card.html
article_card_compact.html

or the presentation may deserve a custom inclusion tag or another component approach.

Use include for simple reusable markup.

Includes and Database Queries

An include itself does not automatically perform database queries.

However, variables accessed inside the included template may trigger lazy database work.

For example:

django

1
2
3
{% for article in articles %}
    {% include "articles/article_card.html" %}
{% endfor %}

Partial:

django

1
{{ article.author.username }}

If the articles were loaded without their authors, accessing:

text

1
article.author

may result in additional database queries.

With many articles, this can cause an N+1 query problem.

The template include is not the cause by itself.

The issue is the data access performed while rendering it.

Avoiding N+1 Queries with Includes

View:

python

1
2
3
4
5
articles = (
    Article.objects
    .filter(is_published=True)
    .select_related("author")
)

Template:

django

1
2
3
{% for article in articles %}
    {% include "articles/article_card.html" with article=article only %}
{% endfor %}

Partial:

django

1
2
3
4
5
6
7
8
9
<article>
    <h2>
        {{ article.title }}
    </h2>

    <p>
        By {{ article.author.username }}
    </p>
</article>

Using select_related("author") allows Django to retrieve the related author efficiently.

When a reusable partial accesses relationships, the view still needs to provide data efficiently.

Includes Should Not Hide Expensive Work

A small-looking parent template:

django

1
{% include "dashboard/summary.html" %}

may hide a large fragment with many loops and relationship lookups.

Reusability is useful, but it should not make performance invisible.

When debugging a slow page, inspect included templates as well as the parent template.

Missing Templates

If Django cannot find the included template:

django

1
{% include "shared/missing.html" %}

template rendering normally fails with:

text

1
TemplateDoesNotExist

Check:

  • the path spelling
  • app namespacing
  • template directories
  • INSTALLED_APPS
  • the project's template configuration

Template Path Mistakes

Given:

text

1
2
3
4
articles/
└── templates/
    └── articles/
        └── article_card.html

use:

django

1
{% include "articles/article_card.html" %}

Do not use the filesystem path:

django

1
{% include "articles/templates/articles/article_card.html" %}

Template names are relative to configured template roots.

Passing the Wrong Variable

Suppose the partial expects:

django

1
{{ article.title }}

but the parent uses:

django

1
{% include "articles/article_card.html" with item=article only %}

The partial does not receive a variable named:

text

1
article

It receives:

text

1
item

Either change the include:

django

1
{% include "articles/article_card.html" with article=article only %}

or change the partial:

django

1
{{ item.title }}

Consistent variable names make partials easier to reuse.

Missing Variables Often Render Silently

Django templates generally render missing variables as empty output rather than raising a Python-style NameError.

Suppose:

django

1
2
3
<h2>
    {{ article.title }}
</h2>

but article is not available.

The output may simply be empty.

This can make context mistakes harder to notice.

Using explicit:

django

1
with ... only

can make dependencies clearer during development.

Includes and Autoescaping

Included templates use Django's normal template rendering behavior, including autoescaping.

For example:

django

1
{{ comment.content }}

will normally escape HTML-sensitive characters.

The fact that the markup lives inside an included template does not change Django's standard escaping rules.

Include Versus Custom Template Tags

For simple reusable markup, include is often enough.

Example:

django

1
{% include "articles/article_card.html" with article=article only %}

A custom inclusion tag can be more appropriate when the reusable component needs Python logic or must build its own context.

Conceptually:

text

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
include
    Existing context or explicit values
    +
    Reusable HTML

inclusion tag
    Python logic
    +
    Custom context
    +
    Reusable HTML

Start with include when no Python-side logic is necessary.

Include Versus a View

Do not create a separate view merely to render every reusable fragment.

For example, an article card inside an article list usually does not need:

text

1
/article-card/42/

and another HTTP request.

Use:

django

1
{% include "articles/article_card.html" %}

inside the existing page request.

A separate view is appropriate when the fragment truly represents a separate endpoint, such as:

  • an AJAX request
  • an HTMX endpoint
  • a standalone resource
  • independently refreshed data

Include Versus JavaScript Components

The include tag is server-side template composition.

The server renders:

django

1
{% include "articles/article_card.html" %}

before sending HTML to the browser.

Conceptually:

text

1
2
3
4
5
6
7
8
9
Django
    ↓
Render parent
    ↓
Render include
    ↓
Combined HTML
    ↓
Browser

A React or JavaScript component is usually rendered or managed on the client side.

These are different approaches.

For a traditional Django-rendered application, include is often enough for reusable HTML fragments.

Include Versus Template Inheritance

A useful rule is:

text

1
2
3
4
5
6
7
Use extends
    when one template defines the
    structure of another template.

Use include
    when one template contains a
    reusable fragment.

For example:

text

1
2
3
4
5
6
7
8
base.html
    inherited layout

article_list.html
    page

article_card.html
    included fragment

Includes Can Be Nested

An included template can itself include another template.

For example:

text

1
2
3
4
5
article_list.html
    ↓
article_card.html
    ↓
user_avatar.html

article_card.html:

django

1
2
3
4
5
6
7
<article>
    {% include "accounts/user_avatar.html" with user=article.author only %}

    <h2>
        {{ article.title }}
    </h2>
</article>

This can be useful, but excessive nesting makes templates harder to follow.

Keep the structure reasonably shallow.

A Complete Article Card Example

Model:

python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from django.conf import settings
from django.db import models


class Article(models.Model):
    title = models.CharField(
        max_length=200,
    )

    summary = models.TextField()

    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )

    is_published = models.BooleanField(
        default=False,
    )

    published_at = models.DateTimeField(
        blank=True,
        null=True,
    )

    def __str__(self):
        return self.title

View:

python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from django.shortcuts import render

from .models import Article


def article_list(request):
    articles = (
        Article.objects
        .filter(is_published=True)
        .select_related("author")
    )

    return render(
        request,
        "articles/article_list.html",
        {
            "articles": articles,
        },
    )

Parent template:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{% extends "base.html" %}

{% block content %}
    <h1>Articles</h1>

    <div class="article-list">
        {% for article in articles %}
            {% include "articles/article_card.html" with article=article only %}
        {% empty %}
            <p>
                No articles are available.
            </p>
        {% endfor %}
    </div>
{% endblock %}

Included template:

django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<article class="article-card">
    <h2>
        {{ article.title }}
    </h2>

    <p>
        By {{ article.author.username }}
    </p>

    <p>
        {{ article.summary }}
    </p>

    {% if article.published_at %}
        <time
            datetime="{{ article.published_at|date:'c' }}"
        >
            {{ article.published_at|date:"F j, Y" }}
        </time>
    {% endif %}
</article>

The parent controls the list.

The included template controls the presentation of one article.

A Pagination Partial

Pagination is another good candidate for an include.

Create:

text

1
shared/pagination.html
django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{% if page_obj.has_other_pages %}
    <nav aria-label="Pagination">
        {% if page_obj.has_previous %}
            <a
                href="?page={{ page_obj.previous_page_number }}"
            >
                Previous
            </a>
        {% endif %}

        <span>
            Page {{ page_obj.number }}
            of {{ page_obj.paginator.num_pages }}
        </span>

        {% if page_obj.has_next %}
            <a
                href="?page={{ page_obj.next_page_number }}"
            >
                Next
            </a>
        {% endif %}
    </nav>
{% endif %}

Use:

django

1
{% include "shared/pagination.html" with page_obj=page_obj only %}

The same fragment can be reused on several list pages.

An Empty-State Partial

Create:

text

1
shared/empty_state.html
django

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<div class="empty-state">
    <h2>
        {{ title }}
    </h2>

    {% if message %}
        <p>
            {{ message }}
        </p>
    {% endif %}
</div>

Use:

django

1
{% include "shared/empty_state.html" with title="No articles" message="Create your first article to get started." only %}

This is a good example of a partial with a small, explicit interface.

An Alert Partial

django

1
2
3
<div class="alert alert-{{ level }}">
    {{ message }}
</div>

Usage:

django

1
{% include "shared/alert.html" with level="success" message="Article saved." only %}

The fragment receives only the values it needs.

Good Partial Design

A useful included template usually has:

  • one clear purpose
  • a small number of expected variables
  • predictable markup
  • little or no hidden business logic
  • no unnecessary database access
  • a descriptive filename

For example:

text

1
article_card.html

is clearer than:

text

1
thing.html

Keep Business Logic Out of Includes

Django templates intentionally provide limited programming logic.

An included template should primarily handle presentation.

Good:

django

1
2
3
{% if article.is_published %}
    <span>Published</span>
{% endif %}

Less desirable is trying to encode a large business workflow into nested template conditions.

If logic determines:

  • who may access data
  • whether an operation is allowed
  • how prices are calculated
  • which records should exist

that logic belongs in Python code.

Do Not Use Includes as a Permission Boundary

Suppose:

django

1
2
3
{% if user.is_staff %}
    {% include "admin/delete_button.html" %}
{% endif %}

This hides the button from ordinary users.

It does not secure the delete view.

The server must still enforce permission checks:

python

1
2
3
4
if not request.user.has_perm(
    "articles.delete_article"
):
    raise PermissionDenied

Template visibility is presentation, not authorization.

Common Beginner Mistakes

Copying Markup Instead of Creating a Partial

If the same substantial HTML appears in several templates, consider an include.

Avoid maintaining several copies of:

django

1
2
3
<article class="article-card">
    ...
</article>

Using include for Page Inheritance

Do not replace a clear:

django

1
{% extends "base.html" %}

structure with many large includes.

Use inheritance for overall layouts.

Expecting Blocks in Includes to Be Overridden

Blocks belong to inheritance.

An include is rendered independently.

Passing Too Much Context

This works:

django

1
{% include "article_card.html" %}

but the partial may silently depend on many parent variables.

For reusable components, consider:

django

1
{% include "article_card.html" with article=article only %}

Using the Wrong Template Path

Use the configured template name:

django

1
{% include "articles/article_card.html" %}

not the physical filesystem path.

Forgetting Variable Names

If the partial expects:

text

1
article

pass:

django

1
with article=...

Overusing Dynamic Template Names

This:

django

1
{% include object.template_name %}

can be useful, but it is harder to search and reason about than a fixed template path.

Use dynamic includes when the flexibility is actually needed.

Creating Extremely Configurable Partials

A partial with many flags can become harder to use than duplicated simple markup.

Keep the interface small.

Ignoring Database Queries During Rendering

A partial may access related model data and cause extra queries.

Optimize the queryset in the view.

Putting Business Rules in Template Partials

Templates should present results, not become the main location for business decisions.

Using Includes for Security

Hiding a button does not prevent direct requests to the underlying view.

Permissions belong on the server.

Testing Included Templates

Most include behavior can be tested through the view that renders the complete page.

For example:

python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from django.test import TestCase
from django.urls import reverse


class ArticleListTests(TestCase):
    def test_article_card_is_rendered(self):
        Article.objects.create(
            title="Django Includes",
            summary="Example summary",
            is_published=True,
        )

        response = self.client.get(
            reverse("article-list"),
        )

        self.assertContains(
            response,
            "Django Includes",
        )

This checks the output users actually receive.

Testing a Partial Directly

For an isolated template test:

python

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from django.template.loader import (
    render_to_string,
)
from django.test import TestCase


class ArticleCardTemplateTests(
    TestCase
):
    def test_card_displays_title(self):
        article = Article(
            title="Django Includes",
            summary="Example",
        )

        html = render_to_string(
            "articles/article_card.html",
            {
                "article": article,
            },
        )

        self.assertIn(
            "Django Includes",
            html,
        )

Direct partial tests can be useful for fragments containing meaningful presentation logic.

Do not test every trivial piece of HTML simply because it is included.

Debugging Includes

When an include does not behave as expected, check:

text

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
1. Is the template path correct?

2. Can Django find the template?

3. Does the parent context contain
   the expected variables?

4. Are variables passed with the
   correct names?

5. Is `only` hiding a variable that
   the partial needs?

6. Is the partial inside the expected
   loop or conditional?

7. Is related model access causing
   unexpected database queries?

8. Are you expecting inheritance
   behavior from an include?

For context problems, temporarily rendering a variable can help:

django

1
{{ article }}

or:

django

1
{{ article.title }}

If it renders empty, check where the value should come from.

For reusable application fragments, a good default is:

django

1
{% include "articles/article_card.html" with article=article only %}

This communicates:

text

1
2
3
4
5
6
7
8
Template:
    articles/article_card.html

Required input:
    article

Access to unrelated parent context:
    No

For simple shared fragments that naturally depend on standard context, a plain include may be fine:

django

1
{% include "shared/messages.html" %}

Use the form that makes the dependency easiest to understand.

When to Use include

Use {% include %} when:

  • markup appears in several templates
  • a large page can be divided into clear visual sections
  • a repeated object needs a reusable card or row
  • several pages share the same pagination
  • forms use a repeated field layout
  • navigation or messages should live in one place
  • the fragment needs only template-level presentation logic

Examples:

text

1
2
3
4
5
6
7
8
9
Article card
Product card
User avatar
Navigation
Pagination
Alert
Form field
Empty state
Table row

When Not to Use include

An include may not be appropriate when:

  • you need page-level inheritance
  • the fragment needs substantial Python logic
  • the markup is used only once and extracting it reduces clarity
  • the component requires many configuration flags
  • the content requires its own HTTP endpoint
  • the abstraction makes performance harder to understand

Use include when it reduces complexity, not simply to create more files.

Think of an included template as a small rendering function.

Conceptually:

text

1
2
3
4
5
article_card(
    article
)
    ↓
HTML

In Django syntax:

django

1
{% include "articles/article_card.html" with article=article only %}

The partial receives data and produces markup.

This mental model encourages small and predictable fragments.

Mini Reference

text

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{% include "template.html" %}
    Render another template using
    the current context

{% include "template.html" with item=value %}
    Pass an explicit variable

{% include "template.html" with a=x b=y %}
    Pass several variables

{% include "template.html" only %}
    Render without the parent context

{% include "template.html" with item=value only %}
    Render using only explicitly
    supplied values

Basic include:

django

1
{% include "shared/navigation.html" %}

Include inside a loop:

django

1
2
3
{% for article in articles %}
    {% include "articles/article_card.html" with article=article only %}
{% endfor %}

Dynamic include:

django

1
{% include template_name %}

Typical structure:

text

1
2
3
4
5
6
7
8
templates/
├── base.html
├── shared/
│   ├── navigation.html
│   └── pagination.html
└── articles/
    ├── article_list.html
    └── article_card.html

Conclusion

Django’s {% include %} template tag provides a simple way to reuse rendered HTML across templates.

The basic pattern is:

django

1
{% include "shared/example.html" %}

The included template receives the current context by default.

Specific values can be passed with:

django

1
{% include "shared/example.html" with item=value %}

and the fragment can be isolated from the surrounding context with:

django

1
{% include "shared/example.html" with item=value only %}

The most important ideas are:

  • include renders another template inside the current one
  • included templates normally receive the current context
  • with passes explicit values
  • only limits the available context
  • includes work especially well for cards, navigation, pagination, messages, and other reusable fragments
  • template inheritance and template inclusion solve different problems
  • included templates should focus on presentation
  • permissions and business rules still belong in Python
  • related-object access inside partials can affect database performance
  • reusable fragments should remain small and predictable

For most Django applications, the best place to start is simple:

django

1
{% include "articles/article_card.html" with article=article only %}

It removes duplicated markup while keeping the parent template easy to read and the reusable fragment easy to understand.

Join the Newsletter

Practical insights on Django, backend systems, deployment, architecture, and real-world development — delivered without noise.

Get updates when new guides, learning paths, cheat sheets, and field notes are published.

No spam. Unsubscribe anytime.



There is no third-party involved so don't worry - we won't share your details with anyone.