io-reactive-router
TypeScript icon, indicating that this package has built-in type declarations

1.0.8 • Public • Published

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! 🚀

Package Sidebar

Install

npm i io-reactive-router

Weekly Downloads

0

Version

1.0.8

License

MIT

Unpacked Size

120 kB

Total Files

5

Last publish

Collaborators

  • alexeykoh