@ramstack/alpinegear-router v1.1.0
@ramstack/alpinegear-router
@ramstack/alpinegear-router
is a plugin for Alpine.js that provides routing-related directives
for Alpine.js, enabling client-side navigation and routing functionality.
Installation
Using CDN
To include the CDN version of this plugin, add the following <script>
tag before the core alpine.js
file:
<!-- alpine.js plugin -->
<script src="https://cdn.jsdelivr.net/npm/@ramstack/alpinegear-router@1/alpinegear-router.min.js" defer></script>
<!-- alpine.js -->
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
Using NPM
Alternatively, you can install the plugin via npm
:
npm install --save @ramstack/alpinegear-router
Then, initialize it in your project:
import Alpine from "alpinejs";
import router from "@ramstack/alpinegear-router";
Alpine.plugin(router);
Alpine.start();
Usage
<div x-data x-router>
<h1>Hello World!</h1>
<div>
<b>Current route:</b>
<pre x-format>{{ JSON.stringify($route, null, 2) }}</pre>
</div>
<!-- Inline template -->
<template x-route="/">
Home page
</template>
<!-- External template -->
<template x-route="/about" x-route:view="/views/about.html"></template>
<nav>
<a x-router:link href="/">Home</a>
<a x-router:link.replace href="/about">About</a>
</nav>
<!-- Render the matching route -->
<main x-router:outlet></main>
</div>
History modes
The router can be configured to use different history modes. There are two available modes:
html5
: The default history mode.hash
: Uses a hash#
in the URL to manage history.
HTML5 mode
The html5
mode uses the browser's history API to manage navigation.
<div x-data x-router:html5>
...
</div>
Since this mode is the default, there is no need to specify it explicitly:
<!-- HTML5 mode is used by default -->
<div x-data x-router>
...
</div>
Hash mode
This mode uses a hash #
in the URL to handle navigation. Unlike html5
mode, it requires no special server-side
configuration since the browser does not send the hash portion of the URL to the server.
<div x-data x-router:hash>
...
</div>
Route directive
Routes are defined using a <template>
element with the x-route
attribute.
<div x-router>
<template x-route="/">
Home page
</template>
<template x-route="/profile/{username}">
Profile
</template>
<!-- Render the matching route -->
<div x-router:outlet></div>
</div>
In this example, two routes are defined: a static /
route representing the homepage,
and a dynamic (parameterized) route /profile/{username}
, where username
is a route parameter enclosed
in curly brackets.
This means that URLs like /profile/john
and /profile/samantha
will both match the same route.
When a route is matched, the username
parameter can be accessed via $route.params.username
.
For more details on dynamic (parameterized) routes, refer to the corresponding section below.
Outlet directive
The x-router:outlet
directive is used to render the matching route's content and can be placed anywhere
within an x-router
element. If no route matches, x-router:outlet
will render nothing.
<div x-router>
<template x-route="/">
Home page
</template>
<template x-route="/profile/{username}">
Profile
</template>
<!-- Render the matching route -->
<div x-router:outlet></div>
</div>
Link directive
<div x-router:hash>
...
<nav>
<a x-router:link href="/">Home</a>
<a x-router:link href="/about">About</a>
</nav>
</div>
The router does not automatically intercept all links. Only links with the x-router:link
directive
inside an x-router
container are handled by the router. Links should always be specified normally,
regardless of the selected history mode.
Applying x-router:link
to parent elements
You can also apply x-router:link
to parent elements, such as <li>
, to enable more flexible markup structures:
<li x-router:link :class="{ active: $active }">
<a href="/about">About</a>
</li>
In this case:
x-router:link
will locate the nested<a>
element within the parent and use itshref
for routing.- The
$active
state will correctly reflect whether the nested link'shref
matches the current route.
Modifier replace
The replace
modifier changes the default navigation behavior of the router. When a link with this modifier is clicked,
it triggers $router.navigate(href, /* replace */ true)
.
Unlike the default behavior, which adds a new entry to the browser's history stack, this option replaces the current history entry with the new URL. As a result, the user's navigation history remains unchanged, and pressing the "Back" button will skip over the replaced entry.
<div x-router:hash>
...
<nav>
<a x-router:link href="/">Home</a>
<!-- The "replace" modifier ensures that clicking this link
replaces the current history entry instead of adding a new one -->
<a x-router:link.replace href="/about">About</a>
</nav>
</div>
Inline and External templates
Routes can be defined using either inline templates or external templates:
Inline templates:
The content of the route is directly written inside the <template>
element.
<template x-route="/">
Home page
</template>
External templates:
The content of the route is loaded from an external HTML file specified in the x-route:view
directive.
<template x-route="/about" x-route:view="/views/about.html"></template>
Magic functions
Magic $route
Indicates the current active route and contains the following properties:
<dl x-format>
<dt>Route pattern:</dt>
<dd>{{ $route.pattern }}</dd>
<dt>Route path:</dt>
<dd>{{ $route.path }}</dd>
<dt>Route params:</dt>
<dd>{{ $route.params.username }}</dd>
</dl>
Magic $active
Returns true
or false
, indicating whether a x-router:link
corresponds to the active route.
<nav>
<a x-router:link href="/" class="{ active: $active }">Home</a>
<a x-router:link href="/about" class="{ active: $active }">About</a>
</nav>
Applying active class to parent element
<li x-router:link :class="{ active: $active }">
<a href="/about">About</a>
</li>
The $active
state will correctly reflect whether the nested link's href
matches the current route.
Route templates
Routes can contain parameters enclosed in {}
that are dynamically bound when a route is matched.
Multiple parameters can be used within a route segment, but they must be separated by a static value. For example:
{controller}{action}
is invalid because {controller}
and {action}
are not separated by a static value.
Instead, valid routes should use static separators:
{controller}/{action}
article/{id}-{title}
Route parameters
Route parameters must have a name and can also include additional attributes.
Literal values and path separators (/
) must match exactly in the URL. Matching is case-insensitive and based
on the decoded URL representation.
Optional and Catch-All Parameters
- Optional parameters: Defined with
?
, meaning they are not required. Example:{id?}
- Catch-all parameters (
*
): Capture the remaining part of the URL. Example:blog/{slug*}
- Matches any URL starting with
blog/
. - The remaining value is assigned to the
slug
parameter.
- Matches any URL starting with
- Required catch-all parameters (
+
): Like*
, but at least one segment must match.
Parameter constraints
Route parameters can have constraints to validate and transform values. Constraints are added using :
after the parameter name.
If a constraint requires arguments, they are placed in parentheses.
Example:
blog/{id:int:min(100)}/{article:minlength(10)}
{id:int:min(100)}
ensuresid
is an integer and at least 100.{article:minlength(10)}
requiresarticle
to be at least 10 characters long.
Some constraints also transform parameters. For example, {id:int}
automatically converts id
to a number.
Default values
Route parameters can have default values, specified similarly to constraints but using =
or default
. Example:
{controller:=(home)}
is equivalent to:
{controller:default(home)}
If no value is provided in the URL, the default is used.
Example route patterns
Route template | URI | Description |
---|---|---|
hello | /hello | Matches only /hello . |
{controller}/{action}/{id?} | /product/list | Matches and sets controller=product , action=list . |
{controller}/{action}/{id?} | /product/list/1 | Matches and sets controller=product , action=list , id=1 . |
{controller:=(home)}/{action:=(index)}/{id?} | / | Matches and sets controller=home , action=index . |
{controller:=(home)}/{action:=(index)}/{id?} | /product | Matches and sets controller=product , action=index . |
blog/{slug*:=(start/with/new/blog)} | /blog | Matches and sets slug=[start,with,new,blog] . |
Route constraints
Constraint | Example | Description |
---|---|---|
int | {id:int} | Matches an integer. |
bool | {id:bool} | Matches true or false (case-insensitive). |
number | {speed:number} | Matches a valid number. |
alpha | {name:alpha} | Matches alphabetic characters (case-insensitive). |
min(value) | {age:min(25)} | Ensures the value is at least 25 . |
max(value) | {age:max(125)} | Ensures the value is no more than 125 . |
range(min,max) | {age:range(25,125)} | Ensures the value is between 25 and 125 . |
length(value) | {file:length(10)} | Requires exactly 10 characters. |
length(min,max) | {file:length(10,20)} | Requires between 10 and 20 characters. |
minlength(value) | {name:minlength(10)} | Requires at least 10 characters. |
maxlength(value) | {name:maxlength(50)} | Requires no more than 50 characters. |
regex(expr) | {id:regex(^\\d{3}-\\d{5}$)} | Must match a specific regex pattern. |
Source Code
You can find the source code for this plugin on GitHub:
https://github.com/rameel/ramstack.alpinegear.js/tree/main/src/plugins/router
Contributions
Bug reports and contributions are welcome.
License
This package is released as open source under the MIT License.
See the LICENSE file for more details.
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago