Skip to content

dj-layouts

A view-composition pattern for Django, inspired by Zend Framework's layouts/views/view-helpers.

dj-layouts is a Django layout composition library that inverts the normal template-inheritance model, making it easier to maintain low coupling in your project.

Instead of a base template with {% block %} holes that child templates fill, your views return their own content as HTML partials. A Layout class assembles the full page by calling other views as named panels — concurrently under ASGI.

The mental model shift

Classic Django dj-layouts
Base template owns the page structure Layout class owns the page structure
Child template fills {% block content %} Content view returns its own partial HTML
Sidebar is a template include Sidebar is a panel — an independent view
All template logic in one render pass Panels render concurrently via asyncio.gather
Base and child tightly coupled Views never import each other

The key insight: views are HTMX-native partials by default. A view decorated with @layout renders the full page when called directly; if called as a panel it just returns its partial. Your view code stays the same either way.

Minimal complete example

myapp/layouts.py

from dj_layouts import Layout, Panel

class DefaultLayout(Layout):
    template = "myapp/layout.html"
    sidebar = Panel("myapp:sidebar")

myapp/views.py

from django.http import HttpResponse
from dj_layouts import layout, panel_only

@layout("myapp.DefaultLayout")
def homepage(request):
    return HttpResponse("<h1>Welcome!</h1>")

@panel_only
def sidebar(request):
    return HttpResponse("<nav>Links here</nav>")

myapp/templates/myapp/layout.html

{% load layouts %}
<!doctype html>
<html>
<head><title>My Site</title></head>
<body>
  <aside>{% panel "sidebar" %}<p>No sidebar.</p>{% endpanel %}</aside>
  <main>{% panel "content" %}{% endpanel %}</main>
</body>
</html>

That's it. homepage renders the full page. The sidebar runs as a separate view, independently testable, and — on ASGI with @async_layout — runs concurrently with any other panels.

Key benefits

  • Views are independent. Panel views never import each other; wiring happens at the Layout level.
  • HTMX-ready. Every view is already a partial. HTMX can target panel views directly.
  • Concurrent rendering. Under ASGI, all panels run via asyncio.gather — no serial waterfall.
  • Independent testability. Test each panel view in isolation, no full-page setup needed.
  • Graceful errors. A failing panel shows a fallback; the rest of the page still renders.

Where to go next

Topic Page
First layout, first view Getting Started
How it all fits together Concepts
Layout class reference Layouts
Panel sources and options Panels
Script/style queues Render Queues
Layout context Layout Context
@layout, @panel_only, @async_layout Decorators
Concurrent panel rendering Async
Error hooks and debug mode Error Handling
Common patterns Patterns
Security considerations Security
request.layout_* attributes Request Attributes
DJ_LAYOUTS settings dict Settings