Django, evolved

next.dj

Full-stack Django apps, built faster

  • Python 3.12โ€“3.14
  • Django 4.2โ€“6.0
  • ASGI ยท WSGI

File-based routing, DJX templates, components, and forms on top of Django. The patterns of a modern web framework, without leaving the Django you already know.

$ uv add next.dj

Core building blocks

The patterns next.dj adds, each shown in real project source.

File router

Pages are files

Add a directory with a page.py under routes/ and it answers a URL. Bracketed segments capture typed parameters.

  • Directories become URL segments, no urls.py
  • [int:post_id] captures a typed path parameter
  • DUrl[int] coerces and injects it by type
routes/posts/[int:post_id]/page.py
from notes.models import Note
from next.pages import context
from next.urls import DUrl

@context("note")
def note(post_id: DUrl[int]) -> Note:
    return Note.objects.get(pk=post_id)
Components

Composable components

Call components by name, pass props, and fill named slots with child markup.

  • One tag, {% component %}, for every component
  • Props are explicit, slots fill block content
  • Co-locate CSS and JS in the component folder
routes/template.djx
{% #component "card" title="Welcome" %}
  {% #slot "content" %}
    <p>Latest update.</p>
  {% /slot %}
{% /component %}
Forms

Forms wired to handlers

Register a handler with @action and bind a form class. CSRF and re-render are handled for you.

  • @action registers a POST endpoint by name
  • form_class binds and validates the body
  • Invalid submissions re-render with errors
routes/page.py
from django.http import HttpResponseRedirect
from next.forms import action
from notes.forms import NoteForm

@action("create_note", form_class=NoteForm)
def create_note(form: NoteForm) -> HttpResponseRedirect:
    form.save()
    return HttpResponseRedirect("/")
Static assets

Assets next to pages

Drop a template.css or component.js beside the file that uses it. The pipeline collects each asset and injects it where the page renders.

  • Asset files pair with a page or component by filename
  • Component assets ship only when the component renders
  • {% collect_styles %} emits the deduplicated output
notes/
routes/
  page.py
  template.djx
  template.css        # collected with the page
  template.js
_components/note_card/
  component.djx
  component.css       # collected with the component
  component.js
Dependency injection

Inject by signature

Declare what a page or action needs as typed parameters. The resolver matches each one to a provider and supplies it.

  • Parameters resolve by name, type, and marker
  • @resolver.dependency publishes a named value
  • Depends("name") pulls it into any signature
routes/notes/[int:note_id]/page.py
from django.http import HttpRequest
from next.deps import Depends
from next.pages import context
from next.urls import DUrl

@context("note")
def note(
    request: HttpRequest,
    note_id: DUrl[int],
    repo: NoteRepo = Depends("note_repo"),
) -> Note:
    return repo.for_user(request.user).get(pk=note_id)
Testing

Tests that know the router

NextClient boots the file router and registers actions, so a test posts to an action by name.

  • NextClient extends Django's test client
  • post_action resolves an action to its URL
  • render_page covers template-only assertions
notes/tests/test_actions.py
from next.testing.client import NextClient

def test_create_note():
    response = NextClient().post_action(
        "create_note", {"title": "Test", "body": ""},
    )
    assert response.status_code == 302

These are the core blocks. next.dj ships more โ€” layouts, signals, autoreload, deployment, and the full reference live in the documentation.