langbrain/Alpine.js DOCS.md

99 KiB

Start Here

Create a blank HTML file somewhere on your computer with a name like: i-love-alpine.html

Using a text editor, fill the file with these contents:

<html>
	<head>    
		<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
	</head>
	
	<body>    
		<h1 x-data="{ message: 'I ❤️ Alpine' }" x-text="message"></h1>
	</body>
</html>

Open your file in a web browser, if you see I ❤️ Alpine, you're ready to rumble!

Now that you're all set up to play around, let's look at three practical examples as a foundation for teaching you the basics of Alpine. By the end of this exercise, you should be more than equipped to start building stuff on your own. Let's goooooo.

Building a counter

Let's start with a simple "counter" component to demonstrate the basics of state and event listening in Alpine, two core features.

Insert the following into the <body> tag:

<div x-data="{ count: 0 }">    
	<button x-on:click="count++">Increment</button>     
	<span x-text="count"></span>
</div>

Now, you can see with 3 bits of Alpine sprinkled into this HTML, we've created an interactive "counter" component.

Let's walk through what's happening briefly:

Declaring data

<div x-data="{ count: 0 }">

Everything in Alpine starts with an x-data directive. Inside of x-data, in plain JavaScript, you declare an object of data that Alpine will track.

Every property inside this object will be made available to other directives inside this HTML element. In addition, when one of these properties changes, everything that relies on it will change as well.

Let's look at x-on and see how it can access and modify the count property from above:

Listening for events

<button x-on:click="count++">Increment</button>

x-on is a directive you can use to listen for any event on an element. We're listening for a click event in this case, so ours looks like x-on:click.

You can listen for other events as you'd imagine. For example, listening for a mouseenter event would look like this: x-on:mouseenter.

When a click event happens, Alpine will call the associated JavaScript expression, count++ in our case. As you can see, we have direct access to data declared in the x-data expression.

You will often see @ instead of x-on:. This is a shorter, friendlier syntax that many prefer. From now on, this documentation will likely use @ instead of x-on:.

Reacting to changes

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

x-text is an Alpine directive you can use to set the text content of an element to the result of a JavaScript expression.

In this case, we're telling Alpine to always make sure that the contents of this span tag reflect the value of the count property.

In case it's not clear, x-text, like most directives accepts a plain JavaScript expression as an argument. So for example, you could instead set its contents to: x-text="count * 2" and the text content of the span will now always be 2 times the value of count.

Building a dropdown

Now that we've seen some basic functionality, let's keep going and look at an important directive in Alpine: x-show, by building a contrived "dropdown" component.

Insert the following code into the <body> tag:

<div x-data="{ open: false }">
	<button @click="open = ! open">Toggle</button>
	<div x-show="open" @click.outside="open = false">Contents...</div>
</div>

Toggle

If you load this component, you should see that the "Contents..." are hidden by default. You can toggle showing them on the page by clicking the "Toggle" button.

The x-data and x-on directives should be familiar to you from the previous example, so we'll skip those explanations.

Toggling elements

<div x-show="open" ...>Contents...</div>

x-show is an extremely powerful directive in Alpine that can be used to show and hide a block of HTML on a page based on the result of a JavaScript expression, in our case: open.

Listening for a click outside

<div ... @click.outside="open = false">Contents...</div>

You'll notice something new in this example: .outside. Many directives in Alpine accept "modifiers" that are chained onto the end of the directive and are separated by periods.

In this case, .outside tells Alpine to instead of listening for a click INSIDE the <div>, to listen for the click only if it happens OUTSIDE the <div>.

This is a convenience helper built into Alpine because this is a common need and implementing it by hand is annoying and complex.

Building a search input

Let's now build a more complex component and introduce a handful of other directives and patterns.

Insert the following code into the <body> tag:

<div 
	 x-data="{        
		 search: '',
		 items: ['foo', 'bar', 'baz'],
		 get filteredItems() {            
			 return this.items.filter(                
				 i => i.startsWith(this.search)            
			)        
		}    
	}"
>
	
	<input x-model="search" placeholder="Search...">
	
	<ul>        
		<template x-for="item in filteredItems" :key="item">
			<li x-text="item"></li>
		</template>
	</ul>
</div>
  • foo
  • bar
  • baz

By default, all of the "items" (foo, bar, and baz) will be shown on the page, but you can filter them by typing into the text input. As you type, the list of items will change to reflect what you're searching for.

Now there's quite a bit happening here, so let's go through this snippet piece by piece.

Multi line formatting

The first thing I'd like to point out is that x-data now has a lot more going on in it than before. To make it easier to write and read, we've split it up into multiple lines in our HTML. This is completely optional and we'll talk more in a bit about how to avoid this problem altogether, but for now, we'll keep all of this JavaScript directly in the HTML.

Binding to inputs

<input x-model="search" placeholder="Search...">

You'll notice a new directive we haven't seen yet: x-model.

x-model is used to "bind" the value of an input element with a data property: "search" from x-data="{ search: '', ... }" in our case.

This means that anytime the value of the input changes, the value of "search" will change to reflect that.

x-model is capable of much more than this simple example.

Computed properties using getters

The next bit I'd like to draw your attention to is the items and filteredItems properties from the x-data directive.

{ 
	items: ['foo', 'bar', 'baz'], 
	get filteredItems() {
		return this.items.filter(
			i => i.startsWith(this.search)
		)
	}
}

The items property should be self-explanatory. Here we are setting the value of items to a JavaScript array of 3 different items (foo, bar, and baz).

The interesting part of this snippet is the filteredItems property.

Denoted by the get prefix for this property, filteredItems is a "getter" property in this object. This means we can access filteredItems as if it was a normal property in our data object, but when we do, JavaScript will evaluate the provided function under the hood and return the result.

It's completely acceptable to forgo the get and just make this a method that you can call from the template, but some prefer the nicer syntax of the getter.

Now let's look inside the filteredItems getter and make sure we understand what's going on there:

return this.items.filter(    i => i.startsWith(this.search))

This is all plain JavaScript. We are first getting the array of items (foo, bar, and baz) and filtering them using the provided callback: i => i.startsWith(this.search).

By passing in this callback to filter, we are telling JavaScript to only return the items that start with the string: this.search, which like we saw with x-model will always reflect the value of the input.

You may notice that up until now, we haven't had to use this. to reference properties. However, because we are working directly inside the x-data object, we must reference any properties using this.[property] instead of simply [property].

Because Alpine is a "reactive" framework. Any time the value of this.search changes, parts of the template that use filteredItems will automatically be updated.

Looping elements

Now that we understand the data part of our component, let's understand what's happening in the template that allows us to loop through filteredItems on the page.

<ul>    
	<template x-for="item in filteredItems">        
		<li x-text="item"></li>
	</template>
</ul>

The first thing to notice here is the x-for directive. x-for expressions take the following form: [item] in [items] where [items] is any array of data, and [item] is the name of the variable that will be assigned to an iteration inside the loop.

Also notice that x-for is declared on a <template> element and not directly on the <li>. This is a requirement of using x-for. It allows Alpine to leverage the existing behavior of <template> tags in the browser to its advantage.

Now any element inside the <template> tag will be repeated for every item inside filteredItems and all expressions evaluated inside the loop will have direct access to the iteration variable (item in this case).

Recap

If you've made it this far, you've been exposed to the following directives in Alpine:

  • x-data
  • x-on
  • x-text
  • x-show
  • x-model
  • x-for

That's a great start, however, there are many more directives to sink your teeth into. The best way to absorb Alpine is to read through this documentation. No need to comb over every word, but if you at least glance through every page you will be MUCH more effective when using Alpine.

Happy Coding!


Essentials:

Installation

There are 2 ways to include Alpine into your project:

  • Including it from a <script> tag
  • Importing it as a module

Either is perfectly valid. It all depends on the project's needs and the developer's taste.

From a script tag

This is by far the simplest way to get started with Alpine. Include the following <script> tag in the head of your HTML page.

<html>    
	<head>
		<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>   
	</head>
</html>

Don't forget the "defer" attribute in the <script> tag.

Notice the @3.x.x in the provided CDN link. This will pull the latest version of Alpine version 3. For stability in production, it's recommended that you hardcode the latest version in the CDN link.

<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js"></script>

That's it! Alpine is now available for use inside your page.

Note that you will still need to define a component with x-data in order for any Alpine.js attributes to work. See https://github.com/alpinejs/alpine/discussions/3805 for more information.

As a module

If you prefer the more robust approach, you can install Alpine via NPM and import it into a bundle.

Run the following command to install it.

$ npm install alpinejs

Now import Alpine into your bundle and initialize it like so:

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

The window.Alpine = Alpine bit is optional, but is nice to have for freedom and flexibility. Like when tinkering with Alpine from the devtools for example.

If you imported Alpine into a bundle, you have to make sure you are registering any extension code IN BETWEEN when you import the Alpine global object, and when you initialize Alpine by calling Alpine.start().

Ensure that Alpine.start() is only called once per page. Calling it more than once will result in multiple "instances" of Alpine running at the same time.


State

State (JavaScript data that Alpine watches for changes) is at the core of everything you do in Alpine. You can provide local data to a chunk of HTML, or make it globally available for use anywhere on a page using x-data or Alpine.store() respectively.

Local state

Alpine allows you to declare an HTML block's state in a single x-data attribute without ever leaving your markup.

Here's a basic example:

<div x-data="{ open: false }">    ...</div>

Now any other Alpine syntax on or within this element will be able to access open. And like you'd guess, when open changes for any reason, everything that depends on it will react automatically.

Nesting data

