File: README.md
Alpine.js
Go to the Alpine docs for most things: Alpine Docs
You are welcome to submit updates to the docs by submitting a PR to this repo. Docs are located in the /packages/docs directory.
Stay here for contribution-related information.
Looking for V2 docs? here they are
Contribution Guide:
Quickstart
- clone this repo locally
- run
npm install&npm run build - Include the
/packages/alpinejs/dist/cdn.jsfile from a<script>tag on a webpage and you're good to go!
Brief Tour
You can get everything installed with: npm install in the root directory of this repo after cloning it locally.
This repo is a "mono-repo" using npm workspaces for managing the packages. Each package has its own folder in the /packages directory.
Rather than having to run separate builds for each package, all package bundles are handled with the same command: npm run build
Here's a brief look at each package in this repo:
| Package | Description |
|---|---|
| alpinejs | The main Alpine repo with all of Alpine's core |
| collapse | A plugin for expanding and collapsing elements using smooth animations |
| csp | A repo to provide a "CSP safe" build of Alpine |
| docs | The Alpine documentation |
| focus | A plugin that allows you to manage focus inside an element |
| history | A plugin for binding data to query string parameters using the history API (name is likely to change) |
| intersect | A plugin for triggering JS expressions based on elements intersecting with the viewport |
| mask | A plugin for automatically formatting a text input field as a user types |
| morph | A plugin for morphing HTML (like morphdom) inside the page intelligently |
| persist | A plugin for persisting Alpine state across page loads |
The compiled JS files (as a result of running npm run [build/watch]) to be included as a <script> tag for example are stored in each package's packages/[package]/dist directory.
Each package should at least have: a "cdn" build that is self-initializing and can be included using the src attribute in a <script defer> tag, and a module.[esm/cjs].js file that is used for importing as a JS module (cjs for node, esm for everything else).
The bundling for Alpine V3 is handled exclusively by ESBuild. All of the configuration for these builds is stored in the scripts/build.js file.
Testing
There are 2 different testing tools used in this repo: Cypress (for integration tests), and Vitest (for unit tests).
All tests are stored inside the /tests folder under /tests/cypress and /tests/vitest.
If you wish to only run Cypress and open it's user interface (recommended during development), you can run: npm run cypress
If you wish to only run Vitest tests, you can run npm run vitest like normal and target specific tests.
File: packages/docs/src/en/advanced/async.md
order: 4 title: Async
Async
Alpine is built to support asynchronous functions in most places it supports standard ones.
For example, let's say you have a simple function called getLabel() that you use as the input to an x-text directive:
function getLabel() {
return 'Hello World!'
}Because getLabel is synchronous, everything works as expected.
Now let's pretend that getLabel makes a network request to retrieve the label and can't return one instantaneously (asynchronous). By making getLabel an async function, you can call it from Alpine using JavaScript's await syntax.
async function getLabel() {
let response = await fetch('/api/label')
return await response.text()
}Additionally, if you prefer calling methods in Alpine without the trailing parenthesis, you can leave them out and Alpine will detect that the provided function is async and handle it accordingly. For example:
File: packages/docs/src/en/advanced/reactivity.md
order: 2 title: Reactivity
Reactivity
Alpine is "reactive" in the sense that when you change a piece of data, everything that depends on that data "reacts" automatically to that change.
Every bit of reactivity that takes place in Alpine, happens because of two very important reactive functions in Alpine's core: Alpine.reactive(), and Alpine.effect().
Alpine uses VueJS's reactivity engine under the hood to provide these functions.
โ Read more about @vue/reactivity
Understanding these two functions will give you super powers as an Alpine developer, but also just as a web developer in general.
Alpine.reactive()
Let's first look at Alpine.reactive(). This function accepts a JavaScript object as its parameter and returns a "reactive" version of that object. For example:
let data = { count: 1 }
let reactiveData = Alpine.reactive(data)Under the hood, when Alpine.reactive receives data, it wraps it inside a custom JavaScript proxy.
A proxy is a special kind of object in JavaScript that can intercept "get" and "set" calls to a JavaScript object.
โ Read more about JavaScript proxies
At face value, reactiveData should behave exactly like data. For example:
console.log(data.count) // 1
console.log(reactiveData.count) // 1
reactiveData.count = 2
console.log(data.count) // 2
console.log(reactiveData.count) // 2What you see here is that because reactiveData is a thin wrapper around data, any attempts to get or set a property will behave exactly as if you had interacted with data directly.
The main difference here is that any time you modify or retrieve (get or set) a value from reactiveData, Alpine is aware of it and can execute any other logic that depends on this data.
Alpine.reactive is only the first half of the story. Alpine.effect is the other half, let's dig in.
Alpine.effect()
Alpine.effect accepts a single callback function. As soon as Alpine.effect is called, it will run the provided function, but actively look for any interactions with reactive data. If it detects an interaction (a get or set from the aforementioned reactive proxy) it will keep track of it and make sure to re-run the callback if any of reactive data changes in the future. For example:
let data = Alpine.reactive({ count: 1 })
Alpine.effect(() => {
console.log(data.count)
})When this code is first run, "1" will be logged to the console. Any time data.count changes, it's value will be logged to the console again.
This is the mechanism that unlocks all of the reactivity at the core of Alpine.
To connect the dots further, let's look at a simple "counter" component example without using Alpine syntax at all, only using Alpine.reactive and Alpine.effect:
Increment
Count:let button = document.querySelector('button')
let span = document.querySelector('span')
let data = Alpine.reactive({ count: 1 })
Alpine.effect(() => {
span.textContent = data.count
})
button.addEventListener('click', () => {
data.count = data.count + 1
})Increment
Count:As you can see, you can make any data reactive, and you can also wrap any functionality in Alpine.effect.
This combination unlocks an incredibly powerful programming paradigm for web development. Run wild and free.
File: packages/docs/src/en/directives/bind.md
order: 4 title: bind
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.
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.
Despite not being included in the above snippet,
x-bindcannot be used if no parent element hasx-datadefined. โ Read more aboutx-data
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.
Toggle Dropdown
Dropdown Contents...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:
The inverse is also available to you. Suppose instead of open, we use a variable with the opposite value: closed.
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:
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:
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.
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:
If hide is false, the DOM element will look like:
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.
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.
One advantage of this approach is being able to mix it in with existing styles on an element:
And like most expressions in Alpine, you can always use the result of a JavaScript expression as the reference:
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.
Open Dropdown
Dropdown Contents
<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' }
File: packages/docs/src/en/directives/cloak.md
order: 12 title: cloak
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 `` tag until its x-show is specifically set to true, preventing any "blip" of the hidden element onto screen as Alpine loads.
This will not 'blip' onto screen at any pointx-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 `` tag until Alpine has set its text content to the message property.
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">
</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 `` 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.
File: packages/docs/src/en/directives/data.md
order: 1 title: data
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:
Toggle Content
Content...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:
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.
Toggle Content
Content...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:
...
...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.
Toggle Content
Content...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.
However, if you wish, you can also eliminate the attribute value entirely if it looks better to you.
Single-element components
Sometimes you may only have a single element inside your Alpine component, like the following:
Hide MeIn these cases, you can declare x-data directly on that single element:
Hide MeRe-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:
Toggle Content
Content...
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('dropdown', () => ({
open: false,
toggle() {
this.open = ! this.open
},
}))
})
</script>โ Read more about Alpine.data(...)
File: packages/docs/src/en/directives/effect.md
order: 11 title: effect
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:
Change MessageWhen 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.
File: packages/docs/src/en/directives/for.md
order: 8 title: for
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.
<template x-for="color in colors">
<li x-text="color"></li>
</template><template x-for="color in colors">
<li x-text="color"></li>
</template>You may also pass objects to x-for.
<template x-for="(value, index) in car">
<li>
:
</li>
</template><template x-for="(value, index) in car">
<li>
:
</li>
</template>There are two rules worth noting about x-for:
x-forMUST 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.
<template x-for="color in colors" :key="color.id">
<li x-text="color.label"></li>
</template>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:
<template x-for="(color, index) in colors">
<li>
</li>
</template>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.
<template x-for="i in 10">
<li x-text="i"></li>
</template>i in this case can be named anything you like.
Despite not being included in the above snippet,
x-forcannot be used if no parent element hasx-datadefined. โ Read more aboutx-data
Contents of a ``
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">
The next color is
</template>but this code will work:
<template x-for="color in colors">
The next color is
</template>