npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

io-reactive-router

v1.0.8

Published

Reactive router for IO-Reactive framework - powerful, lightweight, and fully typed routing solution for modern SPA applications

Readme

io-reactive-router 🚀

Reactive router for IO-Reactive framework – a powerful, lightweight, and fully typed router for building modern SPA applications.

npm version TypeScript License: MIT

✨ Features

  • 🚀 Lightweight – Minimal bundle size
  • Fast – Optimized for maximum performance
  • 🔄 Reactive – Automatic updates on state change
  • 📱 SPA Ready – Full single-page application support
  • 🎨 Animations – Built-in page transitions
  • 🔗 Smart Links – Automatic active state management
  • 📊 Nested Routes – Layout and nested routing support
  • 🔍 Query Parameters – Work with URL parameters
  • 🍞 Breadcrumbs – Automatic breadcrumbs generation
  • 🛡️ Guards – Route protection and middleware
  • 🗂️ Caching – Optimized page caching
  • 🔍 SEO – Page metadata management

📦 Installation

# First, install the core framework
npm install io-reactive

# Then install the router
npm install io-reactive-router

🚀 Quick Start

import { IO } from 'io-reactive';
import { IORouter, Route } from 'io-reactive-router';

// Define routes
const routes = [
    {
        path: '/',
        template: () => new IO('div', { text: 'Home Page' }),
        name: 'home',
    },
    {
        path: '/about',
        template: () => new IO('div', { text: 'About' }),
        name: 'about',
    },
    {
        path: '/users/[id]',
        template: (params) => new IO('div', { text: `User ID: ${params.id}` }),
        name: 'user',
    },
];

// Initialize the router
const router = new IORouter({
    routes: Promise.resolve(routes),
    domain: 'https://myapp.com',
    root: document.getElementById('app'),
});

// Start the router
router.init();

📚 Documentation

🛣️ Defining Routes

Simple Routes

const routes = [
    {
        path: '/',
        template: () => new IO('div', { text: 'Home' }),
        name: 'home',
    },
    {
        path: '/products',
        template: () => new IO('div', { text: 'Products' }),
        name: 'products',
    },
];

Dynamic Routes

const routes = [
    {
        path: '/users/[id]',
        template: (params) =>
            new IO('div', {
                text: `User: ${params.id}`,
            }),
        name: 'user',
    },
    {
        path: '/posts/[slug]/comments/[commentId]',
        template: (params) =>
            new IO('div', {
                text: `Post: ${params.slug}, Comment: ${params.commentId}`,
            }),
        name: 'comment',
    },
];

Nested Routes

const routes = [
    {
        path: '/admin',
        template: () => new IO('div', { text: 'Admin Panel' }),
        name: 'admin',
        children: [
            {
                path: '/users',
                template: () => new IO('div', { text: 'User Management' }),
                name: 'admin-users',
            },
            {
                path: '/settings',
                template: () => new IO('div', { text: 'Settings' }),
                name: 'admin-settings',
            },
        ],
    },
];

Protected Routes

const routes = [
    {
        path: '/profile',
        template: () => new IO('div', { text: 'Profile' }),
        name: 'profile',
        params: {
            isPrivate: true, // Requires authentication
            redirectTo: '/login', // Redirect for unauthenticated
        },
    },
];

🎛️ Router Configuration

const router = new IORouter({
    routes: Promise.resolve(routes),
    domain: 'https://myapp.com',
    root: document.getElementById('app'),

    // Auth function
    auth: async () => {
        const token = localStorage.getItem('authToken');
        return !!token;
    },

    // Layout for all pages
    layout: (components) =>
        new IO('div', {
            classList: ['app-layout'],
            components: [
                () => new IO('header', { text: 'Header' }),
                ...components,
                () => new IO('footer', { text: 'Footer' }),
            ],
        }),

    // Middleware for each route
    middleware: ({ domain, routes, href, state }) => {
        console.log('Navigating to:', href);
        // Analytics, logging, etc.
    },
});

🔗 Navigation

Programmatic Navigation

// Simple navigation
router.navigate('/about');

// Navigation with query params
router.navigate('/products', {
    query: { category: 'electronics', page: '1' },
});

// Navigation with state
router.navigate('/profile', {
    state: { fromPage: 'dashboard' },
    replace: true, // Replace current history entry
});

History Navigation

// Back
router.history('back');

// Forward
router.history('next');

🧩 Components

IOActiveLink – Smart Links

Component for creating links with automatic active state management:

import { IOActiveLink } from 'io-reactive-router';

const navLink = new IOActiveLink('/products', {
    text: 'Products',
    activeClass: 'active',
    exactMatch: true, // Exact path match
    prefetch: true, // Prefetch page
    attributes: {
        'data-analytics': 'nav-products',
    },
});