Data is nestable in Alpine. For example, if you have two elements with Alpine data attached (one inside the other), you can access the parent's data from inside the child element.

<div x-data="{ open: false }">    <div x-data="{ label: 'Content:' }">        <span x-text="label"></span>        <span x-show="open"></span>    </div></div>

This is similar to scoping in JavaScript itself (code within a function can access variables declared outside that function.)

Like you may have guessed, if the child has a data property matching the name of a parent's property, the child property will take precedence.

Single-element data

Although this may seem obvious to some, it's worth mentioning that Alpine data can be used within the same element. For example:

<button x-data="{ label: 'Click Here' }" x-text="label"></button>

Data-less Alpine

Sometimes you may want to use Alpine functionality, but don't need any reactive data. In these cases, you can opt out of passing an expression to x-data entirely. For example:

<button x-data @click="alert('I\'ve been clicked!')">Click Me</button>

Re-usable data

When using Alpine, you may find the need to re-use a chunk of data and/or its corresponding template.

If you are using a backend framework like Rails or Laravel, Alpine first recommends that you extract the entire block of HTML into a template partial or include.

If for some reason that isn't ideal for you or you're not in a back-end templating environment, Alpine allows you to globally register and re-use the data portion of a component using Alpine.data(...).

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

Now that you've registered the "dropdown" data, you can use it inside your markup in as many places as you like:

<div x-data="dropdown">    <button @click="toggle">Expand</button>     <span x-show="open">Content...</span></div> <div x-data="dropdown">    <button @click="toggle">Expand</button>     <span x-show="open">Some Other Content...</span></div>

Global state

If you wish to make some data available to every component on the page, you can do so using Alpine's "global store" feature.

You can register a store using Alpine.store(...), and reference one with the magic $store() method.

Let's look at a simple example. First we'll register the store globally:

Alpine.store('tabs', {    current: 'first',     items: ['first', 'second', 'third'],})

Now we can access or modify its data from anywhere on our page:

<div x-data>    <template x-for="tab in $store.tabs.items">        ...    </template></div> <div x-data>    <button @click="$store.tabs.current = 'first'">First Tab</button>    <button @click="$store.tabs.current = 'second'">Second Tab</button>    <button @click="$store.tabs.current = 'third'">Third Tab</button></div>

Templating

Alpine offers a handful of useful directives for manipulating the DOM on a web page.

Let's cover a few of the basic templating directives here, but be sure to look through the available directives in the sidebar for an exhaustive list.

Text content

Alpine makes it easy to control the text content of an element with the x-text directive.

<div x-data="{ title: 'Start Here' }">    <h1 x-text="title"></h1></div>

Now, Alpine will set the text content of the <h1> with the value of title ("Start Here"). When title changes, so will the contents of <h1>.

Like all directives in Alpine, you can use any JavaScript expression you like. For example:

<span x-text="1 + 2"></span>

The <span> will now contain the sum of "1" and "2".

Toggling elements

Toggling elements is a common need in web pages and applications. Dropdowns, modals, dialogues, "show-more"s, etc... are all good examples.

Alpine offers the x-show and x-if directives for toggling elements on a page.

x-show

Here's a simple toggle component using x-show.

<div x-data="{ open: false }">    <button @click="open = ! open">Expand</button>     <div x-show="open">        Content...    </div></div>

Now the entire <div> containing the contents will be shown and hidden based on the value of open.

Under the hood, Alpine adds the CSS property display: none; to the element when it should be hidden.

This works well for most cases, but sometimes you may want to completely add and remove the element from the DOM entirely. This is what x-if is for.

x-if

Here is the same toggle from before, but this time using x-if instead of x-show.

<div x-data="{ open: false }">    <button @click="open = ! open">Expand</button>     <template x-if="open">        <div>            Content...        </div>    </template></div>

Notice that x-if must be declared on a <template> tag. This is so that Alpine can leverage the existing browser behavior of the <template> element and use it as the source of the target <div> to be added and removed from the page.

When open is true, Alpine will append the <div> to the <template> tag, and remove it when open is false.

Toggling with transitions

Alpine makes it simple to smoothly transition between "shown" and "hidden" states using the x-transition directive.

x-transition only works with x-show, not with x-if.

Here is, again, the simple toggle example, but this time with transitions applied:

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

Let's zoom in on the portion of the template dealing with transitions:

<div x-show="open" x-transition>

x-transition by itself will apply sensible default transitions (fade and scale) to the toggle.

There are two ways to customize these transitions:

  • Transition helpers
  • Transition CSS classes.

Let's take a look at each of these approaches:

Transition helpers

Let's say you wanted to make the duration of the transition longer, you can manually specify that using the .duration modifier like so:

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

Now the transition will last 500 milliseconds.

If you want to specify different values for in and out transitions, you can use x-transition:enter and x-transition:leave:

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

Additionally, you can add either .opacity or .scale to only transition that property. For example:

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

Transition classes

If you need more fine-grained control over the transitions in your application, you can apply specific CSS classes at specific phases of the transition using the following syntax (this example uses Tailwind CSS):

<div    x-show="open"    x-transition:enter="transition ease-out duration-300"    x-transition:enter-start="opacity-0 transform scale-90"    x-transition:enter-end="opacity-100 transform scale-100"    x-transition:leave="transition ease-in duration-300"    x-transition:leave-start="opacity-100 transform scale-100"    x-transition:leave-end="opacity-0 transform scale-90">...</div>

Binding attributes

You can add HTML attributes like classstyledisabled, etc... to elements in Alpine using the x-bind directive.

Here is an example of a dynamically bound class attribute:

<button    x-data="{ red: false }"    x-bind:class="red ? 'bg-red' : ''"    @click="red = ! red">    Toggle Red</button>

As a shortcut, you can leave out the x-bind and use the shorthand : syntax directly:

<button ... :class="red ? 'bg-red' : ''">

Toggling classes on and off based on data inside Alpine is a common need. Here's an example of toggling a class using Alpine's class binding object syntax: (Note: this syntax is only available for class attributes)

<div x-data="{ open: true }">    <span :class="{ 'hidden': ! open }">...</span></div>

Now the hidden class will be added to the element if open is false, and removed if open is true.

Looping elements

Alpine allows for iterating parts of your template based on JavaScript data using the x-for directive. Here is a simple example:

<div x-data="{ statuses: ['open', 'closed', 'archived'] }">    <template x-for="status in statuses">        <div x-text="status"></div>    </template></div>

Similar to x-ifx-for must be applied to a <template> tag. Internally, Alpine will append the contents of <template> tag for every iteration in the loop.

As you can see the new status variable is available in the scope of the iterated templates.

Inner HTML

Alpine makes it easy to control the HTML content of an element with the x-html directive.

<div x-data="{ title: '<h1>Start Here</h1>' }">    <div x-html="title"></div></div>

Now, Alpine will set the text content of the <div> with the element <h1>Start Here</h1>. When title changes, so will the contents of <h1>.

⚠️ Only use on trusted content and never on user-provided content. ⚠️ Dynamically rendering HTML from third parties can easily lead to XSS vulnerabilities.

Events

Alpine makes it simple to listen for browser events and react to them.

Listening for simple events

By using x-on, you can listen for browser events that are dispatched on or within an element.

Here's a basic example of listening for a click on a button:

<button x-on:click="console.log('clicked')">...</button>

As an alternative, you can use the event shorthand syntax if you prefer: @. Here's the same example as before, but using the shorthand syntax (which we'll be using from now on):

<button @click="...">...</button>

In addition to click, you can listen for any browser event by name. For example: @mouseenter@keyup, etc... are all valid syntax.

Listening for specific keys

Let's say you wanted to listen for the enter key to be pressed inside an <input> element. Alpine makes this easy by adding the .enter like so:

<input @keyup.enter="...">

You can even combine key modifiers to listen for key combinations like pressing enter while holding shift:

<input @keyup.shift.enter="...">

Preventing default

When reacting to browser events, it is often necessary to "prevent default" (prevent the default behavior of the browser event).

For example, if you want to listen for a form submission but prevent the browser from submitting a form request, you can use .prevent:

<form @submit.prevent="...">...</form>

You can also apply .stop to achieve the equivalent of event.stopPropagation().

Accessing the event object

Sometimes you may want to access the native browser event object inside your own code. To make this easy, Alpine automatically injects an $event magic variable:

<button @click="$event.target.remove()">Remove Me</button>

Dispatching custom events

In addition to listening for browser events, you can dispatch them as well. This is extremely useful for communicating with other Alpine components or triggering events in tools outside of Alpine itself.

Alpine exposes a magic helper called $dispatch for this:

<div @foo="console.log('foo was dispatched')">    
	<button @click="$dispatch('foo')"></button>
</div>

As you can see, when the button is clicked, Alpine will dispatch a browser event called "foo", and our @foo listener on the <div> will pick it up and react to it.

Listening for events on window

Because of the nature of events in the browser, it is sometimes useful to listen to events on the top-level window object.

This allows you to communicate across components completely like the following example:

<div x-data>    
	<button @click="$dispatch('foo')"></button>
</div> 
<div x-data @foo.window="console.log('foo was dispatched')">...</div>

In the above example, if we click the button in the first component, Alpine will dispatch the "foo" event. Because of the way events work in the browser, they "bubble" up through parent elements all the way to the top-level "window".

Now, because in our second component we are listening for "foo" on the window (with .window), when the button is clicked, this listener will pick it up and log the "foo was dispatched" message.

Lifecycle

Alpine has a handful of different techniques for hooking into different parts of its lifecycle. Let's go through the most useful ones to familiarize yourself with:

Element initialization

