Library / SDK
alpinejs/alpine avatar
alpinejs/alpine

Alpine.js: A Monorepo of Markup-Level Behaviors Reviewed for Adoption

A rugged, minimal framework for composing JavaScript behavior in your markup.

31,930 stars1,388 forksHTMLMIT

At a glance

What is it?
Alpine.js puts JavaScript behavior directly into HTML attributes, and its repository is an npm-workspaces monorepo of a core package plus ten plugins. The framework suits server-rendered pages that need small interactive pieces, but its documentation lives outside the code you clone, and that split shapes both contribution and upgrade work.
Who is it for?
Adopt Alpine.js if you are adding interactivity to server-rendered HTML and want the behavior to live in the markup you already write. Do not adopt it if you need a CSP-safe build and have not checked that the csp package covers the directives you plan to use, or if you expect the repository to be the documentation.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly HTML, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The Problem Alpine.js Solves: Behavior Written Where the Markup Already Is

Most JavaScript frameworks ask you to describe a component in a module and then mount it into a page. Alpine.js inverts that. The repository description calls it "a rugged, minimal framework for composing JavaScript behavior in your markup," and the README points readers straight to the docs site rather than explaining the runtime itself. That framing tells you the intended user: someone with a page already rendered by a server, a template engine, or a CMS, who needs a dropdown, a toggle, or a form interaction without introducing a component build step for that one piece of behavior.

The README is explicit that this repository is not the place to learn the framework. "Go to the Alpine docs for most things," it says, and the docs themselves live in the /packages/docs directory. So the repository you clone is a build and contribution surface, not a tutorial. If your evaluation process depends on reading a README to understand an API, Alpine.js will frustrate that process on the first pass. The README describes packaging, testing, and contribution, not directive semantics.

What Is Actually in the Repository: One Core Package and Ten Plugins

The README gives a package table. The alpinejs package holds "all of Alpine's core." Around it sit ten plugins: collapse for expanding and collapsing elements with animations, csp for a CSP-safe build, docs, focus for managing focus inside an element, history for binding data to query string parameters through the history API, intersect for triggering expressions when elements enter the viewport, mask for formatting a text input as the user types, morph for morphing HTML inside the page, and persist for keeping Alpine state across page loads.

Two details in that table are worth pausing on. First, the README marks the history plugin's name as "likely to change," which is a signal about API stability at the plugin level rather than the core. Second, the plugin list is a map of what the core deliberately does not do. Focus management, viewport observation, input masking, state persistence, and DOM morphing are all separate packages. An Alpine page that uses none of them ships only the core; a page that uses several is assembling a small dependency graph.

The repository is a mono-repo using npm workspaces, with each package in its own folder under /packages. The build output for each package lands in that package's dist directory, and the README states that every package should have at least a cdn build that self-initializes when loaded through a script tag with defer, plus module.esm.js and module.cjs.js files for module imports.

How the Build and Test Pipeline Is Wired

The README states that bundling for Alpine V3 is handled exclusively by ESBuild, and that all build configuration lives in scripts/build.js. That is a single point of configuration for every package in the monorepo. There is no per-package bundler config to reconcile, which keeps the repository mechanical to work in, but it also means a change to scripts/build.js has blast radius across all packages at once.

Getting a working environment is short. Clone the repository, then run npm install and npm run build from the root. The README notes that all package bundles are produced by that same npm run build command rather than separate per-package builds. To try the result, include /packages/alpinejs/dist/cdn.js from a script tag on a webpage.

Testing uses two tools. Cypress handles integration tests and Vitest handles unit tests, with all tests stored under /tests/cypress and /tests/vitest. npm run cypress opens the Cypress interface, which the README recommends during development. npm run vitest runs the unit suite, and the README says you can target specific tests with it. For anyone evaluating whether to contribute a fix, that split matters: a directive-level bug likely needs a Cypress test, while a helper function needs a Vitest test, and the repository gives you both runners as first-class npm scripts.

The CSP Build Is the Constraint Most Teams Miss

