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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@front-utils/router

v0.4.5

Published

Client simple hash router with signals

Readme

Router Utility

A lightweight, flexible hash-based router implementation for modern web applications.

Table of Contents

Overview

This utility provides a hash-based navigation system that can be used to create single-page applications with client-side routing. It consists of two main components:

  1. HashNavigation - A low-level API for managing browser history and hash-based navigation
  2. HashRouter - A higher-level abstraction that provides an easy-to-use router interface
  3. ClientRouter - A React component for declarative route rendering

Installation

# Using npm
npm install @front-utils/router

# Using yarn
yarn add @front-utils/router

# Using bun
bun add @front-utils/router

Browser Compatibility

Router Utility is compatible with all modern browsers that support the History API:

  • Chrome 49+
  • Firefox 45+
  • Safari 10+
  • Edge 12+
  • Opera 36+

For older browsers, consider using a polyfill for the History API.

API Documentation

HashNavigation

HashNavigation provides direct access to browser history and navigation functionality. It's designed as a lightweight wrapper around the browser's History API.

It almost completely implements the native object window.navigation

Properties

  • currentEntry - A read-only signal containing the current history entry
  • entries - A read-only signal containing all history entries
  • canGoBack - A read-only signal indicating if navigation backward is possible
  • canGoForward - A read-only signal indicating if navigation forward is possible

Methods

  • navigate(hash, options) - Navigate to a new hash, optionally with state
  • traverseTo(key, options) - Navigate to a specific history entry by key
  • back(options) - Navigate backward in history
  • forward(options) - Navigate forward in history
  • updateCurrentEntry(options, hash) - Update the state of the current entry, optionally updating the hash
  • updateCurrentEntryHash(hash, newState?: NavigationState | null | undefined) - Update hash and state of the current entry
  • create() - Initialize the navigation system
  • subscribe(callback) - Subscribe to navigation changes with callback receiving current entry, previous entry, and hash
  • destroy() - Clean up all listeners and resources

HashRouter

HashRouter provides a higher-level API for common routing operations, with easier configuration and initialization.

Properties

  • _navigation - Reference to the underlying HashNavigation instance
  • currentEntry - A read-only signal containing the current router history entry with extended functionality
  • state - A read-only signal providing access to the current navigation state
  • hash - A read-only signal providing access to the current hash
  • entries - A read-only signal containing all history entries (inherited from HashNavigation)
  • canGoBack - A read-only signal indicating if navigation backward is possible (inherited from HashNavigation)
  • canGoForward - A read-only signal indicating if navigation forward is possible (inherited from HashNavigation)

Methods

  • create(config) - Initialize the router and subscribe to location changes
  • subscribe(callback) - Subscribe to history changes
  • navigate(hash, state) - Navigate to a specific hash with optional state
  • replaceState(config) - Replace the current state and/or hash
  • goBack() - Navigate back in history
  • goToPrev() - Alias for goBack
  • getHash() - Get the current hash
  • getState() - Get the current state
  • hasPage(hash?) - Check if a page exists in configured routes
  • destroy() - Clean up all listeners and resources
  • getConfig() - Get the current router configuration

RouterHistoryEntry

RouterHistoryEntry extends NavigationHistoryEntry with additional router-specific functionality.

Properties

  • All properties from NavigationHistoryEntry (url, key, id, index, sameDocument, state, hash)
  • pattern - The matched route pattern, if any

Methods

  • getParams<T>() - Get URL parameters extracted from the route pattern
  • getQuery<T>() - Get query parameters from the URL

ClientRouter

ClientRouter is a React component that provides a declarative way to define and render routes in your React application. It automatically handles route changes, component rendering, and passing route parameters as props.

Props

  • router: (HashRouter) - An instance of HashRouter to use for navigation
  • routes: (Map<string, React.ComponentType>) - A Map of route patterns to React components
  • homeUrl: (string) - The default route to redirect to when no route matches
  • notFoundComponent: (React.ComponentType) - Component to render when no route matches
  • className: (string, optional) - CSS class to apply to the router container element

Usage