IOActiveLink options:

  • activeClass – CSS class for active state
  • exactMatch – Exact path match (default: false)
  • prefetch – Prefetch page on hover
  • attributes – Additional link attributes

IOLayout – Layout Component

Component for creating layouts with nested route support:

import { IOLayout } from 'io-reactive-router';

const layout = new IOLayout({
    fallback: () =>
        new IO('div', {
            text: 'Loading...',
            classList: ['loading'],
        }),
    depth: 1, // Nesting depth
    animations: {
        enter: 'fadeIn',
        exit: 'fadeOut',
    },
});

Factory methods:

// Simple layout
const simpleLayout = createIOLayout({
    header: () => new IO('header', { text: 'Header' }),
    footer: () => new IO('footer', { text: 'Footer' }),
});

// Layout with animations
const animatedLayout = createAnimatedIOLayout({
    animation: 'slide',
    duration: 300,
});

🔧 Modules

📊 Query Parameters

Work with URL parameters:

// Get parameters
const queryParams = router.queryParams();
console.log(queryParams()); // { page: '1', filter: 'active' }

// Programmatic control
import { QueryParamsModule } from 'io-reactive-router';

const queryModule = new QueryParamsModule('https://myapp.com');

// Set parameters
queryModule.set({ page: '2', sort: 'name' });

// Get parameters
const params = queryModule.get(); // { page: '2', sort: 'name' }

// Remove parameter
queryModule.remove('sort');

🍞 Breadcrumbs

Automatic breadcrumbs generation:

// Get breadcrumbs
const breadcrumbs = router.breadcrumbs();

// Breadcrumb structure
interface Breadcrumb {
    path: string;
    label: string;
    isActive: boolean;
}

// Use in a component
const breadcrumbsComponent = new IO('nav', {
    components: breadcrumbs.map(
        (crumb) => () =>
            new IOActiveLink(crumb.path, {
                text: crumb.label,
                activeClass: 'breadcrumb-active',
            })
    ),
});

🎨 Route Animations

Simple Animations

import { RouteAnimationModule, FadeAnimation } from 'io-reactive-router';

const animationModule = new RouteAnimationModule(document.getElementById('app'));

// Set animation strategy
animationModule.setAnimationStrategy(
    new FadeAnimation({
        duration: 300,
        easing: 'ease-in-out',
    })
);

Layout Animations

import { LayoutAnimationModule } from 'io-reactive-router';

const layoutModule = new LayoutAnimationModule(document.getElementById('app'));

// Set animation for a specific level
router.setLayoutAnimationStrategy(
    1,
    new FadeAnimation({
        duration: 500,
    })
);

Custom Animations

import { AnimationStrategy } from 'io-reactive-router';

class CustomSlideAnimation implements AnimationStrategy {
    async enter(element: HTMLElement): Promise<void> {
        element.style.transform = 'translateX(100%)';
        element.style.transition = 'transform 0.3s ease';

        await new Promise((resolve) => {
            setTimeout(() => {
                element.style.transform = 'translateX(0)';
                setTimeout(resolve, 300);
            }, 10);
        });
    }

    async exit(element: HTMLElement): Promise<void> {
        element.style.transform = 'translateX(-100%)';
        await new Promise((resolve) => setTimeout(resolve, 300));
    }
}

🛡️ Guards

Route protection with guards:

import { RouteGuard } from 'io-reactive-router';

// Create a guard
const authGuard: RouteGuard = async (to, from, next) => {
    const isAuthenticated = await checkAuth();

    if (isAuthenticated) {
        next(); // Allow navigation
    } else {
        next('/login'); // Redirect to login
    }
};

// Add guard for a specific route
router.addRouteGuard('/profile', authGuard);

// Global guard for all routes
router.addGlobalGuard(authGuard);

🗂️ Caching

Performance optimization via caching:

// Caching is enabled by default
// Prefetch a page
await router.prefetch('/products');

// Clear cache
router.clearCache();

🔍 SEO & Metadata

Manage page metadata:

// Set metadata for a route
router.setRouteMetadata('/products', {
    title: 'Our Products - Online Store',
    description: 'Wide selection of quality products at affordable prices',
    keywords: ['products', 'online store', 'shopping'],
    openGraph: {
        title: 'Our Products',
        description: 'Best products for you',
        image: 'https://myapp.com/og-products.jpg',
        url: 'https://myapp.com/products',
    },
    twitter: {
        card: 'summary_large_image',
        title: 'Our Products',
        description: 'Best products for you',
    },
});

// Get current metadata
const currentMeta = router.getCurrentMetadata();

🎯 Advanced Features

Lazy Loading Routes