The csp package is described as providing a "CSP safe" build of Alpine. In practice, Alpine's core model is evaluating expressions written in HTML attributes, and that is exactly the pattern a strict Content-Security-Policy blocks when it forbids unsafe-eval. The existence of a separate csp package in the repository is the honest signal here: the default build and the CSP-safe build are not the same artifact, and the CSP-safe path is maintained as its own package with its own dist output.

The repository material does not enumerate which directives the CSP build supports or how its expression syntax differs from the default. That gap is the single largest thing to verify before adopting Alpine.js in an environment with a hardened CSP. Reading the source under /packages/csp and the corresponding docs under /packages/docs is the only way to confirm coverage from what is available here. Treating the csp package as a drop-in replacement for the default build without that check is an assumption the README does not support.

Where Alpine.js Is the Wrong Tool

Alpine.js is a poor fit for applications whose state is large, shared across distant parts of the page, and mutated from many places. The framework's premise is behavior composed in markup, and the plugin list reflects that scope: persist saves Alpine state across page loads, history binds data to query string parameters, and morph swaps HTML in place. None of those is a state container for a complex client application, and the README does not present one. If your page needs a router, normalized caches, and server-state synchronization, you are assembling that yourself on top of a markup-first library.

A second failure mode is organizational. Because the README defers to the docs site and the docs live in /packages/docs, a team that adopts Alpine.js without reading the docs source is adopting a framework whose behavior it has only seen in examples. When a directive behaves unexpectedly, the answer is in a directory the README treats as a contribution target, not a reference manual. Teams that expect the repository to explain itself will spend their first week in the wrong folder.

A third is version drift. The repository shows v3.17.0, v3.17.1, and v3.17.2 released within roughly a week of each other, and the README itself links to V2 docs at a tagged tree for anyone still on the older line. Frequent patch releases are normal for a maintained project, but they mean upgrade work is continuous rather than occasional, and the V2 link is a reminder that a major-version migration has already happened once.

Alpine.js Compared with a Component Framework Like Vue

The closest comparison is Vue used without a build step, since both let you write behavior in HTML. The difference is where the component boundary lives. Vue's single-file component model, even in its CDN form, centers on an options object with data, methods, and a template; the logic is declared in JavaScript and the template consumes it. Alpine.js centers on attributes in the markup, and the README's package table shows the framework pushing peripheral concerns (focus, intersection, masking, persistence, morphing) into optional plugins rather than into a component instance.

That has a concrete consequence for review. In a Vue component, a reviewer reads one file. In an Alpine page, the behavior is distributed across the HTML that a server or CMS produced, which can be harder to trace when the markup is generated by something you do not control. The trade is real in both directions: Alpine.js keeps interactivity next to the element it affects, and Vue keeps it in a unit you can test in isolation. The repository's own testing split (Cypress for integration, Vitest for units) suggests Alpine.js leans on integration tests for the behavior that lives in markup.

Licence, Maintenance, and What an Upgrade Actually Costs

The repository is MIT licensed. That permits commercial use and modification, but it also means there is no warranty and no support obligation attached to the code. This is not legal advice; if your organization has licence review requirements, route the MIT text through that process rather than relying on a summary.

Maintenance cost is shaped by the monorepo structure. Upgrading the core is one dependency, but each plugin you use is another package with its own dist output and its own release cadence, and the README flags the history plugin's name as likely to change. Pinning versions and reading the release notes for the specific packages you import is the practical way to manage that. On the contribution side, the cost of a change is npm install, npm run build, and the appropriate test runner under /tests, which is a low bar compared with repositories that require per-package toolchains.

The last push date on the repository is 2026-09-09, and the most recent release listed is v3.17.2 from 2026-09-07. Those dates describe activity, not quality. What they tell you is that the project is being released against, and that a pinned version will not sit still for long.

Editorial conclusion

Adopt Alpine.js if you are adding interactivity to server-rendered HTML and want the behavior to live in the markup you already write. Do not adopt it if you need a CSP-safe build and have not checked that the csp package covers the directives you plan to use, or if you expect the repository to be the documentation. Before committing, verify the exact version you intend to pin, confirm which plugins you need from the packages directory, and read the /packages/docs source for the directive behavior you depend on.

Official sources

  1. alpinejs/alpine on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Community notes

Community notes