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

navlens

v0.4.0

Published

Track client-side navigation history with timestamps across React, Vue, Svelte, and more

Readme

NavLens


Install

npm install navlens
# or
pnpm add navlens
# or
yarn add navlens

Usage

Next.js Next.js (App Router)

Add the tracker to your root layout:

// app/providers.tsx
"use client";
import { NavigationTracker as ReactNavigationTracker, useNextAdapter } from "navlens/next";
import { Suspense } from "react";

function NavigationTracker() {
  const adapter = useNextAdapter();
  return <ReactNavigationTracker adapter={adapter} />;
}

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <>
      <Suspense fallback={null}>
        <NavigationTracker />
      </Suspense>
      {children}
    </>
  );
}
// app/layout.tsx
import { Providers } from "./providers";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Use in any page:

"use client";
import { useRouter } from "next/navigation";
import { getPreviousPath } from "navlens";

export default function ProductDetailPage() {
  const router = useRouter();

  function handleBack() {
    const prev = getPreviousPath();
    if (prev) router.back();
    else router.push("/");
  }

  return <button onClick={handleBack}>← Back</button>;
}

View example


React Router React Router

// src/App.tsx
import { Routes, Route } from "react-router-dom";
import {
  NavigationTracker as ReactNavigationTracker,
  useReactRouterAdapter,
} from "navlens/react-router";

export default function App() {
  const adapter = useReactRouterAdapter();

  return (
    <>
      <ReactNavigationTracker adapter={adapter} />
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/products" element={<ProductsPage />} />
        <Route path="/products/:id" element={<ProductDetailPage />} />
      </Routes>
    </>
  );
}
// src/pages/ProductDetailPage.tsx
import { useNavigate } from "react-router-dom";
import { getPreviousPath } from "navlens";

export default function ProductDetailPage() {
  const navigate = useNavigate();

  function handleBack() {
    const prev = getPreviousPath();
    if (prev) navigate(-1);
    else navigate("/");
  }

  return <button onClick={handleBack}>← Back</button>;
}

View example


Vue Router Vue Router

<!-- src/App.vue -->
<script setup lang="ts">
import { useVueNavigationHistory } from "navlens/vue-router";
useVueNavigationHistory();
</script>

<template>
  <RouterView />
</template>
<!-- src/views/ProductDetailView.vue -->
<script setup lang="ts">
import { useRouter } from "vue-router";
import { getPreviousPath } from "navlens";

const router = useRouter();

function handleBack() {
  const prev = getPreviousPath();
  if (prev) router.back();
  else router.push("/");
}
</script>

<template>
  <button @click="handleBack">← Back</button>
</template>

View example


Nuxt Nuxt

<!-- layouts/default.vue -->
<script setup lang="ts">
import { useVueNavigationHistory } from "navlens/nuxt";
useVueNavigationHistory();
</script>

<template>
  <slot />
</template>
<!-- pages/products/[id].vue -->
<script setup lang="ts">
import { getPreviousPath } from "navlens";

const router = useRouter();

function handleBack() {
  const prev = getPreviousPath();
  if (prev) router.back();
  else navigateTo("/");
}
</script>

View example


Quasar Quasar

// src/boot/navlens.ts
import { boot } from "quasar/wrappers";
import { pushEntry } from "navlens/quasar";

export default boot(({ router }) => {
  router.afterEach((to) => {
    pushEntry(to.fullPath);
  });
});

Register in quasar.config.ts:

boot: ["navlens"];

View example


SvelteKit SvelteKit

<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { afterNavigate } from '$app/navigation'
  import { createNavigationHandler } from 'navlens/svelte'

  afterNavigate(createNavigationHandler())
</script>

<slot />
<!-- src/routes/products/[id]/+page.svelte -->
<script lang="ts">
  import { goto } from '$app/navigation'
  import { getPreviousPath } from 'navlens'

  function handleBack() {
    const prev = getPreviousPath()
    if (prev) history.back()
    else goto('/')
  }
</script>

<button on:click={handleBack}>← Back</button>

View example


API

getPreviousPath(config?)

Returns the path the user was on before the current page, or undefined if there is no previous entry.

import { getPreviousPath } from "navlens";

const prev = getPreviousPath(); // e.g. '/products'

getCurrentPath(config?)

Returns the most recently recorded path.

import { getCurrentPath } from "navlens";

const current = getCurrentPath(); // e.g. '/products/42'

getNavHistory(config?)

Returns the full navigation history array, newest first.

import { getNavHistory } from "navlens";

const history = getNavHistory();
// [{ path: '/products/42', timestamp: 1714000000000 }, ...]

clearNavHistory(config?)

Clears all stored navigation history.

import { clearNavHistory } from "navlens";

clearNavHistory();

pushEntry(path, config?)

Manually push a path into history. Used internally by all adapters.

import { pushEntry } from "navlens";

pushEntry("/custom-path");

Config

All functions accept an optional config object:

interface NavHistoryConfig {
  storageKey?: string; // default: 'navtrace_history'
  maxAgeMs?: number; // default: 1800000 (30 min)
  maxEntries?: number; // default: 50
  storage?: "session" | "local"; // default: 'session'
}
const config = {
  storageKey: "my_app_nav",
  maxAgeMs: 3600000, // 1 hour
  maxEntries: 100,
  storage: "local",
};

getPreviousPath(config);
getNavHistory(config);

Design

  • core/ has zero framework imports
  • sessionStorage by default, localStorage optional
  • No duplicate consecutive entries stored
  • Entries pruned by maxAgeMs + capped at maxEntries
  • Storage errors caught silently (SSR-safe)

Contributing

Setup

git clone https://github.com/farzinfiroozi/navlens.git
cd navlens
pnpm install

Structure

navlens/
├── packages/
│   └── navlens/          # npm package
│       ├── src/
│       │   ├── core/     # storage, helpers, types — zero framework deps
│       │   ├── adapters/ # one file per framework
│       │   ├── hooks/    # react/, vue/, svelte/
│       │   └── components/
│       └── tsup.config.ts
└── examples/
    ├── next/
    ├── react-router/
    ├── vue-router/
    ├── nuxt/
    ├── quasar/
    └── sveltekit/

Commands

# build the package
pnpm --filter navlens build

# watch mode
pnpm --filter navlens dev

# run tests
pnpm --filter navlens test

# typecheck
pnpm --filter navlens typecheck

Guidelines

  • core/ must stay framework-free
  • New adapter → add entry in tsup.config.ts, package.json exports, and src/index.ts
  • All storage reads/writes must be wrapped in try/catch (SSR safety)
  • No consecutive duplicate entries — enforced in pushEntry

License

MIT © Farzin Firoozi