The ClientRouter simplifies route management in React applications by:

  1. Automatically subscribing to route changes
  2. Rendering the appropriate component for the current route
  3. Passing route parameters as props to the rendered component
  4. Handling "not found" routes with a custom component
  5. Cleaning up subscriptions when unmounted

Usage Examples

Basic Setup

import { hashRouter } from '@front-utils/router';

// Initialize the router with routes configuration
const unsubscribe = hashRouter.create({
  onChange: (location) => {
    console.log('Route changed:', location.hash);
    // Update your UI based on the new location
  },
  config: {
    homeUrl: 'home',
    routeNames: ['home', 'about', 'contact', 'products']
  }
});

// Navigate to a different route
hashRouter.navigate('about');

// Clean up when your app is destroyed
// unsubscribe();

Advanced Usage

import { hashRouter } from '@front-utils/router';

// Initialize with more complex state handling
hashRouter.create({
  onChange: (location) => {
    const hash = location.hash;
    const state = location.state;
    
    console.log('Current route:', hash);
    console.log('Route state:', state);
    
    // Update application state or UI based on the route
    renderPage(hash, state);
  },
  config: {
    homeUrl: 'dashboard',
    routeNames: ['dashboard', 'profile', 'settings', 'reports', 'logout']
  }
});

// Navigate with state
hashRouter.navigate('profile', { 
  userId: 123, 
  viewMode: 'edit' 
});

// Check if current route is valid
if (hashRouter.hasPage()) {
  console.log('Current route is valid');
} else {
  console.log('Invalid route, redirecting to home');
  hashRouter.navigate('dashboard');
}

Handling Route Changes

import { hashRouter } from '@front-utils/router';

// Create a route handler function
const handleRouteChange = (location) => {
  const hash = location.hash;
  const appContainer = document.getElementById('app');
  
  // Simple route-based content rendering
  switch (hash) {
    case 'home':
      appContainer.innerHTML = '<h1>Home Page</h1>';
      break;
    case 'about':
      appContainer.innerHTML = '<h1>About Us</h1>';
      break;
    case 'contact':
      appContainer.innerHTML = '<h1>Contact Us</h1>';
      break;
    default:
      appContainer.innerHTML = '<h1>Page Not Found</h1>';
      break;
  }
};

// Initialize the router
hashRouter.create({
  onChange: handleRouteChange,
  config: {
    homeUrl: 'home',
    routeNames: ['home', 'about', 'contact']
  }
});

// Create navigation buttons
document.getElementById('home-btn').addEventListener('click', () => {
  hashRouter.navigate('home');
});

document.getElementById('about-btn').addEventListener('click', () => {
  hashRouter.navigate('about');
});

document.getElementById('contact-btn').addEventListener('click', () => {
  hashRouter.navigate('contact');
});

document.getElementById('back-btn').addEventListener('click', () => {
  hashRouter.goBack();
});

State Management

import { hashRouter } from '@front-utils/router';

// Track a user's navigation through a product catalog
// Initialize with a product catalog setup
hashRouter.create({
  onChange: (location) => {
    const hash = location.hash;
    const state = location.state;
    
    if (hash === 'product') {
      const productId = state?.productId;
      if (productId) {
        // Fetch and display the product
        fetchProductDetails(productId).then(displayProduct);
      }
    }
  },
  config: {
    homeUrl: 'catalog',
    routeNames: ['catalog', 'product', 'cart', 'checkout']
  }
});

// Navigation to a specific product detail page
function viewProduct(productId) {
  hashRouter.navigate('product', { productId });
}

// Update state without changing the route
function updateProductOptions(options) {
  hashRouter.replaceState({ 
    state: { ...hashRouter.getState(), options } 
  });
}

// Example of accessing state from the current entry
function getCurrentProductId() {
  const state = hashRouter.getState();
  return state?.productId;
}

Route Parameters and Queries

import { hashRouter } from '@front-utils/router';

