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

@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-router

Or with yarn:

yarn add @profullstack/spa-router

Client-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 rendered
  • transition: Transition effect to use for page changes
  • renderer: Custom renderer function for content rendering
  • errorHandler: Custom 404 error handler

Methods

  • navigate(path, pushState = true): Navigate to a path
  • registerRoutes(routes): Register multiple routes
  • addRoute(path, routeConfig): Add a single route
  • removeRoute(path): Remove a route
  • getCurrentRoute(): Get the current route
  • back(): Go back in history
  • forward(): Go forward in history
  • use(middleware): Add middleware

Transitions

  • fade(options): Fade transition
  • slide(options): Slide transition
  • none(): No transition
  • custom(fn): Custom transition function

Renderer

  • createRenderer(options): Create a custom renderer with component preservation and translation support
  • createErrorHandler(options): Create a custom error handler

License

MIT