Guide 2022 By davegaeddert

Dynamic page titles in Django

Update! Forge now includes an HTMLTitleMixin that implements the ideas in this guide.

It's easy to forget about the HTML <title> element when building a Django app. You put one in your base.html when you start the project, but forget to actually change the title when making your individual views and templates.

<!doctype html>
<html lang="en">
<head>
    <title>Forge</title>
</head>

But good page titles are incredibly useful!

Forgetting them can hurt both SEO and user experience.

 

There are two ways we recommend implementing HTML page titles in Django:

  1. Using template blocks (simpler)
  2. Using class-based views (more powerful)

Django page titles using template blocks

Template blocks can be used for small pieces of content just like they can be used for entire headers/bodies/footers.

In your base.html, use a standard template block inside the <title> tag:

<!doctype html>
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
</head>

When you extend your template, simply use the {% block title %} to set the page title:

{% extends "base.html" %}

{% block title %}Billing{% endblock %}

{% block content %}
...
{% endblock %}

You can use context variables just like any other template blocks:

{% extends "base.html" %}

{% block title %}{{ obj.name }} billing{% endblock %}

{% block content %}
...
{% endblock %}

That's all there is to it! If you don't know where to start then give this a try. But if you find yourself needing more control then consider moving the logic to your Django views instead...

Django page titles using class-based views

In Forge, we strongly prefer class-based views over function-based views. One reason is because it's much easier to share logic across your views, often across your entire app. Generating page titles is a good example of how this can be useful.

Let's create a view mixin with a page title feature that injects a title variable into the template context:

class PageTitleViewMixin:
    title = ""

    def get_title(self):
        """
        Return the class title attr by default,
        but you can override this method to further customize
        """
        return self.title

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["title"] = self.get_title()
        return context

Then use {{ title }} in your base.html:

<!doctype html>
<html lang="en">
<head>
    <title>{{ title }}</title>
</head>

By using both a title class attribute and a get_title method, it will be easy to set a "static" title for a view:

class BillingView(PageTitleViewMixin, TemplateView):
    template_name = "billing.html"
    title = "Billing"

But also do something more dynamic:

class ProjectView(PageTitleViewMixin, DetailView):
    def get_title(self):
        return self.object.name

You could further extend this to add a suffix to the title automatically:

class PageTitleViewMixin:
    ...

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["title"] = self.get_title() + " - My App"
        return context

Bonus! You could also raise an exception on empty titles, so you don't forget to set the title for every view:

class PageTitleViewMixin:
    ...

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        title = self.get_title()

        if not title:
            raise ValueError("Page title should not be empty")

        context["title"] = title + " - My App"

        return context

Feedback

Questions or ideas for improvement? Let me know on Twitter!