Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Alpine.js is a lightweight JavaScript framework designed to provide reactive and declarative behavior to HTML elements. It is often described as a minimalistic alternative to larger frameworks like Vue.js or React, offering a simpler and more intuitive way to add interactivity to web pages.
Reactive Data Binding
This feature allows you to bind data to HTML elements and automatically update the DOM when the data changes. In this example, clicking the button increments the count, and the span element updates to reflect the new count.
<div x-data="{ count: 0 }">
<button @click="count++">Increment</button>
<span x-text="count"></span>
</div>
Conditional Rendering
Conditional rendering allows you to show or hide elements based on the state of your data. In this example, clicking the button toggles the visibility of the div element.
<div x-data="{ isVisible: true }">
<button @click="isVisible = !isVisible">Toggle</button>
<div x-show="isVisible">This is conditionally rendered</div>
</div>
Event Handling
Alpine.js makes it easy to handle events directly in your HTML. This example shows how to display an alert message when a button is clicked.
<div x-data="{}">
<button @click="alert('Button clicked!')">Click Me</button>
</div>
Looping
You can loop through arrays and render elements for each item using the x-for directive. This example demonstrates how to render a list of items.
<div x-data="{ items: ['Item 1', 'Item 2', 'Item 3'] }">
<template x-for="item in items" :key="item">
<div x-text="item"></div>
</template>
</div>
Vue.js is a progressive JavaScript framework for building user interfaces. It offers a more comprehensive set of features compared to Alpine.js, including a component-based architecture, a robust ecosystem, and advanced state management with Vuex. While Vue.js is more powerful, it also comes with a steeper learning curve and larger bundle size.
React is a popular JavaScript library for building user interfaces, particularly single-page applications. It uses a component-based architecture and a virtual DOM for efficient updates. React is more complex and feature-rich than Alpine.js, making it suitable for larger applications but also requiring more setup and boilerplate code.
Stimulus is a modest JavaScript framework designed to enhance static HTML with just enough behavior to make it dynamic. It is similar to Alpine.js in its simplicity and ease of use, but it focuses more on enhancing existing HTML rather than providing a full reactive framework.
Alpine.js offers you the reactive and declarative nature of big frameworks like Vue or React at a much lower cost.
You get to keep your DOM, and sprinkle in behavior as you see fit.
Think of it like Tailwind for JavaScript.
Note: This tool's syntax is almost entirely borrowed from Vue (and by extension Angular). I am forever grateful for the gift they are to the web.
From CDN: Add the following script to the end of your <head>
section.
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v1.9.0/dist/alpine.js" defer></script>
That's it. It will initialize itself.
From NPM: Install the package from NPM.
npm i alpinejs
Include it in your script.
import 'alpinejs'
For IE11, polyfills will need to be provided. Please load the following scripts before the Alpine script above.
<script src="https://polyfill.io/v3/polyfill.min.js?features=MutationObserver%2CArray.from%2CArray.prototype.forEach%2CMap%2CSet%2CArray.prototype.includes%2CString.prototype.includes%2CPromise%2CNodeList.prototype.forEach%2CObject.values%2CReflect%2CReflect.set"></script>
<script src="https://cdn.jsdelivr.net/npm/proxy-polyfill@0.3.0/proxy.min.js"></script>
Dropdown/Modal
<div x-data="{ open: false }">
<button @click="open = true">Open Dropdown</button>
<ul
x-show="open"
@click.away="open = false"
>
Dropdown Body
</ul>
</div>
Tabs
<div x-data="{ tab: 'foo' }">
<button :class="{ 'active': tab === 'foo' }" @click="tab = 'foo'">Foo</button>
<button :class="{ 'active': tab === 'bar' }" @click="tab = 'bar'">Bar</button>
<div x-show="tab === 'foo'">Tab Foo</div>
<div x-show="tab === 'bar'">Tab Bar</div>
</div>
You can even use it for non-trivial things: Pre-fetching a dropdown's HTML content on hover
<div x-data="{ open: false }">
<button
@mouseenter.once="
fetch('/dropdown-partial.html')
.then(response => response.text())
.then(html => { $refs.dropdown.innerHTML = html })
"
@click="open = true"
>Show Dropdown</button>
<div x-ref="dropdown" x-show="open" @click.away="open = false">
Loading Spinner...
</div>
</div>
There are 13 directives available to you:
Directive |
---|
x-data |
x-init |
x-show |
x-bind |
x-on |
x-model |
x-text |
x-html |
x-ref |
x-if |
x-for |
x-transition |
x-cloak |
And 3 magic properties:
Magic Properties |
---|
$el |
$refs |
$nextTick |
x-data
Example: <div x-data="{ foo: 'bar' }">...</div>
Structure: <div x-data="[JSON data object]">...</div>
x-data
declares a new component scope. It tells the framework to initialize a new component with the following data object.
Think of it like the data
property of a Vue component.
Extract Component Logic
You can extract data (and behavior) into reusable functions:
<div x-data="dropdown()">
<button x-on:click="open()">Open</button>
<div x-show="isOpen()" x-on:click.away="close()">
// Dropdown
</div>
</div>
<script>
function dropdown() {
return {
show: false,
open() { this.show = true },
close() { this.show = false },
isOpen() { return this.show === true },
}
}
</script>
You can also mix-in multiple data objects using object destructuring:
<div x-data="{...dropdown(), ...tabs()}">
x-init
Example: <div x-data"{ foo: 'bar' }" x-init="foo = 'baz"></div>
Structure: <div x-data="..." x-init="[expression]"></div>
x-init
runs an expression when a component is initialized.
If you wish to run code AFTER Alpine has made it's initial updates to the DOM (something like a mounted()
hook in VueJS), you can return a callback from x-init
, and it will be run after:
x-init="return () => { // we have access to the post-dom-initialization state here // }"
x-show
Example: <div x-show="open"></div>
Structure: <div x-show="[expression]"></div>
x-show
toggles the display: none;
style on the element depending if the expression resolves to true
or false
.
x-bind
Note: You are free to use the shorter ":" syntax:
:type="..."
Example: <input x-bind:type="inputType">
Structure: <input x-bind:[attribute]="[expression]">
x-bind
sets the value of an attribute to the result of a JavaScript expression. The expression has access to all the keys of the component's data object, and will update every-time it's data is updated.
Note: attribute bindings ONLY update when their dependencies update. The framework is smart enough to observe data changes and detect which bindings care about them.
x-bind
for class attributes
x-bind
behaves a little differently when binding to the class
attribute.
For classes, you pass in an object who's keys are class names, and values are boolean expressions to determine if those class names are applied or not.
For example:
<div x-bind:class="{ 'hidden': foo }"></div>
In this example, the "hidden" class will only be applied when the value of the foo
data attribute is true
.
x-bind
for boolean attributes
x-bind
supports boolean attributes in the same way that value attributes, using a variable as the condition or any JavaScript expression that resolves to true
or false
.
For example:
<button x-bind:disabled="myVar">Click me</button>
This will add or remove the disabled
attribute when myVar
is true or false respectively.
Most common boolean attributes are supported, like readonly
, required
, etc.
x-on
Note: You are free to use the shorter "@" syntax:
@click="..."
Example: <button x-on:click="foo = 'bar'"></button>
Structure: <button x-on:[event]="[expression]"></button>
x-on
attaches an event listener to the element it's declared on. When that event is emitted, the JavaScript expression set as it's value is executed.
If any data is modified in the expression, other element attributes "bound" to this data, will be updated.
keydown
modifiers
Example: <input type="text" x-on:keydown.escape="open = false">
You can specify specific keys to listen for using keydown modifiers appended to the x-on:keydown
directive. Note that the modifiers are kebab-cased versions of Event.key
values.
Examples: enter
, escape
, arrow-up
, arrow-down
Note: You can also listen for system-modifier key combinations like:
x-on:keydown.cmd.enter="foo"
.away
modifier
Example: <div x-on:click.away="showModal = false"></div>
When the .away
modifier is present, the event handler will only be executed when the event originates from a source other than itself, or its children.
This is useful for hiding dropdowns and modals when a user clicks away from them.
.prevent
modifier
Example: <input type="checkbox" x-on:click.prevent>
Adding .prevent
to an event listener will call preventDefault
on the triggered event. In the above example, this means the checkbox wouldn't actually get checked when a user clicks on it.
.stop
modifier
Example: <div x-on:click="foo = 'bar'"><button x-on:click.stop></button></div>
Adding .stop
to an event listener will call stopPropagation
on the triggered event. In the above example, this means the "click" event won't bubble from the button to the outer <div>
. Or in other words, when a user clicks the button, foo
won't be set to 'bar'
.
.window
modifier
Example: <div x-on:resize.window="isOpen = window.outerWidth > 768 ? false : open"></div>
Adding .window
to an event listener will install the listener on the global window object instead of the DOM node on which it is declared. This is useful for when you want to modify component state when something changes with the window, like the resize event. In this example, when the window grows larger than 768 pixels wide, we will close the modal/dropdown, otherwise maintain the same state.
Note: You can also use the
.document
modifier to attach listeners todocument
instead ofwindow
.once
modifier
Example: <button x-on:mouseenter.once="fetchSomething()"></button>
Adding the .once
modifier to an event listener will ensure that the listener will only be handled once. This is useful for things you only want to do once, like fetching HTML partials and such.
x-model
Example: <input type="text" x-model="foo">
Structure: <input type="text" x-model="[data item]">
x-model
adds "two-way data binding" to an element. In other words, the value of the input element will be kept in sync with the value of the data item of the component.
Note:
x-model
is smart enough to detect changes on text inputs, checkboxes, radio buttons, textareas, selects, and multiple selects. It should behave how Vue would in those scenarios.
x-text
Example: <span x-text="foo"></span>
Structure: <span x-text="[expression]"
x-text
works similarly to x-bind
, except instead of updating the value of an attribute, it will update the innerText
of an element.
x-html
Example: <span x-html="foo"></span>
Structure: <span x-html="[expression]"
x-html
works similarly to x-bind
, except instead of updating the value of an attribute, it will update the innerHTML
of an element.
x-ref
Example: <div x-ref="foo"></div><button x-on:click="$refs.foo.innerText = 'bar'"></button>
Structure: <div x-ref="[ref name]"></div><button x-on:click="$refs.[ref name].innerText = 'bar'"></button>
x-ref
provides a convenient way to retrieve raw DOM elements out of your component. By setting an x-ref
attribute on an element, you are making it available to all event handlers inside an object called $refs
.
This is a helpful alternative to setting ids and using document.querySelector
all over the place.
x-if
Example: <template x-if="true"><div>Some Element</div></template>
Structure: <template x-if="[expression]"><div>Some Element</div></template>
For cases where x-show
isn't sufficient (x-show
sets an element to display: none
if it's false), x-if
can be used to actually remove an element completely from the DOM.
It's important that x-if
is used on a <template></template>
tag because Alpine doesn't use a virtual DOM. This implementation allows Alpine to stay rugged and use the real DOM to work it's magic.
Note:
x-if
must have a single element root inside the<template></template>
tag.
x-for
Example:
<template x-for="item in items" :key="item">
<div x-text="item"></div>
</template>
x-for
is available for cases when you want to create new DOM nodes for each item in an array. This should appear similar to v-for
in Vue, with one exception of needing to exist on a template
tag, and not a regular DOM element.
Note: the
:key
binding is optional, but HIGHLY recommended.
x-transition
Example:
<div
x-show="open"
x-transition:enter="ease-out transition-slow"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="ease-in transition-slow"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
>...</div>
<template x-if="open">
<div
x-transition:enter="ease-out transition-slow"
x-transition:enter-start="opacity-0 scale-90"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="ease-in transition-slow"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-90"
>...</div>
</template>
Alpine offers 6 different transition directives for applying classes to various stages of an element's transition between "hidden" and "shown" states. These directives work both with x-show
AND x-if
.
These behave exactly like VueJs's transition directives, except they have different, more sensible names:
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-cloak
Example: <div x-data="{}" x-cloak></div>
x-cloak
attributes are removed from elements when Alpine initializes. This is useful for hiding pre-initialized DOM. It's typical to add the following global style for this to work:
<style>
[x-cloak] { display: none; }
</style>
$el
Example:
<div x-data>
<button @click="$el.innerHTML = 'foo'">Replace me with "foo"</button>
</div>
$el
is a magic property that can be used to retrieve the root component DOM node.
$refs
Example:
<span x-ref="foo">
<button x-on:click="$refs.foo.innerText = 'bar'">
$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.
$nextTick
Example:
<div x-data="{ fruit: 'apple' }">
<button
x-on:click="
fruit = 'pear';
$nextTick(() => { console.log($event.target.innerText) });
"
x-text="fruit"
>
</div>
$nextTick
is a magic property that allows you to only execute a given expression AFTER Alpine has made it's 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.
Copyright © 2019-2020 Caleb Porzio and contributors
Licensed under the MIT license, see LICENSE.md for details.
FAQs
The rugged, minimal JavaScript framework
The npm package alpinejs receives a total of 185,973 weekly downloads. As such, alpinejs popularity was classified as popular.
We found that alpinejs demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.