VOOZH about

URL: https://blog.logrocket.com/getting-started-alpine/

⇱ Getting started with Alpine - LogRocket Blog


2022-03-16
2108
#js libraries
Godwin Ekuma
16847
👁 Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

Editor’s note: This article was updated on 31 March 2022 to include information about the latest release of Alpine.

👁 Getting Started With Alpine.js

Alpine is a rugged, minimal frontend development framework for adding JavaScript behavior to HTML markups. It enables you to harness the reactive and declarative nature of popular frontend libraries and frameworks such as Angular, React, and Vue, at a much lower cost.

There is no build step and the library file size is about 4KB gzipped. Alpine is not meant to replace frameworks such as Vue and React; if you have a highly interactive single-page app, it’s best to stick to more powerful tools. It’s best used when your project requires only minimal JavaScript, such as when you only need one or two components, like dropdowns, sidebars, tabs, and image selection.

Alpine is also great for server-side rendered apps, such as Laravel, Rails, and AdonisJS, which require you to toggle some JavaScript components. And since it doesn’t have a virtual DOM, it’s easier to set up.

Essentially, Alpine is like Tailwind for JavaScript. The DOM and behavior are not separated; you get to keep your DOM and sprinkle in behavior as you see fit. You can easily write declarative code as opposed to procedural code. Finally, Alpine has a very small footprint in your application.

Now, let’s move on to installation steps and get our hands dirty with Alpine.

Contents

🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

Installation and basic use

Adding Alpine to a project is easy. You can either include it from a script tag through a CDN or import it as a module.

From script tag

Using a script tag is the easiest and most straightforward way to to add Alpine to your project. You just need to add the snippet below at the end of the <head> section of your HTML file:

<script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>

Specifying the version as @3.x.x in the CDN will pull the latest version of Alpine v3. However, in production, it’s recommended to hardcode the latest version in the CDN link.

As a module

With this method, you first need to install Alpine via npm:

npm install alpinejs

Then, import Alpine into your bundle and initialize it:

import Alpine from 'alpinejs'

// optional
window.Alpine = Alpine

// initialize Alpine
Alpine.start()

A basic component in Alpine

<html>
 <head>
 <script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
 </head>
 <body>
 <div x-data="{ isOpen: true }">
 <button x-on:click=" isOpen = !isOpen">Toggle</button>
 <h1 x-show="isOpen">Alpinjs</h1>
 </div>
 </body>
</html>

The first step to using Alpine is to define a state. The state goes wherever it is needed and has the same scope as the HTML selector you put in.

In the code above, we defined a scope using the x-data directive by passing in an object as the value of the state. The x-on directive listens for events. For the button element, we’re listening to the click event, which changes the value of isOpen to true or false. The x-show directive shows or hides an element from the DOM depending on the value of the state object’s isOpen property.

Alpine directives

At the core of the Alpine framework are directives, which change the DOM layout by adding and removing DOM elements, and alter the behavior and appearance of elements in the DOM. Alpine directives starts with a x- followed by the name of the directive.

Let’s quickly go over some of the directives and see how they can be used.

x-data

x-data initializes a new component scope with an object in an HTML element. All child HTML elements have access to the data object that exists in its parent element:

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

x-init

x-init is used to run an expression when a component initializes, and can be used in to set the initial value of the component state:

<div x-data="{ title: 'foo' }" x-init="title = 'bar'"></div>

Also, x-init can be used to run code after a component initializes by passing a callback function:

<div x-data="{ images: [] }"
 x-init="$nextTick(() => {
 fetch('https://pixabay.com/api/?key=15819227-ef2d84d1681b9442aaa9755b8&q=yellow+flowers&image_type=photo')
 .then(response => response.json())
 .then(response => { images = response.hits })
 })"
></div>

x-bind

Alpine provides x-bind as a mechanism for binding value, boolean, and class attributes.

Value attribute binding:

<img x-bind:src="imgSrc">

This sets the value of an attribute to the result of the expression.

Class attribute binding:

<div x-bind:class="{ 'hidden': isClosed }"></div>

For class binding, an object expression is passed. The object keys are class names, and the values are boolean expressions. If the boolean expression evaluates to true, the class name will be applied to that element.


Over 200k developers use LogRocket to create better digital experiences

👁 Image
Learn more →

Boolean attribute binding:

<input type="text" x-bind:hidden="true">

Boolean binding works the same way as attribute binding, but the expression passed has to evaluate to true or false.

x-on

x-on adds an event listener to the element on which it’s declared. When the element emits that event (e.g., a click or input event), an expression (or function) will be executed:

<button x-on:click="foo = 'bar'">Click me</button>

x-show

x-show changes the CSS display property of the element depending on whether the expression evaluates to true or false. If the expression evaluates to false, the element’s display property is set to none. If it resolves to true, the display property is set to block:

<div x-show="isOpen"></div>

x-if

While x-show can be used to toggle the display property of an element, the element is actually not removed from the DOM. The x-if directive doesn’t hide elements with CSS; it adds and removes them entirely from the DOM.

The value of x-if is a boolean expression that can evaluate to true or false. If the expression evaluates to false, x-if removes its host element from the DOM. x-if only works within the template element and must have a single element root inside the template tag:

<template x-if="true">
 <div>...</div>
</template>

x-for

x-for helps when you want to create new DOM nodes for each item in a collection. Just like the x-if directive, the x-for directive needs to exist on a template tag, not a regular DOM element:

<template x-for="item in items" :key="item">
 <div x-text="item"></div>
</template>

x-model

x-model adds a two-way data binding capability to an element, and synchronizes the value of an input element and the component data. It is smart enough to detect changes on text inputs, checkboxes, radio buttons, text areas, and multiple selects, and binds their value to the component data:

