Dynamic page titles in Django
Tags: django, webdev
Published: 2022-03-14 19:57:57

---
title: Dynamic page titles in Django
published: true
description: Using class-based views or template blocks to dynamically generate page titles in a Django app.
tags: django, webdev
canonical_url: https://www.djangoforge.dev/guides/page-titles/
cover_image: https://www.djangoforge.dev/guides/img/page-titles-open-graph.png
---

It's easy to forget about the [HTML `<title>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title) 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.

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

But good page titles are incredibly useful!

![Django site with custom page titles](https://www.djangoforge.dev/guides/img/django-page-titles-dynamic.png)

Forgetting them can hurt both SEO and user experience.

![Django site without custom page titles](https://www.djangoforge.dev/guides/img/django-page-titles-same.png)


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

1. [Using template blocks](#django-page-titles-using-template-blocks) (simpler)
2. [Using class-based views](#django-page-titles-using-class-based-views) (more powerful)

&nbsp;

## 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:

```html
<!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:

```html
{% extends "base.html" %}

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

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

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

```html
{% 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](https://www.djangoforge.dev/), 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:

```python
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`:

```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:

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

But also do something more dynamic:

```python
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:

```python
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:

```python
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
```