https://github.com/hsuanyi-chou/react-page-tracker
lightweight, zero-dependency library providing accurate navigation tracking, fixed document.referrer value, and complete history support for React frameworks. Fully compatible with Next.js, Remix, TanStack Query, and React Router.
https://github.com/hsuanyi-chou/react-page-tracker
browse-history-list fix-document-referrer react
Last synced: 11 months ago
JSON representation
lightweight, zero-dependency library providing accurate navigation tracking, fixed document.referrer value, and complete history support for React frameworks. Fully compatible with Next.js, Remix, TanStack Query, and React Router.
- Host: GitHub
- URL: https://github.com/hsuanyi-chou/react-page-tracker
- Owner: hsuanyi-chou
- License: mit
- Created: 2024-11-12T15:22:53.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-05-16T01:37:19.000Z (about 1 year ago)
- Last Synced: 2025-08-05T08:33:31.461Z (12 months ago)
- Topics: browse-history-list, fix-document-referrer, react
- Language: TypeScript
- Homepage: https://react-page-tracker.typeart.cc/
- Size: 1.01 MB
- Stars: 23
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# React Page Tracker
`react-page-tracker` is a lightweight, zero-dependency library providing accurate navigation tracking, fixed
`document.referrer` value, and complete history support for React frameworks. Fully compatible with Next.js, Remix, TanStack Query, and React Router.
👉 [Demo](https://react-page-tracker.typeart.cc/demo)
📖 [Documentation](https://react-page-tracker.typeart.cc)
🚀 [An awesome use case: Magic Back Button in shadcn/ui expansions](https://shadcnui-expansions.typeart.cc/docs/magic-back-button)
## Features
- 📝 Identifies whether the user navigated to the page via browser back/forward buttons or by clicking a link.
- 🧩 Works with `History.go()`, `history.forward()`, `history.back()`, and `history.pushState()`.
- 🐞 Fixes incorrect `document.referrer` after navigation, providing the correct referrer.
- 💡 Accurately determines whether the current page is the first or last page.
- 🧭 Offers a complete history browsing record.
- 🚀 Supports Next.js, Remix, TanStack Query, and React Router.
- ⚡️ zero deps.
- ⭐️ typed-safe.
### Usage
```tsx
type PageTrackerState = {
/** current page index */
pageIndex: number;
/** correct `document.referrer` */
referrer: string;
/** whether the current page is the first page */
isFirstPage: boolean;
/** whether the current page is the last page */
isLastPage: boolean;
/** whether the user navigated to the page via browser back/forward buttons or by clicking a link */
pageEvent?: PageEvent; // 'back' | 'forward' | 'push' | undefined. undefined for first visit.
/** history browsing record */
pageHistory: string[]; // ['/', '/products', '/pdocuts/1', '/products/2', '/about', ...]
/**
* total page history length.
* When user press `back` button, the `pageHistory`'s end link will become the current link.
* You may need this total length to handle `history.go(N)` to forward N page.
*/
pageHistoryLength: number;
}
```
Simply to get the values you need in any component.
```tsx
const { pageIndex, referrer, isFirstPage, isLastPage, pageEvent, pageHistory } = usePageTrackerStore((state) => state);
```
## Installation
```bash
npm install react-page-tracker
```
▲ Next.js
`layout.tsx`
```diff
+ import { PageTracker } from 'react-page-tracker';
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode; }>) {
return (
+ // next 14, there's an auto detection. A better way is to follow your environment. e,g. `enableStrictModeHandler={process.env.NODE_ENV === 'local'}`
+ // next 15+. always be false
{children}
);
}
```
If you are using `nextjs 15`, you have to set `enableStrictModeHandler` to `false`
In `nextjs 14`, strict mode will execute twice when push a page (e,g. click an Anchor link) which may cause `pageHistory` values error and hard to handle in development mode.
But in remix, tanstack/query, react-router, it will not execute twice. works fine.
There's an automatic detection for nextjs strict mode, so you don't need to set this prop.
But I can't detect 14 or 15, so you have to set this prop to `false` in next 15.
PS: DO NOT set `enableStrictModeHandler` to `true` in production, it may cause issues.
**You are all set up! Now you can access the page tracking information in any component.**
`component.tsx`
```tsx
import { usePageTrackerStore } from 'react-page-tracker';
export const PageTrackerValue = () => {
// get all state
const state = usePageTrackerStore((state) => state);
// get the specific state you'd like to use
const pickState = usePageTrackerStore((state) => ({
isFirstPage: state.isFirstPage,
referrer: state.referrer,
pageHistory: state.pageHistory,
}));
return (
state: {JSON.stringify(state)}
pickState: {JSON.stringify(pickState)}
);
};
```
[Next.js template](https://github.com/hsuanyi-chou/react-page-tracker/tree/main/templates/next-example)
🆁 Remix
force Vite to build to CommonJS
`vite.config.ts`
```diff
export default defineConfig({
+ ssr: {
+ noExternal: ['react-page-tracker'], // force Vite to build to CommonJS
+ },
plugins: [
remix({
future: {
v3_fetcherPersist: true,
v3_relativeSplatPath: true,
v3_throwAbortReason: true,
v3_singleFetch: true,
v3_lazyRouteDiscovery: true,
},
}),
tsconfigPaths(),
],
});
```
`root.tsx`
```diff
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react';
import type { LinksFunction } from '@remix-run/node';
import './tailwind.css';
+ import { PageTracker } from 'react-page-tracker';
export const links: LinksFunction = () => [
{ rel: 'preconnect', href: 'https://fonts.googleapis.com' },
{
rel: 'preconnect',
href: 'https://fonts.gstatic.com',
crossOrigin: 'anonymous',
},
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap',
},
];
export function Layout({ children }: { children: React.ReactNode }) {
return (
+
{children}
);
}
export default function App() {
return ;
}
```
**You are all set up! Now you can access the page tracking information in any component.**
`component.tsx`
```tsx
import { usePageTrackerStore } from 'react-page-tracker';
export const PageTrackerValue = () => {
// get all state
const state = usePageTrackerStore((state) => state);
// get the specific state you'd like to use
const pickState = usePageTrackerStore((state) => ({
isFirstPage: state.isFirstPage,
referrer: state.referrer,
pageHistory: state.pageHistory,
}));
return (
state: {JSON.stringify(state)}
pickState: {JSON.stringify(pickState)}
);
};
```
[Remix template](https://github.com/hsuanyi-chou/react-page-tracker/tree/main/templates/remix-example)
## Contributing
Be welcome to contribute! Here's how you can contribute:
- [Open an issue](https://github.com/hsuanyi-chou/react-page-tracker/issues) if you believe you've encountered a bug.
- Make a [pull request](https://github.com/hsuanyi-chou/react-page-tracker/pull) to add new features/make quality-of-life improvements/fix bugs.
Built by Hsuan Yi, Chou
[](https://www.buymeacoffee.com/typeart)
## License
Licensed under the [MIT License](LICENSE.md).