Building a simple Accordion with Alpine.js in Statamic

Image

If you're using Statamic, adding interactive UI components without heavy JavaScript frameworks is easy thanks to Alpine.js.

In this tutorial, we’ll build a simple accordion step by step — with smooth height animation using the Alpine x-collapse plugin.

✅ Step 1: Install Alpine.js

If you're using Vite (Laravel style), install Alpine first:

npm install alpinejs

Then in:

javascriptresources/js/site.js
import Alpine from 'alpinejs'

window.Alpine = Alpine

Alpine.start()

✅ Step 2: Install the x-collapse Plugin

Now install the collapse plugin:

npm install @alpinejs/collapse

Then update your site.js:

javascriptresources/js/site.js
import Alpine from 'alpinejs'
import collapse from '@alpinejs/collapse'

Alpine.plugin(collapse)

window.Alpine = Alpine
Alpine.start()

If you're not bundling, include it via CDN in your layout:

html
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://unpkg.com/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>

Make sure this is inside your main layout file (e.g. resources/views/layout.antlers.html).

✅ Step 3: Create Your Accordion Markup

Now create your accordion in a Statamic view.

htmlresources/views/partials/accordion.antlers.html
<div x-data="{ open: null }" class="max-w-4xl mx-auto w-full divide-y divide-gray-200">

    <!-- Item 1 -->
    <div class="py-4">
        <button @click="open === 1 ? open = null : open = 1"
            class="flex justify-between gap-4 w-full text-left font-semibold cursor-pointer">
            <span>What is Statamic?</span>
            <span x-text="open === 1 ? '-' : '+'"></span>
        </button>

        <div x-show="open === 1" x-collapse class="text-gray-600">
            <div class="pt-4">
                Statamic is a modern flat-file CMS built on Laravel.
            </div>
        </div>
    </div>

    <!-- Item 2 -->
    <div class="py-4">
        <button @click="open === 2 ? open = null : open = 2"
            class="flex justify-between gap-4 w-full text-left font-semibold cursor-pointer">
            <span>What is Alpine.js?</span>
            <span x-text="open === 2 ? '-' : '+'"></span>
        </button>

        <div x-show="open === 2" x-collapse class="text-gray-600">
            <div class="pt-4">
                Alpine.js is a lightweight JavaScript framework for adding interactivity.
            </div>
        </div>
    </div>
</div>

✅ Step 4: Include the Partial in a Page

Inside your page template:

html
{{ partial:accordion }}

That’s it 🎉
You now have a working accordion with smooth height animation.

🧠 How It Works

x-data

Initializes Alpine state:

javascript
x-data="{ open: null }"

This means no accordion item is open by default.

@click

Toggles the open item:

javascript
open === 1 ? open = null : open = 1

If item 1 is open → close it.
If it's closed → open it.

x-show

Controls visibility:

javascript
x-show="open === 1"

Only shows the content if open equals 1.

x-collapse ✅ (New Addition)

This is the important upgrade.

Instead of using x-transition, we use:

javascript
x-collapse

The collapse plugin automatically animates the element’s height from 0px to auto.

✔ No manual transition classes
✔ No height calculations
✔ Smooth open/close animation
✔ Cleaner markup

🔥 Bonus: Make It Reusable with Statamic Data

If your accordion items come from a Bard field or Replicator:

htmlresources/views/partials/accordion.antlers.html
<div x-data="{ open: null }" class="max-w-4xl mx-auto w-full divide-y divide-gray-200">
    {{ accordion_items }}
        <div class="py-4">
            <button
                @click="open === {{ index }} ? open = null : open = {{ index }}"
                class="flex justify-between gap-4 w-full text-left font-semibold cursor-pointer"
            >
                <span>{{ title }}</span>
                <span x-text="open === {{ index }} ? '-' : '+'"></span>
            </button>

            <div
                x-show="open === {{ index }}"
                x-collapse
                class="mt-2 text-gray-600"
            >
                <div class="pt-4">
                    {{ content }}
                </div>
            </div>
        </div>
    {{ /accordion_items }}
</div>

Now it works dynamically with your CMS content.

🎯 Final Result

You now have:

✅ Lightweight accordion
✅ No heavy JS framework
✅ Fully CMS-driven
✅ Smooth height animation with x-collapse
✅ Clean and reusable