# When is it good to use simple_tag in Django templates In Django, there are usually two ways to pass data to a template. * **Inject via context in the view**: explicitly provide the data needed to render the page. * **Inject globally via a context processor**: automatically add data that is common to all templates. There is a third option: **`simple_tag` (custom template tag)**. When used well, it keeps the view thin and the template clean. When misused, it can obscure where data originates, making maintenance harder. ![Django web development kitchen center](/media/editor_temp/6/40788e2b-5858-409e-b359-d9788aaa0233.png) This article does not aim to provide a definitive answer; instead, it shares the criteria I use in practice to decide when to employ a `simple_tag`. --- ## Defining simple_tag in one sentence {#sec-76501177fcc9} A **function that takes existing template values and returns a lightly processed result**. * It can be called on demand, like pulling data. * It extracts formatting, composition, or conversion logic out of the template. --- ## The boundary with traditional approaches {#sec-3b60f622e31a} ### View injection: “core data for this page” {#sec-d514bca42e56} If the data is essential for rendering the page, I always let the view handle it. * Central lists or detail data that define the page layout. * Results of branching logic based on conditions. * Model data fetched via ORM queries. These are part of the request‑handling flow, so moving them to a template tag can make tracking difficult. ### Context processor: “global data present in every template” {#sec-c66a30ba1780} Examples: * `request` * Logged‑in user information * Site‑wide settings, menus, environment values * Global UI data such as badge or notification counts But context processors have drawbacks. * They appear automatically everywhere, making usage hard to trace. * Heavy logic can increase the cost of every page. Therefore, I keep global data to a minimum and delegate global data processing or composition to `simple_tag`. --- ## Criteria for using simple_tag {#sec-c7f07728cfc9} My rule set is simple: 1. **Is this data core to rendering the page? → view** 2. **Does the ORM touch the data even 1%? → view** 3. **Can it be built by combining or transforming request + context processor values? → simple_tag** 4. **Is it a helper logic separate from the view’s main flow? → simple_tag** The key is: * **Fetching data** (especially from the DB) belongs to the view. * **Formatting data for presentation** belongs to `simple_tag`. --- ## Why “ORM always goes to the view” {#sec-5b9da70ad6d4} Starting ORM queries inside a template tag often leads to problems. * N+1 queries when looping and calling a tag. * Difficulty pinpointing where the query originates. * Ambiguity about where to apply caching or prefetching. So I take a conservative stance: * **All DB‑touching logic lives in the view (or service layer).** * `simple_tag` only processes data that already exists. --- ## Most common pattern: combining request + global context for output {#sec-80c112ad2db5} I make the `request` object and custom context processor values available to every template, and use `simple_tag` to: * Concatenate strings * Change format based on conditions * Convert types * Transform into a structure that is easy for the template to consume The benefits are clear. * The view stays free of output‑only formatting code. * The template can call a short tag when needed. * Since the data is already in the template, the call is lightweight (no DB hit). --- ## Example: moving complex processing from the template to a simple_tag {#sec-ed85f27527c2} Using `{% if %}` and `{% for %}` in templates is natural. However, as conditions grow and nesting deepens, the template becomes a mix of markup and logic, hard to read. For instance, when combining globally injected values (via a context processor) with `request` to build a user‑facing message, the following issues arise: * Long conditional blocks * Many lines for string concatenation and defaults * Nested logic that obscures the flow Extracting the logic into a `simple_tag` keeps the template focused on UI. ```django {% load ui_helpers %} {% greeting_message %} ``` The heavy logic lives inside the tag; the template just calls it in one line. This keeps the template tidy and centralizes the logic for easier testing and maintenance. --- ## Situations where simple_tag shines {#sec-5972b56959fc} Here are cases where I find `simple_tag` particularly useful: * **Repeated formatting across multiple templates** – dates, amounts, labels, or phrases. * **UI state derived from request data** – active menu based on the current path, toggle states from query strings. * **Transforming global context into a template‑friendly structure** – turning a dict into a list of key/value pairs. * **UI helpers unrelated to the view’s main flow** – determining CSS classes, generating badge text, simple permission flags. --- ## The real takeaway: consistent team/project guidelines {#sec-5f9c78060000} My guidelines are not the ultimate answer, but having a rule set offers clear benefits: * The rationale for each decision remains visible over time. * Maintenance and debugging become easier. * Code reviews encounter fewer disputes. I am satisfied with this approach and continue to uphold it. --- ## Final thoughts {#sec-f059f0d4c5aa} What criteria do you use to split responsibilities? * How much global context do you allow? * How far do you push logic into template tags? * Where do you draw the line for “what the view should do”? Even if your criteria differ, having a clear set of rules is ultimately beneficial. --- **Related posts** - [Extracting URLs in Django templates: path vs path_info vs get_full_path vs build_absolute_uri](/ko/whitedec/2026/1/23/django-template-url-extraction/)