<input type="search" x-model="search">

x-text

While x-bind is for attribute binding, x-text is used to set the value of an element’s innerText:

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

x-html

x-html works similarly to x-text, but instead of setting the innerText, it sets the value of the innerHTML of an element:

<span x-html="title"></span>

To learn more about Alpine directives, be sure to check out the docs.

Building an image gallery with Alpine

To demonstrate how these directives can be used together, let’s build a simple image gallery:

<!DOCTYPE html>
<html lang="en">
 <head>
 <!-- Required meta tags -->
 <meta charset="utf-8" />
 <meta
 name="viewport"
 content="width=device-width, initial-scale=1, shrink-to-fit=no"
 />
 <!-- Bootstrap CSS -->
 <link
 rel="stylesheet"
 href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
 integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
 crossorigin="anonymous"
 />
 <!-- Custom CSS -->
 <link rel="stylesheet" href="css/custom.css" />
 <!-- Fonts -->
 <link
 href="https://fonts.googleapis.com/css?family=Lora:400,700|Nunito:400,700"
 rel="stylesheet"
 />
 <script defer src="https://unpkg.com/[email protected]/dist/cdn.min.js"></script>
 <script
 defer
 src="https://use.fontawesome.com/releases/v5.0.7/js/all.js"
 ></script>
 <link
 href="images/favicon_32.ico"
 rel="shortcut icon"
 type="image/x-icon"
 />
 <link href="images/favicon_256.ico" rel="apple-touch-icon" />
 </head>
 <body
 x-data="images()"
 x-init="fetch('https://pixabay.com/api/?key=15819227-ef2d84d1681b9442aaa9755b8&q=yellow+flowers&image_type=photo')
.then(response => response.json())
.then(response => { images = response.hits })"
 >
 <!-- Header section -->
 <header class="navigation">
 <div class="container navigation-content">
 <nav class="navbar navbar-expand-lg navbar-light">
 <a class="navbar-brand" href="index.html"
 ><img
 src="https://godwinekuma.github.io/we-connect/images/logo-white.svg"
 alt="weconnect logo"
 height="50"
 class="navbar-brand-image"
 />
 PictureGram</a
 >
 <button
 class="navbar-toggler"
 type="button"
 data-toggle="collapse"
 data-target="#navbarSupportedContent"
 aria-controls="navbarSupportedContent"
 aria-expanded="false"
 aria-label="Toggle navigation"
 >
 <span class="navbar-toggler-icon"></span>
 </button>
 </nav>
 </div>
 </header>
 <!-- Header section /-->
 <!-- Hero Section -->
 <div>
 <section class="hero">
 <div class="container">
 <div class="d-flex flex-column align-items-center">
 <h1 class="display-4" style="text-align:center">
 Search for images.
 </h1>
 <h2 class="" style="text-align:center">
 Pixel perfect images can be found here.
 </h2>
 <div class="input-group">
 <input
 type="text"
 class="form-control"
 placeholder="search images"
 x-model="q"
 aria-label="Text input with segmented dropdown button"
 />
 <select class="custom-select" x-model="image_type">
 <option selected>choose image type</option>
 <option value="all">All</option>
 <option value="photo">Photo</option>
 <option value="illustration">Illustration</option>
 <option value="vector">Vector</option>
 </select>
 <div class="input-group-append">
 <button
 class="btn btn-primary"
 type="button"
 x-on:click="getImages()"
 >
 Search
 </button>
 </div>
 </div>
 </div>
 </div>
 </section>
 <section id="photos" class="my-5">
 <template x-for="image in images" :key="image.id">
 <img x-bind:src="image.webformatURL" alt="image.tags[0]" />
 </template>
 </section>
 </div>
 <script>
 function images() {
 return {
 images: [],
 q: "",
 image_type: "",
 getImages: async function() {
 console.log("params", this.q, this.image_type);
 const response = await fetch(
 `https://pixabay.com/api/?key=15819227-ef2d84d1681b9442aaa9755b8&q=${
 this.q
 }&image_type=${this.image_type}`
 );
 const data = await response.json();
 this.images = data.hits;
 }
 };
 }
 </script>
 </body>
</html> 

Our gallery app gets a list of images from Pixabay and displays them. The application state is set on the body tag by the x-data directive using a function called images. The function returns an object that contains image, q, image-type, and getImages.

The initial value of an image is set using the x-init directive. The x-init fetches a list of images from Pixabay and sets it as the value of images field. q captures the value of the <input> and is set using the x-model directive. image_type, on the other hand, captures the value of the <select></select> and is also set using the x-model directive.

We attached a click event listener to the <button>. When the button is clicked, the getImages() method in the state is called. The getImages() method fetches new images based on the value of q and image_type.

Alpinejs_Tutorial

No Description

Conclusion

In this tutorial, we covered how to use Alpine and built a sample image gallery component with the framework. Though it might not totally replace other frameworks, it can be used in combination with React or Vue to quickly prototype components without writing much JavaScript. To learn more about Alpine, be sure to check out the framework’s website.

Are you adding new JS libraries to build new features or improve performance? What if they’re doing the opposite?

There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.


More great articles from LogRocket:


LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.

👁 LogRocket Dashboard Free Trial Banner

LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Build confidently — start monitoring for free.

👁 Image
👁 Image
👁 Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

👁 Image
Emmanuel John
Jun 17, 2026 ⋅ 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

👁 Image
Chizaram Ken
Jun 16, 2026 ⋅ 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension — no new framework required.

👁 Image
Ikeh Akinyemi
Jun 12, 2026 ⋅ 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo — with email/password login, Google OAuth, session persistence, and protected routes.

👁 Image
Chinwike Maduabuchi
Jun 9, 2026 ⋅ 13 min read
View all posts

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now