// Setup routes with parameters
hashRouter.create({
  onChange: (location) => {
    // Get route parameters using the extended RouterHistoryEntry
    const params = hashRouter.currentEntry.value.getParams();
    const query = hashRouter.currentEntry.value.getQuery();
    
    console.log('Route params:', params);
    console.log('Query params:', query);
    
    // Example: Rendering different views based on params and query
    if (location.hash.includes('users')) {
      if (params.id) {
        renderUserDetails(params.id, query.tab || 'profile');
      } else {
        renderUsersList(query.page || '1', query.sort || 'name');
      }
    }
  },
  config: {
    homeUrl: 'home',
    // Define routes including parameter patterns
    routeNames: [
      'home', 
      'users', 
      'users/:id', 
      'products/:category/:id'
    ]
  }
});

// Navigate to parameterized routes
hashRouter.navigate('users/123?tab=settings');
hashRouter.navigate('products/electronics/laptop-15?color=silver&price=999');

// Helper function to access params in components
function useRouteParams() {
  return hashRouter.currentEntry.value.getParams();
}

// Helper function to access query parameters
function useQueryParams() {
  return hashRouter.currentEntry.value.getQuery();
}

TypeScript Usage

import { hashRouter, NavigationHistoryEntry, InitializeRouterConfig, RouterHistoryEntry } from '@front-utils/router';

// Type-safe route configuration
interface AppRoutes {
  home: undefined;
  product: { id: string };
  checkout: { items: string[] };
}

// Define your routes
const routeConfig: InitializeRouterConfig = {
  homeUrl: 'home',
  routeNames: ['home', 'product', 'checkout']
};

// Type-safe route handler
function handleRouteChange(location: NavigationHistoryEntry): void {
  const hash = location.hash;
  
  // Access state in a type-safe way
  const state = hashRouter.getState();
  
  switch (hash) {
    case 'home':
      renderHomePage();
      break;
    case 'product':
      if (state && 'id' in state) {
        renderProductPage(state.id as string);
      }
      break;
    case 'checkout':
      if (state && 'items' in state) {
        renderCheckoutPage(state.items as string[]);
      }
      break;
  }
}

// Access route parameters with proper typing
function useTypedParams<T>() {
  return hashRouter.currentEntry.value.getParams<T>();
}

// Initialize with type-safe configuration
hashRouter.create({
  onChange: handleRouteChange,
  config: routeConfig
});

// Type-safe navigation with state
function navigateToProduct(productId: string): void {
  hashRouter.navigate('product', { id: productId });
}

Integration with Frameworks

React Integration

import React, { useState, useEffect, useMemo } from 'react';
import { hashRouter } from '@front-utils/router';

// Custom hook for using the router
function useHashRouter() {
  const [routerInstance, setRouterInstance] = useState(null);
  
  // Initialize once
  useEffect(() => {
    const unsubscribe = hashRouter.create({
      onChange: (location) => {
        // Force a re-render on location change
        setRouterInstance({});
      },
      config: {
        homeUrl: 'home',
        routeNames: ['home', 'users', 'users/:id', 'settings']
      }
    });
    
    return unsubscribe;
  }, []);
  
  // Return consistent references to current values and methods
  return useMemo(() => ({
    currentRoute: hashRouter.getHash(),
    routeState: hashRouter.getState() || {},
    currentEntry: hashRouter.currentEntry.value,
    params: hashRouter.currentEntry.value.getParams(),
    query: hashRouter.currentEntry.value.getQuery(),
    navigate: hashRouter.navigate,
    goBack: hashRouter.goBack,
    canGoBack: hashRouter.canGoBack.value
  }), [routerInstance]);
}

// Example component
function App() {
  const { currentRoute, params, query, navigate, goBack, canGoBack } = useHashRouter();
  
  return (
    <div>
      <nav>
        <button onClick={() => navigate('home')}>Home</button>
        <button onClick={() => navigate('users')}>Users</button>
        <button onClick={() => navigate('settings')}>Settings</button>
        {canGoBack && <button onClick={goBack}>Back</button>}
      </nav>
      
      <main>
        {currentRoute === 'home' && <HomePage />}
        {currentRoute === 'users' && !params.id && <UsersPage page={query.page} />}
        {currentRoute === 'users' && params.id && <UserDetailsPage userId={params.id} />}
        {currentRoute === 'settings' && <SettingsPage />}
      </main>
    </div>
  );
}

