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

better-link

v0.2.0

Published

Enhanced Next.js Link component with isActive functionality

Readme

Better Link

✨ Features

  • Active State Detection - Automatically determines if a link is active based on the current URL
  • Multiple APIs - Choose between different patterns based on your preferences
  • Fully Typed - Complete TypeScript support with proper typing
  • Zero Dependencies - Only peer dependencies on Next.js and React
  • Small Footprint - Lightweight implementation with minimal overhead
  • Flexible Patterns - Support for both class-based and render props patterns

📦 Installation

# Using npm
npm install better-link

# Using yarn
yarn add better-link

# Using pnpm
pnpm add better-link

# Using bun
bun add better-link

🚀 Quick Start

For Next.js App Router (v13+)

If you're using Next.js with App Router, use the special factory pattern:

"use client"; // IMPORTANT: This must be a client component

import { createNavLink } from "better-link";

// Create the NavLink component inside your client component
const NavLink = createNavLink();

export function Navigation() {
  return (
    <nav>
      <NavLink href="/" exact activeClassName="active">
        Home
      </NavLink>
      <NavLink href="/about" activeClassName="active">
        About
      </NavLink>
    </nav>
  );
}

Then import your Navigation component in your layout:

// layout.jsx (server component)
import { Navigation } from "./navigation";

export default function Layout({ children }) {
  return (
    <div>
      <Navigation />
      <main>{children}</main>
    </div>
  );
}

For Other React Applications

import { NavLink } from "better-link";

function Navigation() {
  return (
    <nav>
      <NavLink href="/" exact activeClassName="active">
        Home
      </NavLink>
      <NavLink href="/about" activeClassName="active">
        About
      </NavLink>
    </nav>
  );
}

📚 Usage

Important: Client Components in Next.js

Since better-link uses the usePathname hook from Next.js, make sure to use it in client components:

// navigation.tsx
"use client"; // Add this directive

import { createNavLink } from "better-link";

// Create the component inside your client component
const NavLink = createNavLink();

export function Navigation() {
  return (
    <nav>
      <NavLink href="/" exact activeClassName="active">
        Home
      </NavLink>
      <NavLink href="/about" activeClassName="active">
        About
      </NavLink>
    </nav>
  );
}

Then import your Navigation component in your layout:

// layout.tsx
import { Navigation } from "./navigation";

export default function Layout({ children }) {
  return (
    <div>
      <Navigation />
      <main>{children}</main>
    </div>
  );
}

Using the Factory Pattern for Advanced Links

"use client";

import { createAdvancedNavLink } from "better-link";

// Create the component inside your client component
const AdvancedNavLink = createAdvancedNavLink();

export function SideNav() {
  return (
    <nav>
      <AdvancedNavLink to="/dashboard">
        {({ isActive }) => (
          <span className={isActive ? "text-blue-500" : "text-gray-500"}>
            Dashboard
          </span>
        )}
      </AdvancedNavLink>
    </nav>
  );
}

Basic NavLink

The NavLink component extends Next.js's Link component with active state detection:

import { NavLink } from "better-link";

export default function Navigation() {
  return (
    <nav className="flex gap-4">
      <NavLink
        href="/"
        exact
        className="text-gray-500"
        activeClassName="font-bold text-blue-600"
      >
        Home
      </NavLink>

      <NavLink
        href="/about"
        className="text-gray-500"
        activeClassName="font-bold text-blue-600"
      >
        About
      </NavLink>
    </nav>
  );
}

Render Props Pattern

For more flexibility, use renderChildren to customize your link based on its active state:

import { NavLink } from "better-link";

export default function Navigation() {
  return (
    <nav className="flex gap-4">
      <NavLink
        href="/dashboard"
        renderChildren={(isActive) => (
          <div className="flex items-center gap-2">
            <span
              className={isActive ? "text-blue-600 font-bold" : "text-gray-500"}
            >
              Dashboard
            </span>
            {isActive && (
              <span className="px-2 py-1 bg-blue-100 text-xs rounded-full">
                Active
              </span>
            )}
          </div>
        )}
      >
        Dashboard
      </NavLink>
    </nav>
  );
}

