Write, Run & Share Alpine.js code online using OneCompiler's Alpine.js online editor for free. It's one of the robust, feature-rich online editors for Alpine.js.
Alpine.js is a lightweight JavaScript framework for composing behavior directly in your markup. It offers reactive and declarative features similar to Vue or React, but with a much smaller footprint. With just 15 attributes, 6 properties, and 2 methods, it's easy to learn.
The x-data attribute defines a component's reactive state directly in HTML.
<!-- Simple reactive data -->
<div x-data="{ message: 'Hello, World!' }">
<h1 x-text="message"></h1>
</div>
<!-- With methods -->
<div
x-data="{
count: 0,
increment() { this.count++ },
decrement() { this.count-- }
}"
>
<button @click="decrement">-</button>
<span x-text="count"></span>
<button @click="increment">+</button>
</div>
<!-- Nested objects and arrays -->
<div
x-data="{
user: { name: 'John', email: '[email protected]' },
items: ['Apple', 'Banana', 'Cherry']
}"
>
<p x-text="user.name"></p>
<p x-text="items[0]"></p>
</div>
x-text sets text content (escapes HTML). x-html renders raw HTML.
<div x-data="{ firstName: 'John', lastName: 'Doe' }">
<span x-text="firstName + ' ' + lastName"></span>
<span x-text="`Welcome, ${firstName}!`"></span>
</div>
<!-- HTML binding (use with trusted content only) -->
<div x-data="{ content: '<strong>Bold</strong> text' }">
<div x-html="content"></div>
</div>
The @ shorthand (or x-on) attaches event listeners with optional modifiers.
<div x-data="{ count: 0 }">
<button @click="count++">Increment</button>
<span x-text="count"></span>
</div>
<!-- Event modifiers -->
<form @submit.prevent="console.log('submitted')">
<button type="submit">Submit</button>
</form>
<button @click.once="alert('Only once!')">Click Once</button>
<input @input.debounce.300ms="search($event.target.value)" />
<!-- Keyboard events -->
<input @keydown.enter="submit" @keydown.escape="cancel" />
The : shorthand (or x-bind) dynamically binds attributes.
<div x-data="{ imageUrl: 'photo.jpg', isDisabled: true }">
<img :src="imageUrl" alt="Photo" />
<button :disabled="isDisabled">Disabled</button>
</div>
<!-- Class binding -->
<div x-data="{ isActive: true, hasError: false }">
<div :class="{ 'active': isActive, 'error': hasError }">Content</div>
<div :class="isActive ? 'bg-green' : 'bg-gray'">Content</div>
</div>
<!-- Style binding -->
<div x-data="{ color: 'red', size: 20 }">
<p :style="{ color: color, fontSize: size + 'px' }">Styled</p>
</div>
x-show toggles CSS display. x-if adds/removes elements from DOM.
<!-- x-show: toggles display -->
<div x-data="{ isVisible: true }">
<button @click="isVisible = !isVisible">Toggle</button>
<div x-show="isVisible" x-transition>Visible content</div>
</div>
<!-- x-if: must be on <template> tag -->
<div x-data="{ showForm: false }">
<button @click="showForm = !showForm">Toggle Form</button>
<template x-if="showForm">
<form>
<input placeholder="Name" />
<button>Submit</button>
</form>
</template>
</div>
<!-- x-else -->
<div x-data="{ loggedIn: false }">
<template x-if="loggedIn">
<p>Welcome back!</p>
</template>
<template x-else>
<p>Please log in</p>
</template>
</div>
x-for renders lists from arrays. Must be on a <template> element.
<div x-data="{ items: ['Apple', 'Banana', 'Cherry'] }">
<ul>
<template x-for="item in items">
<li x-text="item"></li>
</template>
</ul>
</div>
<!-- With index -->
<div x-data="{ colors: ['Red', 'Green', 'Blue'] }">
<template x-for="(color, index) in colors">
<p x-text="`${index + 1}. ${color}`"></p>
</template>
</div>
<!-- With key for dynamic lists -->
<div
x-data="{ todos: [{ id: 1, text: 'Learn Alpine' }, { id: 2, text: 'Build app' }] }"
>
<template x-for="todo in todos" :key="todo.id">
<div x-text="todo.text"></div>
</template>
</div>
x-model creates two-way binding between inputs and component state.
<!-- Text input -->
<div x-data="{ name: '' }">
<input x-model="name" placeholder="Enter name" />
<p x-text="'Hello, ' + (name || 'stranger')"></p>
</div>
<!-- Checkbox -->
<div x-data="{ agreed: false }">
<label><input type="checkbox" x-model="agreed" /> I agree</label>
<button :disabled="!agreed">Submit</button>
</div>
<!-- Radio buttons -->
<div x-data="{ color: '' }">
<label><input type="radio" value="red" x-model="color" /> Red</label>
<label><input type="radio" value="blue" x-model="color" /> Blue</label>
<p x-text="'Chosen: ' + color"></p>
</div>
<!-- Select -->
<div x-data="{ country: 'us' }">
<select x-model="country">
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
</select>
</div>
<!-- Modifiers -->
<input x-model.lazy="value" placeholder="Updates on blur" />
<input x-model.number="age" type="number" />
<input x-model.debounce.500ms="search" />
x-transition adds smooth enter/leave transitions with x-show.
<div x-data="{ open: false }">
<button @click="open = !open">Toggle</button>
<div x-show="open" x-transition>Smooth fade</div>
</div>
<!-- Transition modifiers -->
<div x-show="show" x-transition.duration.500ms>500ms transition</div>
<div x-show="show" x-transition.opacity>Fade only</div>
<div x-show="show" x-transition.scale.origin.top>Scale from top</div>
x-init runs on initialization. x-effect runs reactively when dependencies change.
<!-- x-init -->
<div x-data="{ message: '' }" x-init="message = 'Initialized!'">
<p x-text="message"></p>
</div>
<!-- Fetch data on init -->
<div
x-data="{ users: [] }"
x-init="users = await (await fetch('/api/users')).json()"
>
<template x-for="user in users">
<p x-text="user.name"></p>
</template>
</div>
<!-- x-effect: runs when dependencies change -->
<div x-data="{ count: 0 }">
<button @click="count++">Increment</button>
<div x-effect="console.log('Count:', count)"></div>
</div>
Alpine provides $el, $refs, $watch, $dispatch, and $nextTick.
<!-- $refs: reference elements -->
<div x-data>
<input x-ref="input" placeholder="Type here" />
<button @click="$refs.input.focus()">Focus</button>
</div>
<!-- $watch: watch for changes -->
<div x-data="{ count: 0 }" x-init="$watch('count', val => console.log(val))">
<button @click="count++">Count: <span x-text="count"></span></button>
</div>
<!-- $dispatch: custom events -->
<div x-data @notify="alert($event.detail.message)">
<button @click="$dispatch('notify', { message: 'Hello!' })">Notify</button>
</div>
<!-- $nextTick: run after DOM updates -->
<div x-data="{ items: [] }">
<button @click="items.push('new'); $nextTick(() => console.log('updated'))">
Add Item
</button>
</div>
Define reusable component logic with Alpine.data().
<script>
document.addEventListener("alpine:init", () => {
Alpine.data("dropdown", () => ({
open: false,
toggle() {
this.open = !this.open
},
close() {
this.open = false
},
}))
Alpine.data("counter", (initialCount = 0) => ({
count: initialCount,
increment() {
this.count++
},
decrement() {
this.count--
},
}))
})
</script>
<div x-data="dropdown">
<button @click="toggle">Menu</button>
<div x-show="open" @click.outside="close">
<a href="#">Item 1</a>
<a href="#">Item 2</a>
</div>
</div>
<div x-data="counter(10)">
<button @click="decrement">-</button>
<span x-text="count"></span>
<button @click="increment">+</button>
</div>
Share state across components with Alpine.store().
<script>
document.addEventListener("alpine:init", () => {
Alpine.store("darkMode", {
on: false,
toggle() {
this.on = !this.on
},
})
Alpine.store("cart", {
items: [],
add(item) {
this.items.push(item)
},
get total() {
return this.items.reduce((sum, i) => sum + i.price, 0)
},
})
})
</script>
<button x-data @click="$store.darkMode.toggle()">Toggle Dark Mode</button>
<div x-data :class="{ 'dark': $store.darkMode.on }">Theme-aware content</div>
<div x-data>
<span x-text="$store.cart.items.length + ' items'"></span>
<span x-text="'$' + $store.cart.total"></span>
</div>
Alpine has plugins for Mask, Intersect, Persist, and Collapse.
<!-- Mask: input formatting -->
<input x-data x-mask="(999) 999-9999" placeholder="Phone" />
<!-- Persist: save to localStorage -->
<div x-data="{ count: $persist(0) }">
<button @click="count++">Count: <span x-text="count"></span></button>
</div>
<!-- Collapse: smooth height animations -->
<div x-data="{ open: false }">
<button @click="open = !open">Toggle</button>
<div x-show="open" x-collapse>
<p>Smoothly expanding content</p>
</div>
</div>