alpinejs

SKILL.md

Alpine.js Development

Build reactive, declarative interfaces by adding Alpine.js directives directly to HTML markup.

What is Alpine.js

Alpine.js is a lightweight JavaScript framework that provides reactive and declarative behavior directly in HTML markup. It offers Vue-like syntax and reactivity without the build step, making it perfect for enhancing static sites or adding interactivity to server-rendered pages.

Key characteristics:

  • No build step required - include via CDN or npm
  • Declarative syntax using HTML attributes (directives)
  • Reactive data binding and state management
  • Small footprint (~15kb gzipped)
  • Works seamlessly with server-rendered HTML

Installation

Via CDN (Quick Start)

Add the script tag to your HTML <head>:

<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

The defer attribute ensures Alpine loads after the HTML is parsed.

Via npm

npm install alpinejs

Then import and initialize:

import Alpine from 'alpinejs'
window.Alpine = Alpine
Alpine.start()

Core Concepts

State with x-data

Declare reactive state using the x-data directive. All Alpine components start with x-data:

<div x-data="{ count: 0, message: 'Hello' }">
  <!-- State is accessible within this element and its children -->
</div>

Key points:

  • Define data as a JavaScript object
  • Properties are reactive - changes trigger UI updates
  • Data is scoped to the element and its children
  • Child elements can override parent data properties

Event Handling with x-on

Listen to browser events using x-on: or the @ shorthand:

<button @click="count++">Increment</button>
<button x-on:click="count++">Increment (verbose)</button>

Common event modifiers:

  • .prevent - Prevent default behavior
  • .stop - Stop event propagation
  • .outside - Trigger when clicking outside element
  • .window - Listen on window object
  • .debounce - Debounce the handler

Key-specific listeners:

<input @keyup.enter="submit()">
<input @keyup.shift.enter="specialSubmit()">

Templating and Display

x-text - Set element text content:

<span x-text="message"></span>

x-html - Set element HTML (use only with trusted content):

<div x-html="htmlContent"></div>

x-show - Toggle visibility with CSS display property:

<div x-show="isVisible">Content</div>

x-if - Conditionally add/remove element from DOM:

<template x-if="shouldShow">
  <div>Content</div>
</template>

When to use x-show vs x-if:

  • Use x-show when toggling frequently (keeps element in DOM)
  • Use x-if when conditionally rendering expensive content

Looping with x-for

Iterate over arrays to render lists:

<template x-for="item in items" :key="item.id">
  <li x-text="item.name"></li>
</template>

Requirements:

  • Must be on a <template> element
  • Should include :key for proper tracking

Binding Attributes with x-bind

Dynamically bind HTML attributes using x-bind: or : shorthand:

<img :src="imageUrl" :alt="description">
<button :disabled="isProcessing">Submit</button>

Class binding (special object syntax):

<div :class="{ 'active': isActive, 'error': hasError }">

Style binding:

<div :style="{ color: textColor, fontSize: size + 'px' }">

Two-Way Binding with x-model

Bind input values to data properties:

<input x-model="username" type="text">
<textarea x-model="message"></textarea>
<select x-model="country">
  <option value="us">United States</option>
  <option value="ca">Canada</option>
</select>

Modifiers:

  • .number - Convert to number
  • .debounce - Debounce input
  • .throttle - Throttle input

Common Patterns

Toggle Component

<div x-data="{ open: false }">
  <button @click="open = !open">Toggle</button>
  <div x-show="open" x-transition>
    Content to show/hide
  </div>
</div>

Dropdown Component

<div x-data="{ open: false }">
  <button @click="open = !open">Open Dropdown</button>
  <div x-show="open" @click.outside="open = false">
    <a href="#">Option 1</a>
    <a href="#">Option 2</a>
  </div>
</div>

Search/Filter List

<div x-data="{
  search: '',
  items: ['Apple', 'Banana', 'Cherry'],
  get filteredItems() {
    return this.items.filter(i => 
      i.toLowerCase().includes(this.search.toLowerCase())
    )
  }
}">
  <input x-model="search" placeholder="Search...">
  <template x-for="item in filteredItems" :key="item">
    <div x-text="item"></div>
  </template>
</div>

Form with Validation

<div x-data="{ 
  email: '', 
  get isValid() { 
    return this.email.includes('@') 
  } 
}">
  <input x-model="email" type="email">
  <button :disabled="!isValid">Submit</button>
  <span x-show="!isValid" class="error">Invalid email</span>
