Context processor in Django: Create global variables in the template - Toggle Dark Mode with Tailwind 4

Video thumbnail

A context processor in Django is a function that allows me to define global variables available in all templates without having to explicitly pass them in each view.

I like to explain it as if it were a template middleware: it doesn’t intercept requests, but it does “inject” information into the context of the views. This is useful when you need certain data (user, language, visual theme, etc.) to be accessible on all pages.

Difference between context processors and middleware

  • Although they are conceptually similar, they are not the same:
  • Middleware: works at the request/response level.
  • Context processor: acts at the moment of rendering a template.

Default context processors in Django

Django already includes some ready to use, such as:

  • django.template.context_processors.request → adds the request object.
  • django.contrib.auth.context_processors.auth → adds info about the authenticated user.
  • django.contrib.messages.context_processors.messages → injects flash messages.

How to create a custom context processor

The context processor in Django works like middleware for templates and defines global variables that will be available in all templates. First, I create a file context_processors.py inside my application:

djangoshopping/<app>/context_processors.py

def theme(request):
   current_theme = request.session.get('theme', 'light') # 'light' is the default value
   return {'theme': current_theme}

In this case we read the value from the session. If it doesn’t exist, we return a default value, for example "light".
Then we register this context processor in the TEMPLATES settings so that Django uses it:

settings.py

TEMPLATES = [
    {
        ***
        'OPTIONS': {
            'context_processors': [
                ***
                # custom context processors
                'user.context_processors.theme',
            ],
        },
    },
]

Accessing variables in templates

Once registered, I can already use the theme variable in any template without passing it from the view.

Implementing the Light/Dark Theme Toggle

The next thing we are going to do is implement the toggle to select between light mode and dark mode in Tailwind 4 with  Django, which we installed earlier using Django Template.

The context processor always returns the theme variable according to the user’s session. This ensures that the preference persists while navigating through the website.

Class-Based View

For example, I switch to dark mode, then to light mode, reload the page, and I see it reflected. That’s what we are going to implement.

We define a class-based view that receives POST requests:

djangoshopping\user\views.py

from django.shortcuts import redirect
from django.views import View
class ToggleThemeView(View):
    def post(self, request, *args, **kwargs):
        theme = request.POST.get('theme')
        if theme in ['light', 'dark']:
            request.session['theme'] = theme
        return redirect(request.META.get('HTTP_REFERER', 'user.profile'))

Why POST? Because we are updating data (in this case, the session with the theme). The view receives the request, checks that the value is allowed, assigns it to the session, and then reloads the page.

Configure the route:

djangoshopping\djangoshopping\urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('toggle-theme/', ToggleThemeView.as_view(), name='toggle_theme'),
***    

Template with select and Tailwind to change the theme

For the update process, we do it through a form:

<form action="{% url 'toggle_theme' %}" method="post" class="text-gray-700 font-semibold mb-1">
    {% csrf_token %}
    <select name="theme" class="w-full p-2 border border-gray-300 rounded-md form-select">
        <option value="light" {% if theme == 'light' %}selected{% endif %}>
            {% trans "Choose your theme" %}: {% trans "Light" %}
        </option>
        <option value="dark" {% if theme == 'dark' %}selected{% endif %}>
            {% trans "Choose your theme" %}: {% trans "Dark" %}
        </option>
    </select>
    <button type="submit">Send</button>
</form>

It includes a select with two options: light mode and dark mode. 
Each time I change the value, it is saved in the session and the context processor makes it available in all templates. When reloading, the theme is immediately reflected.

And now we used in the master template; for example:

master.html

<html lang="en" class="{{ theme }}">

Using the "theme" variable defined in the context processors

You’re probably wondering how we use the theme variable anywhere in the application. This is achieved with a context processor, which allows us to inject global variables into all templates—in this example, the use of the theme variable.

Best practices and common mistakes

Over time I learned some lessons worth sharing:

  • Use POST to update session data
    • Even if it seems tempting to use GET, the correct approach is to use POST to follow HTTP best practices.
  • Don’t forget to register the context processor in settings.py
    • This is the most common mistake: defining the function but not adding it to TEMPLATES. Without this, it will never work.
  • Avoid complex logic in the context processor
    • The goal is to inject simple and quick-to-calculate data. If you include heavy logic, you’ll slow down the rendering of all pages.

Frequently asked questions about context processors in Django

  • How do I test if a context processor works?
    • You can start the server and check in any template if the variable appears. Another option is to use RequestFactory in tests to simulate a request.
  • Can I use session variables in a context processor?
    • Yes, as I did in the light/dark theme example. In fact, it’s one of the most common use cases.
  • What other practical uses do context processors have?
    • Displaying a shopping cart on all pages.
    • Injecting global settings (logo, language, metadata).
    • Tracking or analytics variables.

Conclusion

With this implementation we already have a theme switcher (light/dark) controlled by the session and available in all templates via a context processor.
We’ll adjust the styling later, but the functional base is already in place.

Context processors in Django are a simple yet very powerful tool. In my case, I used them to implement a light/dark theme system that is stored in the session and is available in all templates thanks to this technique.

If you want to go further, I recommend experimenting with other practical cases such as a shopping cart or UI customization based on the user’s profile.

I agree to receive announcements of interest about this Blog.

Learn what context processors are in Django, how to create them, and how to use them to inject global variables into your templates. I'll show you a practical example with a light/dark theme toggle using sessions and Tailwind, along with best practices, common pitfalls, and FAQs to help you master this tool.

| 👤 Andrés Cruz

🇪🇸 En español