An expressJS-inspired lightweight, powerful client-side router for modern web applications. Works everywhere - ESM, CommonJS, and browsers (including wapka, wapkiz, xtgem)!
npm install slyrouter// Main Router (Express-style)
import SlyRouter from 'slyrouter';
const router = new SlyRouter();
// Core components
import { EnhancedComponent, StateManager } from 'slyrouter/core';
// Plugins
import { SEOPlugin } from 'slyrouter/plugins';
// Utilities
import { DOM, parseQueryString } from 'slyrouter/utils';// Main Router (Express-style)
const SlyRouter = require('slyrouter');
const router = new SlyRouter();
// Core components
const { EnhancedComponent, StateManager } = require('slyrouter/core');
// Plugins
const { SEOPlugin } = require('slyrouter/plugins');
// Utilities
const { DOM, parseQueryString } = require('slyrouter/utils');<script src="https://unpkg.com/slyrouter"></script>
<script>
// Main Router (Express-style)
const router = new SlyRouter();
// Core components are available on the main object
const { EnhancedComponent, StateManager } = SlyRouter;
</script>SlyRouter follows an Express.js-inspired architecture:
- Main Export:
import SlyRouter from 'slyrouter'- The main Router class - Core Components:
import { EnhancedComponent } from 'slyrouter/core'- Building blocks - Plugins:
import { SEOPlugin } from 'slyrouter/plugins'- Extended functionality - Utilities:
import { DOM } from 'slyrouter/utils'- Helper functions
This modular approach allows for optimal tree-shaking and lets you import only what you need.
The main router class that handles navigation and route management.
const router = new SlyRouter(options);Options:
root(string): Base path for all routes (default:'/')mode(string):'history'or'hash'(default:'history')linkSelector(string): Selector for links to handle (default:'[data-sly-link]')formSelector(string): Selector for forms to handle (default:'[data-sly-form]')container(string): DOM element to render components (default:'#app')loadingTemplate(string): HTML to show during loadingerrorTemplate(string): HTML to show on errorsstate(object): StateManager optionsauth(object): AuthService options
Examples:
// ESM
import SlyRouter from 'slyrouter';
const router = new SlyRouter({ container: '#app' });
// CommonJS
const SlyRouter = require('slyrouter');
const router = new SlyRouter({ container: '#app' });
// UMD (browser)
const router = new SlyRouter({ container: '#app' });Add a route to the router.
router.addRoute('/user/:id', UserComponent, {
name: 'user-profile',
title: 'User Profile',
meta: { requiresAuth: true },
guards: [authGuard]
});Parameters:
path(string): Route path with optional parameters (/user/:id)component(Class): Component class to renderoptions(object): Route configuration
Route Options:
name(string): Route name for programmatic navigationtitle(string|function): Page title or function that returns titlemeta(object): Route metadata for guards and SEOguards(array): Array of guard functions
Navigate to a route.
// Basic navigation
router.navigate('/about');
// With query parameters
router.navigate('/search', { query: { q: 'javascript' } });
// Replace current history entry
router.navigate('/login', { replace: true });Options:
query(object): Query parameters to includereplace(boolean): Replace history entry instead of pushing
Browser history navigation.
router.back(); // Go back
router.forward(); // Go forward
router.go(-2); // Go back 2 pagesAdd global middleware.
router.addMiddleware(async (context, router) => {
console.log('Navigating to:', context.route.path);
});Use a plugin.
router.use(AnalyticsPlugin, { trackingId: 'UA-XXXXX' });Get current route information.
const currentRoute = router.getCurrentRoute();
const params = router.getParams(); // Route parameters
const query = router.getQuery(); // URL query parametersBase class for creating components with lifecycle methods and state management.
// ESM
import { EnhancedComponent } from 'slyrouter/core';
class HomePage extends EnhancedComponent {
constructor() {
super();
this.state = { count: 0 };
}
render() {
return `
<div>
<h1>Count: ${this.state.count}</h1>
<button data-sly-on="click:incrementCount">Increment</button>
</div>
`;
}
incrementCount() {
this.setState({ count: this.state.count + 1 });
}
}
// CommonJS
const { EnhancedComponent } = require('slyrouter/core');
class HomePage extends EnhancedComponent { /* ... */ }
// UMD - Note: In UMD, core components are on main object
class HomePage extends SlyRouter.EnhancedComponent { /* ... */ }Called before component enters.
async beforeEnter(context) {
console.log('Entering route with params:', context.params);
await this.loadData(context.params.id);
}Called after component enters.
afterEnter(context) {
this.setupAnalytics();
}Called before component leaves.
beforeLeave() {
this.cleanupEvents();
}Called after component leaves.
afterLeave() {
console.log('Component left');
}Called when component mounts to DOM.
async mount(container, context) {
await this.beforeMount();
this.element = container;
this.update();
await this.afterMount();
}Called when component unmounts from DOM.
async unmount() {
await this.beforeUnmount();
this.eventManager.unbindAll();
await this.afterUnmount();
}Update component state and re-render.
this.setState({ count: 42, message: 'Hello' });Subscribe to global state changes.
this.subscribeToState('user', (user) => {
this.setState({ currentUser: user });
});Use data-sly-on attributes for automatic event binding:
render() {
return `
<button data-sly-on="click:handleClick; mouseenter:handleHover">
Interactive Button
</button>
`;
}
handleClick(event, element) {
console.log('Button clicked!');
}
handleHover(event, element) {
element.style.backgroundColor = 'yellow';
}Navigate from within component.
this.navigate('/dashboard');
this.navigate('/search', { query: { q: 'test' } });DOM query helpers.
const button = this.$('button.submit');
const allInputs = this.$$('input[type="text"]');Create DOM elements.
const div = this.createElement('div', { class: 'card' }, 'Hello World');Global state management with persistence.
// ESM
import { StateManager } from 'slyrouter/core';
// CommonJS
const { StateManager } = require('slyrouter/core');
// UMD
const stateManager = new SlyRouter.StateManager();Set state value.
stateManager.set('user', userData, true); // persist to localStorage
stateManager.set('theme', 'dark'); // volatileGet state value.
const user = stateManager.get('user');Subscribe to state changes.
const unsubscribe = stateManager.subscribe('user', (user) => {
console.log('User changed:', user);
});
// Later...
unsubscribe(); // Stop listeningClear all state.
stateManager.clear();Authentication service with token management.
// ESM
import { AuthService } from 'slyrouter/core';
// CommonJS
const { AuthService } = require('slyrouter/core');
// UMD
const authService = new SlyRouter.AuthService();Authenticate user.
try {
const user = await authService.login({
email: 'user@example.com',
password: 'password'
});
console.log('Logged in:', user);
} catch (error) {
console.error('Login failed:', error);
}Logout user.
await authService.logout();Check if user is authenticated.
const authenticated = await authService.isAuthenticated();
if (authenticated) {
// User is logged in
}Check if user has specific role.
const isAdmin = await authService.hasRole('admin');Get current user and token.
const user = authService.getUser();
const token = authService.getToken();Parse URL query string.
import { parseQueryString } from 'slyrouter/utils';
const query = parseQueryString('?search=test&page=1');
// { search: 'test', page: '1' }Build URL with query parameters.
import { buildURL } from 'slyrouter/utils';
const url = buildURL('/search', { q: 'test', page: 2 });
// '/search?q=test&page=2'Rate limiting functions.
import { debounce } from 'slyrouter/utils';
const search = debounce((query) => {
// API call
}, 300);DOM manipulation helpers.
import { DOM } from 'slyrouter/utils';
DOM.updateMetaTag('description', 'Page description');
const element = DOM.createElement('div', { class: 'card' }, 'Content');Event management with automatic cleanup.
import { EventManager } from 'slyrouter/utils';
const events = new EventManager();
events.bind(button, 'click', () => console.log('clicked'));
events.unbindAll(); // Cleanupimport SlyRouter from 'slyrouter';
import { EnhancedComponent } from 'slyrouter/core';
class App extends EnhancedComponent {
constructor() {
super();
this.state = { user: null };
}
async beforeEnter(context) {
this.subscribeToState('user', (user) => {
this.setState({ user });
});
}
render() {
return `
<nav>
<a href="/" data-sly-link>Home</a>
<a href="/about" data-sly-link>About</a>
${this.state.user ? `
<a href="/dashboard" data-sly-link>Dashboard</a>
` : ''}
</nav>
<div id="app-content"></div>
`;
}
}
const router = new SlyRouter({ container: '#app-content' });
router.addRoute('/', HomeComponent, { title: 'Home' });
router.addRoute('/about', AboutComponent, { title: 'About' });
router.addRoute('/dashboard', DashboardComponent, {
meta: { requiresAuth: true },
guards: [authGuard]
});
// Start the app
router.navigate('/');const SlyRouter = require('slyrouter');
const { EnhancedComponent } = require('slyrouter/core');
class HomeComponent extends EnhancedComponent {
render() {
return '<h1>Welcome Home!</h1>';
}
}
const router = new SlyRouter();
router.addRoute('/', HomeComponent);
router.navigate('/');<!DOCTYPE html>
<html>
<head>
<title>SlyRouter Example</title>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/slyrouter"></script>
<script>
// Main Router (Express-style)
const router = new SlyRouter({ container: '#app' });
// Core components are available on SlyRouter object
class HomePage extends SlyRouter.EnhancedComponent {
render() {
return '<h1>Hello from SlyRouter!</h1>';
}
}
router.addRoute('/', HomePage);
router.navigate('/');
</script>
</body>
</html>Create route guards for authentication and authorization:
// Authentication guard
const authGuard = async ({ to, from, router }) => {
const isAuthenticated = await router.authService.isAuthenticated();
if (!isAuthenticated) {
return '/login'; // Redirect to login
}
return true; // Allow navigation
};
// Role-based guard
const adminGuard = async ({ to, from, router }) => {
const isAdmin = await router.authService.hasRole('admin');
if (!isAdmin) {
return '/unauthorized';
}
return true;
};
// Usage
router.addRoute('/admin', AdminComponent, {
guards: [authGuard, adminGuard]
});Handle forms without page reloads:
<form data-sly-form action="/api/contact" method="POST">
<input type="text" name="name" required>
<input type="email" name="email" required>
<button type="submit">Send</button>
</form>In your component:
async handleFormResponse(result, form) {
if (result.success) {
this.showSuccess('Message sent!');
form.reset();
} else {
this.showError('Failed to send message');
}
}
async handleFormError(error, form) {
this.showError('Network error: ' + error.message);
}Add global middleware for cross-cutting concerns:
// Logging middleware
router.addMiddleware(async (context, router) => {
console.log(`Route change: ${context.from?.path} -> ${context.to.path}`);
});
// Authentication middleware
router.addMiddleware(async (context, router) => {
if (context.to.meta?.requiresAuth) {
const authenticated = await router.authService.isAuthenticated();
if (!authenticated) {
router.navigate('/login');
return false; // Stop navigation
}
}
});Extend SlyRouter with plugins:
// Using built-in plugins
import { SEOPlugin, AnalyticsPlugin } from 'slyrouter/plugins';
router.use(SEOPlugin, {
defaultTitle: 'My App',
defaultDescription: 'A modern web application'
});
router.use(AnalyticsPlugin, {
trackingId: 'UA-XXXXX-Y'
});
// Creating custom plugins
function MyPlugin(router, options) {
router.addMiddleware(async (context) => {
// Plugin logic here
});
}
router.use(MyPlugin, { customOption: true });Persist state across page reloads:
import { StateManager } from 'slyrouter/core';
const stateManager = new StateManager({
persistenceKey: 'myapp_state',
persistKeys: ['user', 'settings', 'theme'] // Only these keys persist
});
// This will be saved to localStorage
stateManager.set('user', userData, true);
// This will not persist
stateManager.set('temporaryData', data);- Modern browsers (Chrome, Firefox, Safari, Edge)
- IE11+ (with polyfills)
- Node.js 14+
- Mobile browsers
- Legacy platforms (wapka, wapkiz, xtgem)
Before:
import { Router } from 'slyrouter';After:
import SlyRouter from 'slyrouter';
// or
import { Router } from 'slyrouter/core';new SlyRouter(options)addRoute(path, component, options)navigate(path, options)back(),forward(),go(delta)addMiddleware(middleware)use(plugin, options)getCurrentRoute(),getParams(),getQuery()
- Lifecycle:
beforeEnter,afterEnter,beforeLeave,afterLeave - State:
setState,subscribeToState - Navigation:
navigate - Utilities:
$,$$,createElement
set(key, value, persist)get(key)subscribe(key, callback)clear()
login(credentials)logout()isAuthenticated()hasRole(role)getUser(),getToken()
We welcome contributions! Please see our Contributing Guide for details.
MIT © Quabynah Davis