Another extremely useful lifecycle hook in Alpine is the x-init directive.

x-init can be added to any element on a page and will execute any JavaScript you call inside it when Alpine begins initializing that element.

<button x-init="console.log('Im initing')">

In addition to the directive, Alpine will automatically call any init() methods stored on a data object. For example:

Alpine.data('dropdown', () => ({    
	init() {        
		// I get called before the element using this data initializes.    
	}
}))

After a state change

Alpine allows you to execute code when a piece of data (state) changes. It offers two different APIs for such a task: $watch and x-effect.

$watch

<div x-data="{ open: false }" x-init="$watch('open', value => console.log(value))">

As you can see above, $watch allows you to hook into data changes using a dot-notation key. When that piece of data changes, Alpine will call the passed callback and pass it the new value. along with the old value before the change.

x-effect

x-effect uses the same mechanism under the hood as $watch but has very different usage.

Instead of specifying which data key you wish to watch, x-effect will call the provided code and intelligently look for any Alpine data used within it. Now when one of those pieces of data changes, the x-effect expression will be re-run.

Here's the same bit of code from the $watch example rewritten using x-effect:

<div x-data="{ open: false }" x-effect="console.log(open)">

Now, this expression will be called right away, and re-called every time open is updated.

The two main behavioral differences with this approach are:

  1. The provided code will be run right away AND when data changes ($watch is "lazy" -- won't run until the first data change)
  2. No knowledge of the previous value. (The callback provided to $watch receives both the new value AND the old one)

Alpine initialization

alpine:init

Ensuring a bit of code executes after Alpine is loaded, but BEFORE it initializes itself on the page is a necessary task.

This hook allows you to register custom data, directives, magics, etc. before Alpine does its thing on a page.

You can hook into this point in the lifecycle by listening for an event that Alpine dispatches called: alpine:init

document.addEventListener('alpine:init', () => {    
	Alpine.data(...)
})

alpine:initialized

Alpine also offers a hook that you can use to execute code AFTER it's done initializing called alpine:initialized:

document.addEventListener('alpine:initialized', () => {
	//
})

Directives

x-data

Everything in Alpine starts with the x-data directive.

x-data defines a chunk of HTML as an Alpine component and provides the reactive data for that component to reference.

Here's an example of a contrived dropdown component:

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

Don't worry about the other directives in this example (@click and x-show), we'll get to those in a bit. For now, let's focus on x-data.

Scope

Properties defined in an x-data directive are available to all element children. Even ones inside other, nested x-data components.

For example:

<div x-data="{ foo: 'bar' }">    
	<span x-text="foo"><!-- Will output: "bar" --></span>     
	<div x-data="{ bar: 'baz' }">        
		<span x-text="foo"><!-- Will output: "bar" --></span>         
		<div x-data="{ foo: 'bob' }">            
			<span x-text="foo"><!-- Will output: "bob" --></span>        
		</div>    
	</div>
</div>

Methods

Because x-data is evaluated as a normal JavaScript object, in addition to state, you can store methods and even getters.

For example, let's extract the "Toggle Content" behavior into a method on x-data.

<div x-data="{ open: false, toggle() { this.open = ! this.open } }">    
	<button @click="toggle()">Toggle Content</button>     
	<div x-show="open">Content...</div>
</div>

Notice the added toggle() { this.open = ! this.open } method on x-data. This method can now be called from anywhere inside the component.

You'll also notice the usage of this. to access state on the object itself. This is because Alpine evaluates this data object like any standard JavaScript object with a this context.

If you prefer, you can leave the calling parenthesis off of the toggle method completely. For example:

<!-- Before -->
<button @click="toggle()">...</button> 
<!-- After -->
<button @click="toggle">...</button>

Getters

JavaScript getters are handy when the sole purpose of a method is to return data based on other state.

Think of them like "computed properties" (although, they are not cached like Vue's computed properties).

Let's refactor our component to use a getter called isOpen instead of accessing open directly.

<div x-data="{    
	 open: false,    
	 get isOpen() { return this.open },    
	 toggle() { this.open = ! this.open },}">    
	 
	 <button @click="toggle()">Toggle Content</button>
	 <div x-show="isOpen">Content...</div>
</div>

Notice the "Content" now depends on the isOpen getter instead of the open property directly.

In this case there is no tangible benefit. But in some cases, getters are helpful for providing a more expressive syntax in your components.

Data-less components

Occasionally, you want to create an Alpine component, but you don't need any data.

In these cases, you can always pass in an empty object.

<div x-data="{}">

However, if you wish, you can also eliminate the attribute value entirely if it looks better to you.

<div x-data>

Single-element components

Sometimes you may only have a single element inside your Alpine component, like the following:

<div x-data="{ open: true }">    
	<button @click="open = false" x-show="open">Hide Me</button>
</div>

In these cases, you can declare x-data directly on that single element:

<button x-data="{ open: true }" @click="open = false" x-show="open">Hide Me</button>

Re-usable Data

If you find yourself duplicating the contents of x-data, or you find the inline syntax verbose, you can extract the x-data object out to a dedicated component using Alpine.data.

Here's a quick example:

<div x-data="dropdown">
	<button @click="toggle">Toggle Content</button>     
	<div x-show="open">Content...</div>
</div> 

<script>    
	document.addEventListener('alpine:init', () => {
		Alpine.data('dropdown', () => ({            
			open: false,             
			toggle() {                
				this.open = ! this.open            
			},        
		}))    
	})
</script>

x-init

The x-init directive allows you to hook into the initialization phase of any element in Alpine.

<div x-init="console.log('I\'m being initialized!')"></div>

In the above example, "I'm being initialized!" will be output in the console before it makes further DOM updates.

Consider another example where x-init is used to fetch some JSON and store it in x-data before the component is processed.

<div x-data="{ posts: [] }"    
	 x-init="posts = await (await fetch('/posts')).json()">...</div>

$nextTick

Sometimes, you want to wait until after Alpine has completely finished rendering to execute some code.

This would be something like useEffect(..., []) in react, or mount in Vue.

By using Alpine's internal $nextTick magic, you can make this happen.

<div x-init="$nextTick(() => { ... })"></div>

Standalone x-init

You can add x-init to any elements inside or outside an x-data HTML block. For example:

<div x-data>    
	<span x-init="console.log('I can initialize')"></span>
</div> 
<span x-init="console.log('I can initialize too')"></span>

Auto-evaluate init() method

If the x-data object of a component contains an init() method, it will be called automatically. For example:

<div x-data="{    
	init() {        
		 console.log('I am called automatically')    
		}}">...</div>

This is also the case for components that were registered using the Alpine.data() syntax.

Alpine.data('dropdown', () => ({    
	init() {        
		console.log('I will get evaluated when initializing each "dropdown" component.')    
	},
}))

If you have both an x-data object containing an init() method and an x-init directive, the x-data method will be called before the directive.

<div    
	x-data="{        
		init() {            
			console.log('I am called first')        
		}    
	}"    
	x-init="console.log('I am called second')">...
</div>

x-show

x-show is one of the most useful and powerful directives in Alpine. It provides an expressive way to show and hide DOM elements.

Here's an example of a simple dropdown component using x-show.

<div x-data="{ open: false }">    
	<button x-on:click="open = ! open">Toggle Dropdown</button>     
	<div x-show="open">Dropdown Contents...</div>
</div>

When the "Toggle Dropdown" button is clicked, the dropdown will show and hide accordingly.

If the "default" state of an x-show on page load is "false", you may want to use x-cloak on the page to avoid "page flicker" (The effect that happens when the browser renders your content before Alpine is finished initializing and hiding it.) You can learn more about x-cloak in its documentation.

With transitions

If you want to apply smooth transitions to the x-show behavior, you can use it in conjunction with x-transition. You can learn more about that directive here, but here's a quick example of the same component as above, just with transitions applied.

<div x-data="{ open: false }">    
	<button x-on:click="open = ! open">Toggle Dropdown</button>     
	<div x-show="open" x-transition>Dropdown Contents...</div>
</div>

Using the important modifier

Sometimes you need to apply a little more force to actually hide an element. In cases where a CSS selector applies the display property with the !important flag, it will take precedence over the inline style set by Alpine.

In these cases you may use the .important modifier to set the inline style to display: none !important.

<div x-data="{ open: false }">
	<button x-on:click="open = ! open">Toggle Dropdown</button>     
	<div x-show.important="open">Dropdown Contents...</div>
</div>

x-bind

x-bind allows you to set HTML attributes on elements based on the result of JavaScript expressions.

For example, here's a component where we will use x-bind to set the placeholder value of an input.

<div x-data="{ placeholder: 'Type here...' }">
	<input type="text" x-bind:placeholder="placeholder">
</div>

Shorthand syntax

If x-bind: is too verbose for your liking, you can use the shorthand: :. For example, here is the same input element as above, but refactored to use the shorthand syntax.

<input type="text" :placeholder="placeholder">

Binding classes

x-bind is most often useful for setting specific classes on an element based on your Alpine state.

Here's a simple example of a simple dropdown toggle, but instead of using x-show, we'll use a "hidden" class to toggle an element.

<div x-data="{ open: false }">    
	<button x-on:click="open = ! open">Toggle Dropdown</button>     
	<div :class="open ? '' : 'hidden'">Dropdown Contents...</div>
</div>

Now, when open is false, the "hidden" class will be added to the dropdown.

Shorthand conditionals

In cases like these, if you prefer a less verbose syntax you can use JavaScript's short-circuit evaluation instead of standard conditionals:

<div :class="show ? '' : 'hidden'">
<!-- Is equivalent to: -->
<div :class="show || 'hidden'">

