Alpine 3.17.1 keeps state beside the HTML it changes
Alpine 3.17.1 lets a page declare local state with x-data, listen with x-on, bind form values with x-model, and show or repeat elements through directives. The result resembles a small template system embedded in ordinary markup. That is useful on a server-rendered product where a dropdown, search filter, or disclosure panel does not justify handing the entire page to a client router and component build. The browser remains the runtime, and the HTML remains readable as the interface.
The design is easy to see in the official counter example: one object holds a count, one click expression changes it, and one text directive displays it. commit 8579eab was the v3.17.1 version bump we tested. The repository around that small example is a 309-file npm workspace with roughly 29,331 source lines, because Alpine also carries its documentation, test suites, build tooling, and official plugins in the same checkout.
The 5.8 MB checkout installs 145 MB of tooling
Our clean checkout occupied 5.8 MB before installation. npm then added 622 packages in 35 seconds and used 145 MB on disk. That is contributor tooling rather than a claim about Alpine's browser payload, which our lab did not measure. The distinction matters because the project's "minimal" positioning describes how developers add behavior to a page, while cloning and changing the framework still brings a normal JavaScript monorepo with browser tests and bundling dependencies.
The workspace contains Alpine core, docs, a CSP variant, and plugins for jobs such as focus, collapse, intersection observation, masking, morphing, and persisted state. esbuild handles the package bundles, with CDN, ESM, and CommonJS outputs described in the repository guide. Our scan found 3 CI workflow files, a tests directory, and no Dockerfile. That last absence became relevant when the 10-second test command reached a missing graphical-system dependency.
What happened when we ran it
Our sandbox installed 622 npm packages in 35 seconds, and the build completed successfully in 9 seconds. The test step then exited with code 1 after 10 seconds. Cypress 15.18.0 reported that the system dependency Xvfb was missing, followed by Error: spawn Xvfb ENOENT on Debian 12. The run used 3 CPUs, 8 GB of RAM, the lab-node:22 image, and an unprivileged container with no secrets.
The log tail does not show an Alpine assertion failure or a completed test count. It shows that Cypress could not start its display layer, so the fair finding is that the documented contributor commands were insufficient in our fresh environment. We did not install Xvfb and rerun because that result is outside the supplied lab record. The npm audit did complete and reported 0 known vulnerabilities at commit 8579eab, with 0 critical, high, moderate, or low findings.
Production setup is shorter than the repository test setup
Version 3.17.1 can be loaded with one deferred CDN tag or installed as the alpinejs npm package. The documentation recommends pinning the exact CDN version for production instead of following the floating 3.x line. Bundled applications import Alpine, optionally expose it on window, and call Alpine.start() once. Plugins and custom directives must register before that call, while script-tag extensions listen for the alpine:init event. None of those paths requires credentials or a backend service.
A consumer should not confuse that small adoption surface with our 622-package contributor install. Building the repository creates each workspace bundle, and npm test launches Cypress rather than the separate Vitest command. The README names both test tools but leaves the Linux display package implicit. Since the repo supplies no Dockerfile, a team standardizing contribution environments should record Xvfb in its own image or setup notes before expecting the 10-second failure to disappear.
Strict CSP uses a separate build with fewer expressions
Alpine 3.17.1 has a dedicated @alpinejs/csp package for sites that disallow unsafe-eval. The standard build evaluates attribute expressions through Function declarations, which still violate that policy even though the project says it does not call eval() itself. The CSP build interprets a supported subset instead. Basic object and array literals, comparisons, updates, method calls, and common bindings remain available, so a counter or menu does not automatically require relaxing the site's script policy.
The subset has real edges. The CSP guide says arrow functions, destructuring, template literals, spread syntax, browser globals, and HTML injection are unsupported in expressions. Complex logic belongs in registered Alpine.data() components. A recently closed 2026 report about escaped string literals in the CSP build received a fix, evidence that this path is maintained. Our 3-CPU sandbox did not execute either runtime because Cypress stopped at Xvfb, so teams with a strict policy should test their own directive expressions.
Four open items and a fresh release show active maintenance
GitHub showed 31,902 stars, 4 open issues and pull requests, and a last push on September 3, 2026. Release v3.17.1 was published on August 31 with an Intersect plugin addition, a dependency update, and a documentation correction. That tiny combined queue is not a bug count, but recent closed items show the repository is processing fixes and contributions. The same week included work on CSP string parsing and a new deferred tree-initialization hook.
One open item deserves attention from form builders. Issue 4885 reports that a dependent select can display its first enabled option while the x-model value remains null, a regression introduced in v3.16.0. Pull request 4888 proposes a fix and Cypress coverage, but it remained open when fetched. Alpine is a good fit for restrained page behavior; compare that exact form pattern before adopting the current line for linked selects.

