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.

  1. 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.

  2. Link navigation

    x-link pushes, x-link.replace replaces, and .activity sets active classes. Ctrl-click, new tabs and external links behave as usual.

  3. Dynamic params

    /users/:id exposes $router.params.id. Add a regex to tell routes apart: /:orderId(\d+) only matches digits.

  4. External templates

    Point a route at template="/user.html" to fetch it on first visit, or template.preload to fetch it at start. Inline content is the fallback.

  5. Hash or HTML5 history

    Normal paths by default, or #/ URLs that need nothing from the server. Either mode can run under a base such as /app.

  6. The $router magic

    Read path, query, params and loading. Call push, replace, resolve, is and not from 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.

example.com/#/
<!-- 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.

Router operations per second with 4,000 routes
OperationOperations per second
match, static6.25M
notfound, miss4.15M
match, miss4.13M
is(), cached3.61M
match, dynamic last1.29M
match, dynamic first1.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.

modeURLServer setupSearch engines
web (default)/users/42fallback to index.htmlsee each path as a page
hash/#/users/42nonesee 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>

Load the router before Alpine. For older browsers, use dist/es6.min.js instead of dist/cdn.min.js.

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

Add routing to the Alpine page you already have.