The inverse is also available to you. Suppose instead of open, we use a variable with the opposite value: closed.

<div :class="closed ? 'hidden' : ''">
<!-- Is equivalent to: -->
<div :class="closed && 'hidden'">

Class object syntax

Alpine offers an additional syntax for toggling classes if you prefer. By passing a JavaScript object where the classes are the keys and booleans are the values, Alpine will know which classes to apply and which to remove. For example:

<div :class="{ 'hidden': ! show }">

This technique offers a unique advantage to other methods. When using object-syntax, Alpine will NOT preserve original classes applied to an element's class attribute.

For example, if you wanted to apply the "hidden" class to an element before Alpine loads, AND use Alpine to toggle its existence you can only achieve that behavior using object-syntax:

<div class="hidden" :class="{ 'hidden': ! show }">

In case that confused you, let's dig deeper into how Alpine handles x-bind:class differently than other attributes.

Special behavior

x-bind:class behaves differently than other attributes under the hood.

Consider the following case.

<div class="opacity-50" :class="hide && 'hidden'">

If "class" were any other attribute, the :class binding would overwrite any existing class attribute, causing opacity-50 to be overwritten by either hidden or ''.

However, Alpine treats class bindings differently. It's smart enough to preserve existing classes on an element.

For example, if hide is true, the above example will result in the following DOM element:

<div class="opacity-50 hidden">

If hide is false, the DOM element will look like:

<div class="opacity-50">

This behavior should be invisible and intuitive to most users, but it is worth mentioning explicitly for the inquiring developer or any special cases that might crop up.

Binding styles

Similar to the special syntax for binding classes with JavaScript objects, Alpine also offers an object-based syntax for binding style attributes.

Just like the class objects, this syntax is entirely optional. Only use it if it affords you some advantage.

<div :style="{ color: 'red', display: 'flex' }"> 
<!-- Will render: -->
<div style="color: red; display: flex;" ...>

Conditional inline styling is possible using expressions just like with x-bind:class. Short circuit operators can be used here as well by using a styles object as the second operand.

<div x-bind:style="true && { color: 'red' }">
<!-- Will render: -->
<div style="color: red;">

One advantage of this approach is being able to mix it in with existing styles on an element:

<div style="padding: 1rem;" :style="{ color: 'red', display: 'flex' }"> 
<!-- Will render: -->
<div style="padding: 1rem; color: red; display: flex;" ...>

And like most expressions in Alpine, you can always use the result of a JavaScript expression as the reference:

<div x-data="{ styles: { color: 'red', display: 'flex' }}">    
	<div :style="styles"></div> 

<!-- Will render: -->

<div ...>   
	<div style="color: red; display: flex;" ...></div>

Binding Alpine Directives Directly

x-bind allows you to bind an object of different directives and attributes to an element.

The object keys can be anything you would normally write as an attribute name in Alpine. This includes Alpine directives and modifiers, but also plain HTML attributes. The object values are either plain strings, or in the case of dynamic Alpine directives, callbacks to be evaluated by Alpine.

<div x-data="dropdown()">    
	<button x-bind="trigger">Open Dropdown</button>     
	<span x-bind="dialogue">Dropdown Contents</span>
</div> 

<script>    
	document.addEventListener('alpine:init', () => {        
		Alpine.data('dropdown', () => ({            
			open: false,             
			trigger: {                
				['x-ref']: 'trigger',                
				['@click']() {                    
					this.open = true                
				},            
			},             
			dialogue: {                
				['x-show']() {                    
					return this.open                
				},                
				['@click.outside']() {                    
					this.open = false                
				},            
			},        
		}))   
	})
</script>

There are a couple of caveats to this usage of x-bind:

When the directive being "bound" or "applied" is x-for, you should return a normal expression string from the callback. For example: ['x-for']() { return 'item in items' }

x-on

x-on allows you to easily run code on dispatched DOM events.

Here's an example of simple button that shows an alert when clicked.

<button x-on:click="alert('Hello World!')">Say Hi</button>

x-on can only listen for events with lower case names, as HTML attributes are case-insensitive. Writing x-on:CLICK will listen for an event named click. If you need to listen for a custom event with a camelCase name, you can use the .camel helper to work around this limitation. Alternatively, you can use x-bind to attach an x-on directive to an element in javascript code (where case will be preserved).

Shorthand syntax

If x-on: is too verbose for your tastes, you can use the shorthand syntax: @.

Here's the same component as above, but using the shorthand syntax instead:

<button @click="alert('Hello World!')">Say Hi</button>

The event object

If you wish to access the native JavaScript event object from your expression, you can use Alpine's magic $event property.

<button @click="alert($event.target.getAttribute('message'))" message="Hello World">Say Hi</button>

In addition, Alpine also passes the event object to any methods referenced without trailing parenthesis. For example:

<button @click="handleClick">...</button> 
<script>    
	function handleClick(e) {        
		// Now you can access the event object (e) directly    
	}
</script>

Keyboard events

Alpine makes it easy to listen for keydown and keyup events on specific keys.

Here's an example of listening for the Enter key inside an input element.

<input type="text" @keyup.enter="alert('Submitted!')">

You can also chain these key modifiers to achieve more complex listeners.

Here's a listener that runs when the Shift key is held and Enter is pressed, but not when Enter is pressed alone.

<input type="text" @keyup.shift.enter="alert('Submitted!')">

You can directly use any valid key names exposed via KeyboardEvent.key as modifiers by converting them to kebab-case.

<input type="text" @keyup.page-down="alert('Submitted!')">

For easy reference, here is a list of common keys you may want to listen for.

Modifier Keyboard Key
.shift Shift
.enter Enter
.space Space
.ctrl Ctrl
.cmd Cmd
.meta Cmd on Mac, Windows key on Windows
.alt Alt
.up .down .left .right Up/Down/Left/Right arrows
.escape Escape
.tab Tab
.caps-lock Caps Lock
.equal Equal, =
.period Period, .
.comma Comma, ,
.slash Forward Slash, /

Mouse events

Like the above Keyboard Events, Alpine allows the use of some key modifiers for handling click events.

Modifier Event Key
.shift shiftKey
.ctrl ctrlKey
.cmd metaKey
.meta metaKey
.alt altKey

These work on clickauxclickcontext and dblclick events, and even mouseovermousemovemouseentermouseleavemouseoutmouseup and mousedown.

Here's an example of a button that changes behaviour when the Shift key is held down.

<button type="button"    
		@click="message = 'selected'"    
		@click.shift="message = 'added to selection'">    
		@mousemove.shift="message = 'add to selection'"    
		@mouseout="message = 'select'"    
		x-text="message"></button>

Note: Normal click events with some modifiers (like ctrl) will automatically become contextmenu events in most browsers. Similarly, right-click events will trigger a contextmenu event, but will also trigger an auxclick event if the contextmenu event is prevented.

Custom events

Alpine event listeners are a wrapper for native DOM event listeners. Therefore, they can listen for ANY DOM event, including custom events.

Here's an example of a component that dispatches a custom DOM event and listens for it as well.

<div x-data @foo="alert('Button Was Clicked!')">    
	<button @click="$event.target.dispatchEvent(new CustomEvent('foo', { bubbles: true }))">...</button>
</div>

When the button is clicked, the @foo listener will be called.

Because the .dispatchEvent API is verbose, Alpine offers a $dispatch helper to simplify things.

Here's the same component re-written with the $dispatch magic property.

<div x-data @foo="alert('Button Was Clicked!')">    
	<button @click="$dispatch('foo')">...</button>
</div>

Modifiers

Alpine offers a number of directive modifiers to customize the behavior of your event listeners.

.prevent

.prevent is the equivalent of calling .preventDefault() inside a listener on the browser event object.

<form @submit.prevent="console.log('submitted')" action="/foo">    
	<button>Submit</button>
</form>

In the above example, with the .prevent, clicking the button will NOT submit the form to the /foo endpoint. Instead, Alpine's listener will handle it and "prevent" the event from being handled any further.

.stop

Similar to .prevent.stop is the equivalent of calling .stopPropagation() inside a listener on the browser event object.

<div @click="console.log('I will not get logged')">    
	<button @click.stop>Click Me</button>
</div>

In the above example, clicking the button WON'T log the message. This is because we are stopping the propagation of the event immediately and not allowing it to "bubble" up to the <div> with the @click listener on it.

.outside

.outside is a convenience helper for listening for a click outside of the element it is attached to. Here's a simple dropdown component example to demonstrate:

<div x-data="{ open: false }">    
	<button @click="open = ! open">Toggle</button>     
	<div x-show="open" @click.outside="open = false">Contents...</div>
</div>

In the above example, after showing the dropdown contents by clicking the "Toggle" button, you can close the dropdown by clicking anywhere on the page outside the content.

This is because .outside is listening for clicks that DON'T originate from the element it's registered on.

It's worth noting that the .outside expression will only be evaluated when the element it's registered on is visible on the page. Otherwise, there would be nasty race conditions where clicking the "Toggle" button would also fire the @click.outside handler when it is not visible.

.window

When the .window modifier is present, Alpine will register the event listener on the root window object on the page instead of the element itself.

<div @keyup.escape.window="...">...</div>

The above snippet will listen for the "escape" key to be pressed ANYWHERE on the page.

Adding .window to listeners is extremely useful for these sorts of cases where a small part of your markup is concerned with events that take place on the entire page.

.document

.document works similarly to .window only it registers listeners on the document global, instead of the window global.

.once

By adding .once to a listener, you are ensuring that the handler is only called ONCE.

<button @click.once="console.log('I will only log once')">...</button>