Using ClientRouter Component

The ClientRouter component provides a declarative way to define and manage routes in your React application:

import React, { useEffect } from 'react';
import { hashRouter, ClientRouter } from '@front-utils/router';

// Define page components
const HomePage = () => <div>Welcome to Home Page</div>;
const AboutPage = () => <div>About Us</div>;
const UserPage = ({ userId }) => <div>User Profile for User {userId}</div>;
const NotFoundPage = () => <div>404 - Page Not Found</div>;

function App() {
  
  // Create a map of routes to components
  const routes = new Map([
    ['home', HomePage],
    ['about', AboutPage],
    ['user/:userId', UserPage]
  ]);
  
  return (
    <div className="app">
      <nav>
        <button onClick={() => hashRouter.navigate('home')}>Home</button>
        <button onClick={() => hashRouter.navigate('about')}>About</button>
        <button onClick={() => hashRouter.navigate('user/123')}>User Profile</button>
      </nav>
      
      <main>
        <ClientRouter
          router={hashRouter}
          routes={routes}
          homeUrl="home"
          notFoundComponent={NotFoundPage}
          className="main-content"
        />
      </main>
    </div>
  );
}

export default App;

Advanced ClientRouter Examples:

  1. With TypeScript and route parameters:
import React, { useEffect } from 'react';
import { hashRouter, ClientRouter } from '@front-utils/router';

interface UserProfileProps {
  userId: string;
  tab?: string;
}

interface ProductProps {
  categoryId: string;
  productId: string;
  color?: string;
}

// Page components
const HomePage = () => <div>Home Page</div>;
const AboutPage = () => <div>About Page</div>;
const UserProfile = ({ userId, tab = 'profile' }: UserProfileProps) => (
  <div>
    <h1>User {userId}</h1>
    <div>Current tab: {tab}</div>
  </div>
);
const ProductPage = ({ categoryId, productId, color }: ProductProps) => (
  <div>
    <h1>Product: {productId}</h1>
    <div>Category: {categoryId}</div>
    {color && <div>Selected color: {color}</div>}
  </div>
);
const NotFoundPage = () => <div>Page Not Found</div>;

function App() {

  const routes = new Map([
    ['home', HomePage],
    ['about', AboutPage],
    ['users/:userId', UserProfile],
    ['products/:categoryId/:productId', ProductPage]
  ]);
  
  return (
    <div>
      <nav>
        <button onClick={() => hashRouter.navigate('home')}>Home</button>
        <button onClick={() => hashRouter.navigate('about')}>About</button>
        <button onClick={() => hashRouter.navigate('users/123?tab=settings')}>
          User 123
        </button>
        <button onClick={() => 
          hashRouter.navigate('products/electronics/laptop?color=silver')
        }>
          Silver Laptop
        </button>
      </nav>
      
      <ClientRouter
        router={hashRouter}
        routes={routes}
        homeUrl="home"
        notFoundComponent={NotFoundPage}
      />
    </div>
  );
}
  1. With React Context for better organization:
import React, { createContext, useContext, useEffect } from 'react';
import { hashRouter, ClientRouter } from '@front-utils/router';

// Create context
const RouterContext = createContext(hashRouter);

// Router provider component
function RouterProvider({ children }) {
  return (
    <RouterContext.Provider value={hashRouter}>
      {children}
    </RouterContext.Provider>
  );
}

// Custom hook for router access
function useRouter() {
  return useContext(RouterContext);
}

// Navigation component
function Navigation() {
  const router = useRouter();
  
  return (
    <nav>
      <button onClick={() => router.navigate('home')}>Home</button>
      <button onClick={() => router.navigate('dashboard')}>Dashboard</button>
      <button onClick={() => router.navigate('settings')}>Settings</button>
      {router.canGoBack.value && (
        <button onClick={() => router.goBack()}>Back</button>
      )}
    </nav>
  );
}

// Page components
const HomePage = () => <h1>Home</h1>;
const DashboardPage = () => <h1>Dashboard</h1>;
const ProfilePage = ({ userId }) => <h1>Profile: {userId}</h1>;
const SettingsPage = () => <h1>Settings</h1>;
const NotFoundPage = () => <h1>Page Not Found</h1>;

