React/Next.js SDK for fnel funnel tracking. This package provides a React Provider and hooks for easy integration of fnel tracking into your React applications.
- 🚀 Easy Integration: Simple Provider pattern with React hooks
- 🔄 Auto-initialization: Automatically loads and initializes the fnel SDK
- 📱 React Native Ready: Works with React Native (web only)
- 🎯 Funnel Tracking: Dedicated hooks for funnel step tracking
- đź’ľ State Management: Automatic state synchronization with the SDK
- 🛡️ TypeScript: Full TypeScript support with comprehensive types
- đź”§ Debug Support: Access to debug information and SDK state
npm install @fnel/react
# or
yarn add @fnel/react
# or
pnpm add @fnel/react
import { FnelProvider } from '@fnel/react';
function App() {
return (
<FnelProvider apiToken="your-api-token-here">
<YourApp />
</FnelProvider>
);
}
import { useFnel } from '@fnel/react';
function LandingPage() {
const { track, isInitialized } = useFnel();
useEffect(() => {
if (isInitialized) {
track({
name: 'landing_page',
step: 1,
funnel: 'abc123def'
});
}
}, [isInitialized, track]);
return <div>Welcome to our landing page!</div>;
}
The main provider component that initializes the fnel SDK.
<FnelProvider
apiToken="your-api-token"
autoInit={true}
onInit={(result) => console.log('Initialized:', result)}
onError={(error) => console.error('Error:', error)}
>
{children}
</FnelProvider>
apiToken
(required): Your fnel API tokenautoInit
(optional): Whether to automatically initialize on mount (default: true)onInit
(optional): Callback when initialization succeedsonError
(optional): Callback when errors occur
The main hook that provides access to all fnel functionality.
const {
isInitialized,
userId,
track,
version,
getUserId,
getQueueLength,
clearQueue,
clearStorage,
reset
} = useFnel();
isInitialized
: Boolean indicating if the SDK is readyuserId
: Current user ID (string or null)track
: Function to track eventsversion
: SDK version stringgetUserId()
: Function to get current user IDgetQueueLength()
: Function to get number of queued eventsclearQueue()
: Function to clear the event queueclearStorage()
: Function to clear stored user datareset()
: Function to reset the entire SDK state
A convenience hook for tracking funnel steps.
const { trackStep, trackLandingPage, trackConversion, isInitialized } = useFunnelTracking('funnel123');
// Track a specific step
await trackStep(2, 'signup_form', { source: 'google_ads' });
// Track landing page
await trackLandingPage({ referrer: 'facebook' });
// Track conversion
await trackConversion({ value: 99.99, currency: 'USD' });
funnelId
: The short ID of your funnel
trackStep(step, name?, data?)
: Track a specific funnel steptrackLandingPage(data?)
: Track landing page (step 1)trackConversion(data?)
: Track conversion (step 999)isInitialized
: Boolean indicating if the SDK is ready
import { useFnel } from '@fnel/react';
function CheckoutPage() {
const { track, isInitialized } = useFnel();
const handlePurchase = async () => {
if (isInitialized) {
try {
await track({
name: 'purchase_completed',
step: 5,
funnel: 'ecommerce_funnel',
amount: 99.99,
currency: 'USD'
});
console.log('Purchase tracked successfully!');
} catch (error) {
console.error('Failed to track purchase:', error);
}
}
};
return (
<button onClick={handlePurchase}>
Complete Purchase
</button>
);
}
import { useFunnelTracking } from '@fnel/react';
function SignupFlow() {
const { trackStep, isInitialized } = useFunnelTracking('signup_funnel');
const handleEmailSubmit = async (email: string) => {
if (isInitialized) {
await trackStep(2, 'email_entered', { email });
}
// Continue with signup logic
};
const handleFormSubmit = async (formData: any) => {
if (isInitialized) {
await trackStep(3, 'form_submitted', { fields: Object.keys(formData) });
}
// Submit form
};
return (
<div>
<input
type="email"
placeholder="Enter email"
onBlur={(e) => handleEmailSubmit(e.target.value)}
/>
<button onClick={() => handleFormSubmit({ email: 'test@example.com' })}>
Sign Up
</button>
</div>
);
}
import { FnelProvider } from '@fnel/react';
function App() {
const handleInit = (result) => {
console.log('fnel initialized with user:', result.userId);
};
const handleError = (error) => {
console.error('fnel error:', error);
// Maybe show a user-friendly error message
};
return (
<FnelProvider
apiToken={process.env.REACT_APP_FNEL_TOKEN}
autoInit={true}
onInit={handleInit}
onError={handleError}
>
<YourApp />
</FnelProvider>
);
}
import { FnelProvider } from '@fnel/react';
function App() {
return (
<FnelProvider apiToken="your-token" autoInit={false}>
<ManualInitExample />
</FnelProvider>
);
}
function ManualInitExample() {
const { track, isInitialized } = useFnel();
const handleManualInit = async () => {
// You can manually trigger initialization here
// The SDK will handle this automatically when you first call track()
};
const handleTrack = async () => {
if (!isInitialized) {
console.log('SDK not ready, event will be queued');
}
await track({
name: 'manual_track',
step: 1,
funnel: 'test_funnel'
});
};
return (
<div>
<button onClick={handleTrack}>
Track Event
</button>
<p>Status: {isInitialized ? 'Ready' : 'Not Ready'}</p>
</div>
);
}
The SDK automatically handles common errors and provides callbacks for error handling:
<FnelProvider
apiToken="your-token"
onError={(error) => {
if (error.message.includes('Subscription')) {
// Handle subscription errors
showUpgradePrompt();
} else if (error.message.includes('timeout')) {
// Handle timeout errors
showRetryMessage();
} else {
// Handle other errors
console.error('Unexpected error:', error);
}
}}
>
{children}
</FnelProvider>
Access debug information for development:
import { fnelSDK } from '@fnel/react';
// Get debug info
const debugInfo = fnelSDK.getDebugInfo();
console.log('Debug info:', debugInfo);
// Or access directly from window (if available)
if (window._fnelDebug) {
console.log('API Token:', window._fnelDebug.apiToken());
console.log('User ID:', window._fnelDebug.userId());
console.log('Initialized:', window._fnelDebug.isInitialized());
console.log('Queue:', window._fnelDebug.queue());
console.log('Version:', window._fnelDebug.version);
}
The package includes comprehensive TypeScript types:
import { FnelEvent, FnelInitResult, FnelTrackResult } from '@fnel/react';
// Define custom event data
interface CustomEventData extends FnelEvent {
amount?: number;
currency?: string;
source?: string;
}
// Use with tracking
const trackCustomEvent = async (data: CustomEventData) => {
const result: FnelTrackResult = await track(data);
return result;
};
- Chrome 60+
- Firefox 55+
- Safari 12+
- Edge 79+
This package works with React Native (web only) as it depends on browser APIs like localStorage
and fetch
.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
For support, email support@fnel.app or join our Slack channel.