@profullstack/spa-router
v1.14.0
Published
Simple SPA router with smooth transitions, component loading, and Shadow DOM support
Downloads
102
Readme
@profullstack/spa-router
A lightweight, feature-rich SPA router with smooth transitions and Shadow DOM support.
Features
- Simple API for defining routes
- Support for dynamic routes with parameters
- Smooth page transitions with customizable effects
- Shadow DOM support for intercepting clicks
- History API integration
- Route guards and middleware
- Lazy loading support
- Web component integration
- Enhanced renderer with component preservation and translation support
Installation
npm install @profullstack/spa-routerOr with yarn:
yarn add @profullstack/spa-routerClient-Side Installation
For browser environments, you can use CDNs to import the router directly without npm:
Using ESM.sh (Recommended for better source maps)
<script type="module">
import { Router, transitions, renderer } from 'https://esm.sh/@profullstack/[email protected]';
// Initialize router
const router = new Router({
rootElement: '#app',
transition: transitions.fade({ duration: 150 }),
renderer: renderer.createRenderer()
});
// Define routes...
</script>Using jsDelivr (Direct file path for better debugging)
<script type="module">
import { Router, transitions, renderer } from 'https://cdn.jsdelivr.net/npm/@profullstack/[email protected]/dist/index.esm.js';
// Initialize router
const router = new Router({
rootElement: '#app',
transition: transitions.fade({ duration: 150 }),
renderer: renderer.createRenderer()
});
// Define routes...
</script>Basic Usage
// Using npm package
import { Router, transitions } from '@profullstack/spa-router';
// Or using CDN
// import { Router, transitions } from 'https://esm.sh/@profullstack/[email protected]';
// Or using jsDelivr
// import { Router, transitions } from 'https://cdn.jsdelivr.net/npm/@profullstack/[email protected]/dist/index.esm.js';
// Initialize router
const router = new Router({
rootElement: '#app',
transition: transitions.fade({ duration: 150 })
});
// Define routes
router.registerRoutes({
'/': {
view: () => '<h1>Home Page</h1>'
},
'/about': {
view: () => '<h1>About Page</h1>'
},
'/users/:id': {
view: (params) => `<h1>User Profile: ${params.id}</h1>`
}
});
// Programmatic navigation
document.querySelector('#nav-about').addEventListener('click', () => {
router.navigate('/about');
});Advanced Usage
Custom Transitions
import { Router, transitions } from '@profullstack/spa-router';
const router = new Router({
rootElement: '#app',
transition: transitions.slide({ direction: 'left', duration: 300 })
});Enhanced Renderer
The router includes an enhanced renderer that supports component preservation and translations:
import { Router, transitions, renderer } from '@profullstack/spa-router';
// Create a custom renderer with translation support
const customRenderer = renderer.createRenderer({
// Function to translate a container
translateContainer: (container) => {
// Your translation logic here
container.querySelectorAll('[data-i18n]').forEach(el => {
const key = el.getAttribute('data-i18n');
el.textContent = translate(key);
});
},
// Function to apply RTL direction to document
applyRTLToDocument: () => {
// Your RTL logic here
document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
}
});
const router = new Router({
rootElement: '#app',
transition: transitions.fade({ duration: 300 }),
renderer: customRenderer
});Route Guards
router.registerRoutes({
'/admin': {
view: () => '<h1>Admin Dashboard</h1>',
beforeEnter: (to, from, next) => {
if (!isAuthenticated()) {
return next('/login');
}
next();
}
}
});Middleware
router.use(async (to, from, next) => {
// Log all navigation
console.log(`Navigating from ${from} to ${to.path}`);
next();
});Lazy Loading Components
router.registerRoutes({
'/dashboard': {
component: () => import('./components/dashboard.js')
}
});Web Component Integration
router.registerRoutes({
'/api-keys': {
component: 'api-key-manager',
props: { apiVersion: 'v1' }
}
});Server-Side Configuration
For SPA routing to work properly with direct URL access, you need to configure your server to redirect all requests to your index.html file. Here are examples for popular Node.js frameworks:
Hono.js
import { Hono } from 'hono';
import { serveStatic } from 'hono/serve-static';
import { join } from 'path';
const app = new Hono();
// Serve static files from the 'public' directory
app.use('/*', serveStatic({ root: './public' }));
// SPA fallback - redirect all unmatched routes to index.html
app.get('*', async (c) => {
// Check if the request is for a file with extension
const path = c.req.path;
if (path.match(/\.\w+$/)) {
// Let the static middleware handle it
return c.next();
}
// Otherwise serve index.html for SPA routing
return c.html(
await Bun.file(join(process.cwd(), 'public', 'index.html')).text()
);
});
export default app;Express.js
import express from 'express';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
// Get current file directory with ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const app = express();
// Serve static files from the 'public' directory
app.use(express.static(join(__dirname, 'public')));
// SPA fallback - redirect all unmatched routes to index.html
app.get('*', (req, res) => {
// Check if the request is for a file with extension
if (req.path.match(/\.\w+$/)) {
// Let Express handle 404 for missing files
return res.status(404).send('Not found');
}
// Otherwise serve index.html for SPA routing
res.sendFile(join(__dirname, 'public', 'index.html'));
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});Koa.js
import Koa from 'koa';
import serve from 'koa-static';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { createReadStream } from 'fs';
// Get current file directory with ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const app = new Koa();
// Serve static files from the 'public' directory
app.use(serve(join(__dirname, 'public')));
// SPA fallback - redirect all unmatched routes to index.html
app.use(async (ctx) => {
// Check if the request is for a file with extension
if (ctx.path.match(/\.\w+$/)) {
// Let koa-static handle 404 for missing files
return;
}
// Otherwise serve index.html for SPA routing
ctx.type = 'html';
ctx.body = createReadStream(join(__dirname, 'public', 'index.html'));
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});API Reference
Router
Constructor Options
rootElement: CSS selector for the root element where content will be renderedtransition: Transition effect to use for page changesrenderer: Custom renderer function for content renderingerrorHandler: Custom 404 error handler
Methods
navigate(path, pushState = true): Navigate to a pathregisterRoutes(routes): Register multiple routesaddRoute(path, routeConfig): Add a single routeremoveRoute(path): Remove a routegetCurrentRoute(): Get the current routeback(): Go back in historyforward(): Go forward in historyuse(middleware): Add middleware
Transitions
fade(options): Fade transitionslide(options): Slide transitionnone(): No transitioncustom(fn): Custom transition function
Renderer
createRenderer(options): Create a custom renderer with component preservation and translation supportcreateErrorHandler(options): Create a custom error handler
License
MIT
