Farzin.io

Farzin.io
cover
NavLens, Your Browser Back Button’s Memory Upgrade

Published on 5/8/2026

NavLens, Your Browser Back Button’s Memory Upgrade

How I stopped re-writing the same navigation history hack in every project and built a proper solution

NavigationNpmOpen Source

The Problem I Kept Ignoring

Every project I've worked on eventually hits the same wall: a page opens via a deep link, the user taps the back button, and the browser has nowhere to go. Or you need to know where the user came from to conditionally render a "Back" button. Or you want to track navigation patterns for analytics.

The classic band-aid is passing the previous URL inside router state or as a query param:

router.push(`/product/${id}?from=/shop`)

It works. Until it doesn't, stale params, missing state after a refresh, inconsistency across navigations. I've written this hack (or some variation of it) in nearly every project I've touched, always slightly differently, always leaving it a little messy.

So I decided to fix it once and for all.

Introducing NavLens

NavLens is a small, framework-agnostic library that tracks client-side navigation history with timestamps. It exposes a simple API:
  • getPreviousPath(), where the user came from
  • getNavHistory(), the full breadcrumb trail
  • getCurrentPath(), the most recently recorded path
  • clearNavHistory(), reset everything

That's it. No opinions on what you do with the data.

import { getPreviousPath, getNavHistory } from 'navlens' const prev = getPreviousPath() // '/shop' const history = getNavHistory() // [{ path: '/products/42', timestamp: 1714000000000 }, ...]
NavLens framework support

Framework Adapters

NavLens can be used across the popular routing stacks: Next.js, React Router, Vue Router, Nuxt, Quasar, and SvelteKit. Each adapter keeps the core package framework-agnostic, so you don't wire up listeners yourself.

You can find the install command and adapter entry points on npm.

Use Cases

The core idea is simple: any time your app needs to understand where a user has been before reaching the current route, that logic should not be re-invented page by page.

NavLens is useful when:

  • A page is opened from a deep link, push notification, email, or shared URL, and the browser back button has no meaningful in-app destination
  • You want to show a contextual Back button only when there is actually a previous in-app route
  • You need lightweight navigation history tracking for product analytics or debugging user flows
  • You want to avoid passing a previousUrl, from, or router state value during every navigation
  • You want timestamped route history that can help with fraud detection or automation detection

I built it because I kept solving this in a slightly different way in every project, sometimes cleanly, sometimes with ugly query params or router state. NavLens turns that repeated workaround into one tested package. It is already running in production in a Next.js app, and the adapter layer makes the same idea usable across the popular frameworks listed above.

The main use case that drove this library. A user opens a product page directly from a push notification. They tap back. router.back() sends them to whatever was open in their browser before your app, completely outside your control.

With NavLens, you can safely handle this:

'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('/shop') // safe fallback } return <button onClick={handleBack}>← Back</button> }

No query params. No router state gymnastics. No duplicated logic per page.

Fraud & Automation Detection

A less obvious use case: detecting bots or automated sessions.

Real users take time between page visits. A script that hammers through your checkout flow or scrapes your pricing pages will leave a very different timestamp pattern in the history stack.

import { getNavHistory } from 'navlens' function isSuspiciousSession(thresholdMs = 300) { const history = getNavHistory() if (history.length < 2) return false return history.some((entry, i) => { const next = history[i + 1] if (!next) return false return entry.timestamp - next.timestamp < thresholdMs }) } // flag the session, show a captcha, or bail out of the flow if (isSuspiciousSession()) { // handle accordingly }

Since NavLens stores a timestamp with every entry, you have everything you need to build this logic yourself, NavLens just gives you the raw data, the decision is yours.

Design Principles

A few things I was strict about while building this:

  • Zero framework imports in core, core/ is pure TypeScript with no dependencies
  • SSR-safe, all storage reads/writes are wrapped in try/catch, no window access on the server
  • No consecutive duplicates, refreshing a page doesn't pollute the history stack
  • Configurable, you control storage type, max age, key prefix, and entry cap
const config = { storageKey: 'my_app_nav', maxAgeMs: 3600000, // 1 hour maxEntries: 100, storage: 'local', // or 'session' (default) } getPreviousPath(config) getNavHistory(config)

The library is live in production on a Next.js app and tested across all the adapters listed above.

If you've ever pasted ?from=... into a URL just to make a back button work, this one's for you.
Share this post