.debounce

Sometimes it is useful to "debounce" an event handler so that it only is called after a certain period of inactivity (250 milliseconds by default).

For example if you have a search field that fires network requests as the user types into it, adding a debounce will prevent the network requests from firing on every single keystroke.

<input @input.debounce="fetchResults">

Now, instead of calling fetchResults after every keystroke, fetchResults will only be called after 250 milliseconds of no keystrokes.

If you wish to lengthen or shorten the debounce time, you can do so by trailing a duration after the .debounce modifier like so:

<input @input.debounce.500ms="fetchResults">

Now, fetchResults will only be called after 500 milliseconds of inactivity.

.throttle

.throttle is similar to .debounce except it will release a handler call every 250 milliseconds instead of deferring it indefinitely.

This is useful for cases where there may be repeated and prolonged event firing and using .debounce won't work because you want to still handle the event every so often.

For example:

<div @scroll.window.throttle="handleScroll">...</div>

The above example is a great use case of throttling. Without .throttle, the handleScroll method would be fired hundreds of times as the user scrolls down a page. This can really slow down a site. By adding .throttle, we are ensuring that handleScroll only gets called every 250 milliseconds.

Fun Fact: This exact strategy is used on this very documentation site to update the currently highlighted section in the right sidebar.

Just like with .debounce, you can add a custom duration to your throttled event:

<div @scroll.window.throttle.750ms="handleScroll">...</div>

Now, handleScroll will only be called every 750 milliseconds.

.self

By adding .self to an event listener, you are ensuring that the event originated on the element it is declared on, and not from a child element.

<button @click.self="handleClick">Click Me<img src="..."></button>

In the above example, we have an <img> tag inside the <button> tag. Normally, any click originating within the <button> element (like on <img> for example), would be picked up by a @click listener on the button.

However, in this case, because we've added a .self, only clicking the button itself will call handleClick. Only clicks originating on the <img> element will not be handled.

.camel

<div @custom-event.camel="handleCustomEvent">...</div>

Sometimes you may want to listen for camelCased events such as customEvent in our example. Because camelCasing inside HTML attributes is not supported, adding the .camel modifier is necessary for Alpine to camelCase the event name internally.

By adding .camel in the above example, Alpine is now listening for customEvent instead of custom-event.

.dot

<div @custom-event.dot="handleCustomEvent">...</div>

Similar to the .camelCase modifier there may be situations where you want to listen for events that have dots in their name (like custom.event). Since dots within the event name are reserved by Alpine you need to write them with dashes and add the .dot modifier.

In the code example above custom-event.dot will correspond to the event name custom.event.

.passive

Browsers optimize scrolling on pages to be fast and smooth even when JavaScript is being executed on the page. However, improperly implemented touch and wheel listeners can block this optimization and cause poor site performance.

If you are listening for touch events, it's important to add .passive to your listeners to not block scroll performance.

<div @touchstart.passive="...">...</div>

.capture

Add this modifier if you want to execute this listener in the event's capturing phase, e.g. before the event bubbles from the target element up the DOM.

<div @click.capture="console.log('I will log first')">    
	<button @click="console.log('I will log second')"></button>
</div>

x-text

x-text sets the text content of an element to the result of a given expression.

Here's a basic example of using x-text to display a user's username.

<div x-data="{ username: 'calebporzio' }">    
	Username: <strong x-text="username"></strong>
</div>

Now the <strong> tag's inner text content will be set to "calebporzio".

x-html

x-html sets the "innerHTML" property of an element to the result of a given expression.

⚠️ Only use on trusted content and never on user-provided content. ⚠️ Dynamically rendering HTML from third parties can easily lead to XSS vulnerabilities.

Here's a basic example of using x-html to display a user's username.

<div x-data="{ username: '<strong>calebporzio</strong>' }">    
	Username: <span x-html="username"></span>
</div>

Now the <span> tag's inner HTML will be set to "calebporzio".

x-model

x-model allows you to bind the value of an input element to Alpine data.

Here's a simple example of using x-model to bind the value of a text field to a piece of data in Alpine.

<div x-data="{ message: '' }">    
	<input type="text" x-model="message">     
	<span x-text="message"></span>
</div>

Now as the user types into the text field, the message will be reflected in the <span> tag.

x-model is two-way bound, meaning it both "sets" and "gets". In addition to changing data, if the data itself changes, the element will reflect the change.

We can use the same example as above but this time, we'll add a button to change the value of the message property.

<div x-data="{ message: '' }">    
	<input type="text" x-model="message">     
	<button x-on:click="message = 'changed'">Change Message</button>
</div>

Now when the <button> is clicked, the input element's value will instantly be updated to "changed".

x-model works with the following input elements:

  • <input type="text">
  • <textarea>
  • <input type="checkbox">
  • <input type="radio">
  • <select>
  • <input type="range">

Text inputs

<input type="text" x-model="message"> 
<span x-text="message"></span>

Textarea inputs

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

Checkbox inputs

Single checkbox with boolean

<input type="checkbox" id="checkbox" x-model="show"> 
<label for="checkbox" x-text="show"></label>

Multiple checkboxes bound to array

<input type="checkbox" value="red" x-model="colors">
<input type="checkbox" value="orange" x-model="colors">
<input type="checkbox" value="yellow" x-model="colors"> Colors: <span x-text="colors"></span>

Radio inputs

<input type="radio" value="yes" x-model="answer">
<input type="radio" value="no" x-model="answer"> 

Answer: <span x-text="answer"></span>

Select inputs

Single select

<select x-model="color">    
	<option>Red</option>    
	<option>Orange</option>    
	<option>Yellow</option>
</select> 

Color: <span x-text="color"></span>

Single select with placeholder

<select x-model="color">    
	<option value="" disabled>Select A Color</option>    
	<option>Red</option>    
	<option>Orange</option>    <option>Yellow</option></select> Color: <span x-text="color"></span>

Multiple select

<select x-model="color" multiple>    
	<option>Red</option>    
	<option>Orange</option>    
	<option>Yellow</option>
</select> 

Colors: <span x-text="color"></span>

Dynamically populated Select Options

<select x-model="color">    
	<template x-for="color in ['Red', 'Orange', 'Yellow']">        
		<option x-text="color"></option>    
	</template>
</select> 

Color: <span x-text="color"></span>

Range inputs

<input type="range" x-model="range" min="0" max="1" step="0.1">
<span x-text="range"></span>

Modifiers

.lazy

On text inputs, by default, x-model updates the property on every keystroke. By adding the .lazy modifier, you can force an x-model input to only update the property when user focuses away from the input element.

This is handy for things like real-time form-validation where you might not want to show an input validation error until the user "tabs" away from a field.

<input type="text" x-model.lazy="username">
<span x-show="username.length > 20">The username is too long.</span>

.number

By default, any data stored in a property via x-model is stored as a string. To force Alpine to store the value as a JavaScript number, add the .number modifier.

<input type="text" x-model.number="age"><span x-text="typeof age"></span>

.boolean

By default, any data stored in a property via x-model is stored as a string. To force Alpine to store the value as a JavaScript boolean, add the .boolean modifier. Both integers (1/0) and strings (true/false) are valid boolean values.

<select x-model.boolean="isActive">    
	<option value="true">Yes</option>    
	<option value="false">No</option>
</select>
<span x-text="typeof isActive"></span>

.debounce

By adding .debounce to x-model, you can easily debounce the updating of bound input.

This is useful for things like real-time search inputs that fetch new data from the server every time the search property changes.

<input type="text" x-model.debounce="search">

The default debounce time is 250 milliseconds, you can easily customize this by adding a time modifier like so.

<input type="text" x-model.debounce.500ms="search">

.throttle

Similar to .debounce you can limit the property update triggered by x-model to only updating on a specified interval.

The default throttle interval is 250 milliseconds, you can easily customize this by adding a time modifier like so.

<input type="text" x-model.throttle.500ms="search">

.fill

By default, if an input has a value attribute, it is ignored by Alpine and instead, the value of the input is set to the value of the property bound using x-model.

But if a bound property is empty, then you can use an input's value attribute to populate the property by adding the .fill modifier.

Programmatic access

Alpine exposes under-the-hood utilities for getting and setting properties bound with x-model. This is useful for complex Alpine utilities that may want to override the default x-model behavior, or instances where you want to allow x-model on a non-input element.

You can access these utilities through a property called _x_model on the x-modeled element. _x_model has two methods to get and set the bound property:

  • el._x_model.get() (returns the value of the bound property)
  • el._x_model.set() (sets the value of the bound property)
<div x-data="{ username: 'calebporzio' }">    
	<div x-ref="div" x-model="username"></div>     
	<button @click="$refs.div._x_model.set('phantomatrix')">
		Change username to: 'phantomatrix'    
	</button>     
	<span x-text="$refs.div._x_model.get()"></span>
</div>

x-modelable

x-modelable allows you to expose any Alpine property as the target of the x-model directive.

Here's a simple example of using x-modelable to expose a variable for binding with x-model.

<div x-data="{ number: 5 }">    
	<div x-data="{ count: 0 }" x-modelable="count" x-model="number">        
		<button @click="count++">Increment</button>    
	</div>     
	
	Number: <span x-text="number"></span>
</div>

As you can see the outer scope property "number" is now bound to the inner scope property "count".

Typically this feature would be used in conjunction with a backend templating framework like Laravel Blade. It's useful for abstracting away Alpine components into backend templates and exposing state to the outside through x-model as if it were a native input.

x-for

Alpine's x-for directive allows you to create DOM elements by iterating through a list. Here's a simple example of using it to create a list of colors based on an array.