const routes = [
    {
        path: '/dashboard',
        template: () => import('./pages/Dashboard').then((m) => m.Dashboard),
        name: 'dashboard',
    },
];

Multiple Layouts

const adminLayout = (components) =>
    new IO('div', {
        classList: ['admin-layout'],
        components: [() => new IO('aside', { text: 'Admin Menu' }), () => new IO('main', { components })],
    });

const publicLayout = (components) =>
    new IO('div', {
        classList: ['public-layout'],
        components: [() => new IO('header', { text: 'Public Header' }), () => new IO('main', { components })],
    });

// Using different layouts
const routes = [
    {
        path: '/admin',
        template: () => new IO('div', { text: 'Admin' }),
        layout: adminLayout,
    },
    {
        path: '/',
        template: () => new IO('div', { text: 'Home' }),
        layout: publicLayout,
    },
];

Navigation State

// Get navigation state
const navState = router.getNavigationState();

// Pass state during navigation
router.navigate('/profile', {
    state: {
        previousPage: 'dashboard',
        userAction: 'edit',
    },
});

📋 API Reference

IORouter

Constructor

new IORouter(config: iIORouter)

Methods

  • init() – Initialize the router
  • navigate(path, options?) – Programmatic navigation
  • history(direction) – History navigation
  • breadcrumbs() – Get breadcrumbs
  • queryParams() – Work with query parameters
  • addRouteGuard(path, guard) – Add guard for a route
  • addGlobalGuard(guard) – Add global guard
  • setRouteMetadata(path, metadata) – Set metadata
  • getCurrentMetadata() – Get current metadata
  • getCurrentRoute() – Get current route
  • getNavigationState() – Get navigation state
  • prefetch(path) – Prefetch page

Route

Interface

interface iRoute {
    path: string;
    template: routeIO;
    name: string;
    params?: {
        isPrivate?: boolean;
        redirectTo?: string;
    };
    children?: iRoute[];
}

🔧 TypeScript Support

Full TypeScript support with strict typing:

import type {
    iIORouter,
    iRoute,
    iRoutes,
    path,
    routeIO,
    AnimationStrategy,
    RouteGuard,
    RouteMetadata,
} from 'io-reactive-router';

// Typed routes
const routes: iRoute[] = [
    {
        path: '/',
        template: () => new IO('div', { text: 'Home' }),
        name: 'home',
    },
];

// Typed config
const config: iIORouter = {
    routes: Promise.resolve(routes),
    domain: 'https://example.com',
};

🎨 Styling

CSS Classes for Animations

/* Fade animations */
.route-enter {
    opacity: 0;
}

.route-enter-active {
    opacity: 1;
    transition: opacity 0.3s ease;
}

.route-exit {
    opacity: 1;
}

.route-exit-active {
    opacity: 0;
    transition: opacity 0.3s ease;
}

/* Slide animations */
.route-slide-enter {
    transform: translateX(100%);
}

.route-slide-enter-active {
    transform: translateX(0);
    transition: transform 0.3s ease;
}

.route-slide-exit-active {
    transform: translateX(-100%);
    transition: transform 0.3s ease;
}

Active Links

.nav-link {
    color: #666;
    text-decoration: none;
    transition: color 0.2s ease;
}

.nav-link.active {
    color: #007bff;
    font-weight: bold;
}

.nav-link:hover {
    color: #0056b3;
}

🚀 Performance

Optimizations

  1. Lazy Loading – Load components on demand
  2. Prefetching – Prefetch critical pages
  3. Caching – Cache rendered components
  4. Tree Shaking – Remove unused code

Recommendations

  • Use prefetch for critical pages
  • Apply lazy loading for rarely used routes
  • Optimize animations for better performance
  • Use memoization in heavy components

🔍 Debugging

Enable Debug Mode

// In development mode
if (process.env.NODE_ENV === 'development') {
    // Log navigation
    window.addEventListener('io:navigate', (event) => {
        console.log('Navigation:', event.detail);
    });
}

Developer Tools

// Access router from browser console
declare global {
    interface Window {
        ioRouter: IORouter;
    }
}

// In browser console
window.ioRouter.navigate('/debug');
console.log(window.ioRouter.getCurrentRoute());

📈 Migration

From React Router

// React Router
<Route path="/users/:id" component={UserPage} />

// IO-Router
{
    path: '/users/[id]',
    template: (params) => new UserPage(params.id),
    name: 'user'
}

From Vue Router

// Vue Router
{ path: '/user/:id', component: User }

// IO-Router
{
    path: '/users/[id]',
    template: (params) => new User(params.id),
    name: 'user'
}

🤝 Contributing

We welcome contributions! Please see the contributing guide.

📄 License

MIT © Alexey Koh

🔗 Links


Build modern web apps with io-reactive-router! 🚀