Alpine Router
The simple client-side router for Alpine.js.
About
An easy to use but feature-packed client-router for use with Alpine.js. It can be used to handle routes manually, render specific views, and automatically render server-rendered pages-with preload!
Features:
- Easy and familiar syntax well integrated with Alpine.js.
- Automatically dispatch relative links and handle them (optional).
- Hash routing!
- Display server rendered pages: automatically load server-rendered pages with preloading (like turbolinks, optional)
- Render views: manually set the view for each route and have it rendered! (optional)
- Easily tweakable through many Settings!
-
Magic helper
$router
to access current route, props, redirect, ect. from alpine components!
Installation
It works but not ready for production yet, needs reviewing and more testing, help welcome!
CDN
Include the following <script>
tag in the <head>
of your document:
<script src="https://cdn.jsdelivr.net/npm/alpinejs-router@0.0.9/dist/complete.umd.js"></script>
ES6 Module:
import 'https://cdn.jsdelivr.net/npm/alpinejs-router@0.0.9/dist/complete.umd.js';
NPM
npm install alpinejs-router
import 'alpinejs-router';
import 'alpinejs';
Important: This must be added before loading Alpine.js.
Note: Smaller, feature-specific builds will be available soon ^^
Usage
Handle routes
- Create an Alpine component with the
x-router
attribute. - Declare routes by creating a template tag with
x-route
andx-handler
attributes. - The
x-handler
must be a method of the router component.
<div x-data="handle()" x-router>
<template x-route="/hello/:name" x-handler="hello"></template>
<template x-route="/" x-handler="main"></template>
<template x-route="notfound" x-handler="notfound"></template>
</div>
Important: There can only be one router in the page!
function handle() {
return {
main() {
console.log('main');
},
hello(context) {
console.log('hello,', context.props.name);
},
notfound(context) {
console.error(context.path + ' is not found');
},
};
}
Context Object
The handler takes context
which consist of:
- context.route (/path/:var)
- context.path (/path/something)
- context.props ({var: something})
- context.hash (hash fragment without the #)
- context.query (search query without the ?)
- context.go(path: string) function that allow you to redirect to another page.
-
-
Important usage witihn x-handler:
return context.go('/path');
-
Important usage witihn x-handler:
Hash routing
You may use hash routing by simply adding x-hash
attribute to the router component:
<div x-data="handle()" x-router x-hash>...</div>
Page rendering
You can use Alpine Router to render server generated pages without reloads!
<div x-data x-router x-render></div>
By default this will fetch the whole page and replaces the <body>
content.
To use antoher element instead, set its selector: x-render="#content"
.
Handling routes while using x-render
You can also handle routes while all pages render normally!
<div x-data="handle()" x-router x-render>
<template x-route="/hello/:name" x-handler="hello"></template>
</div>
Note: The routes will be handled before the page is rendered.
Notfound and specifying routes
By default, 404 pages are left to the server to handle. However, if you'd like to specify the routes allowed, you can do it like this:
<div x-data="handle()" x-router x-render>
<template x-route="/"></template>
<template x-route="/hello/:name"></template>
<template x-route="notfound" x-handler="notfound"></template>
</div>
As you see, the handler is optional on routes as the page will be rendered regardless, but you can add it if you need it.
Notes:
By default routes not found will be pushed to history.
That can be prevented by setting AlpineRouter.settings.pushNotfoundToHistory = false
.
Views rendering
Unlike page rendering, you get to specify the view for each route.
- Routes can share views.
- View are simply html files.
- Can specify selector as well.
<div x-data x-router x-views="#content">
<template x-route="/" x-view="/home.html"></template>
<template x-route="/hello/:name" x-view="/hello.html"></template>
<template x-route="notfound" x-view="/404.html"></template>
</div>
Notes: :
- A view is required for each route.
- You can cache views if they're static and not dynamically generated by adding
x-static
to the router: -
<div x-data x-router x-views x-static>
- You can set the selector by specifying it
x-views
. leaving it empty will default to '#content'. - You can also handle routes while using views
-
- Note: The routes will be handled before the page is rendered.
Authorization
If you'd like to make checks before actually displaying a view/page, using authentication/authorization etc, you can make your checks in the handler. Then within the handler, if you need to redirect the user to another page simply return context.go('/another/page')
this way it'll prevent the views from rendering and go to the other page directly.
Example:
The route you'd like to authorize: In this example the user will only be allowed to edit their own profile
<div x-data="router()" x-router x-views>
...
<template
x-route="/profile/:username/edit"
x-handler="editprofile"
x-view="/editprofile.html"
></template>
<template
x-route="/unauthorized"
x-view="/unauthorized.html"
></template>
...
The handler: (auth
is a placeholder name, replace it with your own auth provider methods)
editprofile(context) {
if (context.props.username != auth.username) {
return context.go('/unauthorized');
}
}
This works for when using both
x-views
orx-render
Tip! To access the current context (props etc) from within the views, use the $router Magic Helper or
window.AlpineRouter.currentContext
.
Important: Make sure the view don't have an Alpine Router component in them! Keep the router component outside of the specified selector.
Can't use
body
as the selector to avoid that issue.
Redirecting
You can navigate to another page by calling AlpineRouter.navigate(path)
with path being the path you want to navigate to.
Settings:
There are a few settings you may tweak for your liking.
To access or set them from javascript, use AlpineRouter.settings.name = value
.
with name being the name of the settings as seen bellow:
name | default | description |
---|---|---|
interceptLinks | true | whether or not to intercept links |
pushNotfoundToHistory | true | whether or not paths that are not found should be pushed to history |
render.preload | true | whether to preload pages on mouse over links |
render.preloadtime | 200 | time to wait to preload pages on mouse over links |
views.static | false | views are not dynamically generated, this will cache views for later use. |
Base path: is set with the x-base
attribute.
Trailing slash: x-slash="add"
or empty to force adding trailing slash or x-slash="remove"
to force removing them.
Events:
Alpine Router dispatch these events:
name | recipient | description |
---|---|---|
routerloaded | window | when the router and routes are initialized |
loadstart | window | when the page start loading |
loadend | window | when the page loading ends |
Global Context
You can access current path's context from alpine components use $router magic helper or from anywhere in your javascript by accessing window.AlpineRouter.currentContext
.
Magic Helper
To make it easier to access the current context from anywhere, you can use the $router
magic helper:
Usage:
Refer to global context.
$router.props.name
, $router.go(path)
, $router.hash
, etc.
Contributing:
Please refer to CONTRIBUTING.md
Credits
This library uses modified chunks of code from this tutorial & from page.js. The parts used are speficied in source comments.
Acknowledgment
@KevinBatdorf for the page rendering idea and early feedback!
Versioning
This projects follow the Semantic Versioning guidelines.
License
Copyright (c) 2021 Rafik El Hadi Houari and contributors
Licensed under the MIT license, see LICENSE.md for details.
Code from Page.js is licenced under the MIT License. Copyright (c) 2012 TJ Holowaychuk tj@vision-media.ca
Code from Simple-javascript-router tutorial is licenced under the MIT License. Copyright (c) 2021 Vijit Ail (https://github.com/vijitail).