New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

route-hints

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

route-hints

A React component and utilities for suggesting similar routes when users hit 404 pages in Next.js applications using Levenshtein distance

latest
Source
npmnpm
Version
1.0.0
Version published
Maintainers
1
Created
Source

route-hints

A React component and utilities for suggesting similar routes when users hit 404 pages in Next.js applications using Levenshtein distance algorithm.

Features

  • 🔍 Automatic Route Discovery: Scans your Next.js App Router directory structure to find all valid routes
  • 📏 Levenshtein Distance: Uses fast and accurate string similarity matching
  • ⚛️ React Component: Drop-in component for displaying route suggestions
  • 🎨 Customizable: Extensive styling and configuration options
  • 🚀 TypeScript Support: Fully typed for better developer experience
  • 📦 Lightweight: Minimal dependencies and optimized for performance

Installation

npm install route-hints
# or
yarn add route-hints
# or
pnpm add route-hints

Quick Start

1. Generate Routes (Optional)

You can automatically discover routes from your Next.js app directory:

// scripts/generate-routes.js
const { generateRoutesFile } = require('route-hints');

// Generate routes.json file from your app directory
generateRoutesFile('./app', './public/routes.json');

Add this to your package.json build process:

{
  "scripts": {
    "build": "node scripts/generate-routes.js && next build"
  }
}
// app/not-found.tsx
'use client';

import { useRouteSuggestions } from 'route-hints';