Advanced Pattern

For even more control, use the AdvancedNavLink component with the render props pattern:

import { AdvancedNavLink } from "better-link";

export default function Navigation() {
  const menuItems = [
    { id: "home", label: "Home", href: "/" },
    { id: "dashboard", label: "Dashboard", href: "/dashboard" },
    { id: "settings", label: "Settings", href: "/settings" },
  ];

  return (
    <nav className="flex flex-col gap-2">
      {menuItems.map((item) => (
        <AdvancedNavLink to={item.href} key={item.id}>
          {({ isActive }) => (
            <div
              className={`
                p-3 rounded-lg transition-all
                ${
                  isActive
                    ? "bg-blue-50 text-blue-600 border-l-4 border-blue-600"
                    : "bg-white text-gray-600 hover:bg-gray-50"
                }
              `}
            >
              {item.label}
              {isActive && <span className="ml-2">•</span>}
            </div>
          )}
        </AdvancedNavLink>
      ))}
    </nav>
  );
}

🔧 API Reference

NavLink

NavLink extends Next.js's Link component with active state functionality.

Props

| Prop | Type | Default | Description | | ----------------- | ---------------------------------------- | -------- | ------------------------------------------------------------------- | | href | string | Required | URL to navigate to | | children | React.ReactNode | Required | Link content | | className | string | '' | Base class name for the link | | activeClassName | string | '' | Class name to apply when the link is active | | exact | boolean | false | If true, the link will only be active when the path matches exactly | | renderChildren | (isActive: boolean) => React.ReactNode | - | Function to render children with isActive state |

All other props from Next.js's Link component are also supported.

AdvancedNavLink

AdvancedNavLink provides a more flexible API using render props pattern.

Props

| Prop | Type | Default | Description | | ---------- | ------------------------------------------------------------------------ | -------- | ------------------------------------------ | | to | string | Required | URL to navigate to | | key | string \| number | - | Optional key for the link | | children | React.ReactNode \| ((props: { isActive: boolean }) => React.ReactNode) | Required | Either static content or a render function |

All other props from Next.js's Link component are also supported.

🔍 How It Works

better-link uses Next.js's usePathname hook to determine if a link is active. It compares the current pathname with the link's href or to property:

// For exact matching
const isActive = pathname === href;

// For partial matching (default)
const isActive = pathname.startsWith(href);

🧰 Use Cases

  • Navigation Menus - Highlight the current section in your app
  • Breadcrumbs - Show the active path in breadcrumb navigation
  • Tab Interfaces - Build tab interfaces with active indicators
  • Sidebar Navigation - Create sidebar menus with active states
  • Pagination - Highlight the current page in pagination controls

🌱 Upcoming Features

  • [ ] Support for pattern matching URLs
  • [ ] Active state based on query parameters
  • [ ] Animation helpers for transitions
  • [ ] Additional accessibility improvements

🤝 Contributing

Contributions are always welcome! Feel free to open issues or submit pull requests.

  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

📄 License

Distributed under the MIT License. See LICENSE for more information.

📌 Built With

❓ Troubleshooting

"Invalid Hook Call" Error in Next.js App Router

If you encounter an error like this:

Invalid hook call. Hooks can only be called inside of the body of a function component.

Make sure you are using the factory pattern for Next.js App Router:

"use client"; // IMPORTANT!

import { createNavLink } from "better-link";

// Create the component inside your client component
const NavLink = createNavLink();

export function Navigation() {
  return (
    <nav>
      <NavLink href="/" exact activeClassName="active">
        Home
      </NavLink>
    </nav>
  );
}

This factory pattern ensures hooks are used correctly and avoids the invalid hook call error.

🌱 Upcoming Features

  • [ ] Support for pattern matching URLs
  • [ ] Active state based on query parameters
  • [ ] Animation helpers for transitions
  • [ ] Additional accessibility improvements