Use when adding, changing, or debugging Alpine.js behavior in Django-rendered templates, especially when Alpine.js coexists with HTMX partial updates.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add thedixitjain/the-mega-skill-library --skill alpinejs-django --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Alpinejs Django?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/thedixitjain-alpinejs-django)More formats (shields.io, HTML) on the badges page.
---
name: alpinejs-django
description: "Use when adding, changing, or debugging Alpine.js behavior in Django-rendered templates, especially when Alpine.js coexists with HTMX partial updates."
category: engineering-core
source_repo: hashgraph-online/awesome-codex-plugins
source_path: "plugins/LVTD-LLC/skills/skills/alpinejs-django/SKILL.md"
source_url: https://github.com/hashgraph-online/awesome-codex-plugins/blob/HEAD/plugins/LVTD-LLC/skills/skills/alpinejs-django/SKILL.md
---
# Alpine.js with Django
Use this skill for local browser state in Django-rendered templates: dropdowns,
modals, tabs, disclosures, inline filters, disabled/loading states, small
preview interactions, and client-only toggles. Use Django views/forms/models as
the source of truth for durable state. Use HTMX when the server needs to return
fresh HTML.
## Setup checks
- Identify how the project loads Alpine. Prefer the existing static asset or
bundling pipeline as the source of truth.
- Find the base template, layout, or bundled entrypoint that loads HTMX,
Alpine, and app JavaScript. Preserve the current ordering unless changing
initialization deliberately.
- Determine whether the loaded Alpine file is self-starting or module-based. Do
not call `Alpine.start()` when the project already loads a self-starting
Alpine build. If using a module build, register components and start Alpine
from the owning entrypoint.
- Simple Alpine behavior should usually live inline in the Django template with
`x-data`.
- If a reusable component or store is worth extracting, register it before
Alpine starts. Check the existing script order before relying on
`document.addEventListener("alpine:init", ...)` from an app-wide script.
- When reusable Alpine registration is needed, add or reuse a script or
entrypoint that runs before Alpine starts. Register components and stores
inside `document.addEventListener("alpine:init", ...)`, include the file
through the project's normal asset path, and avoid double-starting Alpine.
## Ownership rules
- Django owns persistence, authorization, validation, redirects, and rendered
HTML.
- HTMX owns server round trips and DOM swaps.
- Alpine owns ephemeral state already present in the browser.
- Plain JavaScript modules or bundled entrypoints own shared DOM behavior that
is not naturally scoped to one Alpine component.
- Do not duplicate the same behavior in Alpine and a plain JS module.
## Template patterns
Use a small `x-data` object near the markup it controls:
```html
<div
x-data="{ open: false }"
@keydown.escape.window="open = false"
@click.outside="open = false"
>
<button
type="button"
:aria-expanded="open.toString()"
@click.stop="open = !open"
>
Menu
</button>
<div x-cloak x-show="open" x-transition>
...
</div>
</div>
```
Prefer Alpine directives over manual DOM manipulation:
- `x-show` for toggling visibility while keeping the element in the DOM.
- `x-if` on a `<template>` when the element should be created and destroyed.
- `x-model` for client-only input state; Django form submission and validation
still happen on the server.
- `:class`, `:disabled`, `:aria-expanded`, and `:hidden` for state-derived
attributes.
- `$watch` for one named state transition; `x-effect` only when the dependency
set is simple and intentional.
- `$dispatch` for browser events between Alpine components or from Alpine to
HTMX triggers.
Use `x-cloak` for anything hidden by default. Ensure the base CSS includes the
required `[x-cloak] { display: none !important; }` rule.
## Django data
Keep Django interpolation out of complex JavaScript expressions when possible.
For simple values, prefer HTML attributes:
```html
<div
data-initial-label="{{ object.name }}"
x-data="{ label: '' }"
x-init="label = $el.dataset.initialLabel"
>
<span x-text="label"></span>
</div>
```
For structured values, prefer Django's `json_script` and parse it in `x-init`:
```html
{{ rows|json_script:"rows-data" }}
<div
x-data="{ rows: [] }"
x-init="rows = JSON.parse(document.getElementById('rows-data').textContent)"
>
<template x-for="row in rows" :key="row.id">
<span x-text="row.name"></span>
</template>
</div>
```
Avoid `|safe` inside Alpine expressions. If the browser must display text, use
`x-text`. Use `x-html` only for trusted, already-sanitized HTML; most HTML should
be rendered by Django or returned through HTMX.
## Alpine and HTMX
When HTMX swaps a fragment containing `x-data`, Alpine should initialize the new
component automatically. Do not restart Alpine after HTMX swaps.
Keep persistent Alpine state outside HTMX targets that will be replaced:
```html
<section x-data="{ panelOpen: false }">
<button type="button" @click="panelOpen = !panelOpen">Filters</button>
<div id="results" hx-get="{% url 'search_results' %}" hx-trigger="change from:#filters">
...
</div>
</section>
```
Use events as the boundary between HTMX responses and Alpine state. From Django,
set an `HX-Trigger` or `HX-Trigger-After-Swap` response header, then listen from
Alpine:
```html
<div x-data="{ open: true }" @profile-saved.window="open = false">
<form hx-post="{% url 'profile_update' %}" hx-target="#profile-panel" hx-swap="outerHTML">
...
</form>
</div>
```
Listen for HTMX lifecycle events in kebab case from Alpine, such as
`@htmx:after-swap.window`, because HTML attributes are case-insensitive.
If Alpine dispatches an event that HTMX should react to, make the trigger
explicit:
```html
<button type="button" x-data @click="$dispatch('refresh-results')">
Refresh
</button>
<div hx-get="{% url 'results' %}" hx-trigger="refresh-results from:body">
...
</div>
```
## Accessibility
- Keep real buttons as `<button type="button">` unless submitting a form.
- Bind ARIA state from Alpine state, especially `aria-expanded`,
`aria-controls`, `aria-selected`, and `aria-hidden`.
- Support `@keydown.escape.window` for dismissible overlays.
- Use `@click.outside` for popovers and menus, paired with a visible trigger.
- Preserve focus behavior for modals, menus, and swapped HTMX content. If focus
trapping is needed, add a focused plain JS module or Alpine plugin deliberately
instead of hand-rolling a brittle trap in attributes.
## Security and CSP
- Treat all Alpine expressions as JavaScript running in the user's browser.
- Do not put secrets, signed tokens, or privileged data into `x-data`,
`data-*`, `json_script`, or hidden inputs unless the user is allowed to see
them.
- If strict Content Security Policy without `unsafe-eval` is required, switch
deliberately to Alpine's CSP build and retest Alpine expressions. The default
`alpinejs/dist/cdn.min.js` build is not the CSP build.
- If Alpine code makes `fetch()` requests, include Django CSRF headers or use
existing HTMX/Django forms instead.
## Validation
- For template-only Alpine changes, run the Django template or view tests that
cover the page, plus `npm run lint` if JavaScript modules changed.
- For HTMX interactions, test the full page and the partial response path.
- If the project has a frontend build step, run it before relying on static
output.
- Manually verify stateful controls in light and dark mode when changing visible
UI behavior.
---
**Source:** [`hashgraph-online/awesome-codex-plugins`](https://github.com/hashgraph-online/awesome-codex-plugins) → `plugins/LVTD-LLC/skills/skills/alpinejs-django/SKILL.md`
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!