🎯 Ejemplos recomendados
Balanced sample collections from various categories for you to explore
Ejemplos de Alpine.js
Ejemplos de Alpine.js incluyendo directivas, componentes, datos reactivos y patrones modernos de Alpine
💻 Hola Mundo con Alpine.js html
🟢 simple
Configuración básica de Alpine.js y ejemplos deHola Mundo
<!-- Alpine.js Hello World Examples -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alpine.js Hello World</title>
<!-- Alpine.js CDN -->
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.demo { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.demo h3 { margin-top: 0; color: #333; }
button { margin: 5px; padding: 8px 16px; cursor: pointer; }
input, select { margin: 5px; padding: 5px; }
.hidden { display: none; }
</style>
</head>
<body>
<h1>Alpine.js Hello World Examples</h1>
<!-- 1. Basic reactive text -->
<div class="demo">
<h3>1. Basic Reactive Text</h3>
<div x-data="{ message: 'Hello, Alpine.js!' }">
<p x-text="message"></p>
<input
type="text"
x-model="message"
placeholder="Type something..."
>
</div>
</div>
<!-- 2. Simple counter -->
<div class="demo">
<h3>2. Simple Counter</h3>
<div x-data="{ count: 0 }">
<button x-on:click="count--">Decrement</button>
<span x-text="count"></span>
<button x-on:click="count++">Increment</button>
<br><br>
<button x-on:click="count = 0">Reset</button>
</div>
</div>
<!-- 3. Toggle visibility -->
<div class="demo">
<h3>3. Toggle Visibility</h3>
<div x-data="{ open: false }">
<button x-on:click="open = !open">
<span x-show="!open">Show Content</span>
<span x-show="open">Hide Content</span>
</button>
<div x-show="open" x-transition>
<p style="margin-top: 10px; padding: 10px; background: #f0f0f0; border-radius: 3px;">
This content can be toggled on and off!
</p>
</div>
</div>
</div>
<!-- 4. User greeting -->
<div class="demo">
<h3>4. User Greeting</h3>
<div x-data="{
name: 'Guest',
greeting: 'Welcome'
}">
<input
type="text"
x-model="name"
placeholder="Enter your name"
x-on:input="greeting = 'Hello, ' + name + '!'"
>
<p x-text="greeting" x-show="name !== 'Guest'"></p>
</div>
</div>
<!-- 5. Multiple data properties -->
<div class="demo">
<h3>5. Multiple Data Properties</h3>
<div x-data="{
firstName: 'John',
lastName: 'Doe',
age: 25,
city: 'New York',
get fullName() {
return this.firstName + ' ' + this.lastName;
},
get info() {
return this.fullName + ' (' + this.age + ', ' + this.city + ')';
}
}">
<input type="text" x-model="firstName" placeholder="First Name">
<input type="text" x-model="lastName" placeholder="Last Name">
<input type="number" x-model="age" placeholder="Age">
<input type="text" x-model="city" placeholder="City">
<div style="margin-top: 10px;">
<strong>Full Name:</strong> <span x-text="fullName"></span><br>
<strong>Info:</strong> <span x-text="info"></span>
</div>
</div>
</div>
<!-- 6. Array manipulation -->
<div class="demo">
<h3>6. Array Manipulation</h3>
<div x-data="{
items: ['Apple', 'Banana', 'Orange'],
newItem: '',
addItem() {
if (this.newItem.trim()) {
this.items.push(this.newItem.trim());
this.newItem = '';
}
},
removeItem(index) {
this.items.splice(index, 1);
}
}">
<div style="margin-bottom: 10px;">
<input
type="text"
x-model="newItem"
placeholder="Add new item"
x-on:keyup.enter="addItem()"
>
<button x-on:click="addItem()">Add</button>
</div>
<ul>
<template x-for="(item, index) in items">
<li>
<span x-text="item"></span>
<button
x-on:click="removeItem(index)"
style="margin-left: 10px;"
>
Remove
</button>
</li>
</template>
</ul>
<p x-show="items.length === 0">No items in the list</p>
<p x-show="items.length > 0">
Total items: <span x-text="items.length"></span>
</p>
</div>
</div>
<!-- 7. Todo list -->
<div class="demo">
<h3>7. Todo List</h3>
<div x-data="{
todos: [
{ text: 'Learn Alpine.js', completed: false },
{ text: 'Build something cool', completed: false }
],
newTodo: '',
addTodo() {
if (this.newTodo.trim()) {
this.todos.push({
text: this.newTodo.trim(),
completed: false
});
this.newTodo = '';
}
},
toggleTodo(index) {
this.todos[index].completed = !this.todos[index].completed;
},
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.completed);
},
get completedCount() {
return this.todos.filter(todo => todo.completed).length;
},
get activeCount() {
return this.todos.filter(todo => !todo.completed).length;
}
}">
<form x-on:submit.prevent="addTodo()" style="margin-bottom: 15px;">
<input
type="text"
x-model="newTodo"
placeholder="What needs to be done?"
style="width: 300px;"
>
<button type="submit">Add</button>
</form>
<ul style="list-style: none; padding: 0;">
<template x-for="(todo, index) in todos">
<li style="margin: 5px 0; padding: 8px; background: #f9f9f9; border-radius: 3px;">
<input
type="checkbox"
x-model="todo.completed"
style="margin-right: 8px;"
>
<span
x-text="todo.text"
:style="todo.completed ? 'text-decoration: line-through; opacity: 0.6;' : ''"
></span>
</li>
</template>
</ul>
<div style="margin-top: 15px;">
<span x-text="activeCount"></span> active,
<span x-text="completedCount"></span> completed
<button
x-on:click="clearCompleted()"
x-show="completedCount > 0"
style="margin-left: 10px;"
>
Clear completed
</button>
</div>
</div>
</div>
<!-- 8. Form with validation -->
<div class="demo">
<h3>8. Form with Validation</h3>
<div x-data="{
name: '',
email: '',
message: '',
errors: {},
validate() {
this.errors = {};
if (!this.name.trim()) {
this.errors.name = 'Name is required';
}
if (!this.email.trim()) {
this.errors.email = 'Email is required';
} else if (!this.email.includes('@')) {
this.errors.email = 'Email is invalid';
}
if (!this.message.trim()) {
this.errors.message = 'Message is required';
}
return Object.keys(this.errors).length === 0;
},
submit() {
if (this.validate()) {
alert('Form submitted successfully!\n' +
'Name: ' + this.name + '\n' +
'Email: ' + this.email + '\n' +
'Message: ' + this.message);
}
}
}">
<form x-on:submit.prevent="submit()">
<div style="margin-bottom: 10px;">
<label>Name:</label><br>
<input
type="text"
x-model="name"
style="width: 250px;"
>
<span x-show="errors.name" style="color: red; display: block;">
<span x-text="errors.name"></span>
</span>
</div>
<div style="margin-bottom: 10px;">
<label>Email:</label><br>
<input
type="email"
x-model="email"
style="width: 250px;"
>
<span x-show="errors.email" style="color: red; display: block;">
<span x-text="errors.email"></span>
</span>
</div>
<div style="margin-bottom: 10px;">
<label>Message:</label><br>
<textarea
x-model="message"
rows="4"
style="width: 250px;"
></textarea>
<span x-show="errors.message" style="color: red; display: block;">
<span x-text="errors.message"></span>
</span>
</div>
<button type="submit">Submit</button>
</form>
</div>
</div>
<!-- 9. Color picker -->
<div class="demo">
<h3>9. Color Picker</h3>
<div x-data="{
color: '#3B82F6',
presetColors: ['#EF4444', '#F59E0B', '#10B981', '#3B82F6', '#8B5CF6', '#EC4899'],
setColor(newColor) {
this.color = newColor;
}
}">
<div style="display: flex; align-items: center; gap: 15px;">
<label>Choose Color:</label>
<input
type="color"
x-model="color"
style="width: 50px; height: 40px;"
>
<div
style="width: 100px; height: 40px; background-color: white; border: 2px solid #ddd; border-radius: 5px;"
:style="'background-color: ' + color"
></div>
<span x-text="color"></span>
</div>
<div style="margin-top: 15px;">
<p>Quick Colors:</p>
<template x-for="presetColor in presetColors">
<button
x-on:click="setColor(presetColor)"
:style="'background-color: ' + presetColor + '; color: white; margin: 2px; padding: 8px; border: none; border-radius: 3px; cursor: pointer;'"
x-text="presetColor"
></button>
</template>
</div>
</div>
</div>
<!-- 10. Shopping cart -->
<div class="demo">
<h3>10. Shopping Cart</h3>
<div x-data="{
products: [
{ id: 1, name: 'Laptop', price: 999 },
{ id: 2, name: 'Mouse', price: 25 },
{ id: 3, name: 'Keyboard', price: 75 }
],
cart: [],
addToCart(product) {
const existingItem = this.cart.find(item => item.id === product.id);
if (existingItem) {
existingItem.quantity++;
} else {
this.cart.push({ ...product, quantity: 1 });
}
},
removeFromCart(productId) {
this.cart = this.cart.filter(item => item.id !== productId);
},
updateQuantity(productId, quantity) {
const item = this.cart.find(item => item.id === productId);
if (item) {
if (quantity <= 0) {
this.removeFromCart(productId);
} else {
item.quantity = quantity;
}
}
},
get total() {
return this.cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
},
get itemCount() {
return this.cart.reduce((sum, item) => sum + item.quantity, 0);
}
}">
<div style="display: flex; gap: 30px;">
<!-- Products -->
<div style="flex: 1;">
<h4>Products</h4>
<template x-for="product in products">
<div style="border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
<h5 x-text="product.name"></h5>
<p>$<span x-text="product.price"></span></p>
<button x-on:click="addToCart(product)">Add to Cart</button>
</div>
</template>
</div>
<!-- Cart -->
<div style="flex: 1;">
<h4>Cart (<span x-text="itemCount"></span> items)</h4>
<div x-show="cart.length === 0">
<p>Your cart is empty</p>
</div>
<template x-for="item in cart">
<div style="border: 1px solid #ddd; padding: 10px; margin-bottom: 10px; border-radius: 5px;">
<h5 x-text="item.name"></h5>
<p>$<span x-text="item.price"></span> x
<input
type="number"
x-model.number="item.quantity"
min="1"
style="width: 60px;"
> = $<span x-text="item.price * item.quantity"></span>
</p>
<button x-on:click="removeFromCart(item.id)">Remove</button>
</div>
</template>
<div x-show="cart.length > 0" style="margin-top: 15px; padding-top: 15px; border-top: 2px solid #333;">
<strong>Total: $<span x-text="total"></span></strong>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
💻 Directivas de Alpine.js html
🟡 intermediate
Guía completa de directivas de Alpine.js y su uso
<!-- Alpine.js Directives Guide -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alpine.js Directives</title>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.demo { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.demo h3 { margin-top: 0; color: #333; }
button { margin: 5px; padding: 8px 16px; cursor: pointer; }
input, select, textarea { margin: 5px; padding: 5px; }
.highlight { background: yellow; }
.error { color: red; }
.success { color: green; }
.fade-enter { opacity: 0; }
.fade-enter-active { transition: opacity 0.5s; }
.slide-enter { transform: translateX(-100%); }
.slide-enter-active { transition: transform 0.3s; }
.box { padding: 20px; margin: 10px 0; background: #f0f0f0; border-radius: 5px; }
</style>
</head>
<body>
<h1>Alpine.js Directives Guide</h1>
<!-- 1. x-data - Component Initialization -->
<div class="demo">
<h3>1. x-data - Component Initialization</h3>
<div x-data="{ message: 'Hello World!' }">
<p x-text="message"></p>
</div>
<div x-data="{
count: 0,
increment() { this.count++ },
decrement() { this.count-- }
}">
<button x-on:click="decrement()">-</button>
<span x-text="count"></span>
<button x-on:click="increment()">+</button>
</div>
</div>
<!-- 2. x-init - Initialization Hook -->
<div class="demo">
<h3>2. x-init - Initialization Hook</h3>
<div x-data="{
time: new Date().toLocaleTimeString(),
startClock() {
setInterval(() => {
this.time = new Date().toLocaleTimeString();
}, 1000);
}
}"
x-init="startClock()">
<p>Current time: <span x-text="time"></span></p>
</div>
<div x-data="{ data: null }"
x-init="fetch('https://jsonplaceholder.typicode.com/posts/1')
.then(response => response.json())
.then(json => data = json)">
<div x-show="data">
<p><strong>Title:</strong> <span x-text="data.title"></span></p>
<p><strong>Body:</strong> <span x-text="data.body"></span></p>
</div>
<div x-show="!data">
<p>Loading...</p>
</div>
</div>
</div>
<!-- 3. x-show / x-hide - Conditional Rendering -->
<div class="demo">
<h3>3. x-show / x-hide - Conditional Rendering</h3>
<div x-data="{
isVisible: true,
hasError: false,
isLoading: false
}">
<button x-on:click="isVisible = !isVisible">Toggle Visibility</button>
<button x-on:click="hasError = !hasError">Toggle Error</button>
<button x-on:click="isLoading = !isLoading">Toggle Loading</button>
<div class="box" x-show="isVisible">
This content is conditionally visible
</div>
<div class="box error" x-show="hasError">
Error message goes here
</div>
<div class="box" x-show="isLoading">
Loading... Please wait
</div>
</div>
</div>
<!-- 4. x-if - True/False Block Rendering -->
<div class="demo">
<h3>4. x-if - True/False Block Rendering</h3>
<div x-data="{ user: null }">
<button x-on:click="user = { name: 'John', role: 'admin' }">
Login as Admin
</button>
<button x-on:click="user = { name: 'Guest', role: 'guest' }">
Login as Guest
</button>
<button x-on:click="user = null">
Logout
</button>
<template x-if="user">
<div class="box">
<p>Welcome, <span x-text="user.name"></span>!</p>
<template x-if="user.role === 'admin'">
<p>You have admin privileges.</p>
</template>
<template x-if="user.role === 'guest'">
<p>You have guest access.</p>
</template>
</div>
</template>
<template x-if="!user">
<div class="box">
<p>Please log in to continue.</p>
</div>
</template>
</div>
</div>
<!-- 5. x-for - Looping -->
<div class="demo">
<h3>5. x-for - Looping</h3>
<div x-data="{
users: [
{ id: 1, name: 'Alice', age: 25, role: 'admin' },
{ id: 2, name: 'Bob', age: 30, role: 'user' },
{ id: 3, name: 'Charlie', age: 35, role: 'user' }
],
addUser() {
this.users.push({
id: this.users.length + 1,
name: 'User ' + (this.users.length + 1),
age: 20,
role: 'user'
});
}
}">
<button x-on:click="addUser()">Add User</button>
<h4>Users List:</h4>
<template x-for="user in users">
<div class="box">
<p><strong>Name:</strong> <span x-text="user.name"></span></p>
<p><strong>Age:</strong> <span x-text="user.age"></span></p>
<p><strong>Role:</strong> <span x-text="user.role"></span></p>
</div>
</template>
<h4>With Index:</h4>
<template x-for="(user, index) in users">
<div class="box">
<p><span x-text="index + 1"></span>. <span x-text="user.name"></span></p>
</div>
</template>
<h4>Admins Only:</h4>
<template x-for="user in users.filter(u => u.role === 'admin')">
<div class="box">
<p>Admin: <span x-text="user.name"></span></p>
</div>
</template>
</div>
</div>
<!-- 6. x-text - Text Content -->
<div class="demo">
<h3>6. x-text - Text Content</h3>
<div x-data="{
message: 'Hello Alpine.js',
htmlContent: '<strong>Bold Text</strong>',
computeGreeting() {
return 'Welcome to ' + this.message;
}
}">
<p>Direct text: <span x-text="message"></span></p>
<p>Computed text: <span x-text="computeGreeting()"></span></p>
<p>Escaped HTML: <span x-text="htmlContent"></span></p>
<p>String manipulation: <span x-text="message.toUpperCase()"></span></p>
<p>Template literal: <span x-text="`Length: ${message.length}`"></span></p>
</div>
</div>
<!-- 7. x-html - HTML Content -->
<div class="demo">
<h3>7. x-html - HTML Content (Use with caution)</h3>
<div x-data="{
safeHtml: '<p>This is <strong>safe</strong> HTML</p>',
userInput: '',
renderedInput: ''
}">
<div>
<label>Enter HTML (will be rendered):</label>
<input
type="text"
x-model="userInput"
placeholder="Enter some HTML"
>
<button x-on:click="renderedInput = userInput">Render HTML</button>
</div>
<h4>Safe HTML:</h4>
<div x-html="safeHtml" class="box"></div>
<h4>User Input (warning - this can be dangerous!):</h4>
<div x-show="renderedInput" x-html="renderedInput" class="box"></div>
<p style="color: orange; font-weight: bold;">
⚠️ Warning: x-html can expose you to XSS attacks. Only use with trusted content!
</p>
</div>
</div>
<!-- 8. x-model - Two-way Data Binding -->
<div class="demo">
<h3>8. x-model - Two-way Data Binding</h3>
<div x-data="{
text: '',
number: 0,
checkbox: false,
radio: 'option1',
select: 'apple',
textarea: '',
multiSelect: [],
terms: false
}">
<h4>Text Input:</h4>
<input type="text" x-model="text" placeholder="Type something...">
<p>You typed: <span x-text="text"></span></p>
<h4>Number Input:</h4>
<input type="number" x-model.number="number">
<p>Number doubled: <span x-text="number * 2"></span></p>
<h4>Checkbox:</h4>
<label>
<input type="checkbox" x-model="checkbox">
I agree to terms (<span x-text="checkbox ? 'Yes' : 'No'"></span>)
</label>
<h4>Radio Buttons:</h4>
<label><input type="radio" x-model="radio" value="option1"> Option 1</label>
<label><input type="radio" x-model="radio" value="option2"> Option 2</label>
<p>Selected: <span x-text="radio"></span></p>
<h4>Select Dropdown:</h4>
<select x-model="select">
<option value="apple">Apple</option>
<option value="banana">Banana</option>
<option value="orange">Orange</option>
</select>
<p>Selected fruit: <span x-text="select"></span></p>
<h4>Textarea:</h4>
<textarea x-model="textarea" rows="3" style="width: 100%;"></textarea>
<p>Character count: <span x-text="textarea.length"></span></p>
<h4>Multi-select:</h4>
<select x-model="multiSelect" multiple>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>
<p>Selected colors: <span x-text="multiSelect.join(', ')"></span></p>
</div>
</div>
<!-- 9. x-on - Event Handling -->
<div class="demo">
<h3>9. x-on - Event Handling</h3>
<div x-data="{
message: 'Click me!',
mousePosition: { x: 0, y: 0 },
keyPress: '',
formInput: '',
handleClick() {
this.message = 'Clicked!';
setTimeout(() => {
this.message = 'Click me!';
}, 1000);
},
handleMouseMove(event) {
this.mousePosition.x = event.offsetX;
this.mousePosition.y = event.offsetY;
},
handleKeydown(event) {
this.keyPress = event.key;
},
handleSubmit(event) {
event.preventDefault();
alert('Form submitted: ' + this.formInput);
this.formInput = '';
}
}">
<!-- Click events -->
<button x-on:click="handleClick()" x-text="message"></button>
<button x-on:click="message = 'Directly changed!'">Direct Change</button>
<button x-on:click.once="message = 'This only works once!'">Once Only</button>
<!-- Mouse events -->
<div
class="box"
x-on:mousemove="handleMouseMove($event)"
style="height: 100px; position: relative;"
>
Mouse position: (<span x-text="mousePosition.x"></span>, <span x-text="mousePosition.y"></span>)
</div>
<!-- Keyboard events -->
<input
type="text"
x-on:keydown="handleKeydown($event)"
placeholder="Type to see key pressed"
>
<p>Last key: <span x-text="keyPress"></span></p>
<!-- Form events -->
<form x-on:submit="handleSubmit($event)">
<input
type="text"
x-model="formInput"
placeholder="Enter text to submit"
required
>
<button type="submit">Submit</button>
</form>
<!-- Modifier examples -->
<div style="margin-top: 15px;">
<p>With modifiers:</p>
<input
type="text"
placeholder="Enter and press Enter"
x-on:keydown.enter="alert('Enter pressed!')"
>
<button
x-on:click.shift="alert('Shift+Click!')"
x-on:click.ctrl="alert('Ctrl+Click!')"
x-on:click.alt="alert('Alt+Click!')"
>
Click with modifier keys
</button>
</div>
</div>
</div>
<!-- 10. x-bind - Attribute Binding -->
<div class="demo">
<h3>10. x-bind - Attribute Binding</h3>
<div x-data="{
color: '#3B82F6',
width: 100,
isDisabled: false,
href: 'https://example.com',
alt: 'Sample image',
sizes: ['small', 'medium', 'large'],
selectedSize: 'medium'
}">
<!-- Style binding -->
<div
class="box"
:style="`background-color: ${color}; width: ${width}px; height: 50px;`"
></div>
<!-- Class binding -->
<div
class="box"
:class="{
'highlight': color === '#3B82F6',
'error': color === '#EF4444',
'success': color === '#10B981'
}"
>
Dynamic styling based on color
</div>
<!-- Attribute binding -->
<a :href="href" :target="'_blank'">External Link</a><br>
<img :src="'https://picsum.photos/100/100'" :alt="alt" style="margin-top: 10px;"><br>
<!-- Multiple attributes -->
<button
:disabled="isDisabled"
:style="`background-color: ${isDisabled ? '#ccc' : '#3B82F6'};`"
>
<span x-text="isDisabled ? 'Disabled' : 'Enabled'"></span>
</button>
<button x-on:click="isDisabled = !isDisabled">
Toggle Disabled
</button>
<!-- Select binding -->
<select x-model="selectedSize">
<template x-for="size in sizes">
<option :value="size" x-text="size"></option>
</template>
</select>
<span>Selected: <span x-text="selectedSize"></span></span>
<!-- Dynamic attributes -->
<div
x-data="dynamicAttrs = { id: 'dynamic-id', 'data-value': '123' }"
v-bind="dynamicAttrs"
>
This div has dynamic attributes
</div>
</div>
</div>
<!-- 11. x-transition - Transitions -->
<div class="demo">
<h3>11. x-transition - Transitions</h3>
<div x-data="{
show: false,
showSlide: false,
showFade: false,
items: ['Item 1', 'Item 2', 'Item 3'],
currentItemIndex: 0,
nextItem() {
this.currentItemIndex = (this.currentItemIndex + 1) % this.items.length;
}
}">
<!-- Basic transition -->
<button x-on:click="show = !show">Toggle Basic Transition</button>
<div
x-show="show"
x-transition
class="box"
>
This content fades in and out
</div>
<!-- Fade transition -->
<button x-on:click="showFade = !showFade">Toggle Fade Transition</button>
<div
x-show="showFade"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="box"
>
This content has a custom fade transition
</div>
<!-- Slide transition -->
<button x-on:click="showSlide = !showSlide">Toggle Slide Transition</button>
<div
x-show="showSlide"
x-transition:enter="transition ease-out duration-500"
x-transition:enter-start="opacity-0 transform -translate-x-full"
x-transition:enter-end="opacity-100 transform translate-x-0"
x-transition:leave="transition ease-in duration-500"
x-transition:leave-start="opacity-100 transform translate-x-0"
x-transition:leave-end="opacity-0 transform -translate-x-full"
class="box"
>
This content slides in and out
</div>
<!-- Carousel with transitions -->
<div style="position: relative; height: 100px;">
<button x-on:click="nextItem()">Next Item</button>
<template x-for="(item, index) in items">
<div
x-show="currentItemIndex === index"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
style="position: absolute; width: 100%;"
>
<div class="box">
<h4 x-text="item"></h4>
<p>Item <span x-text="index + 1"></span> of <span x-text="items.length"></span></p>
</div>
</div>
</template>
</div>
</div>
</div>
<!-- 12. x-ref - Element References -->
<div class="demo">
<h3>12. x-ref - Element References</h3>
<div x-data="{
message: '',
inputRef: null,
init() {
// Auto-focus input after 2 seconds
setTimeout(() => {
if (this.$refs.inputRef) {
this.$refs.inputRef.focus();
}
}, 2000);
},
focusInput() {
this.$refs.inputRef.focus();
},
scrollToTop() {
this.$refs.containerRef.scrollTo({ top: 0, behavior: 'smooth' });
},
getInputValue() {
this.message = 'Input value: ' + this.$refs.inputRef.value;
},
changeBackgroundColor() {
this.$refs.boxRef.style.backgroundColor =
'#' + Math.floor(Math.random()*16777215).toString(16);
}
}"
x-init="init()">
<!-- Reference to input -->
<div>
<input
type="text"
x-ref="inputRef"
placeholder="This input will auto-focus after 2 seconds"
>
<button x-on:click="focusInput()">Focus Input</button>
<button x-on:click="getInputValue()">Get Value</button>
</div>
<!-- Reference to div -->
<div
x-ref="boxRef"
class="box"
style="margin-top: 10px;"
>
Click the button to change my background color
</div>
<button x-on:click="changeBackgroundColor()">Change Background</button>
<!-- Reference to container -->
<div
x-ref="containerRef"
style="height: 100px; overflow-y: auto; border: 1px solid #ccc; margin-top: 10px;"
>
<p style="height: 200px;">Scrollable content</p>
<p>More content...</p>
</div>
<button x-on:click="scrollToTop()">Scroll to Top</button>
<!-- Show message -->
<p x-show="message" x-text="message" style="margin-top: 10px; color: green;"></p>
</div>
</div>
<!-- 13. x-cloak - Prevent Flash -->
<div class="demo">
<h3>13. x-cloak - Prevent Flash</h3>
<style>
[x-cloak] { display: none !important; }
</style>
<div x-data="{ message: 'This content was hidden initially' }">
<div x-cloak>
<h4>This content is hidden until Alpine.js loads:</h4>
<p x-text="message"></p>
</div>
<div>
<h4>This content is visible immediately:</h4>
<p>This will show even before Alpine.js loads</p>
</div>
</div>
<p style="color: blue;">
The content above with x-cloak is hidden initially to prevent the "flash" of unstyled content before Alpine.js initializes.
</p>
</div>
</body>
</html>
💻 Propiedades Mágicas de Alpine.js html
🟡 intermediate
Trabajando con propiedades mágicas y ayudantes integrados de Alpine.js
<!-- Alpine.js Magic Properties -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alpine.js Magic Properties</title>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.demo { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.demo h3 { margin-top: 0; color: #333; }
.box { padding: 15px; margin: 10px 0; background: #f8f9fa; border-radius: 5px; border: 1px solid #dee2e6; }
.highlight { background: #fff3cd; border-color: #ffeaa7; }
.error { background: #f8d7da; border-color: #f5c6cb; color: #721c24; }
.success { background: #d4edda; border-color: #c3e6cb; color: #155724; }
button { margin: 5px; padding: 8px 16px; cursor: pointer; }
input, select { margin: 5px; padding: 5px; }
.scroll-container { height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; }
.modal {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center;
z-index: 1000;
}
.modal-content { background: white; padding: 20px; border-radius: 8px; max-width: 500px; }
</style>
</head>
<body>
<h1>Alpine.js Magic Properties</h1>
<!-- 1. $el - Current Element -->
<div class="demo">
<h3>1. $el - Current Element</h3>
<div x-data="{ message: 'Click me!' }">
<button
x-on:click="$el.textContent = 'Clicked!'"
style="padding: 10px 20px; font-size: 16px;"
x-text="message"
></button>
<div
class="box"
x-on:mouseover="$el.style.backgroundColor = '#e3f2fd'"
x-on:mouseout="$el.style.backgroundColor = '#f8f9fa'"
>
Hover over this box to change its background
</div>
<input
type="text"
x-on:focus="$el.style.borderColor = '#2196f3'"
x-on:blur="$el.style.borderColor = '#ddd'"
placeholder="Focus me to see border change"
>
</div>
</div>
<!-- 2. $el - Element Manipulation -->
<div class="demo">
<h3>2. $el - Advanced Element Manipulation</h3>
<div x-data="{
counter: 0,
increment() {
this.counter++;
// Add highlight effect
$el.classList.add('highlight');
setTimeout(() => {
$el.classList.remove('highlight');
}, 500);
},
changeColor() {
const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7'];
const randomColor = colors[Math.floor(Math.random() * colors.length)];
$el.style.backgroundColor = randomColor;
$el.style.color = 'white';
}
}">
<div
class="box"
x-on:click="increment()"
style="cursor: pointer;"
>
<p>Click count: <span x-text="counter"></span></p>
<small>Click this box to increment and see highlight effect</small>
</div>
<button x-on:click="changeColor()">Change My Color</button>
</div>
</div>
<!-- 3. $refs - Element References -->
<div class="demo">
<h3>3. $refs - Element References</h3>
<div x-data="{
message: '',
focusInput() {
this.$refs.myInput.focus();
this.$refs.myInput.select();
},
getInputValue() {
this.message = this.$refs.myInput.value;
},
scrollToTop() {
this.$refs.scrollContainer.scrollTop = 0;
},
scrollToBottom() {
this.$refs.scrollContainer.scrollTop = this.$refs.scrollContainer.scrollHeight;
},
toggleClass() {
this.$refs.targetBox.classList.toggle('highlight');
},
getElementInfo() {
const element = this.$refs.infoElement;
this.message = `Tag: ${element.tagName}, ID: ${element.id}, Classes: ${element.className}`;
}
}">
<input
type="text"
x-ref="myInput"
placeholder="This is the referenced input"
value="Hello World!"
>
<button x-on:click="focusInput()">Focus & Select</button>
<button x-on:click="getInputValue()">Get Value</button>
<div
x-ref="scrollContainer"
class="scroll-container"
>
<p>This is scrollable content...</p>
<p>Scroll down and use the buttons to navigate</p>
<div style="height: 100px; background: #f0f0f0; margin: 10px 0;">More content 1</div>
<div style="height: 100px; background: #e0e0e0; margin: 10px 0;">More content 2</div>
<div style="height: 100px; background: #d0d0d0; margin: 10px 0;">More content 3</div>
<div style="height: 100px; background: #c0c0c0; margin: 10px 0;">End of content</div>
</div>
<button x-on:click="scrollToTop()">Scroll to Top</button>
<button x-on:click="scrollToBottom()">Scroll to Bottom</button>
<div
x-ref="targetBox"
class="box"
>
<button x-on:click="toggleClass()">Toggle My Class</button>
</div>
<div
x-ref="infoElement"
id="info-div"
class="box highlight"
>
Element with ID and multiple classes
</div>
<button x-on:click="getElementInfo()">Get Element Info</button>
<p x-show="message" x-text="message" class="success"></p>
</div>
</div>
<!-- 4. $store - Global Store -->
<div class="demo">
<h3>4. $store - Global Store</h3>
<div x-data="{
theme: 'light',
user: { name: 'John', role: 'admin' },
notifications: [],
addNotification(message) {
this.notifications.push({
id: Date.now(),
message,
timestamp: new Date().toLocaleTimeString()
});
// Keep only last 5 notifications
if (this.notifications.length > 5) {
this.notifications.shift();
}
},
clearNotifications() {
this.notifications = [];
}
}"
x-init="$store.theme = theme; $store.user = user; $store.notifications = notifications;">
<!-- Theme switcher -->
<div class="box">
<h4>Theme: <span x-text="$store.theme"></span></h4>
<button x-on:click="$store.theme = 'light'">Light Theme</button>
<button x-on:click="$store.theme = 'dark'">Dark Theme</button>
<p>Current theme stored in $store.theme: <span x-text="$store.theme"></span></p>
</div>
<!-- User info -->
<div class="box">
<h4>User Info (from $store):</h4>
<p>Name: <span x-text="$store.user.name"></span></p>
<p>Role: <span x-text="$store.user.role"></span></p>
<button x-on:click="$store.user.name = 'Jane'">Change Name to Jane</button>
</div>
<!-- Notifications -->
<div class="box">
<h4>Notifications:</h4>
<button x-on:click="addNotification('New notification!')">
Add Notification
</button>
<button x-on:click="clearNotifications()">
Clear All
</button>
<div style="margin-top: 10px;">
<template x-for="notification in $store.notifications">
<div class="highlight" style="margin-bottom: 5px; padding: 8px;">
<small x-text="notification.timestamp"></small>:
<span x-text="notification.message"></span>
</div>
</template>
</div>
<p x-show="$store.notifications.length === 0">No notifications</p>
</div>
</div>
</div>
<!-- 5. $event - Event Object -->
<div class="demo">
<h3>5. $event - Event Object</h3>
<div x-data="{
mousePosition: { x: 0, y: 0 },
keyInfo: '',
clickInfo: '',
submitInfo: '',
handleMouseMove(e) {
this.mousePosition.x = e.offsetX;
this.mousePosition.y = e.offsetY;
},
handleKeydown(e) {
this.keyInfo = `Key: ${e.key}, Code: ${e.code}, Ctrl: ${e.ctrlKey}, Shift: ${e.shiftKey}`;
},
handleClick(e) {
this.clickInfo = `Clicked at (${e.clientX}, ${e.clientY}) on element: ${e.target.tagName}`;
},
handleSubmit(e) {
e.preventDefault();
this.submitInfo = `Form submitted with ${new FormData(e.target).get('username')}`;
}
}">
<!-- Mouse events -->
<div
class="box"
style="height: 100px; cursor: crosshair; position: relative;"
x-on:mousemove="handleMouseMove($event)"
x-on:click="handleClick($event)"
>
<p>Move mouse here: (<span x-text="mousePosition.x"></span>, <span x-text="mousePosition.y"></span>)</p>
<p x-show="clickInfo" x-text="clickInfo"></p>
</div>
<!-- Keyboard events -->
<input
type="text"
x-on:keydown="handleKeydown($event)"
placeholder="Type to see event info"
style="width: 300px;"
>
<p x-show="keyInfo" x-text="keyInfo"></p>
<!-- Form events -->
<form x-on:submit="handleSubmit($event)">
<input type="text" name="username" placeholder="Username" required>
<button type="submit">Submit</button>
</form>
<p x-show="submitInfo" x-text="submitInfo"></p>
<!-- Event prevention and propagation -->
<div
class="box"
x-on:click="alert('Outer div clicked!')"
style="position: relative; padding: 20px;"
>
<p>Outer div (clickable)</p>
<button
x-on:click.stop="alert('Button clicked without propagating')"
>
Stop Propagation
</button>
<button
x-on:click.prevent="alert('Default prevented')"
>
Prevent Default
</button>
<button
x-on:click.self="alert('Only when clicking the div itself')"
>
Self Only
</button>
</div>
</div>
</div>
<!-- 6. $dispatch - Custom Events -->
<div class="demo">
<h3>6. $dispatch - Custom Events</h3>
<div x-data="{
customMessage: '',
eventData: null,
lastEvent: '',
handleMessage(event) {
this.customMessage = event.detail.message;
this.eventData = event.detail;
this.lastEvent = 'Received: ' + event.type;
},
handleNotification(event) {
alert('Notification: ' + event.detail.message);
}
}"
x-on:custom-message="handleMessage($event)"
x-on:show-notification="handleNotification($event)">
<!-- Event dispatcher -->
<div class="box">
<h4>Event Dispatchers:</h4>
<button x-on:click="$dispatch('custom-message', { message: 'Hello from dispatcher!', timestamp: Date.now() })">
Dispatch Custom Message
</button>
<button x-on:click="$dispatch('show-notification', { message: 'This is a notification!' })">
Show Notification
</button>
<button x-on:click="$dispatch('user-action', { action: 'click', element: 'button' })">
Dispatch User Action
</button>
</div>
<!-- Event listener -->
<div class="box">
<h4>Event Receiver:</h4>
<p>Custom Message: <span x-text="customMessage"></span></p>
<p>Last Event: <span x-text="lastEvent"></span></p>
<div x-show="eventData">
<h5>Event Details:</h5>
<template x-for="(value, key) in eventData">
<p><small><strong x-text="key"></strong>: <span x-text="value"></span></small></p>
</template>
</div>
</div>
<!-- Cross-component communication -->
<div class="box">
<h4>Cross-Component Communication:</h4>
<button x-on:click="$dispatch('theme-change', { theme: 'dark' })">
Change Theme to Dark
</button>
<button x-on:click="$dispatch('theme-change', { theme: 'light' })">
Change Theme to Light
</button>
</div>
<div
x-data="{ currentTheme: 'light' }"
x-on:theme-change="currentTheme = $event.detail.theme"
class="box"
>
<p>Current Theme: <span x-text="currentTheme"></span></p>
<div :style="'background: ' + (currentTheme === 'dark' ? '#333' : '#fff') + '; color: ' + (currentTheme === 'dark' ? '#fff' : '#333') + '; padding: 10px;'">
Themed content based on dispatched events
</div>
</div>
</div>
</div>
<!-- 7. $watch - Reactive Watching -->
<div class="demo">
<h3>7. $watch - Reactive Watching</h3>
<div x-data="{
counter: 0,
message: '',
searchQuery: '',
results: [],
user: { name: '', email: '' },
watchCount: 0,
init() {
// Watch for counter changes
this.$watch('counter', (value) => {
this.message = `Counter changed to: ${value}`;
this.watchCount++;
});
// Watch for search query
this.$watch('searchQuery', (value) => {
if (value.length > 2) {
// Simulate search
setTimeout(() => {
this.results = [`Result for "${value}"`, `Another result for "${value}"`];
}, 500);
} else {
this.results = [];
}
});
// Deep watch user object
this.$watch('user', (value) => {
console.log('User changed:', value);
}, { deep: true });
}
}">
<div class="box">
<h4>Counter Watcher:</h4>
<button x-on:click="counter++">Increment</button>
<button x-on:click="counter--">Decrement</button>
<button x-on:click="counter = 0">Reset</button>
<p>Counter: <span x-text="counter"></span></p>
<p>Watch triggered: <span x-text="watchCount"></span> times</p>
<p x-show="message" x-text="message"></p>
</div>
<div class="box">
<h4>Search Watcher:</h4>
<input
type="text"
x-model="searchQuery"
placeholder="Search for something (type 3+ characters)"
style="width: 300px;"
>
<div x-show="results.length > 0">
<h5>Results:</h5>
<template x-for="result in results">
<div class="highlight" style="padding: 5px; margin: 2px 0;">
<span x-text="result"></span>
</div>
</template>
</div>
</div>
<div class="box">
<h4>Deep Watcher:</h4>
<input
type="text"
x-model="user.name"
placeholder="Name"
>
<input
type="email"
x-model="user.email"
placeholder="Email"
>
<p>User object: <span x-text="JSON.stringify(user)"></span></p>
<small>Check console to see deep watcher in action</small>
</div>
</div>
</div>
<!-- 8. $nextTick - DOM Update Timing -->
<div class="demo">
<h3>8. $nextTick - DOM Update Timing</h3>
<div x-data="{
items: ['Item 1', 'Item 2'],
newItem: '',
message: '',
addItem() {
this.items.push(this.newItem);
this.newItem = '';
// Wait for DOM to update
this.$nextTick(() => {
// Scroll to the new item
const allItems = document.querySelectorAll('.list-item');
const lastItem = allItems[allItems.length - 1];
if (lastItem) {
lastItem.scrollIntoView({ behavior: 'smooth' });
}
this.message = 'New item added and scrolled into view!';
setTimeout(() => {
this.message = '';
}, 2000);
});
},
highlightLastItem() {
this.$nextTick(() => {
const items = document.querySelectorAll('.list-item');
if (items.length > 0) {
items[items.length - 1].classList.add('highlight');
setTimeout(() => {
items[items.length - 1].classList.remove('highlight');
}, 1000);
}
});
}
}">
<div class="box">
<h4>Add Items with Auto-Scroll:</h4>
<input
type="text"
x-model="newItem"
placeholder="Enter item name"
x-on:keyup.enter="addItem()"
>
<button x-on:click="addItem()">Add Item</button>
<button x-on:click="highlightLastItem()">Highlight Last Item</button>
<div class="scroll-container" style="max-height: 200px; overflow-y: auto; margin-top: 10px;">
<template x-for="(item, index) in items">
<div
class="list-item"
style="padding: 8px; margin: 2px 0; background: #f8f9fa; border-left: 3px solid #007bff;"
>
<span x-text="index + 1"></span>. <span x-text="item"></span>
</div>
</template>
</div>
<p x-show="message" x-text="message" class="success"></p>
</div>
</div>
</div>
</body>
</html>
💻 Plugins y Utilidades de Alpine.js html
🔴 complex
Plugins, extensiones y funciones de utilidad de Alpine.js
<!-- Alpine.js Plugins and Utilities -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Alpine.js Plugins and Utilities</title>
<!-- Alpine.js Core -->
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
<!-- Alpine.js Plugins -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; }
.demo { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 8px; background: #fafafa; }
.demo h3 { margin-top: 0; color: #333; border-bottom: 2px solid #007bff; padding-bottom: 5px; }
.box { padding: 15px; margin: 10px 0; background: white; border-radius: 5px; border: 1px solid #e0e0e0; }
.highlight { background: #fff3cd; border-color: #ffeaa7; }
.success { background: #d4edda; border-color: #c3e6cb; color: #155724; }
.error { background: #f8d7da; border-color: #f5c6cb; color: #721c24; }
button {
margin: 5px; padding: 10px 15px; cursor: pointer;
border: 1px solid #007bff; background: #007bff; color: white;
border-radius: 4px; transition: all 0.2s;
}
button:hover { background: #0056b3; }
button.secondary { background: #6c757d; border-color: #6c757d; }
button.danger { background: #dc3545; border-color: #dc3545; }
input, select, textarea { margin: 5px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
.tab-content { padding: 15px; border: 1px solid #ddd; border-top: none; }
.scroll-spy-nav {
position: sticky; top: 20px; background: white; padding: 10px;
border: 1px solid #ddd; border-radius: 5px; max-width: 200px;
}
.modal-overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.5); display: flex; align-items: center;
justify-content: center; z-index: 1000;
}
.modal-content {
background: white; padding: 30px; border-radius: 8px;
max-width: 500px; width: 90%; max-height: 80vh; overflow-y: auto;
}
</style>
</head>
<body>
<h1>Alpine.js Plugins and Utilities</h1>
<!-- 1. Mask Plugin -->
<div class="demo">
<h3>1. Mask Plugin</h3>
<div x-data="{
phone: '',
date: '',
creditCard: '',
currency: '',
time: '',
postalCode: ''
}">
<div class="grid">
<div class="box">
<h4>Phone Number</h4>
<input
type="text"
x-model="phone"
x-mask="(999) 999-9999"
placeholder="(123) 456-7890"
>
<p>Formatted: <strong x-text="phone"></strong></p>
</div>
<div class="box">
<h4>Date</h4>
<input
type="text"
x-model="date"
x-mask="99/99/9999"
placeholder="12/25/2023"
>
<p>Formatted: <strong x-text="date"></strong></p>
</div>
<div class="box">
<h4>Credit Card</h4>
<input
type="text"
x-model="creditCard"
x-mask="9999 9999 9999 9999"
placeholder="1234 5678 9012 3456"
>
<p>Formatted: <strong x-text="creditCard"></strong></p>
</div>
<div class="box">
<h4>Currency</h4>
<input
type="text"
x-model="currency"
x-mask="$9,999.99"
placeholder="$1,234.56"
>
<p>Formatted: <strong x-text="currency"></strong></p>
</div>
<div class="box">
<h4>Time</h4>
<input
type="text"
x-model="time"
x-mask="99:99"
placeholder="14:30"
>
<p>Formatted: <strong x-text="time"></strong></p>
</div>
<div class="box">
<h4>Postal Code</h4>
<input
type="text"
x-model="postalCode"
x-mask="99999"
placeholder="12345"
>
<p>Formatted: <strong x-text="postalCode"></strong></p>
</div>
</div>
</div>
</div>
<!-- 2. Anchor Plugin (Smooth Scroll) -->
<div class="demo">
<h3>2. Anchor Plugin (Smooth Scroll)</h3>
<div x-data="{ activeSection: '' }">
<nav class="scroll-spy-nav">
<h4>Navigation:</h4>
<a href="#section1" x-anchor>Section 1</a><br>
<a href="#section2" x-anchor>Section 2</a><br>
<a href="#section3" x-anchor>Section 3</a><br>
<a href="#section4" x-anchor>Section 4</a><br>
<p>Active: <strong x-text="activeSection"></strong></p>
</nav>
<div style="margin-left: 220px;">
<section id="section1" class="box" style="min-height: 200px; margin-bottom: 20px;">
<h3>Section 1</h3>
<p>This is the first section. Click the navigation links to see smooth scrolling in action.</p>
</section>
<section id="section2" class="box" style="min-height: 200px; margin-bottom: 20px;">
<h3>Section 2</h3>
<p>This is the second section. The anchor plugin provides smooth scrolling between sections.</p>
</section>
<section id="section3" class="box" style="min-height: 200px; margin-bottom: 20px;">
<h3>Section 3</h3>
<p>This is the third section. Scroll spy functionality highlights the active section.</p>
</section>
<section id="section4" class="box" style="min-height: 200px; margin-bottom: 20px;">
<h3>Section 4</h3>
<p>This is the fourth section. The scroll behavior is smooth and automatic.</p>
</section>
</div>
</div>
</div>
<!-- 3. Focus Plugin -->
<div class="demo">
<h3>3. Focus Plugin (Trap Focus)</h3>
<div x-data="{
showModal: false,
formData: {
name: '',
email: '',
message: ''
}
}">
<button x-on:click="showModal = true">Open Modal with Focus Trap</button>
<div x-show="showModal" x-trap="showModal" class="modal-overlay">
<div class="modal-content">
<h3>Contact Form</h3>
<p>Focus is trapped within this modal. Press Tab to navigate.</p>
<form>
<div style="margin-bottom: 15px;">
<label>Name:</label><br>
<input
type="text"
x-model="formData.name"
placeholder="Your name"
style="width: 100%;"
>
</div>
<div style="margin-bottom: 15px;">
<label>Email:</label><br>
<input
type="email"
x-model="formData.email"
placeholder="[email protected]"
style="width: 100%;"
>
</div>
<div style="margin-bottom: 15px;">
<label>Message:</label><br>
<textarea
x-model="formData.message"
placeholder="Your message"
rows="4"
style="width: 100%;"
></textarea>
</div>
<div style="margin-bottom: 15px;">
<button
type="button"
x-on:click="alert('Form submitted: ' + JSON.stringify(formData))"
>
Submit
</button>
<button
type="button"
class="secondary"
x-on:click="showModal = false"
>
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- 4. Intersect Plugin -->
<div class="demo">
<h3>4. Intersect Plugin (Scroll Animations)</h3>
<div>
<div class="box"
x-intersect:enter.opacity-100.transition.duration-500ms
x-intersect:leave.opacity-0.transition.duration-500ms>
<h3>Fade In/Out</h3>
<p>This box fades in when it enters the viewport and fades out when it leaves.</p>
</div>
<div class="box"
x-intersect:enter.translate-y-0.transition.duration-700ms
x-intersect:leave.-translate-y-8.transition.duration-700ms>
<h3>Slide Animation</h3>
<p>This box slides up when it enters the viewport and slides down when it leaves.</p>
</div>
<div class="box"
x-data="{ hasIntersected: false }"
x-intersect.once="hasIntersected = true">
<h3>Once Only</h3>
<p>This animation only triggers once when first visible:
<span x-text="hasIntersected ? '✓ Triggered' : '○ Not triggered'"></span></p>
</div>
<div class="box"
x-data="{
visibleCount: 0,
countIntersect(event) {
if (event.detail.isIntersecting) {
this.visibleCount++;
}
}
}"
x-intersect.margin.-100px="countIntersect($event)">
<h3>With Margin</h3>
<p>Triggers when 100px from viewport edge. Visible count:
<strong x-text="visibleCount"></strong></p>
</div>
<div class="box"
x-data="{ threshold: 0.5 }"
x-intersect.threshold.50="console.log('50% visible!')">
<h3>Custom Threshold</h3>
<p>Triggers when 50% of this element is visible (check console).</p>
<label>Threshold:
<input type="number" x-model.number="threshold" min="0" max="1" step="0.1">
<span x-text="threshold"></span>
</label>
</div>
<!-- Add some spacing to allow scrolling -->
<div style="height: 300px;"></div>
</div>
</div>
<!-- 5. Persist Plugin -->
<div class="demo">
<h3>5. Persist Plugin (Local Storage)</h3>
<div x-data="{
preferences: {
theme: $persist('light').as('theme'),
language: $persist('en').as('language'),
notifications: $persist(true).as('notifications')
},
user: {
name: $persist('').as('userName'),
visits: $persist(0).as('visitCount')
},
cart: $persist([]).as('shoppingCart'),
todoList: $persist(['Learn Alpine.js', 'Build cool projects']).as('todos'),
init() {
this.user.visits++;
console.log('Welcome back! Visit count:', this.user.visits);
},
addItem() {
this.cart.push({
id: Date.now(),
name: 'Item ' + (this.cart.length + 1),
price: Math.floor(Math.random() * 100) + 1
});
},
clearCart() {
this.cart = [];
},
addTodo(event) {
if (event.target.value.trim()) {
this.todoList.push(event.target.value.trim());
event.target.value = '';
}
},
removeTodo(index) {
this.todoList.splice(index, 1);
}
}">
<div class="grid">
<div class="box">
<h4>User Preferences</h4>
<div style="margin-bottom: 10px;">
<label>Theme:</label>
<select x-model="preferences.theme">
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="auto">Auto</option>
</select>
</div>
<div style="margin-bottom: 10px;">
<label>Language:</label>
<select x-model="preferences.language">
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
</select>
</div>
<div style="margin-bottom: 10px;">
<label>
<input type="checkbox" x-model="preferences.notifications">
Enable notifications
</label>
</div>
<p><small>All preferences are saved to localStorage!</small></p>
</div>
<div class="box">
<h4>User Info</h4>
<div style="margin-bottom: 10px;">
<label>Name:</label>
<input
type="text"
x-model="user.name"
placeholder="Your name"
>
</div>
<p>Visit count: <strong x-text="user.visits"></strong></p>
<button class="danger" x-on:click="user.visits = 0">Reset Visits</button>
</div>
<div class="box">
<h4>Shopping Cart</h4>
<button x-on:click="addItem()">Add Random Item</button>
<button class="secondary" x-on:click="clearCart()">Clear Cart</button>
<div style="margin-top: 10px;">
<template x-for="item in cart">
<div style="padding: 5px; background: #f8f9fa; margin: 2px 0; border-radius: 3px;">
<span x-text="item.name"></span> - $<span x-text="item.price"></span>
</div>
</template>
<p x-show="cart.length === 0">Cart is empty</p>
<p x-show="cart.length > 0">
Total: $<strong x-text="cart.reduce((sum, item) => sum + item.price, 0)"></strong>
</p>
</div>
</div>
<div class="box">
<h4>Todo List</h4>
<div style="margin-bottom: 10px;">
<input
type="text"
x-on:keyup.enter="addTodo($event)"
placeholder="Add todo and press Enter"
>
</div>
<ul style="list-style: none; padding: 0;">
<template x-for="(todo, index) in todoList">
<li style="margin: 5px 0; padding: 8px; background: #e9ecef; border-radius: 3px;">
<span x-text="todo"></span>
<button
class="danger"
style="float: right; padding: 2px 8px;"
x-on:click="removeTodo(index)"
>
×
</button>
</li>
</template>
</ul>
<p x-show="todoList.length === 0">No todos yet</p>
</div>
</div>
</div>
</div>
<!-- 6. Custom Magic Properties -->
<div class="demo">
<h3>6. Custom Magic Properties</h3>
<div>
<p>Custom magic properties allow you to extend Alpine.js functionality:</p>
<div x-data="{
message: 'Hello Custom Magic!',
init() {
// Add custom magic property
Alpine.magic('now', () => new Date().toLocaleTimeString());
// Custom magic with parameter
Alpine.magic('format', () => (value, type) => {
switch(type) {
case 'currency': return '$' + parseFloat(value).toFixed(2);
case 'percent': return value + '%';
case 'uppercase': return value.toUpperCase();
default: return value;
}
});
// Custom magic for API calls
Alpine.magic('api', () => ({
get: async (endpoint) => {
const response = await fetch(endpoint);
return response.json();
},
post: async (endpoint, data) => {
const response = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return response.json();
}
}));
},
price: 123.45,
percentage: 75,
text: 'hello world',
data: null,
async loadData() {
try {
this.data = await this.$api.get('https://jsonplaceholder.typicode.com/posts/1');
} catch (error) {
console.error('API call failed:', error);
}
}
}">
<div class="box">
<h4>Custom Magic Properties in Action</h4>
<p>Current time: <strong x-text="$now"></strong></p>
<p>Price formatted: <strong x-text="$format(price, 'currency')"></strong></p>
<p>Percentage: <strong x-text="$format(percentage, 'percent')"></strong></p>
<p>Uppercase: <strong x-text="$format(text, 'uppercase')"></strong></p>
<button x-on:click="loadData()">Load API Data</button>
<div x-show="data" class="success" style="margin-top: 10px; padding: 10px;">
<h5>API Response:</h5>
<p><strong>Title:</strong> <span x-text="data.title"></span></p>
<p><strong>Body:</strong> <span x-text="data.body"></span></p>
</div>
</div>
</div>
</div>
</div>
<!-- 7. Alpine Utilities -->
<div class="demo">
<h3>7. Alpine Utilities</h3>
<div x-data="{
componentData: { count: 0, message: 'Hello' },
methods: {
increment() { this.componentData.count++; },
reset() {
this.componentData.count = 0;
this.componentData.message = 'Reset';
}
},
init() {
// Alpine utilities
console.log('Alpine version:', Alpine.version);
console.log('Current component data:', this.$data);
// Store some data
Alpine.store('app', {
version: '1.0.0',
user: { name: 'Demo User' }
});
},
inspectData() {
alert('Component data: ' + JSON.stringify(this.$data, null, 2));
},
inspectStore() {
alert('App store: ' + JSON.stringify(Alpine.store('app'), null, 2));
}
}">
<div class="box">
<h4>Component Utilities</h4>
<p>Count: <strong x-text="componentData.count"></strong></p>
<p>Message: <strong x-text="componentData.message"></strong></p>
<button x-on:click="methods.increment()">Increment</button>
<button x-on:click="methods.reset()">Reset</button>
<button class="secondary" x-on:click="inspectData()">Inspect Data</button>
<button class="secondary" x-on:click="inspectStore()">Inspect Store</button>
</div>
<div class="box">
<h4>Alpine Store</h4>
<p>App Version: <strong x-text="$store.app.version"></strong></p>
<p>User: <strong x-text="$store.app.user.name"></strong></p>
<button x-on:click="$store.app.user.name = 'Updated User'">Update User</button>
</div>
</div>
</div>
</body>
</html>