React Integration
This guide covers integrating TraceScout into React applications, including best practices for initialization and common patterns.
Basic Setup
Create a TraceScout Provider
Create a component to handle SDK initialization:
// components/TraceScoutProvider.tsx
'use client';
import { useEffect, useRef } from 'react';
interface TraceScoutProviderProps {
children: React.ReactNode;
}
export function TraceScoutProvider({ children }: TraceScoutProviderProps) {
const initialized = useRef(false);
useEffect(() => {
// Prevent double initialization in React Strict Mode
if (initialized.current) return;
initialized.current = true;
// Load SDK from CDN
const script = document.createElement('script');
script.src = 'https://cdn.tracescout.com/sdk/tracescout.min.js';
script.async = true;
script.onload = () => {
window.TraceScout?.init({
apiKey: process.env.NEXT_PUBLIC_TRACESCOUT_API_KEY!,
organizationId: process.env.NEXT_PUBLIC_TRACESCOUT_ORGANIZATION_ID!,
projectId: process.env.NEXT_PUBLIC_TRACESCOUT_PROJECT_ID!,
environment: process.env.NODE_ENV,
enableSessionRecording: true,
recordingMaskAllInputs: true,
});
};
document.head.appendChild(script);
return () => {
// Cleanup on unmount
window.TraceScout?.destroy?.();
};
}, []);
return <>{children}</>;
}
Add to App
Wrap your application with the provider:
// app/layout.tsx or pages/_app.tsx
import { TraceScoutProvider } from '@/components/TraceScoutProvider';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<TraceScoutProvider>
{children}
</TraceScoutProvider>
</body>
</html>
);
}
Using npm Package
If you prefer the npm package:
// components/TraceScoutProvider.tsx
'use client';
import { useEffect, useRef } from 'react';
import TraceScout from '@tracescout/suite';
export function TraceScoutProvider({ children }: { children: React.ReactNode }) {
const initialized = useRef(false);
useEffect(() => {
if (initialized.current) return;
initialized.current = true;
TraceScout.init({
apiKey: process.env.NEXT_PUBLIC_TRACESCOUT_API_KEY!,
organizationId: process.env.NEXT_PUBLIC_TRACESCOUT_ORGANIZATION_ID!,
projectId: process.env.NEXT_PUBLIC_TRACESCOUT_PROJECT_ID!,
environment: process.env.NODE_ENV,
});
return () => {
TraceScout.destroy?.();
};
}, []);
return <>{children}</>;
}
Client Component Required
TraceScout must run in the browser. Make sure to use 'use client' directive in Next.js 13+ App Router.
Custom Hooks
useTraceScout Hook
Create a hook for easy access to TraceScout methods:
// hooks/useTraceScout.ts
import { useCallback } from 'react';
export function useTraceScout() {
const trackEvent = useCallback((name: string, data?: Record<string, any>) => {
window.TraceScout?.addCustomEvent?.(name, data);
}, []);
const identifyUser = useCallback((userId: string, traits?: Record<string, any>) => {
window.TraceScout?.setUser?.(userId, traits);
}, []);
const startRecording = useCallback(() => {
return window.TraceScout?.startSessionRecording?.();
}, []);
const stopRecording = useCallback(() => {
return window.TraceScout?.stopSessionRecording?.();
}, []);
return {
trackEvent,
identifyUser,
startRecording,
stopRecording,
};
}
Usage in Components
import { useTraceScout } from '@/hooks/useTraceScout';
function CheckoutButton() {
const { trackEvent } = useTraceScout();
const handleClick = () => {
trackEvent('checkout_started', {
cartValue: 99.99,
itemCount: 3,
});
// Continue with checkout...
};
return <button onClick={handleClick}>Checkout</button>;
}
User Identification
Identify users when they log in:
function LoginHandler() {
const { identifyUser } = useTraceScout();
const handleLogin = async (credentials: Credentials) => {
const user = await loginUser(credentials);
// Identify user in TraceScout
identifyUser(user.id, {
email: user.email,
name: user.name,
plan: user.subscription,
});
};
return <LoginForm onSubmit={handleLogin} />;
}
Error Boundaries
Integrate with React Error Boundaries:
import { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback: ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
state = { hasError: false };
static getDerivedStateFromError(): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// Track error in TraceScout
window.TraceScout?.addCustomEvent?.('react_error', {
error: error.message,
stack: error.stack,
componentStack: errorInfo.componentStack,
});
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
export default ErrorBoundary;
Privacy in Forms
Use CSS classes to control recording:
function SensitiveForm() {
return (
<form>
{/* This input will be masked */}
<input
type="text"
name="email"
className="tracescout-mask"
placeholder="Email"
/>
{/* This entire section won't be recorded */}
<div className="tracescout-block">
<input type="password" name="password" />
<input type="text" name="ssn" placeholder="SSN" />
</div>
<button type="submit">Submit</button>
</form>
);
}
TypeScript Support
Add type definitions:
// types/tracescout.d.ts
declare global {
interface Window {
TraceScout?: {
init: (config: TraceScoutConfig) => Promise<void>;
startSessionRecording: (options?: RecordingOptions) => SessionInfo;
stopSessionRecording: () => SessionResult;
addCustomEvent: (name: string, data?: Record<string, any>) => void;
setUser: (userId: string, traits?: Record<string, any>) => void;
destroy?: () => void;
};
}
}
interface TraceScoutConfig {
apiKey: string;
organizationId: string;
projectId: string;
environment: string;
enableSessionRecording?: boolean;
recordingMaskAllInputs?: boolean;
// ... other options
}
interface SessionInfo {
sessionId: string;
startTime: number;
status: string;
}
interface SessionResult extends SessionInfo {
duration: number;
totalEvents: number;
events: any[];
}
export {};
Best Practices
Performance Tips
- Initialize TraceScout as early as possible
- Use the
useRefpattern to prevent double initialization - Lazy load the SDK in non-critical paths
- Configure appropriate chunk sizes for your use case
Conditional Recording
Only record in production:
useEffect(() => {
if (process.env.NODE_ENV !== 'production') return;
// Initialize TraceScout...
}, []);
Recording Specific User Flows
function CheckoutFlow() {
const { startRecording, stopRecording, trackEvent } = useTraceScout();
useEffect(() => {
// Start recording when entering checkout
startRecording();
trackEvent('checkout_flow_started');
return () => {
// Stop when leaving
stopRecording();
trackEvent('checkout_flow_ended');
};
}, []);
return <CheckoutSteps />;
}