Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Plugins

Plugins add new directives and magic properties to Alpine. Alpine has official plugins for common tasks: keep data across page reloads, hold focus inside a modal, find out that an element came into view, expand a block smoothly.

Loading a plugin

With the CDN build, the plugin script goes into index.html before the Alpine script. The first example is the collapse plugin, which smoothly expands a block's height with the x-collapse directive:

<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<div x-data="{ open: false }">
  <button @click="open = !open">Task details</button>
  <div x-show="open" x-collapse>
    <p>Buy 2% milk, one liter. Stop by the store after work.</p>
  </div>
</div>

The block does not jump into place — it slides down smoothly and collapses the same way.

In an npm project the plugin is installed as a package and registered in src/main.js before Alpine.start():

npm install @alpinejs/collapse
// src/main.js
import Alpine from 'alpinejs';
import collapse from '@alpinejs/collapse';
 
Alpine.plugin(collapse);
Alpine.start();

persist: saving data

The @alpinejs/persist plugin adds $persist. A value wrapped in it is saved to localStorage and restored after a page reload:

<!-- index.html, after loading @alpinejs/persist and Alpine -->
<div x-data="{ tasks: $persist([]).as('todo-tasks') }">
  <button @click="tasks.push('Task ' + (tasks.length + 1))">Add</button>
  <p x-text="tasks.join(', ')"></p>
</div>

Add a few tasks and reload the page — they are still there. The .as() method sets the storage key. This example does not run on the lesson page: the result window has no access to localStorage.

focus: trapping focus

The @alpinejs/focus plugin adds the x-trap directive. While its value is true, the Tab key moves focus only between the elements inside the block — which is what modals need:

<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<div x-data="{ open: false }">
  <button @click="open = true">New task</button>
  <div x-show="open" x-trap="open" style="border: 1px solid #ccc; padding: 8px">
    <input placeholder="Title">
    <button @click="open = false">Close</button>
  </div>
</div>

Once it opens, the cursor is already in the field, and Tab switches only between the field and the "Close" button.

intersect: coming into view

The @alpinejs/intersect plugin adds x-intersect: the expression runs when the element enters the visible area. This is how the next page of a list is loaded:

<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/intersect@3.x.x/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
 
<div x-data="{ count: 10 }" style="height: 150px; overflow: auto; border: 1px solid #ccc">
  <template x-for="i in count">
    <p x-text="'Task ' + i"></p>
  </template>
  <div x-intersect="count += 10; console.log('Loaded:', count)">Loading…</div>
</div>

Scroll the box down — each time "Loading…" comes into view, ten more tasks are added and the console prints the count. The .once modifier runs the expression only the first time.

Common mistake

Placing the plugin script after the Alpine script. Alpine starts before the plugin registers, and a directive like x-collapse simply does nothing: the block appears without animation. Load plugins above Alpine, and in an npm project call Alpine.plugin() before Alpine.start().

Thanks for reading!

Donate to the site — we work hard for you.