An open API service indexing awesome lists of open source software.

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.

Awesome Lists containing this project

README

          

# React Page Tracker


react-page-tracker Logo

`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


[![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/typeart)

## License

Licensed under the [MIT License](LICENSE.md).