// Main app component
function MainApp() {
  const router = useRouter();
  const routes = new Map([
    ['home', HomePage],
    ['dashboard', DashboardPage],
    ['profile/:userId', ProfilePage],
    ['settings', SettingsPage]
  ]);
  
  return (
    <div className="app">
      <Navigation />
      <ClientRouter
        router={router}
        routes={routes}
        homeUrl="home"
        notFoundComponent={NotFoundPage}
      />
    </div>
  );
}

// Root component with provider
export default function App() {
  return (
    <RouterProvider>
      <MainApp />
    </RouterProvider>
  );
}
  1. With Code Splitting:
import React, { Suspense, lazy, useEffect } from 'react';
import { hashRouter, ClientRouter } from '@front-utils/router';

// Lazy loaded components
const HomePage = lazy(() => import('./pages/Home'));
const AboutPage = lazy(() => import('./pages/About'));
const UserProfilePage = lazy(() => import('./pages/UserProfile'));
const NotFoundPage = lazy(() => import('./pages/NotFound'));

// Loading component
const Loading = () => <div className="loading">Loading...</div>;

function App() {  
  const routes = new Map([
    ['home', HomePage],
    ['about', AboutPage],
    ['user/:userId', UserProfilePage]
  ]);
  
  return (
    <div className="app">
      <nav>
        <button onClick={() => hashRouter.navigate('home')}>Home</button>
        <button onClick={() => hashRouter.navigate('about')}>About</button>
        <button onClick={() => hashRouter.navigate('user/123')}>User Profile</button>
      </nav>
      
      <Suspense fallback={<Loading />}>
        <ClientRouter
          router={hashRouter}
          routes={routes}
          homeUrl="home"
          notFoundComponent={NotFoundPage}
        />
      </Suspense>
    </div>
  );
}

Vue Integration

// router.js
import { hashRouter } from '@front-utils/router';
import { ref, readonly, computed } from 'vue';

// Initialize the router
const unsubscribe = hashRouter.create({
  onChange: () => {
    // Vue's reactivity will pick up changes through the computed properties
  },
  config: {
    homeUrl: 'home',
    routeNames: ['home', 'about', 'contact', 'users/:id']
  }
});

// Create reactive references to router state
const currentHash = computed(() => hashRouter.getHash());
const currentState = computed(() => hashRouter.getState() || {});
const routeParams = computed(() => hashRouter.currentEntry.value.getParams());
const queryParams = computed(() => hashRouter.currentEntry.value.getQuery());
const canGoBack = computed(() => hashRouter.canGoBack.value);

export default {
  currentHash: readonly(currentHash),
  currentState: readonly(currentState),
  routeParams: readonly(routeParams),
  queryParams: readonly(queryParams),
  canGoBack: readonly(canGoBack),
  navigate: hashRouter.navigate,
  goBack: hashRouter.goBack,
  destroy: unsubscribe
};

// App.vue
<template>
  <div>
    <nav>
      <button @click="navigate('home')">Home</button>
      <button @click="navigate('about')">About</button>
      <button @click="navigate('contact')">Contact</button>
      <button @click="navigate(`users/${userId}`)">User Profile</button>
      <button v-if="canGoBack" @click="goBack">Back</button>
    </nav>
    
    <component :is="currentComponent" 
               :params="routeParams" 
               :query="queryParams"
               v-bind="currentState"></component>
  </div>
</template>

<script>
import router from './router';
import HomePage from './components/HomePage.vue';
import AboutPage from './components/AboutPage.vue';
import ContactPage from './components/ContactPage.vue';
import UserPage from './components/UserPage.vue';

export default {
  setup() {
    const userId = '123'; // Example user ID
    
    const getComponent = () => {
      const hash = router.currentHash.value;
      
      if (hash === 'home') return HomePage;
      if (hash === 'about') return AboutPage;
      if (hash === 'contact') return ContactPage;
      if (hash.startsWith('users/')) return UserPage;
      
      return HomePage; // Default
    };
    
    return {
      userId,
      currentComponent: computed(() => getComponent()),
      routeParams: router.routeParams,
      queryParams: router.queryParams,
      currentState: router.currentState,
      canGoBack: router.canGoBack,
      navigate: router.navigate,
      goBack: router.goBack
    };
  }
};
</script>

