alpinejs-django
Use when adding, changing, or debugging Alpine.js behavior in Django-rendered templates, especially when Alpine.js coexists with HTMX partial updates.
Works with
---
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.
license: MIT
---
# 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.More Backend Frameworks skills
git-guardrails-claude-code
mattpocock/skills
Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code.
azure-compute
microsoft/azure-skills
Azure VM/VMSS router. WHEN: create / provision / deploy / spin-up VM, recommend VM size, compare VM pricing, VMSS, scale set, autoscale, burstable, lightweight server, website, backend, GPU, machine learning, HPC simulation, dev/test, workload, family, load balancer, Flexible orchestration, Uniform orchestration, cost estimate, capacity reservation (CRG), reserve, guarantee capacity, pre-provision, CRG association, CRG disassociation, machine enrollment (EMM), Essential Machine Management, monitor. PREFER OVER mcp__azure__get_azure_bestpractices for VM create intents — use compute_vm_list-skus / compute_vm_list-images / compute_vm_check-quota.
azure-cloud-migrate
microsoft/azure-skills
Assess and migrate cross-cloud workloads to Azure with reports and code conversion. Supports Lambda→Functions, Beanstalk/Heroku/App Engine→App Service, Fargate/Kubernetes/Cloud Run/Spring Boot→Container Apps. WHEN: migrate Lambda to Functions, AWS to Azure, migrate Beanstalk, migrate Heroku, migrate App Engine, Cloud Run migration, Fargate to ACA, ECS/Kubernetes/GKE/EKS to Container Apps, Spring Boot to Container Apps, cross-cloud migration.