<ul x-data="{ colors: ['Red', 'Orange', 'Yellow'] }">    
	<template x-for="color in colors">        
		<li x-text="color"></li>    
	</template>
</ul>

You may also pass objects to x-for.

<ul x-data="{ car: { make: 'Jeep', model: 'Grand Cherokee', color: 'Black' } }">    
	<template x-for="(value, index) in car">        
		<li>            
			<span x-text="index"></span>: <span x-text="value"></span>        
		</li>    
	</template>
</ul>

There are two rules worth noting about x-for:

x-for MUST be declared on a <template> element. That <template> element MUST contain only one root element

Keys

It is important to specify unique keys for each x-for iteration if you are going to be re-ordering items. Without dynamic keys, Alpine may have a hard time keeping track of what re-orders and will cause odd side-effects.

<ul x-data="{ 
	colors: [    
		{ id: 1, label: 'Red' },    
		{ id: 2, label: 'Orange' },    
		{ id: 3, label: 'Yellow' },]}">    
	<template x-for="color in colors" :key="color.id">        
		<li x-text="color.label"></li>    
	</template>
</ul>

Now if the colors are added, removed, re-ordered, or their "id"s change, Alpine will preserve or destroy the iterated <li>elements accordingly.

Accessing indexes

If you need to access the index of each item in the iteration, you can do so using the ([item], [index]) in [items] syntax like so:

<ul x-data="{ colors: ['Red', 'Orange', 'Yellow'] }">    
	<template x-for="(color, index) in colors">        
		<li>            
			<span x-text="index + ': '"></span>            
			<span x-text="color"></span>        
		</li>    
	</template>
</ul>

You can also access the index inside a dynamic :key expression.

<template x-for="(color, index) in colors" :key="index">

Iterating over a range

If you need to simply loop n number of times, rather than iterate through an array, Alpine offers a short syntax.

<ul>    
	<template x-for="i in 10">        
		<li x-text="i"></li>    
	</template>
</ul>

i in this case can be named anything you like.

Contents of a <template>

As mentioned above, an <template> tag must contain only one root element.

For example, the following code will not work:

<template x-for="color in colors">    
	<span>The next color is </span><span x-text="color">
</template>

but this code will work:

<template x-for="color in colors">    
	<p><span>The next color is </span><span x-text="color"></p>
</template>

x-transition

Alpine provides a robust transitions utility out of the box. With a few x-transition directives, you can create smooth transitions between when an element is shown or hidden.

There are two primary ways to handle transitions in Alpine:

The transition helper

The simplest way to achieve a transition using Alpine is by adding x-transition to an element with x-show on it. For example:

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

Toggle

As you can see, by default, x-transition applies pleasant transition defaults to fade and scale the revealing element.

You can override these defaults with modifiers attached to x-transition. Let's take a look at those.

Customizing duration

Initially, the duration is set to be 150 milliseconds when entering, and 75 milliseconds when leaving.

You can configure the duration you want for a transition with the .duration modifier:

<div ... x-transition.duration.500ms>

The above <div> will transition for 500 milliseconds when entering, and 500 milliseconds when leaving.

If you wish to customize the durations specifically for entering and leaving, you can do that like so:

<div ...    
	x-transition:enter.duration.500ms    
	x-transition:leave.duration.400ms>

Customizing delay

You can delay a transition using the .delay modifier like so:

<div ... x-transition.delay.50ms>

The above example will delay the transition and in and out of the element by 50 milliseconds.

Customizing opacity

By default, Alpine's x-transition applies both a scale and opacity transition to achieve a "fade" effect.

If you wish to only apply the opacity transition (no scale), you can accomplish that like so:

<div ... x-transition.opacity>

Customizing scale

Similar to the .opacity modifier, you can configure x-transition to ONLY scale (and not transition opacity as well) like so:

<div ... x-transition.scale>

The .scale modifier also offers the ability to configure its scale values AND its origin values:

<div ... x-transition.scale.80>

The above snippet will scale the element up and down by 80%.

Again, you may customize these values separately for enter and leaving transitions like so:

<div ...    x-transition:enter.scale.80    x-transition:leave.scale.90>

To customize the origin of the scale transition, you can use the .origin modifier:

<div ... x-transition.scale.origin.top>

Now the scale will be applied using the top of the element as the origin, instead of the center by default.

Like you may have guessed, the possible values for this customization are: topbottomleft, and right.

If you wish, you can also combine two origin values. For example, if you want the origin of the scale to be "top right", you can use: .origin.top.right as the modifier.

Applying CSS classes

For direct control over exactly what goes into your transitions, you can apply CSS classes at different stages of the transition.

The following examples use TailwindCSS utility classes.

<div x-data="{ open: false }">    
	<button @click="open = ! open">Toggle</button>     
	<div        
		x-show="open"        
		x-transition:enter="transition ease-out duration-300"        
		x-transition:enter-start="opacity-0 scale-90"        
		x-transition:enter-end="opacity-100 scale-100"        
		x-transition:leave="transition ease-in duration-300"        
		x-transition:leave-start="opacity-100 scale-100"        
		x-transition:leave-end="opacity-0 scale-90">Hello 👋</div>
</div>
Directive Description
:enter Applied during the entire entering phase.
:enter-start Added before element is inserted, removed one frame after element is inserted.
:enter-end Added one frame after element is inserted (at the same time enter-start is removed), removed when transition/animation finishes.
:leave Applied during the entire leaving phase.
:leave-start Added immediately when a leaving transition is triggered, removed after one frame.
:leave-end Added one frame after a leaving transition is triggered (at the same time leave-start is removed), removed when the transition/animation finishes.

x-effect

x-effect is a useful directive for re-evaluating an expression when one of its dependencies change. You can think of it as a watcher where you don't have to specify what property to watch, it will watch all properties used within it.

If this definition is confusing for you, that's ok. It's better explained through an example:

<div x-data="{ label: 'Hello' }" x-effect="console.log(label)">    
	<button @click="label += ' World!'">Change Message</button>
</div>

When this component is loaded, the x-effect expression will be run and "Hello" will be logged into the console.

Because Alpine knows about any property references contained within x-effect, when the button is clicked and label is changed, the effect will be re-triggered and "Hello World!" will be logged to the console.

x-ignore

By default, Alpine will crawl and initialize the entire DOM tree of an element containing x-init or x-data.

If for some reason, you don't want Alpine to touch a specific section of your HTML, you can prevent it from doing so using x-ignore.

<div x-data="{ label: 'From Alpine' }">    
	<div x-ignore>        
		<span x-text="label"></span>    
	</div>
</div>

In the above example, the <span> tag will not contain "From Alpine" because we told Alpine to ignore the contents of the div completely.

x-ref

x-ref in combination with $refs is a useful utility for easily accessing DOM elements directly. It's most useful as a replacement for APIs like getElementById and querySelector.

<button @click="$refs.text.remove()">Remove Text</button> 
<span x-ref="text">Hello 👋</span>

x-cloak

Sometimes, when you're using AlpineJS for a part of your template, there is a "blip" where you might see your uninitialized template after the page loads, but before Alpine loads.

x-cloak addresses this scenario by hiding the element it's attached to until Alpine is fully loaded on the page.

For x-cloak to work however, you must add the following CSS to the page.

[x-cloak] { display: none !important; }

The following example will hide the <span> tag until its x-show is specifically set to true, preventing any "blip" of the hidden element onto screen as Alpine loads.

<span x-cloak x-show="false">This will not 'blip' onto screen at any point</span>

x-cloak doesn't just work on elements hidden by x-show or x-if: it also ensures that elements containing data are hidden until the data is correctly set. The following example will hide the <span> tag until Alpine has set its text content to the message property.

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

When Alpine loads on the page, it removes all x-cloak property from the element, which also removes the display: none; applied by CSS, therefore showing the element.

Alternative to global syntax

If you'd like to achieve this same behavior, but avoid having to include a global style, you can use the following cool, but admittedly odd trick:

<template x-if="true">    <span x-text="message"></span></template>

This will achieve the same goal as x-cloak by just leveraging the way x-if works.

Because <template> elements are "hidden" in browsers by default, you won't see the <span> until Alpine has had a chance to render the x-if="true" and show it.

Again, this solution is not for everyone, but it's worth mentioning for special cases.

x-teleport

The x-teleport directive allows you to transport part of your Alpine template to another part of the DOM on the page entirely.

This is useful for things like modals (especially nesting them), where it's helpful to break out of the z-index of the current Alpine component.

x-teleport

By attaching x-teleport to a <template> element, you are telling Alpine to "append" that element to the provided selector.

