A client-side router for Alpine.js
Routes in your HTML.
Alpine renders them.
Add x-route templates and x-link anchors to the page you already have. Dynamic params, external templates, and hash or HTML5 history, in 3.2 kB gzipped.
$ npm install @shaun/alpinejs-router
- 3.2 kB
- minified and gzipped
- 6.25M
- static route matches a second
- 0
- dependencies besides Alpine.js
- 2
- history modes, hash and HTML5
Features
Routing for pages that
already use Alpine.
One plugin, two directives, one magic. MIT licensed.
-
Routes in markup
Each route is a
<template x-route>with one root element. It renders when the path matches and is removed when it stops matching. -
Link navigation
x-linkpushes,x-link.replacereplaces, and.activitysets active classes. Ctrl-click, new tabs and external links behave as usual. -
Dynamic params
/users/:idexposes$router.params.id. Add a regex to tell routes apart:/:orderId(\d+)only matches digits. -
External templates
Point a route at
template="/user.html"to fetch it on first visit, ortemplate.preloadto fetch it at start. Inline content is the fallback. -
Hash or HTML5 history
Normal paths by default, or
#/URLs that need nothing from the server. Either mode can run under abasesuch as/app. -
The
$routermagicRead
path,query,paramsandloading. Callpush,replace,resolve,isandnotfrom any expression.
Live demo
Click around. This is the router.
The frame runs this repository's build in hash mode, so the address bar shows what the router would write.
<!-- three of the six routes in the frame --> <body x-data x-init="$router.config({ mode: 'hash' })"> <a x-link.activity href="/users/42">User 42</a> <template x-route="/users/:id(\d+)"> <h1>User #<span x-text="$router.params.id"></span></h1> </template> <template x-route="/about" template="./about.html"></template> <template x-route.notfound> <h1>Not found</h1> </template>
Full source: site/demo/index.html. The repository also has a larger examples/ app.
Performance
6.25M static matches a second.
1.27M dynamic ones.
Operations per second with 4,000 routes, 1,000 of each kind. Node.js v26.2.0. Higher is better.
| Operation | Operations per second |
|---|---|
| match, static | 6.25M |
| notfound, miss | 4.15M |
| match, miss | 4.13M |
| is(), cached | 3.61M |
| match, dynamic last | 1.29M |
| match, dynamic first | 1.27M |
Static paths are looked up directly and is() results are cached, so neither grows with the route count. Dynamic routes are indexed by their first path segment, which is why the first and last of 1,000 cost the same. Reproduce with npm run bench:router; numbers vary by machine.
History modes
Pick the URL shape you want.
| mode | URL | Server setup | Search engines |
|---|---|---|---|
web (default) | /users/42 | fallback to index.html | see each path as a page |
hash | /#/users/42 | none | see one page |
Set either with $router.config({ mode, base }) in an x-init. In web mode, serve your index.html for any path that is not a static file, or a reload on /users/42 returns a 404.
Get started
Two script tags, or one install.
<script src="https://unpkg.com/@shaun/alpinejs-router@1.x.x/dist/cdn.min.js" defer></script>
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
$ npm install @shaun/alpinejs-router
$ yarn add @shaun/alpinejs-router
$ bun add @shaun/alpinejs-router
Load the router before Alpine. For older browsers, use dist/es6.min.js instead of dist/cdn.min.js.
Alpine.js 3 is a peer dependency. Register the plugin before Alpine.start(), as below.
Alpine.js 3 is a peer dependency. Register the plugin before Alpine.start(), as below.
Alpine.js 3 is a peer dependency. Register the plugin before Alpine.start(), as below.
With a bundler
import Alpine from 'alpinejs' import router from '@shaun/alpinejs-router' Alpine.plugin(router) Alpine.start()A complete page
When something else fits better
Use a framework router, such as Vue Router, if you need:
- Guards that can cancel a navigation
- Routes defined in JavaScript, not markup
- Server-side rendering