use-page-view
A React hook for tracking page views and user engagement time. This hook provides real-time tracking of how long users spend on a page, their activity status, and flexible callbacks for implementing custom persistence and analytics.
Features
- 📊 Track time spent on pages
- 👀 Monitor user activity and page visibility
- 🔄 Periodic updates through callback
- ⏱️ Configurable thresholds and intervals
- 🎯 Support for one-time tracking
- 🔑 User identification support
- 🚀 Lightweight with minimal overhead
- 🌐 SSR/React Router/Next.js compatible
- 🛠️ Flexible - implement your own persistence strategy
Installation
npm install use-page-view
# or
yarn add use-page-view
# or
pnpm add use-page-view
Quick Start
import { usePageView } from 'use-page-view';
function MyPage() {
const { timeSpent, isActive } = usePageView({
pageId: 'my-page',
onPageView: (data) => console.log('Page view:', data),
});
return (
<div>
<h1>My Page</h1>
<p>
Time spent: {timeSpent}s {isActive ? '🟢 Active' : '🔴 Inactive'}
</p>
</div>
);
}
Compatibility
- React: 16.8+ (hooks support required)
- TypeScript: Full TypeScript support included
- Browsers: Modern browsers
- SSR: Compatible with React Router, Next.js and other SSR frameworks
- React Native: Not supported (web-only)
Usage
import { usePageView } from 'use-page-view';
function BlogPost() {
const { timeSpent, isActive } = usePageView({
pageId: 'blog-post-123',
userId: 'user-456', // Optional
minTimeThreshold: 10, // Minimum time before recording (seconds)
heartbeatInterval: 30, // How often to send updates (seconds)
inactivityThreshold: 60, // Time before user is considered inactive (seconds)
onPageView: async (data) => {
try {
await fetch('/api/track-page-view', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
} catch (error) {
console.error('Failed to track page view:', error);
}
},
});
return (
<div>
<div>
Time: {formatTime(timeSpent)} {isActive ? '🟢' : '🔴'}
</div>
<article>Your content here...</article>
</div>
);
}
// Helper function to format time
function formatTime(seconds: number): string {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
API
usePageView(options)
Options
Option | Type | Default | Description |
---|---|---|---|
pageId |
string |
Required | Unique identifier for the page being tracked |
userId |
string |
Optional | User identifier if user is logged in |
minTimeThreshold |
number |
5 |
Minimum time in seconds before recording a view |
heartbeatInterval |
number |
30 |
How often to send updates in seconds |
inactivityThreshold |
number |
30 |
Time in seconds before user is considered inactive |
onPageView |
(data: PageViewData) => void |
Optional | Callback function to handle page view data |
trackOnce |
boolean |
false |
Track only the initial view |
trackOnceDelay |
number |
0 |
Minimum time in seconds before recording a view when trackOnce is true |
Returns
Property | Type | Description |
---|---|---|
timeSpent |
number |
Total time spent on the page in seconds |
isActive |
boolean |
Whether the user is currently active on the page |
PageViewData
interface PageViewData {
pageId: string;
userId?: string;
timeSpent: number;
isActive: boolean;
}
Examples
Basic Usage
function Article() {
const { timeSpent, isActive } = usePageView({
pageId: 'article-789',
onPageView: (data) => {
console.log('Article view:', data);
},
});
return (
<div>
<h1>Article Title</h1>
<div>Time spent: {formatTime(timeSpent)}</div>
<p>Article content...</p>
</div>
);
}
One-time Tracking
function LandingPage() {
const { timeSpent, isActive } = usePageView({
pageId: 'landing-page',
trackOnce: true,
trackOnceDelay: 30, // Track after 30 seconds
onPageView: (data) => {
analytics.track('landing_page_view', data);
},
});
return (
<div>
<h1>Welcome</h1>
<div>Time on page: {formatTime(timeSpent)}</div>
</div>
);
}
Error Handling
function PageWithErrorHandling() {
const { timeSpent, isActive } = usePageView({
pageId: 'important-page',
onPageView: async (data) => {
try {
const response = await fetch('/api/analytics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
} catch (error) {
console.error('Analytics tracking failed:', error);
// Optionally queue for retry or use alternative tracking
}
},
});
return <div>Your page content</div>;
}
Features in Detail
Time Tracking
- Tracks total time spent on the page
- Updates every second
- Handles browser tab switching and page visibility changes
- Supports custom initial time values for persistence scenarios
Activity Monitoring
- Detects user activity through mouse, keyboard, and touch events
- Monitors page visibility changes
- Updates active status based on user engagement
- Configurable inactivity threshold
Flexible Persistence
- No built-in persistence to keep the library lightweight
- Easy to implement custom persistence strategies
Callback System
- Periodic updates through
onPageView
callback - Configurable update interval with
heartbeatInterval
- Minimum time threshold with
minTimeThreshold
- One-time tracking option with
trackOnce
Performance
- Minimal overhead: Efficient event handling with automatic cleanup
- Memory efficient: Uses
requestIdleCallback
when available - Bundle size: < 5KB minified + gzipped
- No dependencies: Zero external dependencies for maximum compatibility
- Automatic cleanup: All event listeners and timers are cleaned up on unmount
FAQ
Q: Does this work with SSR/React Router/Next.js? A: Yes, the hook safely handles server-side rendering by checking for browser environment before initializing.
Q: How do I persist time tracking across page reloads? A: Implement your own persistence strategy using the examples above. You can use localStorage, sessionStorage, databases, or any other storage method that fits your needs.
Q: How accurate is the time tracking? A: Time tracking is updated every second and accounts for page visibility changes and user inactivity.
Q: Can I track multiple pages in the same app?
A: Yes, use different pageId
values for each page or component you want to track separately.
Q: Does this affect my app's performance? A: No, the hook uses efficient event handling and cleanup. The bundle size is minimal (< 3KB).
Q: What events are considered "activity"? A: Mouse movement, clicks, keyboard input, touch events, and scroll events are all considered user activity.
Q: Why doesn't the hook include built-in localStorage support? A: To keep the library lightweight and flexible. Different applications have different persistence needs, and this approach allows you to implement exactly what you need without bloating the core library.
Contributing
Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.
License
This project is licensed under the MIT License - see the LICENSE file for details.