node package manager


npm version Build Status Coverage Status Code Climate devDependency Status Unicorn

Vanilla UI Router

Simple vanilla JavaScript router to be used inside a single page app to add routing capabilities.

The router comes with zero dependencies and can be used with any other libraries. It's based on the hashchange-Event.


$ npm install --save vanilla-ui-router

As UMD module this runs everywhere (ES6 modules, CommonJS, AMD and with good ol’ globals).


Let's assume your initial markup has the following structure:

<!DOCTYPE html>
    <link rel="stylesheet" type="text/css" href="styles.min.css" />
    <!-- Entry point, dynamic content is rendered into this DOM element -->
    <main id="app"></main>
    <!-- Bundle where your JavaScript logic lives, even the router configuration -->
    <script src="bundle.js"></script> 

Then you could configure the router with the following JavaScript:

import {createRouter} from 'vanilla-ui-router';
// Initialize the router with the dynamic DOM entry point 
const router = createRouter(document.getElementById('app'));
    // Start route: The server side URL without a hash 
    .addRoute('', () => {
            Use navigateTo(…) to make dynamic route changes, i.e. to redirect to another route
    .addRoute('home', (domEntryPoint) => {
        domEntryPoint.textContent = 'I am the home route.';
    .addRoute('about/:aboutId/:editable', (domEntryPoint, routeParams) => {
        console.log('I am the about route.');
            routeParams are extracted from the URL and are casted to the correct type
        console.log(routeParams); // => { aboutId: 42, editable:false } 
        If routes get more complex, e.g. you need to render a template URL,
        pass a configuration object as second parameter (instead of the function)
    .addRoute('route-with-template-url', {
        templateUrl: 'path/to/template.html' // is loaded and gets rendered 
    .addRoute('route-with-template-string/:id', {
        templateString: '<p>Lorem ipsum dolor.</p>',
        routeHandler: (domEntryPoint, routeParams) => {
            It's called just after rendering the template, so you can add route-specific logic.
            But only if needed!
        You can also define a templateId, i.e. if you have a template-script inside
        your markup like:
        <script type="text/template" id="template42">
                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolor, tenetur?
    .addRoute('route-with-template-id/:id', {
        templateId: 'template42'
    .addRoute('route-with-dispose', {
        routeHandler: () => {},
        dispose: () => {
            // Is called before navigating to another route to do some cleanup if needed. 
    .addRoute('inject-custom-data', {
        routeHandler: (domEntryPoint, routeParams, {customData}) => {
            // It's passed as the last parameter of the route, for instance to pass a redux store. 
    }, { customData: 'moep'}) // if you need to pass custom data to your routes 
    .otherwise(() => {
        // If no route configuration matches, the otherwise route is invoked. 
        console.log('I am the otherwise route');


Please be aware of the licenses of the components we use in this project. Everything else that has been developed by the contributions to this project is under MIT License.