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

teeny-tiny-router

v1.0.3

Published

A minimal, dependency-free client-side router with caching, script execution, and SSR compatibility

Readme

teeny-tiny-router

A minimal, dependency-free client-side router for modern web applications with built-in caching, script execution, and SSR compatibility.

Features

  • Ultra lightweight - Single file, no dependencies
  • Automatic link interception - Works with regular <a> tags
  • Route parameters - Support for :id params and * wildcards
  • Page caching - Automatic caching of fetched pages
  • Intelligent prefetch - Automatic prefetching on hover with configurable delay
  • Script execution - Runs scripts from dynamically loaded content
  • SSR friendly - Works seamlessly with server-side rendered pages
  • Flexible rendering - Custom or automatic content rendering
  • Modern ES modules - Native module support

Installation

NPM

npm install teeny-tiny-router

CDN

<script type="module">
  import { MiniRouter } from "https://unpkg.com/teeny-tiny-router/dist/teeny-tiny-router.es.js";
</script>

Quick Start

Basic Setup

<!DOCTYPE html>
<html>
  <head>
    <title>My App</title>
  </head>
  <body>
    <nav>
      <a href="/">Home</a>
      <a href="/about">About</a>
      <a href="/posts/123">Post 123</a>
    </nav>

    <div id="app"></div>

    <script type="module">
      import { MiniRouter } from "./src/index.js";

      const router = new MiniRouter();

      // Handle navigation events
      router.on("navigate", ({ title, body }) => {
        if (title) document.title = title;
        document.querySelector("#app").innerHTML = body;
      });

      // Define routes
      router.route("/", () => {
        console.log("Home page loaded");
      });

      router.route("/posts/:id", (params) => {
        console.log("Post ID:", params.id);
      });

      // Initialize current page
      router.navigate(location.pathname, { replace: true });
    </script>
  </body>
</html>

API Reference

Constructor Options

const router = new MiniRouter({
  htmlExtension: true, // Add .html to URLs
  interceptAllLinks: true, // Auto-intercept internal links
  contentSelector: "#app", // CSS selector to extract content from fetched pages
  prefetchOnHover: true, // Enable automatic prefetch on hover
  prefetchDelay: 0, // Delay in ms before prefetch starts
});

Options

  • htmlExtension (boolean, default: true) - Automatically adds .html extension to URLs that don't have one
  • interceptAllLinks (boolean, default: true) - Automatically intercepts all internal links. Set to false to use opt-in mode with data-link attribute
  • contentSelector (string, default: '#app') - CSS selector used to extract content from fetched HTML pages. If element not found, falls back to <body> content
  • prefetchOnHover (boolean, default: true) - Automatically prefetch pages when hovering over links
  • prefetchDelay (number, default: 0) - Delay in milliseconds before starting prefetch on hover. Set to 0 for instant prefetch, or a higher value (e.g., 100-300ms) to avoid prefetching during quick mouse movements

Methods

router.route(pattern, callback)

Define a route handler.

// Simple route
router.route("/about", () => {
  console.log("About page");
});

// Route with parameters
router.route("/users/:id", (params) => {
  console.log("User ID:", params.id);
});

// Wildcard route
router.route("/admin/*", (params) => {
  console.log("Admin path:", params["*"]);
});

// Override content from route
router.route("/custom", () => {
  return {
    title: "Custom Page",
    body: "<h1>Custom Content</h1>",
  };
});

router.navigate(url, options)

Programmatically navigate to a URL.

// Navigate to a new page
router.navigate("/about");

// Replace current history entry
router.navigate("/about", { replace: true });

router.on(event, callback)

Listen to router events.

// Navigation events
router.on("navigate", ({ url, title, body }) => {
  document.title = title;
  document.querySelector("#app").innerHTML = body;
});

// Route-specific events
router.on("route:/users/:id", ({ params, url, title, body }) => {
  console.log("User route matched:", params.id);
});

// Prefetch events
router.on("prefetch", ({ url, data }) => {
  console.log("Page prefetched:", url);
});

router.on("prefetch:error", ({ url, error }) => {
  console.log("Prefetch failed:", url, error);
});

router.prefetch(url)

Manually prefetch a page in the background.

// Prefetch a single page
router.prefetch("/about");

router.prefetchAll(urls)

Prefetch multiple pages at once.

// Prefetch multiple pages
router.prefetchAll(["/about", "/contact", "/services"]);