Contract Interfaces

The library is built on the following TypeScript interfaces:

HashNavigation Interface

export interface HashNavigation {
  // Public signals
  currentEntry: ReadonlySignal<NavigationHistoryEntry>;
  entries: ReadonlySignal<NavigationHistoryEntry[]>;
  canGoBack: ReadonlySignal<boolean>;
  canGoForward: ReadonlySignal<boolean>;
  
  // Navigation methods
  navigate: (hash: string, options?: NavigationOptions) => NavigationResult;
  traverseTo: (key: string, options?: NavigationOptions) => NavigationResult | null;
  back: (options?: NavigationOptions) => NavigationResult | null;
  forward: (options?: NavigationOptions) => NavigationResult | null;
  updateCurrentEntry: (options?: NavigationOptions) => void;
  
  // Subscription method
  subscribe: (
    callback: (
      entry: NavigationHistoryEntry,
      prevEntry: NavigationHistoryEntry | null,
      hash: string
    ) => void
  ) => VoidFunction;
  
  // Init
  create: () => void;
  // Cleanup
  destroy: () => void;

  updateCurrentEntryHash: (hash: string, newState?: NavigationState | null | undefined) => void;
}

HashRouter Interface

export interface HashRouter extends Pick<HashNavigation, 'entries' | 'canGoBack' | 'canGoForward'> {
  _navigation: HashNavigation;
  currentEntry: ReadonlySignal<RouterHistoryEntry>;
  state: ReadonlySignal<NavigationState>;
  hash: ReadonlySignal<string>;
  create: (config: SubscribeChangeConfig) => VoidFunction;
  subscribe: (callback: (update: NavigationHistoryEntry, prevLocation?: NavigationHistoryEntry | null) => void) => VoidFunction;
  navigate: (hash: string, state?: Record<string, unknown>) => NavigationResult;
  replaceState: (config?: {state?: Record<string, unknown>; hash?: string;}) => void;
  goBack: VoidFunction;
  goToPrev: VoidFunction;
  getHash: () => string;
  getState: () => NavigationState | undefined;
  hasPage: (hash?: string) => boolean;
  destroy: VoidFunction;
  getConfig: () => InitializeRouterConfig | null;
}

Router History Entry Interface

export interface RouterHistoryEntry extends NavigationHistoryEntry {
  pattern?: string;
  getParams: <T extends Record<string, string> = Record<string, string>>() => T;
  getQuery : <T extends QueryParams>() => T,
}

Configuration Interfaces

export interface InitializeRouterConfig {
  homeUrl: string;
  routeNames: string[];
}

export interface SubscribeChangeConfig {
  onChange: (loc: NavigationHistoryEntry) => void;
  config: InitializeRouterConfig;
}

Navigation History Interfaces

export type NavigationState = Record<string, unknown>;

export interface NavigationHistoryEntry {
  url: string;
  key: string;
  id: string;
  index: number;
  sameDocument: boolean;
  state?: any;
  hash: string;
}

export interface NavigationResult {
  committed: Promise<NavigationHistoryEntry>;
  finished: Promise<NavigationHistoryEntry>;
}

export interface NavigationOptions {
  state?: unknown;
  info?: unknown;
}

export interface QueryParams {
  [key: string]: string
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Troubleshooting

Hash changes not being detected

  • Make sure the router is initialized before any navigation occurs
  • Check that the route names are correctly defined in your configuration

State not persisting between navigations

  • Ensure you're using navigate() with state parameter correctly
  • Verify you're retrieving state with getState() method

Route parameters not working

  • Ensure your route patterns are correctly defined in the routeNames array
  • Check that you're accessing parameters with getParams() method from the router entry

Conflicts with other routers

  • This router uses hash-based navigation, so avoid using other hash-change listeners
  • If using with another framework's router, ensure they're not both handling the same routes

License

This project is licensed under the MIT License - see the LICENSE file for details.