@zaraz/react
v1.0.0
Published
A powerful, type-safe React library for Cloudflare Zaraz Web API with Next.js optimizations
Downloads
4
Maintainers
Readme
@zaraz/react
A powerful, type-safe React library for Cloudflare Zaraz Web API with first-class Next.js support.
Features
- 🎯 Type-Safe: Full TypeScript support with comprehensive type definitions
- ⚡ Next.js Optimized: Built with Next.js in mind, handles SSR/SSG gracefully
- 🪝 React Hooks: Modern hook-based API (
useZaraz,useZarazEcommerce) - 🛒 E-commerce Ready: Dedicated e-commerce tracking with full event type safety
- 🔧 Developer Experience: Excellent IntelliSense support and helpful error messages
- 📦 Tree-Shakeable: Only bundle what you use
- 🚀 Zero Dependencies: Minimal bundle size (peer deps: React only)
- 🔍 Debug Mode: Built-in debug utilities for development
- ♿ SSR Safe: Works seamlessly with server-side rendering
Installation
# Using bun (recommended)
bun add @zaraz/react
# Using npm
npm install @zaraz/react
# Using yarn
yarn add @zaraz/react
# Using pnpm
pnpm add @zaraz/reactQuick Start
1. Wrap your app with ZarazProvider
// app/layout.tsx (Next.js App Router)
import { ZarazProvider } from '@zaraz/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<ZarazProvider config={{ devMode: process.env.NODE_ENV === 'development' }}>
{children}
</ZarazProvider>
</body>
</html>
);
}// _app.tsx (Next.js Pages Router)
import { ZarazProvider } from '@zaraz/react';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<ZarazProvider>
<Component {...pageProps} />
</ZarazProvider>
);
}2. Use the hooks in your components
'use client'; // Required for Next.js App Router
import { useZaraz } from '@zaraz/react';
export function SignUpButton() {
const { track, isReady } = useZaraz();
const handleSignUp = async () => {
await track('sign_up_clicked', {
source: 'landing_page',
plan: 'pro',
});
// Your sign-up logic here
};
return (
<button onClick={handleSignUp} disabled={!isReady}>
Sign Up
</button>
);
}Usage Examples
Basic Event Tracking
import { useZaraz } from '@zaraz/react';
function MyComponent() {
const { track } = useZaraz();
const handleButtonClick = () => {
track('button_clicked', {
label: 'Get Started',
location: 'hero_section',
});
};
return <button onClick={handleButtonClick}>Get Started</button>;
}Setting Persistent Variables
import { useZaraz } from '@zaraz/react';
import { useEffect } from 'react';
function UserProfile({ user }) {
const { set } = useZaraz();
useEffect(() => {
// Set user ID for all future events (persists across sessions)
set('user_id', user.id, { scope: 'persist' });
// Set temporary page-level variable
set('page_type', 'profile', { scope: 'page' });
// Unset a variable
return () => {
set('page_type', undefined);
};
}, [user.id, set]);
return <div>Welcome, {user.name}</div>;
}E-commerce Tracking
import { useZarazEcommerce } from '@zaraz/react';
function ProductPage({ product }) {
const ecommerce = useZarazEcommerce();
// Track product view
useEffect(() => {
ecommerce.viewProduct({
product_id: product.id,
name: product.name,
price: product.price,
brand: product.brand,
category: product.category,
currency: 'USD',
});
}, [product]);
// Track add to cart
const handleAddToCart = async () => {
await ecommerce.addProduct({
product_id: product.id,
name: product.name,
price: product.price,
quantity: 1,
currency: 'USD',
});
// Add to cart logic...
};
return (
<div>
<h1>{product.name}</h1>
<p>${product.price}</p>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}Complete Order Tracking
import { useZarazEcommerce } from '@zaraz/react';
function CheckoutSuccess({ order }) {
const { completeOrder } = useZarazEcommerce();
useEffect(() => {
completeOrder({
order_id: order.id,
affiliation: 'Online Store',
total: order.total,
revenue: order.subtotal,
shipping: order.shipping,
tax: order.tax,
discount: order.discount,
coupon: order.couponCode,
currency: 'USD',
products: order.items.map((item) => ({
product_id: item.id,
sku: item.sku,
name: item.name,
price: item.price,
quantity: item.quantity,
category: item.category,
})),
});
}, [order, completeOrder]);
return <div>Thank you for your order!</div>;
}Debug Mode
import { useZaraz } from '@zaraz/react';
function DebugPanel() {
const { debug } = useZaraz();
const enableDebug = () => {
debug('your-debug-key-from-cloudflare');
};
const disableDebug = () => {
debug(); // Call without key to disable
};
return (
<div>
<button onClick={enableDebug}>Enable Debug</button>
<button onClick={disableDebug}>Disable Debug</button>
</div>
);
}API Reference
<ZarazProvider>
The main provider component that wraps your application.
Props
interface ZarazProviderProps {
config?: ZarazConfig;
children: React.ReactNode;
}
interface ZarazConfig {
/** Enable debug mode on initialization */
debug?: boolean;
/** Debug key from Zaraz settings */
debugKey?: string;
/** Wait for Zaraz to load before tracking (default: true) */
waitForLoad?: boolean;
/** Timeout in ms to wait for Zaraz (default: 5000) */
loadTimeout?: number;
/** Custom error handler */
onError?: (error: Error) => void;
/** Log events to console in development (default: false) */
devMode?: boolean;
}Example
<ZarazProvider
config={{
devMode: process.env.NODE_ENV === 'development',
waitForLoad: true,
loadTimeout: 10000,
onError: (error) => console.error('Zaraz error:', error),
}}
>
<App />
</ZarazProvider>useZaraz()
Hook to access core Zaraz functionality.
Returns
interface ZarazContextValue {
/** Whether Zaraz is loaded and ready */
isReady: boolean;
/** Track a custom event */
track: (eventName: string, eventProperties?: EventProperties) => Promise<void>;
/** Set a persistent variable */
set: (key: string, value: string | number | boolean | undefined, options?: ZarazSetOptions) => void;
/** Track e-commerce events */
ecommerce: <T extends ZarazEcommerceEvent>(
eventName: T,
parameters: ZarazEcommerceEventParams<T>
) => Promise<void>;
/** Enable/disable debug mode */
debug: (debugKey?: string) => void;
/** Current configuration */
config: ZarazConfig;
}Example
const { track, set, isReady, debug, config } = useZaraz();
// Track event
await track('page_viewed', { page: 'home' });
// Set variable
set('user_tier', 'premium', { scope: 'persist' });
// Check if ready
if (isReady) {
// Track something
}useZarazEcommerce()
Hook providing convenient e-commerce tracking methods.
Returns
interface UseZarazEcommerceReturn {
// Product interactions
viewProduct: (params: SingleProductEventParams) => Promise<void>;
clickProduct: (params: SingleProductEventParams) => Promise<void>;
addProduct: (params: SingleProductEventParams) => Promise<void>;
removeProduct: (params: SingleProductEventParams) => Promise<void>;
addProductToWishlist: (params: SingleProductEventParams) => Promise<void>;
// Lists and search
viewProductList: (params: ProductListEventParams) => Promise<void>;
searchProducts: (params: ProductSearchEventParams) => Promise<void>;
// Cart
viewCart: (params?: Partial<ProductListEventParams>) => Promise<void>;
// Checkout flow
startCheckout: (params?: Partial<OrderEventParams>) => Promise<void>;
viewCheckoutStep: (params: CheckoutStepEventParams) => Promise<void>;
completeCheckoutStep: (params: CheckoutStepEventParams) => Promise<void>;
enterPaymentInfo: (params: PaymentInfoEventParams) => Promise<void>;
enterShippingInfo: (params?: Partial<OrderEventParams>) => Promise<void>;
// Orders
completeOrder: (params: OrderEventParams) => Promise<void>;
updateOrder: (params: OrderEventParams) => Promise<void>;
refundOrder: (params: OrderEventParams) => Promise<void>;
cancelOrder: (params: OrderEventParams) => Promise<void>;
// Promotions
viewPromotion: (params: PromotionEventParams) => Promise<void>;
clickPromotion: (params: PromotionEventParams) => Promise<void>;
// Raw access
raw: <T extends ZarazEcommerceEvent>(
eventName: T,
parameters: ZarazEcommerceEventParams<T>
) => Promise<void>;
}Next.js Integration
App Router (Next.js 13+)
// app/layout.tsx
import { ZarazProvider } from '@zaraz/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
{/* Zaraz script will be injected by Cloudflare */}
</head>
<body>
<ZarazProvider config={{ devMode: process.env.NODE_ENV === 'development' }}>
{children}
</ZarazProvider>
</body>
</html>
);
}// app/page.tsx or any client component
'use client';
import { useZaraz } from '@zaraz/react';
export default function Home() {
const { track } = useZaraz();
return (
<button onClick={() => track('cta_clicked')}>
Click Me
</button>
);
}Pages Router (Next.js 12 and below)
// pages/_app.tsx
import { ZarazProvider } from '@zaraz/react';
import type { AppProps } from 'next/app';
export default function App({ Component, pageProps }: AppProps) {
return (
<ZarazProvider>
<Component {...pageProps} />
</ZarazProvider>
);
}Page View Tracking (App Router)
// app/layout.tsx
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { useZaraz } from '@zaraz/react';
export function PageViewTracker() {
const pathname = usePathname();
const { track, isReady } = useZaraz();
useEffect(() => {
if (isReady) {
track('page_view', { path: pathname });
}
}, [pathname, isReady, track]);
return null;
}Page View Tracking (Pages Router)
// pages/_app.tsx
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { ZarazProvider, useZaraz } from '@zaraz/react';
function PageViewTracker() {
const router = useRouter();
const { track, isReady } = useZaraz();
useEffect(() => {
const handleRouteChange = (url: string) => {
track('page_view', { path: url });
};
if (isReady) {
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}
}, [router.events, isReady, track]);
return null;
}
export default function App({ Component, pageProps }: AppProps) {
return (
<ZarazProvider>
<PageViewTracker />
<Component {...pageProps} />
</ZarazProvider>
);
}TypeScript Support
This library is written in TypeScript and provides comprehensive type definitions.
Custom Event Types
import { useZaraz } from '@zaraz/react';
// Define your event types
interface MyEvents {
user_signup: {
plan: 'free' | 'pro' | 'enterprise';
source: string;
};
purchase_completed: {
amount: number;
currency: string;
items: number;
};
}
function MyComponent() {
const { track } = useZaraz();
// Type-safe event tracking
const handleSignup = (plan: MyEvents['user_signup']['plan']) => {
track('user_signup', {
plan,
source: 'landing_page',
});
};
return <button onClick={() => handleSignup('pro')}>Sign Up</button>;
}E-commerce Type Safety
import { useZarazEcommerce, type OrderEventParams } from '@zaraz/react';
function Checkout() {
const { completeOrder } = useZarazEcommerce();
const order: OrderEventParams = {
order_id: '12345',
total: 99.99,
currency: 'USD',
products: [
{
product_id: 'abc',
name: 'T-Shirt',
price: 29.99,
quantity: 2,
},
],
};
return <button onClick={() => completeOrder(order)}>Complete Order</button>;
}Advanced Usage
Conditional Tracking
import { useZaraz } from '@zaraz/react';
function useConditionalTracking() {
const { track } = useZaraz();
const userConsent = useUserConsent(); // Your consent logic
return {
track: (eventName: string, properties?: EventProperties) => {
if (userConsent.analytics) {
return track(eventName, properties);
}
return Promise.resolve();
},
};
}Custom Hook for Specific Events
import { useZaraz } from '@zaraz/react';
import { useCallback } from 'react';
export function useFormTracking() {
const { track } = useZaraz();
const trackFormStart = useCallback(
(formName: string) => {
track('form_started', { form_name: formName });
},
[track]
);
const trackFormComplete = useCallback(
(formName: string) => {
track('form_completed', { form_name: formName });
},
[track]
);
const trackFormError = useCallback(
(formName: string, error: string) => {
track('form_error', { form_name: formName, error });
},
[track]
);
return { trackFormStart, trackFormComplete, trackFormError };
}Accessing Zaraz Directly
For advanced use cases, you can access the Zaraz API directly:
import { getZarazAPI, isZarazAvailable, waitForZaraz } from '@zaraz/react';
// Check if Zaraz is available
if (isZarazAvailable()) {
const zaraz = getZarazAPI();
zaraz?.track('custom_event');
}
// Wait for Zaraz to load
async function ensureZaraz() {
try {
const zaraz = await waitForZaraz(10000);
zaraz.track('zaraz_loaded');
} catch (error) {
console.error('Zaraz failed to load:', error);
}
}SSR Considerations
This library is fully SSR-safe and handles server-side rendering gracefully:
- All Zaraz calls are automatically skipped on the server
- No
windowobject access errors - Hooks can be used in any component without SSR guards
- Automatically detects and waits for Zaraz on client-side hydration
// ✅ This works perfectly in SSR
function MyComponent() {
const { track } = useZaraz();
// No need for window checks or useEffect guards
const handleClick = () => {
track('button_clicked'); // Automatically skipped on server
};
return <button onClick={handleClick}>Click Me</button>;
}Browser Compatibility
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- React 16.8+ (Hooks support required)
- Next.js 12+ (Pages Router or App Router)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT © [Your Name]
Resources
Support
- GitHub Issues: Report a bug or request a feature
- Documentation: Full API documentation
