DEV Community

Cover image for Dynamic page titles in Django
Dave Gaeddert for Dropseed

Posted on • Originally published at djangoforge.dev

Dynamic page titles in Django

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>
Enter fullscreen mode Exit fullscreen mode

But good page titles are incredibly useful!

Django site with custom page titles

Forgetting them can hurt both SEO and user experience.

Django site without custom page titles

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>
Enter fullscreen mode Exit fullscreen mode

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 %}
Enter fullscreen mode Exit fullscreen mode

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

{% extends "base.html" %}

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

{% block content %}
...
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

<!doctype html>
<html lang="en">
<head>
    <title>{{ title }}</title>
</head>
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

But also do something more dynamic:

class ProjectView(PageTitleViewMixin, DetailView):
    def get_title(self):
        return self.object.name
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (0)