API Reference¶
This page provides the API reference generated automatically from docstrings in django-htmx-nav.
htmx_nav.swaps¶
Swap: an out-of-band or <hx-partial> fragment rendered alongside the main content of an HTMX response.
- class htmx_nav.swaps.Swap(template_name=None, context=None, content=None, target_id=None, swap_style='innerHTML', wrap=None, include_if=True)[source]¶
Bases:
objectRepresents an out-of-band (OOB) or <hx-partial> fragment for HTMX responses.
- Parameters:
template_name (
str|None) – Path to the template or partial (e.g., “nav.html#sidebar”). Mutually exclusive with content. Required unless swap_style=”delete” or content is set.content (
str|None) – Ready-made fragment body, bypassing template rendering. Auto-escaped like a template variable unless wrapped in mark_safe. Mutually exclusive with template_name.context (
Mapping[str,Any] |None) – Context mapping for the fragment. Also serves as fallback context during full-page renders. Ignored when content is set.target_id (
str|None) – Target DOM element ID. If None, renders without auto-wrapping.swap_style (
str) – HTMX swap strategy (“innerHTML”, “outerHTML”, “delete”, etc.).wrap (
Literal['oob','hx-partial'] |None) – Auto-wrap mode (“oob” or “hx-partial”). Defaults to HTMX_NAV_DEFAULT_SWAP_WRAP setting. Ignored when swap_style=”delete”.include_if (
str|Callable[[HttpRequest],bool] |bool) – Predicate determining if the swap applies to the request.
- Raises:
ValueError – If both or neither of template_name/content are given for a non-delete swap, or if target_id is omitted for a delete swap.
- applies_to(request)[source]¶
Evaluates include_if against request to decide inclusion.
- Return type:
bool
- content = None¶
- context = None¶
- classmethod delete(target_id, include_if=True)[source]¶
Builds an OOB delete swap that removes target_id from the DOM.
Equivalent to <div id=”{target_id}” hx-swap-oob=”delete”></div>.
- Parameters:
target_id (
str) – DOM element ID to remove.include_if (
str|Callable[[HttpRequest],bool] |bool) – Predicate determining if the swap applies to the request.
- Return type:
- Returns:
A Swap configured for deletion.
- include_if = True¶
- render(request, parent_context=None, using=None)[source]¶
Renders the swap to an HTML string.
Delete swaps render immediately without touching the template engine. Swaps built with content skip rendering too, escaping the value as a template variable would. All others render template_name with context merged over parent_context. In every non-delete case the result is then wrapped for OOB or hx-partial delivery when target_id is set.
- Return type:
str
- swap_style = 'innerHTML'¶
- target_id = None¶
- template_name = None¶
- classmethod text(target_id, content, swap_style='innerHTML', wrap=None, include_if=True)[source]¶
Builds a swap from a ready-made string, skipping template rendering.
- Parameters:
target_id (
str) – Target DOM element ID.content (
str) – The fragment body.swap_style (
str) – HTMX swap strategy.wrap (
Literal['oob','hx-partial'] |None) – Auto-wrap mode; defaults to HTMX_NAV_DEFAULT_SWAP_WRAP.include_if (
str|Callable[[HttpRequest],bool] |bool) – Predicate determining if the swap applies to the request.
- Return type:
- Returns:
A Swap that renders content directly.
- wrap = None¶
- htmx_nav.swaps.Swaps¶
Type alias for a single Swap, list/tuple of Swaps, or None. :meta hide-value:
htmx_nav.shortcuts¶
Rendering shortcuts.
render_with_swaps is the htmx-aware counterpart to django.shortcuts.render, for any HTMX view that wants to piggyback out-of-band swaps — no navigation concept required. render_nav adds PartialSpec-driven partial/block resolution on top, for views that participate in this package’s tab/nav-state model.
- htmx_nav.shortcuts.render_nav(request, template_name, context=None, content_type=None, status=None, using=None, *, partial='#content', swaps=None, title=None)[source]¶
Renders a Django template with HTMX partial resolution and OOB swaps.
- Parameters:
request (
HttpRequest) – The HTTP request object.template_name (
str) – Path to the full template containing partial blocks.context (
Mapping[str,Any] |None) – Optional template context.content_type (
str|None) – Optional response content type.status (
int|None) – Optional HTTP status code.using (
str|None) – Optional template engine.partial (
str|Callable[[HttpRequest],str|None] |Mapping[str,str|Callable[[HttpRequest],bool] |bool] |None) – Specifies which partial to render for HTMX requests. Can be a block name (“#content”), standalone path, callable, or dict mapping names to targets. Defaults to “#content”.swaps (
Swap|list[Swap] |tuple[Swap,...] |None) – Additional out-of-band swaps to include.title (
str|None) – Optional page title. Overrides title context variable.
- Return type:
TemplateResponse- Returns:
A TemplateResponse with partial resolution and OOB swaps.
Example
# Basic partial selection return render_nav( request, "project/detail.html", {"project": project}, partial="#tab_content", ) # Multiple partial targets with swaps return render_nav( request, "project/detail.html", {"project": project}, partial={ "#tab_content": targeting("tab-content"), "#main_content": targeting("main-content"), "#content": True, # fallback }, swaps=[ Swap("partials/sidebar.html", target_id="sidebar"), Swap("partials/notification.html", target_id="flash"), ], title=project.name, )
- htmx_nav.shortcuts.render_with_swaps(request, template_name, context=None, content_type=None, status=None, using=None, *, swaps=None, title=None)[source]¶
Renders a template and appends swap fragments on HTMX requests.
This is the foundational HTMX-aware renderer. Unlike render_nav, it does no partial resolution or navigation state management. Use it for HTMX responses that need out-of-band swaps without navigation involvement.
- Parameters:
request (
HttpRequest) – The HTTP request object.template_name (
str) – Path to the main template.context (
Mapping[str,Any] |None) – Optional template context.content_type (
str|None) – Optional response content type.status (
int|None) – Optional HTTP status code.using (
str|None) – Optional template engine.swaps (
Swap|list[Swap] |tuple[Swap,...] |None) – A single Swap, list, or None. Additional HTML fragments to append as out-of-band swaps.title (
str|None) – Optional page title. Overrides title context variable and injects a <title> element for HTMX requests.
- Return type:
TemplateResponse- Returns:
A TemplateResponse with swaps appended as post-render callbacks if the request is HTMX.
Notes
Adds “HX-Request” to Vary headers for proper caching.
Context from swaps is merged with main context (swap context wins).
Title injection is HTML-escaped.
Example
def form_submit(request): form = MyForm(request.POST) if form.is_valid(): obj = form.save() return render_with_swaps( request, "form/success.html", {"form": form}, swaps=[ Swap("components/badge.html", context={"count": get_count()}), Swap("components/notification.html", target_id="flash-messages"), ], title="Success!", )
htmx_nav.partials¶
PartialSpec: what template or block to render for a given HTMX request.
- htmx_nav.partials.PartialSpec = str | collections.abc.Callable[[django.http.request.HttpRequest], str | None] | collections.abc.Mapping[str, str | collections.abc.Callable[[django.http.request.HttpRequest], bool] | bool] | None¶
Specifies what template or partial block to render for an HTMX request.
- Values resolve to:
Block name (“#name”): Appended to base template as template.html#name.
Standalone path (“path/to/template.html”): Renders in place of base template.
None: Forces a full-page render.
Examples
"#content" "partials/_tab_content.html" "partials/navigation_components.html#sidebar" lambda request: "#tab_content" if htmx_target_is(request, "tabs") else "#content" { "partials/_tab_content.html": targeting("tabs"), "#main_content": targeting("main"), "#content": True, }
htmx_nav.shell¶
make_shell_renderer: a render_nav wrapper that always includes a fixed list of navigational Swaps alongside whatever extra_swaps the caller passes per-call.
Earlier versions took a single shell_template + context_builder and built exactly one Swap internally. That collapsed every navigational region into one fragment, which meant giving up what Swap already does per-region for free: independent target_id, independent include_if for conditional inclusion, and independent debug-swap highlighting (the debug marker in swaps.py is emitted per Swap, keyed on that Swap’s own target_id — one shell Swap means one marker for the whole shell).
This version takes a swaps builder instead: a callable that returns whatever Swap(s) should always accompany this shell for a given request. Each returned Swap is a full Swap — its own template, context, target_id, include_if — so per-region conditional rendering and per-region debug highlighting both fall out for free, the same way they would for any hand-written render_nav(…, swaps=[…]) call. make_shell_renderer’s only remaining job is merging that fixed list with per-call extra_swaps and forwarding to render_nav.
- class htmx_nav.shell.ShellRenderer(*args, **kwargs)[source]¶
Bases:
ProtocolCallable signature for renderers produced by make_shell_renderer.
- htmx_nav.shell.make_shell_renderer(swaps, *, partial='#content')[source]¶
Creates a renderer that always includes a fixed set of Swaps.
- Parameters:
swaps (
Swap|list[Swap] |tuple[Swap,...] |None|Callable[[HttpRequest],Swap|list[Swap] |tuple[Swap,...] |None]) – Swaps inclued in this request by default. Using a Callable allows the swap context to vary based on the request.partial (
str|Callable[[HttpRequest],str|None] |Mapping[str,str|Callable[[HttpRequest],bool] |bool] |None) – Default PartialSpec used unless overridden per-call.
- Return type:
- Returns:
A render_shell function with the signature of ShellRenderer.
Example
def build_shell_swaps(request): return [ Swap("nav/_sidebar.html", sidebar_context(request), target_id="sidebar"), Swap("nav/_breadcrumbs.html", crumbs_context(request), target_id="breadcrumbs"), ] render_shell = make_shell_renderer(build_shell_swaps) def project_detail(request, pk): project = get_object_or_404(Project, pk=pk) return render_shell(request, "app/project_detail.html", {"project": project})
htmx_nav.targeting¶
HTMX request-targeting: the Target condition type, predicates built from it, low-level HX-Target matching, and Target evaluation.
- htmx_nav.targeting.Target = str | collections.abc.Callable[[django.http.request.HttpRequest], bool] | bool¶
Condition deciding whether a partial or swap applies to an HTMX request.
Examples
"main-content" # Matches HX-Target header targeting("main-content", "modal") not_targeting("sidebar") True False
- htmx_nav.targeting.has_messages(request)[source]¶
Predicate that returns True if there are pending Django messages for the request.
Checks the message storage backend. Like Django’s own message checks, calling this marks pending messages as read for this response.
- Return type:
bool
Example
Swap( "partials/messages.html", target_id="messages", include_if=has_messages )
- htmx_nav.targeting.htmx_target_is(request, *dom_ids)[source]¶
Checks if the request’s HX-Target header matches any given DOM ID.
- Parameters:
request (
HttpRequest) – The incoming HTTP request.*dom_ids (
str) – DOM element IDs to match against (e.g., “content”, “#content”).
- Return type:
bool- Returns:
True if the request target matches any provided ID.
Example
if htmx_target_is(request, "tab-content", "modal-body"): ...
htmx_nav.views¶
- htmx_nav.views.make_shell_view_mixin(render=None, *, default_swaps=None, default_partial='#content')[source]¶
Build a mixin that routes a CBV’s response through render_nav — directly by default, or through a shell renderer if one is supplied.
- Parameters:
render (
ShellRenderer|None) – Optional render function, usually created by make_shell_renderer. When omitted, the mixin calls render_nav directly — no make_shell_renderer required for a CBV to use swaps.default_swaps (
Swap|list[Swap] |tuple[Swap,...] |None) – Swap(s) applied on every view using this mixin, combined with (not replaced by) each view’s own get_extra_swaps().default_partial (
str|Callable[[HttpRequest],str|None] |Mapping[str,str|Callable[[HttpRequest],bool] |bool] |None) – Partial spec used unless a view overrides get_partial().
- Return type:
type
- Override points on the view:
get_extra_swaps(): this view’s Swap(s), runs after self.request/self.object are set.
get_title() / title class attribute.
get_partial(): overrides default_partial per-view.
get_shell_template_name(): defaults to get_template_names()[0].
Example
ShellViewMixin = make_shell_view_mixin() class TicketListView(ShellViewMixin, ListView): template_name = "pages/project.html" def get_extra_swaps(self): return [sidebar_swap, breadcrumb_swap]
htmx_nav.helpers¶
- htmx_nav.helpers.cache_on_request(request, key, builder)[source]¶
Caches and returns a computed value on the Django request object.
- Parameters:
request (
HttpRequest) – The Django HTTP request.key (
str) – The attribute name to store the cached value under.builder (
Callable[[],TypeVar(T)]) – A callable that generates the value if not already cached.
- Return type:
TypeVar(T)- Returns:
The cached or newly computed value.
htmx_nav.testing¶
Testing utilities for projects that use htmx_nav.
Provides assertion helpers for Django and HTMX test suites to ensure that full-page reloads, HTMX page-shell swaps, and HTMX partial-tab swaps yield identical context state and rendered HTML markup.
- htmx_nav.testing.assert_html_equal(a, b, *, label_a='a', label_b='b')[source]¶
Asserts two HTML documents/fragments are structurally equal.
Normalizes both inputs with Django’s parse_html (whitespace/attribute- order insensitive) and strips htmx_nav debug-swap markers before comparing, so mismatches reflect real content differences. On failure, raises with a unified diff rather than a raw string/byte comparison, which is unreadable for anything beyond trivial fragments.
Useful directly when comparing two full response bodies (e.g. verifying a response is identical regardless of HX-Target); assert_shell_composition uses the same comparison internally for its fragment-level checks.
- Parameters:
a (
bytes|str) – First HTML document or fragment, bytes or str.b (
bytes|str) – Second HTML document or fragment, bytes or str.label_a (
str) – Label for a used in the diff output on mismatch.label_b (
str) – Label for b used in the diff output on mismatch.
- Raises:
AssertionError – If the two documents differ structurally, with a unified diff of the normalized HTML.
- Return type:
None
- htmx_nav.testing.assert_shell_composition(client, url, *, page_shell_kwargs, tab_shell_kwargs, full_reload_kwargs=None, page_container_id='page-content', tab_container_id='tab-content', self_wrapped=False)[source]¶
Assert that full-page reloads and HTMX swap variants compose identical HTML.
Performs requests across three HTMX interaction tiers (full page reload, page-shell swap, and component/tab swap) and verifies the resulting markup actually nests and matches — catching bugs assert_shell_parity can’t see: a partial response missing the wrapper element its hx-target expects to swap into, template branching on request headers that produces different markup from identical context, or an OOB/ hx-partial fragment (sidebar, breadcrumbs, tabs, …) whose content has silently drifted from what the full page renders for that same region.
- Verifies, in order:
Full-reload’s #{page_container_id} vs. page_shell’s primary (non-fragment) content.
Each OOB/hx-partial fragment on the page_shell response vs. the matching id’s contents on the full-reload response.
Page_shell’s #{tab_container_id} vs. tab_shell’s primary content — the actual nesting check.
Full-reload’s #{tab_container_id} vs. tab_shell’s primary content — transitive; catches drift that 1+3 alone could miss if page_shell and full reload happened to agree by coincidence.
Each OOB/hx-partial fragment on the tab_shell response vs. the matching id’s contents on the full-reload response.
Requires beautifulsoup4 (pip install beautifulsoup4).
- Parameters:
client (
Client) – The Django test client instance.url (
str) – The target endpoint URL.page_shell_kwargs (
dict[str,Any]) – kwargs for client.get representing a page-level swap (e.g. {“HTTP_HX_REQUEST”: “true”, “HTTP_HX_TARGET”: “page-content”}).tab_shell_kwargs (
dict[str,Any]) – kwargs for client.get representing a component/tab-level swap.full_reload_kwargs (
dict[str,Any] |None) – kwargs for a standard browser GET. Defaults to {}.page_container_id (
str) – The element id targeted by page-level swaps.tab_container_id (
str) – The element id targeted by tab-level swaps.self_wrapped (
bool) – Whether swap responses re-emit their own container wrapper (hx-swap=”outerHTML” convention) rather than rendering only the container’s children (the default — matches render_nav/Swap and Django 6 {% partialdef %}). Only affects the primary content comparison; extracted OOB/ hx-partial fragments are always compared by inner content, since Swap.render never re-emits the wrapper it produces.
- Returns:
{“full_reload”: resp, “page_shell”: resp, “tab_shell”: resp} for further assertions.
- Return type:
dict[str,Any]- Raises:
AssertionError – If any request returns a non-200 status code, if a referenced container id isn’t found, or if HTML markup diverges between response modes.
- htmx_nav.testing.assert_shell_parity(client, url, *, requests, checks)[source]¶
Verify that context state remains consistent across different swap modes.
Issues a GET request to url for every entry in requests and executes assertion callbacks against response contexts to ensure shell state parity (e.g. active navigation links, breadcrumbs, sidebar items).
- Parameters:
client (
Client) – The Django test client instance used to execute GET requests.url (
str) – The target URL endpoint to test.requests (
dict[str,dict[str,Any]]) – A mapping of request scenario labels to keyword arguments passed directly to client.get (e.g. HTMX headers).checks (
dict[str,Callable[[Any],Any]]) – A mapping of check labels to extraction callables. Each callable receives response.context and returns an extracted value to compare.
- Returns:
A mapping of request labels to their corresponding Django HTTP response objects.
- Return type:
dict[str,Any]- Raises:
AssertionError – If any context value produced by a check fails to match the baseline value established by the first request.
Example
requests = { "full_reload": {}, "page_shell": {"HTTP_HX_REQUEST": "true", "HTTP_HX_TARGET": "page-content"}, } checks = { "breadcrumbs": lambda ctx: [c["label"] for c in ctx["nav"]["breadcrumbs"]], } responses = assert_shell_parity( client, "/dashboard/", requests=requests, checks=checks )
htmx_nav.debugging¶
Shared debug-swap marker: the inline <script> that flashes a target element when HTMX_NAV_DEBUG_SWAPS is enabled.
_build_marker_script is the single implementation, used both by Swap.render() internally (target_id-driven auto-wrap) and by the public debug_swap_marker / {% htmx_nav_debug_marker %} (hand-built OOB fragments that skip Swap’s wrapping).
- htmx_nav.debugging.debug_swap_marker(target_id)[source]¶
Inline <script> that flashes target_id when swap debugging is enabled; empty string otherwise.
Use when hand-building an OOB fragment (writing hx-swap-oob yourself) instead of letting Swap(target_id=…) wrap it, and you still want HTMX_NAV_DEBUG_SWAPS highlighting. Must be placed inside the element carrying target_id.
- Return type:
str