export default function NotFound() {
  const { suggestions, isLoading, error, hasNoSuggestions } = useRouteSuggestions(
    typeof window !== 'undefined' ? window.location.pathname : '/',
    {
      threshold: 3,
      maxSuggestions: 5
    }
  );

  return (
    <div className="not-found-page">
      <h1>404 - Page Not Found</h1>
      <p>The page you're looking for doesn't exist.</p>
      
      {isLoading && <p>Loading suggestions...</p>}
      
      {error && <p>Error: {error}</p>}
      
      {hasNoSuggestions && <p>No similar pages found.</p>}
      
      {suggestions.length > 0 && (
        <div>
          <h3>Did you mean one of these pages?</h3>
          <ul>
            {suggestions.map((suggestion) => (
              <li key={suggestion.path}>
                <a href={suggestion.path}>
                  {suggestion.path}
                </a>
                <small> (similarity: {suggestion.distance})</small>
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

3. Alternative: Use the render prop component

// app/not-found.tsx
import { RouteData } from 'route-hints';

export default function NotFound() {
  return (
    <div className="not-found-page">
      <h1>404 - Page Not Found</h1>
      <p>The page you're looking for doesn't exist.</p>
      
      <RouteData 
        invalidPath={typeof window !== 'undefined' ? window.location.pathname : '/'}
        threshold={3}
        maxSuggestions={5}
      >
        {({ suggestions, isLoading, error, hasNoSuggestions }) => {
          if (isLoading) return <p>Loading suggestions...</p>;
          if (error) return <p>Error: {error}</p>;
          if (hasNoSuggestions) return <p>No similar pages found.</p>;
          
          return (
            <div className="my-custom-suggestions">
              <h3>Did you mean one of these pages?</h3>
              <div className="suggestion-grid">
                {suggestions.map((suggestion) => (
                  <div key={suggestion.path} className="suggestion-card">
                    <a href={suggestion.path} className="suggestion-link">
                      {suggestion.path}
                    </a>
                    <span className="similarity-score">
                      Score: {suggestion.distance}
                    </span>
                  </div>
                ))}
              </div>
            </div>
          );
        }}
      </RouteData>
    </div>
  );
}

4. Using Direct Functions (No React)

For non-React usage or server-side logic:

import { getSuggestions, collectRoutes } from 'route-hints';

// Collect routes from your app directory
const routes = collectRoutes('./app');

// Get suggestions for an invalid path
const suggestions = getSuggestions('/prodcuts', routes, { 
  threshold: 2, 
  maxSuggestions: 3 
});

console.log(suggestions);
// Output: [{ path: '/products', distance: 1 }]

API Reference

Hooks

useRouteSuggestions(invalidPath, options)

The main React hook for getting route suggestions data.

Parameters:

  • invalidPath: string - The current invalid path that triggered 404
  • options: UseRouteSuggestionsOptions - Configuration options

Returns:

interface UseRouteSuggestionsReturn {
  suggestions: RouteSuggestion[];
  isLoading: boolean;
  error: string | null;
  hasNoSuggestions: boolean;
}

Options:

interface UseRouteSuggestionsOptions {
  routes?: string[];             // Optional: Custom list of valid routes
  threshold?: number;            // Default: 3 - Maximum Levenshtein distance
  maxSuggestions?: number;       // Default: 5 - Maximum number of suggestions  
  caseSensitive?: boolean;       // Default: false - Case sensitive matching
  autoLoad?: boolean;            // Default: true - Auto-load suggestions
  routesJsonPath?: string;       // Default: '/routes.json' - Path to routes file
}

Components

RouteData

A render prop component that provides suggestion data to its children.

Props:

interface RouteDataProps extends UseRouteSuggestionsOptions {
  invalidPath: string;
  children: (data: UseRouteSuggestionsReturn) => React.ReactNode;
}

SuggestedRoutes (Optional styled component)

A pre-styled React component for displaying route suggestions. You can still use this if you want a quick implementation, but we recommend using the data hook or render prop component for full design control.

Utilities

collectRoutes(appDirPath: string): string[]

Scans a Next.js App Router directory and returns all valid route paths.

import { collectRoutes } from 'route-hints';

const routes = collectRoutes('./app');
console.log(routes); // ['/','  '/about', '/products', ...]

generateRoutesFile(appDirPath: string, outputPath: string): void

Generates a JSON file containing all discovered routes.

import { generateRoutesFile } from 'route-hints';

generateRoutesFile('./app', './public/routes.json');

getSuggestions(invalidPath: string, validRoutes: string[], options?: SuggestionOptions): RouteSuggestion[]

Returns all route suggestions within the specified threshold.

import { getSuggestions } from 'route-hints';

const suggestions = getSuggestions('/prodcts', ['/products', '/about'], {
  threshold: 2,
  caseSensitive: false
});
// Returns: [{ path: '/products', distance: 1 }]

getBestSuggestions(invalidPath: string, validRoutes: string[], maxSuggestions?: number, threshold?: number): RouteSuggestion[]

Returns the best route suggestions up to the specified limit.

import { getBestSuggestions } from 'route-hints';

const bestSuggestions = getBestSuggestions('/prodcts', routes, 3, 2);

createRouteMatcher(config: RouteMatcherConfig)

Creates a configured route matcher with predefined settings.

import { createRouteMatcher } from 'route-hints';

const matcher = createRouteMatcher({
  threshold: 2,
  maxSuggestions: 5,
  caseSensitive: false,
  includeExactMatches: true,
  includeStartsWithMatches: true
});

const suggestions = matcher.getSuggestions('/prodcts', routes);

Styling

Default CSS Classes

The component uses these CSS classes that you can style:

.route-suggestions {
  /* Main container */
}

.route-suggestions__title {
  /* Suggestion title */
}

.route-suggestions__list {
  /* Suggestions list (ul) */
}

.route-suggestions__item {
  /* Individual suggestion (li) */
}

.route-suggestions__link {
  /* Suggestion links (a) */
}

Custom Styling Example

.route-suggestions {
  padding: 1rem;
  border: 1px solid #e1e5e9;
  border-radius: 0.5rem;
  background-color: #f8f9fa;
}

.route-suggestions__title {
  color: #495057;
  margin-bottom: 0.5rem;
  font-size: 1.1rem;
}

.route-suggestions__list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.route-suggestions__item {
  margin-bottom: 0.25rem;
}

.route-suggestions__link {
  color: #007bff;
  text-decoration: none;
  padding: 0.25rem 0.5rem;
  border-radius: 0.25rem;
  display: inline-block;
  transition: background-color 0.2s;
}

.route-suggestions__link:hover {
  background-color: #e9ecef;
  text-decoration: underline;
}

How Route Discovery Works

The route discovery mechanism:

  • Scans the App Router directory (app/ folder)
  • Identifies page files (page.tsx, page.ts, page.js)
  • Builds URL paths based on folder structure
  • Handles special cases:
    • Route groups (marketing) - don't affect URL structure
    • Private folders _components - ignored
    • Dynamic routes [id] - included as /[id]

Example Directory Structure

app/
├── page.tsx                    # → /
├── about/
│   └── page.tsx               # → /about
├── products/
│   ├── page.tsx               # → /products
│   └── [id]/
│       └── page.tsx           # → /products/[id]
├── (marketing)/
│   ├── pricing/
│   │   └── page.tsx           # → /pricing
│   └── features/
│       └── page.tsx           # → /features
└── _components/
    └── Header.tsx             # Ignored (private folder)

Generated routes: ["/", "/about", "/products", "/products/[id]", "/pricing", "/features"]

Advanced Usage

Server-Side Route Generation

For better performance, generate routes at build time:

// next.config.js
const { generateRoutesFile } = require('route-hints');

/** @type {import('next').NextConfig} */
const nextConfig = {
  webpack: (config, { isServer, dev }) => {
    if (isServer && !dev) {
      // Generate routes during production build
      generateRoutesFile('./app', './public/routes.json');
    }
    return config;
  },
};

module.exports = nextConfig;

Custom Threshold Logic

import { getSuggestions, calculateSimilarity } from 'route-hints';

function getSmartSuggestions(invalidPath: string, routes: string[]) {
  // Use different thresholds based on path length
  const threshold = invalidPath.length <= 5 ? 1 : Math.floor(invalidPath.length * 0.3);
  
  return getSuggestions(invalidPath, routes, threshold);
}

Integration with Analytics

'use client';

import { SuggestedRoutes } from 'route-hints';
import { useEffect } from 'react';

export default function NotFound() {
  useEffect(() => {
    // Track 404 events
    if (typeof window !== 'undefined') {
      analytics.track('404_Page_View', {
        path: window.location.pathname,
        referrer: document.referrer
      });
    }
  }, []);

  return (
    <div>
      <h1>404 - Page Not Found</h1>
      <SuggestedRoutes
        invalidPath={typeof window !== 'undefined' ? window.location.pathname : '/'}
      />
    </div>
  );
}

TypeScript

This package includes comprehensive TypeScript definitions:

import type { 
  RouteSuggestion, 
  SuggestedRoutesProps 
} from 'route-hints';

const suggestion: RouteSuggestion = {
  path: '/products',
  distance: 1
};

Contributing

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

License

MIT © [Your Name]

Support

  • 🐛 Report Issues
  • 📖 Documentation
  • 💬 Discussions

Made with ❤️ for the Next.js community

Keywords

nextjs

FAQs

Package last updated on 21 Aug 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts