Interactivity with Alpine.js

On this page

Interactivity with Alpine.js

Every page built by BX Sites already loads Alpine.js

  • it's what powers the built-in dark-mode toggle and language dropdown in each of the three built-in themes. That same Alpine instance is available to your own page content too, for free: no bxsites.json setting to flip, no extraJs entry to add, no extra <script> tag to write in your markdown.

Since raw block-level HTML passes through untouched in your markdown, you can drop Alpine's x-data/x-show/@click/etc. attributes straight onto any HTML block and it just works.

Before reaching for Alpine

Most "interactive" needs already have a purpose-built directive block that doesn't require writing any JS yourself - reach for these first:

Alpine is for the interactive content those don't cover - anything with its own client-side state.

A copy-to-clipboard button

A common one: a button next to an install command that copies it and confirms the copy:

<div x-data="{ copied: false }">
  <button type="button" @click="navigator.clipboard.writeText( 'box install bx-sites' ); copied = true; setTimeout( () => copied = false, 1500 )">
    <span x-show="!copied">Copy install command</span>
    <span x-show="copied" x-cloak>Copied!</span>
  </button>
</div>

A live filter

Filtering a list client-side, no server round-trip:

<div x-data="{ query: '' }">
  <input type="text" x-model="query" placeholder="Filter providers...">
  <ul>
    <li x-show="'local'.includes( query.toLowerCase() )">local (static index, no server)</li>
    <li x-show="'algolia'.includes( query.toLowerCase() )">algolia (hosted DocSearch)</li>
    <li x-show="'pagefind'.includes( query.toLowerCase() )">pagefind (indexed at build time)</li>
  </ul>
</div>

x-model binds the input's value to Alpine state; each <li>'s x-show re-evaluates on every keystroke.

x-data fundamentals, if you're new to Alpine

x-data declares a scope's own reactive state as a plain JS object; anything inside that element can read/write it, and x-show/x-text/ x-model/@click (shorthand for x-on:click) all react to it changing:

<div x-data="{ count: 0 }">
  <button type="button" @click="count++">Clicked <span x-text="count"></span> times</button>
</div>

See Alpine's own documentation for the full directive list (x-if, x-for, x-transition, and more).

Things to know

  • It's core, not optional. The theme chrome (dark mode, language switcher) depends on Alpine, so it can't be turned off in bxsites.json the way mermaid/math can.
  • Version. Currently alpinejs@3.14.1, vendored with this module and served from site/assets/vendor/alpine/ - no CDN involved. Check a theme's own layout.bxm for the exact <script> tag if you need to know precisely what's loaded.
  • Strict CSP. Alpine's default build evaluates the JS expressions inside x-data/@click etc. directly, which needs unsafe-eval under a strict Content-Security-Policy. If your deployment can't allow that, don't rely on Alpine in your page content.
  • Keep it light. A docs page should stay fast and simple - small, self-contained widgets (a copy button, a filter, a toggle) are a good fit; a full client-side app isn't what this is for.
Edit this page Download Markdown Last updated Aug 23, 2026, 2:17:28 AM