</div>

Transitions

Add smooth transitions with x-transition:

Simple transition:

<div x-show="open" x-transition>
  Content with fade and scale transition
</div>

Custom duration:

<div x-show="open" x-transition.duration.500ms>

Separate in/out durations:

<div 
  x-show="open"
  x-transition:enter.duration.500ms
  x-transition:leave.duration.1000ms
>

Transition specific properties:

<div x-show="open" x-transition.opacity>
<div x-show="open" x-transition.scale>

Reusable Components

Define reusable component logic with Alpine.data():

Alpine.data('dropdown', () => ({
  open: false,
  toggle() {
    this.open = !this.open
  },
  close() {
    this.open = false
  }
}))

Use in HTML:

<div x-data="dropdown">
  <button @click="toggle">Toggle</button>
  <div x-show="open" @click.outside="close">
    Dropdown content
  </div>
</div>

Global State

Share state across components using Alpine.store():

Alpine.store('auth', {
  user: null,
  loggedIn: false,
  login(user) {
    this.user = user
    this.loggedIn = true
  }
})

Access in templates:

<div x-data>
  <span x-show="$store.auth.loggedIn" x-text="$store.auth.user"></span>
  <button @click="$store.auth.login('John')">Login</button>
</div>

Magic Properties

Alpine provides magic properties accessible anywhere:

  • $el - Reference to current DOM element
  • $refs - Access elements marked with x-ref
  • $store - Access global stores
  • $watch - Watch for data changes
  • $dispatch - Dispatch custom events
  • $nextTick - Execute after next DOM update
  • $root - Access root element of component
  • $data - Access entire data object
  • $id - Generate unique IDs

Example using $refs:

<div x-data>
  <input x-ref="emailInput" type="email">
  <button @click="$refs.emailInput.focus()">Focus Email</button>
</div>

Best Practices

Keep data objects simple - Start with minimal state and add as needed:

<!-- Good -->
<div x-data="{ open: false }">

<!-- Avoid over-engineering -->
<div x-data="{ state: { ui: { modal: { open: false } } } }">

Use getters for computed values:

{
  items: [1, 2, 3],
  get total() {
    return this.items.reduce((sum, i) => sum + i, 0)
  }
}

Prevent FOUC (Flash of Unstyled Content) with x-cloak:

<style>
  [x-cloak] { display: none !important; }
</style>
<div x-data x-cloak>
  <!-- Content hidden until Alpine initializes -->
</div>

Extract complex logic to Alpine.data():

// Instead of inline in HTML
Alpine.data('complexForm', () => ({
  // Complex initialization and methods
}))

Use event modifiers appropriately:

<form @submit.prevent="handleSubmit">
<div @click.outside="close">

Common Gotchas

x-for requires template element:

<!-- Wrong -->
<div x-for="item in items">

<!-- Correct -->
<template x-for="item in items">
  <div x-text="item"></div>
</template>

x-if also requires template:

<template x-if="condition">
  <div>Content</div>
</template>

Accessing parent scope - Use this when needed:

{
  items: ['a', 'b'],
  get filteredItems() {
    return this.items.filter(...)  // Must use this.items
  }
}

Script defer is important:

<!-- Ensures Alpine loads after DOM is ready -->
<script defer src="...alpine.min.js"></script>

Quick Reference

Directive Purpose Example
x-data Define component state <div x-data="{ count: 0 }">
x-text Set text content <span x-text="message">
x-html Set HTML content <div x-html="content">
x-show Toggle visibility <div x-show="isVisible">
x-if Conditional rendering <template x-if="show">
x-for Loop over array <template x-for="item in items">
x-on/@ Listen to events <button @click="handler">
x-bind/: Bind attributes <img :src="url">
x-model Two-way binding <input x-model="value">
x-transition Add transitions <div x-show="open" x-transition>

Additional Resources

For comprehensive directive documentation and advanced patterns:

  • references/directives-reference.md - Complete guide to all Alpine directives
  • references/advanced-patterns.md - Advanced component patterns and techniques

Official documentation: https://alpinejs.dev

Weekly Installs
2
GitHub Stars
1
First Seen
1 day ago
Installed on
opencode2
amp1
cline1
cursor1
kimi-cli1
codex1