pinecone-router

The extendable client-side router for Alpine.js.

Usage no npm install needed!

<script type="module">
  import pineconeRouter from 'https://cdn.skypack.dev/pinecone-router';
</script>

README

GitHub tag (latest by date) npm bundle size Downloads from JSDelivr npm David Changelog Sponsor

Pinecone Router

The extendable client-side router for Alpine.js.

About

An easy to use but feature-packed client-side router for use with Alpine.js.

It can be used to:

  • Handle routes & process route variables.
  • Use magic helper $router helper to display elements dynamically etc. inside all Alpine.js Components.
  • Many more using middlewares!.

Features:

  • :smile: Easy and familiar syntax well integrated with Alpine.js.
    • The router is an Alpine component, handlers and settings are set in its data.
  • :link: Automatically dispatch relative links and handle them.
  • :hash: Hash routing.
  • :heavy_plus_sign: Extendable using tiny Middlewares! 🪜.
  • :sparkles: Magic $router helper to access current route, params, redirect, ect. from all alpine components!
  • :gear: Easily configurable through settings!
  • Typescript definitions

Demo: Pinecone example, (source code).

Installation

Check the CHANGELOG before updates.

CDN

Include the following <script> tag in the <head> of your document, before Alpine.js:

<script src="https://cdn.jsdelivr.net/npm/pinecone-router@1.x.x/dist/index.umd.js"></script>

ES6 Module:

import 'https://cdn.jsdelivr.net/npm/pinecone-router@1.x.x/dist/index.module.js';

NPM

npm install pinecone-router
// load pinecone router
import 'pinecone-router';
// then load alpine.js
import 'alpinejs';

Important: This must be added before loading Alpine.js.

Usage

Demo & Usage Example

Handle routes

  1. Create an Alpine.js component with an empty x-router attribute.
  2. Declare routes by creating a template tag with x-route and x-handler attributes.
<div x-data="router()" x-router>
    <!-- You can pass in a function name -->
    <template x-route="/" x-handler="home"></template>
    <!-- Or an anonymous/arrow function -->
    <template x-route="/home" x-handler="(ctx) => ctx.redirect('/')"></template>
    <!-- Or even an array of multiple function names/anonymous functions! -->
    <template x-route="/hello/:name" x-handler="[checkName, hello]"></template>
    <!-- 404 handler -->
    <template x-route="notfound" x-handler="notfound"></template>
</div>

<div id="app"></div>

Important: There can only be one router in the page!

The javascript:

can also be embedded inside x-data.

function router() {
    return {
        main(context) {
            document.querySelector('#app').innerHTML = `<h1>Home</h1>`;
        },
        checkName(context) {
            // if the name is "home" go to the home page.
            if (context.params.name.toLowerCase() == 'home') {
                // redirecting is done by returning the context.redirect method.
                return context.redirect('/');
            }
        },
        hello(context) {
            document.querySelector(
                '#app'
            ).innerHTML = `<h1>Hello, ${context.params.name}</h1>`;
        },
        notfound(context) {
            document.querySelector('#app').innerHTML = `<h1>Not Found</h1>`;
        },
    };
}

Context Object

The handler takes a context argument which consists of:

  • context.route (/path/:var) The route set with x-route.
  • context.path (/path/something) The path visited by the client.
  • context.params ({var: something}) Object that contains route parameters if any.
  • context.hash hash fragment without the #
  • context.query search query without the ?
  • context.redirect(path: string) function that allow you to redirect to another page.
    • Important: usage within x-handler: return context.redirect('/path');

Route matching

Parameters can be made optional by adding a ?, or turned into a wildcard match by adding * (zero or more characters) or + (one or more characters):

<template x-route="/b/:id" x-handler="..."></template>
<template x-route="/c/:remaining_path*" x-handler="..."></template>
<template x-route="/d/:remaining_path+" x-handler="..."></template>

Borrowed from Preact Router

Redirecting

It can be done many ways! here's how:

From an Alpine component:

Redirecting from the handler:

To redirect from inside a handler function return the context's redirect method:

handler(context) {
    ...
    return context.redirect(path)
}

Important: inside the handler you must return the context.redirect() function.

Middlewares

Pinecone Router is extendable through middlewares!

Official Middlewares

Create your own middlewares using this template!

Settings:

To override settings simply add a settings parameter to your router component's data.

Note: you don't have to specify all, just the ones you want to override.

function router() {
    return {
        // configuration
        settings: {
            /**
             * @type {boolean}
             * @summary enable hash routing
             */
            hash: false,
            /**
             * @type {string}
             * @summary The base path of the site, for example /blog
             * Note: do not use with using hash routing!
             */
            basePath: '/',
        }
        // handlers
        ...
    };
}

Bypass link handling

Adding a native attribute to a link will prevent Pinecone Router from handling it:

<a href="/foo" native>Foo</a>

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.PineconeRouter.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.params.name, $router.redirect(path), $router.hash, etc.

Loading bar

You can easily use nProgress.

Tip: if you're going to fetch views, you can use this middleware which provide loading events

Demo Source

Advanced

Show

Adding & Removing routes with Javascript

you can add routes & remove them anytime using Javascript.

Adding a route:

window.PineconeRouter.add(path, handlers);
  • path: string, the route's path.
  • handlers: array of functions, the handlers of the route.

Removing a route:

window.PineconeRouter.remove(path);
  • path: string, the path of the route you want to remove.

Navigating from Javascript:

To navigate to another page from javascript you can:

window.PineconeRouter.navigate(path);

Browser Support

Supports same versions supported by Alpine.js by default, including IE11.

dist/index.modern is provided if you want to only support modern browsers with es6+ support.

full list and_chr 89, and_ff 86, and_qq 10.4, and_uc 12.12, android 89, baidu 7.12, chrome 90, chrome 89, chrome 88, chrome 87, edge 90, edge 89, edge 88, firefox 87, firefox 86, firefox 78, ie 11, ios_saf 14.0-14.5, ios_saf 13.4-13.7, kaios 2.5, op_mini all, op_mob 62, opera 73, opera 72, safari 14, safari 13.1, samsung 13.0, samsung 12.0

Contributing:

Please refer to CONTRIBUTING.md

Sponsor ♥️

If you find this helpful and would like to support my work you can Donate with Coinbase

Any amount would be appreciated ^^

Credits

This library uses modified chunks of code from this tutorial & from page.js. The parts used are specified in source comments.

Acknowledgment

@KevinBatdorf for many ideas and early feedback!

Disclaimer: Not affiliated with the Alpine.js team, developed independently.

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.

Creative Commons License
Pinecone Router Logo by Rafik El Hadi Houari is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Code from Page.js is licensed under the MIT License. Copyright (c) 2012 TJ Holowaychuk tj@vision-media.ca

Code from Simple-javascript-router tutorial is licensed under the MIT License. Copyright (c) 2021 Vijit Ail (https://github.com/vijitail).

Route matching function from Preact Router is licensed under the MIT License. Copyright (c) 2015 Jason Miller