The x-teleport selector can be any string you would normally pass into something like document.querySelector. It will find the first element that matches, be it a tag name (body), class name (.my-class), ID (#my-id), or any other valid CSS selector.

Here's a contrived modal example:

<body>    
	<div x-data="{ open: false }">        
		<button @click="open = ! open">Toggle Modal</button>         
		<template x-teleport="body">            
			<div x-show="open">Modal contents...</div>        
		</template>    
	</div>     
	<div>Some other content placed AFTER the modal markup.</div>     
	
	...
	
</body>

Notice how when toggling the modal, the actual modal contents show up AFTER the "Some other content..." element? This is because when Alpine is initializing, it sees x-teleport="body" and appends and initializes that element to the provided element selector.

Forwarding events

Alpine tries its best to make the experience of teleporting seamless. Anything you would normally do in a template, you should be able to do inside an x-teleport template. Teleported content can access the normal Alpine scope of the component as well as other features like $refs$root, etc...

However, native DOM events have no concept of teleportation, so if, for example, you trigger a "click" event from inside a teleported element, that event will bubble up the DOM tree as it normally would.

To make this experience more seamless, you can "forward" events by simply registering event listeners on the <template x-teleport...> element itself like so:

<div x-data="{ open: false }">    
	<button @click="open = ! open">Toggle Modal</button>     
	<template x-teleport="body" @click="open = false">        
		<div x-show="open">Modal contents... (click to close)</div>    
	</template>
</div>

Notice how we are now able to listen for events dispatched from within the teleported element from outside the <template> element itself?

Alpine does this by looking for event listeners registered on <template x-teleport...> and stops those events from propagating past the live, teleported, DOM element. Then, it creates a copy of that event and re-dispatches it from <template x-teleport...>.

Nesting

Teleporting is especially helpful if you are trying to nest one modal within another. Alpine makes it simple to do so:

<div x-data="{ open: false }">    
	<button @click="open = ! open">Toggle Modal</button>     
	<template x-teleport="body">        
		<div x-show="open">
			
			Modal contents...             
			
			<div x-data="{ open: false }">                
				<button @click="open = ! open">Toggle Nested Modal</button>
					<template x-teleport="body">                    
						<div x-show="open">Nested modal contents...</div>
					</template>            
			</div>        
		</div>    
	</template>
</div>

After toggling "on" both modals, they are authored as children, but will be rendered as sibling elements on the page, not within one another.

x-if

x-if is used for toggling elements on the page, similarly to x-show, however it completely adds and removes the element it's applied to rather than just changing its CSS display property to "none".

Because of this difference in behavior, x-if should not be applied directly to the element, but instead to a <template> tag that encloses the element. This way, Alpine can keep a record of the element once it's removed from the page.

<template x-if="open">    
	<div>Contents...</div>
</template>

Unlike x-showx-if, does NOT support transitioning toggles with x-transition.

Remember: <template> tags can only contain one root level element.

x-id

x-id allows you to declare a new "scope" for any new IDs generated using $id(). It accepts an array of strings (ID names) and adds a suffix to each $id('...') generated within it that is unique to other IDs on the page.

x-id is meant to be used in conjunction with the $id(...) magic.

Visit the $id documentation for a better understanding of this feature.

Here's a brief example of this directive in use:

<div x-id="['text-input']">    
	<label :for="$id('text-input')">Username</label>    
	<!-- for="text-input-1" -->     
	<input type="text" :id="$id('text-input')">    
	<!-- id="text-input-1" -->
</div> 

<div x-id="['text-input']">    
	<label :for="$id('text-input')">Username</label>    
	<!-- for="text-input-2" -->     
	<input type="text" :id="$id('text-input')">    
	<!-- id="text-input-2" -->
</div>

Magics

$el

$el is a magic property that can be used to retrieve the current DOM node.

<button @click="$el.innerHTML = 'Hello World!'">Replace me with "Hello World!"</button>

$refs

$refs is a magic property that can be used to retrieve DOM elements marked with x-ref inside the component. This is useful when you need to manually manipulate DOM elements. It's often used as a more succinct, scoped, alternative to document.querySelector.

<button @click="$refs.text.remove()">Remove Text</button> <span x-ref="text">Hello 👋</span>

Remove Text

Hello 👋

Now, when the <button> is pressed, the <span> will be removed.

Limitations

In V2 it was possible to bind $refs to elements dynamically, like seen below:

<template x-for="item in items" :key="item.id" >    <div :x-ref="item.name">    some content ...    </div></template>

However, in V3, $refs can only be accessed for elements that are created statically. So for the example above: if you were expecting the value of item.name inside of $refs to be something like Batteries, you should be aware that $refs will actually contain the literal string 'item.name' and not Batteries.

$store

You can use $store to conveniently access global Alpine stores registered using Alpine.store(...). For example:

<button x-data @click="$store.darkMode.toggle()">Toggle Dark Mode</button> ... <div x-data :class="$store.darkMode.on && 'bg-black'">    ...</div>  <script>    document.addEventListener('alpine:init', () => {        Alpine.store('darkMode', {            on: false,             toggle() {                this.on = ! this.on            }        })    })</script>

Given that we've registered the darkMode store and set on to "false", when the <button> is pressed, on will be "true" and the background color of the page will change to black.

Single-value stores

If you don't need an entire object for a store, you can set and use any kind of data as a store.

Here's the example from above but using it more simply as a boolean value:

<button x-data @click="$store.darkMode = ! $store.darkMode">Toggle Dark Mode</button> ... <div x-data :class="$store.darkMode && 'bg-black'">    ...</div>  <script>    document.addEventListener('alpine:init', () => {        Alpine.store('darkMode', false)    })</script>

→ Read more about Alpine stores

$watch

You can "watch" a component property using the $watch magic method. For example:

<div x-data="{ open: false }" x-init="$watch('open', value => console.log(value))">    <button @click="open = ! open">Toggle Open</button></div>

In the above example, when the button is pressed and open is changed, the provided callback will fire and console.log the new value:

You can watch deeply nested properties using "dot" notation

<div x-data="{ foo: { bar: 'baz' }}" x-init="$watch('foo.bar', value => console.log(value))">    <button @click="foo.bar = 'bob'">Toggle Open</button></div>

When the <button> is pressed, foo.bar will be set to "bob", and "bob" will be logged to the console.

Getting the "old" value

$watch keeps track of the previous value of the property being watched, You can access it using the optional second argument to the callback like so:

<div x-data="{ open: false }" x-init="$watch('open', (value, oldValue) => console.log(value, oldValue))">    <button @click="open = ! open">Toggle Open</button></div>

Deep watching

$watch automatically watches from changes at any level but you should keep in mind that, when a change is detected, the watcher will return the value of the observed property, not the value of the subproperty that has changed.

<div x-data="{ foo: { bar: 'baz' }}" x-init="$watch('foo', (value, oldValue) => console.log(value, oldValue))">    <button @click="foo.bar = 'bob'">Update</button></div>

When the <button> is pressed, foo.bar will be set to "bob", and "{bar: 'bob'} {bar: 'baz'}" will be logged to the console (new and old value).

⚠️ Changing a property of a "watched" object as a side effect of the $watch callback will generate an infinite loop and eventually error.

<!-- 🚫 Infinite loop --><div x-data="{ foo: { bar: 'baz', bob: 'lob' }}" x-init="$watch('foo', value => foo.bob = foo.bar)">    <button @click="foo.bar = 'bob'">Update</button></div>

$dispatch

$dispatch is a helpful shortcut for dispatching browser events.

<div @notify="alert('Hello World!')">    <button @click="$dispatch('notify')">        Notify    </button></div>

Notify

You can also pass data along with the dispatched event if you wish. This data will be accessible as the .detail property of the event:

<div @notify="alert($event.detail.message)">    <button @click="$dispatch('notify', { message: 'Hello World!' })">        Notify    </button></div>

Notify

Under the hood, $dispatch is a wrapper for the more verbose API: element.dispatchEvent(new CustomEvent(...))

Note on event propagation

Notice that, because of event bubbling, when you need to capture events dispatched from nodes that are under the same nesting hierarchy, you'll need to use the .window modifier:

Example:

<!-- 🚫 Won't work --><div x-data>    <span @notify="..."></span>    <button @click="$dispatch('notify')">Notify</button></div> <!-- ✅ Will work (because of .window) --><div x-data>    <span @notify.window="..."></span>    <button @click="$dispatch('notify')">Notify</button></div>

The first example won't work because when notify is dispatched, it'll propagate to its common ancestor, the div, not its sibling, the <span>. The second example will work because the sibling is listening for notify at the window level, which the custom event will eventually bubble up to.

Dispatching to other components

You can also take advantage of the previous technique to make your components talk to each other:

Example:

<div    x-data="{ title: 'Hello' }"    @set-title.window="title = $event.detail">    <h1 x-text="title"></h1></div> <div x-data>    <button @click="$dispatch('set-title', 'Hello World!')">Click me</button></div><!-- When clicked, the content of the h1 will set to "Hello World!". -->

Dispatching to x-model

You can also use $dispatch() to trigger data updates for x-model data bindings. For example:

<div x-data="{ title: 'Hello' }">    <span x-model="title">        <button @click="$dispatch('input', 'Hello World!')">Click me</button>        <!-- After the button is pressed, `x-model` will catch the bubbling "input" event, and update title. -->    </span></div>

This opens up the door for making custom input components whose value can be set via x-model.

$nextTick

$nextTick is a magic property that allows you to only execute a given expression AFTER Alpine has made its reactive DOM updates. This is useful for times you want to interact with the DOM state AFTER it's reflected any data updates you've made.

<div x-data="{ title: 'Hello' }">    <button        @click="            title = 'Hello World!';            $nextTick(() => { console.log($el.innerText) });        "        x-text="title"    ></button></div>

In the above example, rather than logging "Hello" to the console, "Hello World!" will be logged because $nextTick was used to wait until Alpine was finished updating the DOM.

Promises

$nextTick returns a promise, allowing the use of $nextTick to pause an async function until after pending dom updates. When used like this, $nextTick also does not require an argument to be passed.

<div x-data="{ title: 'Hello' }">    <button        @click="            title = 'Hello World!';            await $nextTick();            console.log($el.innerText);        "        x-text="title"    ></button></div>

$root

$root is a magic property that can be used to retrieve the root element of any Alpine component. In other words the closest element up the DOM tree that contains x-data.

<div x-data data-message="Hello World!">    <button @click="alert($root.dataset.message)">Say Hi</button></div>

$data

$data is a magic property that gives you access to the current Alpine data scope (generally provided by x-data).

Most of the time, you can just access Alpine data within expressions directly. for example x-data="{ message: 'Hello Caleb!' }" will allow you to do things like x-text="message".

However, sometimes it is helpful to have an actual object that encapsulates all scope that you can pass around to other functions:

<div x-data="{ greeting: 'Hello' }">    <div x-data="{ name: 'Caleb' }">        <button @click="sayHello($data)">Say Hello</button>    </div></div> <script>    function sayHello({ greeting, name }) {        alert(greeting + ' ' + name + '!')    }</script>

Say Hello

Now when the button is pressed, the browser will alert Hello Caleb! because it was passed a data object that contained all the Alpine scope of the expression that called it (@click="...").

Most applications won't need this magic property, but it can be very helpful for deeper, more complicated Alpine utilities.

$id

$id is a magic property that can be used to generate an element's ID and ensure that it won't conflict with other IDs of the same name on the same page.

This utility is extremely helpful when building re-usable components (presumably in a back-end template) that might occur multiple times on a page, and make use of ID attributes.

Things like input components, modals, listboxes, etc. will all benefit from this utility.

Basic usage

Suppose you have two input elements on a page, and you want them to have a unique ID from each other, you can do the following:

<input type="text" :id="$id('text-input')"><!-- id="text-input-1" --> <input type="text" :id="$id('text-input')"><!-- id="text-input-2" -->

As you can see, $id takes in a string and spits out an appended suffix that is unique on the page.

Grouping with x-id

Now let's say you want to have those same two input elements, but this time you want <label> elements for each of them.

This presents a problem, you now need to be able to reference the same ID twice. One for the <label>'s for attribute, and the other for the id on the input.

Here is a way that you might think to accomplish this and is totally valid:

<div x-data="{ id: $id('text-input') }">    <label :for="id"> <!-- "text-input-1" -->    <input type="text" :id="id"> <!-- "text-input-1" --></div> <div x-data="{ id: $id('text-input') }">    <label :for="id"> <!-- "text-input-2" -->    <input type="text" :id="id"> <!-- "text-input-2" --></div>

This approach is fine, however, having to name and store the ID in your component scope feels cumbersome.

To accomplish this same task in a more flexible way, you can use Alpine's x-id directive to declare an "id scope" for a set of IDs:

<div x-id="['text-input']">    <label :for="$id('text-input')"> <!-- "text-input-1" -->    <input type="text" :id="$id('text-input')"> <!-- "text-input-1" --></div> <div x-id="['text-input']">    <label :for="$id('text-input')"> <!-- "text-input-2" -->    <input type="text" :id="$id('text-input')"> <!-- "text-input-2" --></div>

As you can see, x-id accepts an array of ID names. Now any usages of $id() within that scope, will all use the same ID. Think of them as "id groups".

Nesting

As you might have intuited, you can freely nest these x-id groups, like so:

<div x-id="['text-input']">    <label :for="$id('text-input')"> <!-- "text-input-1" -->    <input type="text" :id="$id('text-input')"> <!-- "text-input-1" -->     <div x-id="['text-input']">        <label :for="$id('text-input')"> <!-- "text-input-2" -->        <input type="text" :id="$id('text-input')"> <!-- "text-input-2" -->    </div></div>

Keyed IDs (For Looping)

Sometimes, it is helpful to specify an additional suffix on the end of an ID for the purpose of identifying it within a loop.

For this, $id() accepts an optional second parameter that will be added as a suffix on the end of the generated ID.

A common example of this need is something like a listbox component that uses the aria-activedescendant attribute to tell assistive technologies which element is "active" in the list:

<ul    x-id="['list-item']"    :aria-activedescendant="$id('list-item', activeItem.id)">    <template x-for="item in items" :key="item.id">        <li :id="$id('list-item', item.id)">...</li>    </template></ul>

This is an incomplete example of a listbox, but it should still be helpful to demonstrate a scenario where you might need each ID in a group to still be unique to the page, but also be keyed within a loop so that you can reference individual IDs within that group.

Globals

Alpine.data

Alpine.data(...) provides a way to re-use x-data contexts within your application.

Here's a contrived dropdown component for example:

<div x-data="dropdown">    <button @click="toggle">...</button>     <div x-show="open">...</div></div> <script>    document.addEventListener('alpine:init', () => {        Alpine.data('dropdown', () => ({            open: false,             toggle() {                this.open = ! this.open            }        }))    })</script>

As you can see we've extracted the properties and methods we would usually define directly inside x-data into a separate Alpine component object.

Registering from a bundle

If you've chosen to use a build step for your Alpine code, you should register your components in the following way:

import Alpine from 'alpinejs'import dropdown from './dropdown.js' Alpine.data('dropdown', dropdown) Alpine.start()

This assumes you have a file called dropdown.js with the following contents:

export default () => ({    open: false,     toggle() {        this.open = ! this.open    }})

Initial parameters

In addition to referencing Alpine.data providers by their name plainly (like x-data="dropdown"), you can also reference them as functions (x-data="dropdown()"). By calling them as functions directly, you can pass in additional parameters to be used when creating the initial data object like so:

<div x-data="dropdown(true)">
Alpine.data('dropdown', (initialOpenState = false) => ({    open: initialOpenState}))

Now, you can re-use the dropdown object, but provide it with different parameters as you need to.

Init functions

If your component contains an init() method, Alpine will automatically execute it before it renders the component. For example:

Alpine.data('dropdown', () => ({    init() {        // This code will be executed before Alpine        // initializes the rest of the component.    }}))

Destroy functions

If your component contains a destroy() method, Alpine will automatically execute it before cleaning up the component.

A primary example for this is when registering an event handler with another library or a browser API that isn't available through Alpine. See the following example code on how to use the destroy() method to clean up such a handler.

Alpine.data('timer', () => ({    timer: null,    counter: 0,    init() {      // Register an event handler that references the component instance      this.timer = setInterval(() => {        console.log('Increased counter to', ++this.counter);      }, 1000);    },    destroy() {        // Detach the handler, avoiding memory and side-effect leakage        clearInterval(this.timer);    },}))

An example where a component is destroyed is when using one inside an x-if:

<span x-data="{ enabled: false }">    <button @click.prevent="enabled = !enabled">Toggle</button>     <template x-if="enabled">        <span x-data="timer" x-text="counter"></span>    </template></span>

Using magic properties

If you want to access magic methods or properties from a component object, you can do so using the this context:

Alpine.data('dropdown', () => ({    open: false,     init() {        this.$watch('open', () => {...})    }}))

Encapsulating directives with x-bind

If you wish to re-use more than just the data object of a component, you can encapsulate entire Alpine template directives using x-bind.

The following is an example of extracting the templating details of our previous dropdown component using x-bind:

<div x-data="dropdown">    <button x-bind="trigger"></button>     <div x-bind="dialogue"></div></div>
Alpine.data('dropdown', () => ({    open: false,     trigger: {        ['@click']() {            this.open = ! this.open        },    },     dialogue: {        ['x-show']() {            return this.open        },    },}))

Alpine.store

Alpine offers global state management through the Alpine.store() API.

Registering A Store

You can either define an Alpine store inside of an alpine:init listener (in the case of including Alpine via a <script> tag), OR you can define it before manually calling Alpine.start() (in the case of importing Alpine into a build):

From a script tag:

<script>    document.addEventListener('alpine:init', () => {        Alpine.store('darkMode', {            on: false,             toggle() {                this.on = ! this.on            }        })    })</script>

From a bundle:

import Alpine from 'alpinejs' Alpine.store('darkMode', {    on: false,     toggle() {        this.on = ! this.on    }}) Alpine.start()

Accessing stores

You can access data from any store within Alpine expressions using the $store magic property:

<div x-data :class="$store.darkMode.on && 'bg-black'">...</div>

You can also modify properties within the store and everything that depends on those properties will automatically react. For example:

<button x-data @click="$store.darkMode.toggle()">Toggle Dark Mode</button>

Additionally, you can access a store externally using Alpine.store() by omitting the second parameter like so:

<script>    Alpine.store('darkMode').toggle()</script>

Initializing stores

If you provide init() method in an Alpine store, it will be executed right after the store is registered. This is useful for initializing any state inside the store with sensible starting values.

<script>    document.addEventListener('alpine:init', () => {        Alpine.store('darkMode', {            init() {                this.on = window.matchMedia('(prefers-color-scheme: dark)').matches            },             on: false,             toggle() {                this.on = ! this.on            }        })    })</script>

Notice the newly added init() method in the example above. With this addition, the on store variable will be set to the browser's color scheme preference before Alpine renders anything on the page.

Single-value stores

If you don't need an entire object for a store, you can set and use any kind of data as a store.

Here's the example from above but using it more simply as a boolean value:

<button x-data @click="$store.darkMode = ! $store.darkMode">Toggle Dark Mode</button> ... <div x-data :class="$store.darkMode && 'bg-black'">    ...</div>  <script>    document.addEventListener('alpine:init', () => {        Alpine.store('darkMode', false)    })</script>

Alpine.bind

Alpine.bind(...) provides a way to re-use x-bind objects within your application.

Here's a simple example. Rather than binding attributes manually with Alpine:

<button type="button" @click="doSomething()" :disabled="shouldDisable"></button>

You can bundle these attributes up into a reusable object and use x-bind to bind to that:

<button x-bind="SomeButton"></button> <script>    document.addEventListener('alpine:init', () => {        Alpine.bind('SomeButton', () => ({            type: 'button',             '@click'() {                this.doSomething()            },             ':disabled'() {                return this.shouldDisable            },        }))    })</script>

End



End of doc