Django

Templates and static files — the basics

How Django finds the right HTML file to render, the small set of template tags that get used constantly, and how CSS/JS/images actually reach the browser.

Beginner

3 min read

Where Django looks for templates

myapp/
    templates/
        myapp/
            article_list.html
            article_detail.html

By default, Django looks for templates inside a templates/ folder within each app. Nesting an extra folder named after the app itself (templates/myapp/...) is a deliberate, near-universal convention, not an accident — without it, two different apps that both happen to have a detail.html template would collide, since Django searches across every installed app's templates/ folder as one combined namespace. render(request, "myapp/article_detail.html", context) in a view is what actually loads and renders one of these files.

Template inheritance: not repeating the same HTML shell everywhere

{# base.html #}
<!DOCTYPE html>
<html>
<head><title>{% block title %}My Site{% endblock %}</title></head>
<body>
    <nav>...</nav>
    {% block content %}{% endblock %}
</body>
</html>
{# article_detail.html #}
{% extends "myapp/base.html" %}
 
{% block title %}{{ article.title }}{% endblock %}
 
{% block content %}
  <h1>{{ article.title }}</h1>
  <p>{{ article.body }}</p>
{% endblock %}

{% extends %} and {% block %} are how Django avoids copy-pasting the same <html>/<head>/navigation markup into every single page template. base.html defines the overall page shell with named, empty {% block %} sections; any template that extends it fills in those specific blocks, inheriting everything else from the parent automatically. This is the template-layer equivalent of the class inheritance covered in the OOP domain — a child template overrides specific named sections while reusing the rest, the same "override just what's different, inherit the rest" shape.

Including a reusable snippet

{# navbar.html #}
<nav><a href="/">Home</a> | <a href="/articles/">Articles</a></nav>
{% include "myapp/navbar.html" %}

{% include %} inserts one template directly inside another — useful for a genuinely reusable fragment (a navbar, a footer, a single article "card" repeated in a list) that isn't a full page shell the way {% extends %}'s base template is. The rule of thumb: {% extends %} for "this page's overall skeleton," {% include %} for "this smaller, reusable chunk."

Static files: CSS, JavaScript, and images

myapp/
    static/
        myapp/
            style.css
            script.js
{% load static %}
<link rel="stylesheet" href="{% static 'myapp/style.css' %}">

CSS, JavaScript, and image files are static files — content served as-is, not rendered through the template engine. They live in a static/ folder (mirroring the same app-namespaced structure as templates/, for the same reason), and {% static 'path' %} — after {% load static %} at the top of the template — generates the correct URL to reference one. Using {% static %} instead of hardcoding a path like /static/myapp/style.css directly matters because the actual URL static files are served from can be configured differently in production (often served by a separate web server or CDN entirely, not Django itself) — the tag generates whatever URL is currently correct, rather than a path that might be wrong once deployed.

Why static files need {% load static %} and templates don't need {% load %} at all for basic tags

{% load static %} loads the static template tag library — Django's basic built-in tags (if, for, block, extends) are always available, but more specialized tag sets are grouped into libraries that have to be explicitly loaded before use, keeping the always-available core tag set small and avoiding naming collisions between different libraries' tags. Forgetting {% load static %} and then using {% static %} anyway raises a clear template syntax error rather than silently failing, which makes this specific mistake quick to spot and fix.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. Why do Django templates conventionally live at templates/myapp/template.html, not templates/template.html?

2. What's the difference between {% extends %} and {% include %}?

3. Why use {% static 'myapp/style.css' %} instead of hardcoding /static/myapp/style.css?

4. What has to happen before {% static %} can be used in a template?