router.clearCache(url?)

Clear the page cache.

// Clear specific page from cache
router.clearCache("/about");

// Clear entire cache
router.clearCache();

router.getCacheInfo()

Get cache statistics.

const info = router.getCacheInfo();
console.log(`Cache size: ${info.size}`);
console.log(`Cached URLs:`, info.urls);

router.setPrefetchOnHover(enabled)

Enable or disable prefetch on hover.

// Disable prefetch on hover
router.setPrefetchOnHover(false);

// Re-enable prefetch on hover
router.setPrefetchOnHover(true);

Route Parameters

// Named parameters
router.route("/users/:id/posts/:postId", (params) => {
  console.log(params.id); // user ID
  console.log(params.postId); // post ID
});

// Wildcard (catch-all)
router.route("/files/*", (params) => {
  console.log(params["*"]); // everything after /files/
});

Content Override

Route handlers can return content to override the fetched page:

// Return HTML string
router.route("/dynamic", () => {
  return "<h1>Dynamic Content</h1>";
});

// Return object with title and body
router.route("/custom", () => {
  return {
    title: "Custom Title",
    body: "<h1>Custom Body</h1>",
  };
});

Prefetch Features

Automatic Prefetch on Hover

By default, the router automatically prefetches pages when users hover over links, significantly improving navigation speed. Prefetch happens instantly (no delay) to maximize the chance of having content ready before the user clicks.

Manual Prefetch

You can manually prefetch pages to warm up the cache:

// Prefetch important pages on app startup
router.prefetchAll(["/about", "/contact", "/products"]);

// Prefetch based on user behavior
document.addEventListener("scroll", () => {
  router.prefetch("/next-section");
});

Disable Prefetch for Specific Links

Use the data-no-prefetch attribute to prevent automatic prefetching:

<!-- This link won't be prefetched on hover -->
<a href="/heavy-page" data-no-prefetch>Heavy Page</a>

<!-- External links are automatically excluded -->
<a href="https://external-site.com">External</a>

<!-- Download links are automatically excluded -->
<a href="/document.pdf" download>Download PDF</a>

Prefetch Events

Monitor prefetch activity with events:

router.on("prefetch", ({ url, data }) => {
  console.log(`✓ Prefetched: ${url}`);
});

router.on("prefetch:error", ({ url, error }) => {
  console.log(`✗ Prefetch failed: ${url}`);
});

Cache Management

Monitor and manage the page cache:

// Check cache status
const { size, urls } = router.getCacheInfo();
console.log(`${size} pages cached: ${urls.join(", ")}`);

// Clear cache when needed
if (size > 50) {
  router.clearCache(); // Clear all
}

// Or clear specific pages
router.clearCache("/old-page");

SSR Integration

With Go html/template

// main.go
func aboutHandler(w http.ResponseWriter, r *http.Request) {
    tmpl := `
<!DOCTYPE html>
<html>
<head>
    <title>About Us</title>
</head>
<body>
    <div id="app">
        <h1>About Page</h1>
        <p>Server-rendered content</p>
        <script>
            console.log('Page-specific script executed');
        </script>
    </div>
</body>
</html>`
    w.Header().Set("Content-Type", "text/html")
    fmt.Fprint(w, tmpl)
}

With Node.js/Express

app.get("/about", (req, res) => {
  res.send(`
<!DOCTYPE html>
<html>
<head>
    <title>About</title>
</head>
<body>
    <div id="app">
        <h1>About Page</h1>
        <script>console.log('About page loaded');</script>
    </div>
</body>
</html>
  `);
});

Advanced Usage

Script Execution

Scripts in loaded content are automatically executed:

import { MiniRouter, executeScripts } from "./src/index.js";

router.on("navigate", ({ title, body }) => {
  if (title) document.title = title;
  const app = document.querySelector("#app");
  app.innerHTML = body;
  executeScripts(app); // Manually execute scripts if needed
});

Custom Link Handling

// Disable automatic link interception
const router = new MiniRouter({ interceptAllLinks: false });

// Use data-link attribute for specific links
<a href="/about" data-link>
  About
</a>;

Browser Support

  • Chrome 61+
  • Firefox 60+
  • Safari 10.1+
  • Edge 16+

Requires ES2017+ support (async/await, modules).

Contributing

  1. Fork the repository
  2. Create your feature branch
  3. Add tests if applicable
  4. Submit a pull request

License

MIT License - see LICENSE file for details.