Installation
@pulsora/react provides React hooks and components for seamless Pulsora integration.
Prerequisites
- React 16.8 or higher (hooks support)
- @pulsora/core package
NPM Installation
1. Install Both Packages
npm install @pulsora/react @pulsora/core
Or using Yarn:
yarn add @pulsora/react @pulsora/core
Or using pnpm:
pnpm add @pulsora/react @pulsora/core
2. Set Up Provider
Wrap your app with PulsoraProvider:
import { PulsoraProvider } from '@pulsora/react';
function App() {
return (
<PulsoraProvider config={{ apiToken: 'pub_your_token' }}>
<YourApp />
</PulsoraProvider>
);
}
3. Use Hooks
import { usePageview, useEvent } from '@pulsora/react';
function MyComponent() {
usePageview(); // Automatic pageview tracking
const trackEvent = useEvent();
return <button onClick={() => trackEvent('button_click')}>Click Me</button>;
}
TypeScript Support
Full TypeScript support is included:
import { PulsoraProvider, PulsoraConfig } from '@pulsora/react';
const config: PulsoraConfig = {
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
debug: process.env.NODE_ENV === 'development',
};
function App() {
return (
<PulsoraProvider config={config}>
<YourApp />
</PulsoraProvider>
);
}
Framework Setup
Create React App
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PulsoraProvider } from '@pulsora/react';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<PulsoraProvider config={{ apiToken: 'your-token' }}>
<App />
</PulsoraProvider>
</React.StrictMode>,
);
Next.js App Router
// app/providers.tsx
'use client';
import { PulsoraProvider } from '@pulsora/react';
import { usePathname } from 'next/navigation';
import { usePageview } from '@pulsora/react';
function PageviewTracker() {
const pathname = usePathname();
usePageview({ trigger: pathname });
return null;
}
export function Providers({ children }: { children: React.ReactNode }) {
return (
<PulsoraProvider
config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
}}
>
<PageviewTracker />
{children}
</PulsoraProvider>
);
}
// app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Next.js Pages Router
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { PulsoraProvider } from '@pulsora/react';
import { useRouter } from 'next/router';
import { usePageview } from '@pulsora/react';
function PageviewTracker() {
const router = useRouter();
usePageview({ trigger: router.asPath });
return null;
}
export default function App({ Component, pageProps }: AppProps) {
return (
<PulsoraProvider
config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
}}
>
<PageviewTracker />
<Component {...pageProps} />
</PulsoraProvider>
);
}
Remix
// app/root.tsx
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useLocation,
} from '@remix-run/react';
import { PulsoraProvider, usePageview } from '@pulsora/react';
function Document({ children }: { children: React.ReactNode }) {
const location = useLocation();
usePageview({ trigger: location.pathname });
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
{children}
<ScrollRestoration />
<Scripts />
<LiveReload />
</body>
</html>
);
}
export default function App() {
return (
<PulsoraProvider
config={{
apiToken: process.env.PULSORA_TOKEN!,
}}
>
<Document>
<Outlet />
</Document>
</PulsoraProvider>
);
}
Gatsby
// gatsby-browser.js & gatsby-ssr.js
import React from 'react';
import { PulsoraProvider } from '@pulsora/react';
export const wrapRootElement = ({ element }) => (
<PulsoraProvider
config={{
apiToken: process.env.GATSBY_PULSORA_TOKEN,
}}
>
{element}
</PulsoraProvider>
);
// src/components/Layout.js
import { usePageview } from '@pulsora/react';
export default function Layout({ children }) {
usePageview(); // Tracks on mount
return (
<div>
<header>...</header>
<main>{children}</main>
<footer>...</footer>
</div>
);
}
Vite
// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PulsoraProvider } from '@pulsora/react';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<PulsoraProvider
config={{
apiToken: import.meta.env.VITE_PULSORA_TOKEN,
}}
>
<App />
</PulsoraProvider>
</React.StrictMode>,
);
Environment Variables
Create React App
# .env
REACT_APP_PULSORA_TOKEN=pub_your_token
<PulsoraProvider config={{
apiToken: process.env.REACT_APP_PULSORA_TOKEN
}}>
Next.js
# .env.local
NEXT_PUBLIC_PULSORA_TOKEN=pub_your_token
<PulsoraProvider config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN
}}>
Vite
# .env
VITE_PULSORA_TOKEN=pub_your_token
<PulsoraProvider config={{
apiToken: import.meta.env.VITE_PULSORA_TOKEN
}}>
Remix
# .env
PULSORA_TOKEN=pub_your_token
Load in root.tsx:
export const loader = () => {
return json({
ENV: {
PULSORA_TOKEN: process.env.PULSORA_TOKEN,
},
});
};
Configuration Options
The PulsoraProvider accepts all @pulsora/core configuration options:
interface PulsoraConfig {
apiToken: string; // Required: Your public API token
endpoint?: string; // Optional: Custom API endpoint
autoPageviews?: boolean; // Optional: Auto-track pageviews (default: true)
debug?: boolean; // Optional: Enable debug logging (default: false)
maxRetries?: number; // Optional: Max retry attempts (default: 10)
retryBackoff?: number; // Optional: Initial retry delay in ms (default: 1000)
}
Development Configuration
<PulsoraProvider config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
debug: process.env.NODE_ENV === 'development',
endpoint: process.env.NODE_ENV === 'development'
? 'http://localhost:8000/api/ingest'
: undefined
}}>
Production Configuration
<PulsoraProvider config={{
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN!,
debug: false,
autoPageviews: true
}}>
SSR Considerations
Next.js SSR/SSG
The package is SSR-safe and won't initialize on the server:
// This is safe in SSR/SSG
export async function getServerSideProps() {
return {
props: {
// Your props
},
};
}
function Page() {
usePageview(); // Only runs in browser
return <div>...</div>;
}
Hydration Safety
Avoid tracking during hydration:
function Component() {
const [isHydrated, setIsHydrated] = useState(false);
const trackEvent = useEvent();
useEffect(() => {
setIsHydrated(true);
}, []);
const handleClick = () => {
if (isHydrated) {
trackEvent('button_click');
}
};
return <button onClick={handleClick}>Click</button>;
}
Verification
1. Check Provider Setup
import { usePulsora } from '@pulsora/react';
function DebugComponent() {
const pulsora = usePulsora();
useEffect(() => {
console.log('Pulsora initialized:', pulsora.isInitialized());
}, [pulsora]);
return null;
}
2. Enable Debug Mode
<PulsoraProvider config={{
apiToken: 'your-token',
debug: true // See console logs
}}>
3. Test Tracking
function TestTracking() {
const trackEvent = useEvent();
return (
<button
onClick={() => {
trackEvent('test_event', { timestamp: Date.now() });
console.log('Event tracked - check Network tab');
}}
>
Test Tracking
</button>
);
}
Common Issues
"usePulsora must be used within PulsoraProvider"
Ensure your component is wrapped with PulsoraProvider:
// ❌ Wrong
function App() {
const pulsora = usePulsora(); // Error!
return <div>...</div>;
}
// ✅ Correct
function App() {
return (
<PulsoraProvider config={{ apiToken: 'token' }}>
<MyComponent />
</PulsoraProvider>
);
}
function MyComponent() {
const pulsora = usePulsora(); // Works!
return <div>...</div>;
}
"Cannot read property 'init' of undefined"
Check that both packages are installed:
npm list @pulsora/react @pulsora/core
Build Errors
For TypeScript projects, ensure you have React types:
npm install --save-dev @types/react @types/react-dom
Bundle Size Impact
The React package adds minimal overhead:
- @pulsora/react: ~800 bytes gzipped
- @pulsora/core: ~3KB gzipped
- Total: ~3.8KB gzipped
Best Practices
1. Single Provider
Only use one PulsoraProvider at the app root:
// ✅ Good
<PulsoraProvider config={config}>
<App />
</PulsoraProvider>
// ❌ Bad - multiple providers
<PulsoraProvider config={config1}>
<PulsoraProvider config={config2}>
<App />
</PulsoraProvider>
</PulsoraProvider>
2. Environment Variables
Always use environment variables for tokens:
// ✅ Good
apiToken: process.env.NEXT_PUBLIC_PULSORA_TOKEN;
// ❌ Bad
apiToken: 'pub_hardcoded_token';
3. Error Boundaries
Wrap with error boundary to prevent tracking errors from crashing your app:
class AnalyticsErrorBoundary extends React.Component {
componentDidCatch(error) {
console.error('Analytics error:', error);
}
render() {
return this.props.children;
}
}
<AnalyticsErrorBoundary>
<PulsoraProvider config={config}>
<App />
</PulsoraProvider>
</AnalyticsErrorBoundary>;
Next Steps
- Provider Setup - Configure the PulsoraProvider
- Hooks Reference - Learn about all available hooks
- Examples - See real-world implementations
- SSR Support - Server-side rendering guide