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
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
getPreviousPath(), where the user came fromgetNavHistory(), the full breadcrumb trailgetCurrentPath(), the most recently recorded pathclearNavHistory(), 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 }, ...]

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.
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.
Solving the Deep Link Problem
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
windowaccess 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.
?from=... into a URL just to make a back button work, this one's for you.