React Integration
Complete guide to using Produck SDK with React.
Overview
The React integration provides hooks, components, and context for seamless integration into React applications.
import {
ProduckProvider, // Context provider
ProduckChat, // Chat widget component
ProduckTarget, // Auto-scroll & highlight wrapper
useProduckAction, // Register action handlers
useProduckMessages, // Access chat state
useProduck, // Access full context
useProduckReady, // Check SDK status
} from '@produck/sdk/react';ProduckProvider
Wrap your application with ProduckProvider to enable SDK functionality.
Basic Usage
app/layout.tsx
import { ProduckProvider } from '@produck/sdk/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
<ProduckProvider
config={{
sdkKey: process.env.NEXT_PUBLIC_SDK_KEY,
}}
>
{children}
</ProduckProvider>
</body>
</html>
);
}Props
interface ProduckProviderProps {
config: {
sdkKey: string; // Your SDK project key (required)
apiUrl?: string; // API endpoint (default: http://localhost:4001/api/v1)
};
children: ReactNode; // Your app components
onAction?: (key: string, payload: ActionPayload) => void; // Global action callback
onError?: (error: Error) => void; // Global error handler
}With Callbacks
<ProduckProvider
config={{ sdkKey: 'your-key' }}
onAction={(key, payload) => {
// Track all actions in analytics
analytics.track('Produck Action', { action: key });
}}
onError={(error) => {
// Send errors to monitoring service
Sentry.captureException(error);
}}
>
{children}
</ProduckProvider>ProduckChat
Pre-built chat widget component.
Basic Usage
import { ProduckChat } from '@produck/sdk/react';
function App() {
return (
<div>
<YourContent />
<ProduckChat position="bottom-right" />
</div>
);
}Props
interface ProduckChatProps {
position?: 'bottom-right' | 'bottom-left' | 'inline'; // Chat position
theme?: 'light' | 'dark'; // Color theme
primaryColor?: string; // Accent color (hex)
title?: string; // Header title
placeholder?: string; // Input placeholder
className?: string; // Custom CSS class
style?: React.CSSProperties; // Inline styles
}Customization Examples
Custom Colors
<ProduckChat
position="bottom-right"
theme="dark"
primaryColor="#8b5cf6"
title="Ask me anything!"
placeholder="Type your question..."
/>Inline Chat
<div className="my-chat-container">
<ProduckChat
position="inline"
className="w-full h-[500px]"
/>
</div>Conditional Rendering
function App() {
const [showChat, setShowChat] = useState(false);
return (
<>
<button onClick={() => setShowChat(true)}>
Open Support
</button>
{showChat && (
<ProduckChat
position="inline"
onClose={() => setShowChat(false)}
/>
)}
</>
);
}useProduckAction
Register action handlers that respond to operations.
Basic Usage
import { useProduckAction } from '@produck/sdk/react';
function ContactButton() {
const [isOpen, setIsOpen] = useState(false);
useProduckAction('open-contact', () => {
setIsOpen(true);
});
return (
<>
<button onClick={() => setIsOpen(true)}>Contact</button>
{isOpen && <ContactModal onClose={() => setIsOpen(false)} />}
</>
);
}With Payload
useProduckAction('show-product', (payload) => {
console.log('Action triggered:', payload);
// payload.actionKey = 'show-product'
// payload.name = 'Show Product'
// payload.responseMessage = '...'
setProductId(payload.actionConfig.productId);
setShowProduct(true);
});Async Handlers
useProduckAction('submit-form', async (payload) => {
setLoading(true);
try {
await api.submitForm(formData);
showSuccess('Form submitted!');
} catch (error) {
showError('Failed to submit');
} finally {
setLoading(false);
}
});With Dependencies
function ProductDetail({ productId }) {
const [quantity, setQuantity] = useState(1);
// Handler re-registers when dependencies change
useProduckAction('add-to-cart', () => {
addToCart(productId, quantity);
}, [productId, quantity]);
return <div>...</div>;
}Signature
function useProduckAction(
actionKey: string,
handler: (payload: ActionPayload) => void | Promise<void>,
deps?: React.DependencyList
): void;useProduckMessages
Access chat messages and send messages programmatically.
Basic Usage
import { useProduckMessages } from '@produck/sdk/react';
function CustomChat() {
const { messages, isLoading, sendMessage } = useProduckMessages();
return (
<div>
<MessageList messages={messages} />
{isLoading && <LoadingIndicator />}
<ChatInput onSend={sendMessage} />
</div>
);
}Build Custom Chat UI
function CustomChatWidget() {
const { messages, isLoading, sendMessage } = useProduckMessages();
const [input, setInput] = useState('');
const handleSend = async () => {
if (!input.trim()) return;
await sendMessage(input);
setInput('');
};
return (
<div className="chat-container">
<div className="messages">
{messages.map((msg, i) => (
<div key={i} className={`message ${msg.role}`}>
<p>{msg.content}</p>
{msg.action && (
<span className="action-badge">
Action: {msg.action.name}
</span>
)}
</div>
))}
</div>
{isLoading && <LoadingSpinner />}
<div className="input-area">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
placeholder="Ask something..."
/>
<button onClick={handleSend}>Send</button>
</div>
</div>
);
}Return Type
interface UseProduckMessagesReturn {
messages: ChatMessage[];
isLoading: boolean;
sendMessage: (message: string) => Promise<void>;
}
interface ChatMessage {
role: 'user' | 'assistant';
content: string;
action?: ActionPayload;
}useProduck
Access the full SDK context and instance.
Basic Usage
import { useProduck } from '@produck/sdk/react';
function SDKDebugger() {
const {
sdk, // ProduckSDK instance
isReady, // Is SDK initialized?
sessionToken, // Current session token
messages, // Chat messages
isLoading, // Is message being sent?
sendMessage, // Send a message
register, // Register action handler
unregister, // Unregister action handler
} = useProduck();
return (
<div>
<p>Status: {isReady ? 'Ready' : 'Initializing...'}</p>
<p>Session: {sessionToken}</p>
<p>Messages: {messages.length}</p>
</div>
);
}Advanced: Manual Registration
function AdvancedComponent() {
const { register, unregister } = useProduck();
useEffect(() => {
const handler = (payload) => {
console.log('Dynamic action!', payload);
};
register('dynamic-action', handler);
return () => unregister('dynamic-action');
}, [register, unregister]);
return <div>...</div>;
}Return Type
interface ProduckContextValue {
sdk: ProduckSDK | null;
isReady: boolean;
sessionToken: string | null;
messages: ChatMessage[];
isLoading: boolean;
sendMessage: (message: string) => Promise<void>;
register: (key: string, handler: ActionHandler) => void;
unregister: (key: string) => void;
}useProduckReady
Check if SDK is initialized.
Basic Usage
import { useProduckReady } from '@produck/sdk/react';
function StatusIndicator() {
const isReady = useProduckReady();
if (!isReady) {
return <div>Connecting to Produck...</div>;
}
return <div>✅ Connected</div>;
}Conditional Rendering
function App() {
const isReady = useProduckReady();
return (
<div>
<Header />
{isReady ? (
<MainContent />
) : (
<LoadingScreen message="Initializing AI assistant..." />
)}
</div>
);
}ProduckTarget
Wrap components to auto-scroll and highlight when triggered.
Basic Usage
import { ProduckTarget } from '@produck/sdk/react';
function PricingSection() {
return (
<ProduckTarget actionKey="show-pricing">
<section id="pricing">
<h2>Pricing</h2>
<PricingCards />
</section>
</ProduckTarget>
);
}When user says "show me pricing", this section will:
- Scroll into view
- Highlight with animation
- Trigger custom callback (optional)
Props
interface ProduckTargetProps {
actionKey: string; // Operation to respond to
children: ReactNode; // Content to wrap
onTrigger?: (payload: ActionPayload) => void; // Custom callback
scrollIntoView?: boolean; // Auto-scroll (default: true)
highlightDuration?: number; // Highlight duration (ms, default: 3000)
highlightStyle?: React.CSSProperties; // Custom highlight styles
}With Custom Callback
<ProduckTarget
actionKey="show-features"
onTrigger={(payload) => {
console.log('User viewed features');
analytics.track('Features Viewed', { source: 'ai' });
}}
>
<FeaturesSection />
</ProduckTarget>Custom Styling
<ProduckTarget
actionKey="show-testimonials"
highlightStyle={{
outline: '3px solid #f59e0b',
backgroundColor: 'rgba(245, 158, 11, 0.1)',
borderRadius: '8px',
}}
highlightDuration={5000}
>
<TestimonialsSection />
</ProduckTarget>Disable Auto-scroll
<ProduckTarget
actionKey="highlight-banner"
scrollIntoView={false} // Only highlight, don't scroll
>
<HeroBanner />
</ProduckTarget>Patterns & Examples
Multi-Step Flows
function OnboardingFlow() {
const [step, setStep] = useState(1);
useProduckAction('start-onboarding', () => setStep(1));
useProduckAction('next-step', () => setStep(s => s + 1));
useProduckAction('previous-step', () => setStep(s => s - 1));
useProduckAction('skip-onboarding', () => setStep(999));
return <div>Step {step} content...</div>;
}State Management
function ShoppingCart() {
const [cart, setCart] = useCart();
useProduckAction('add-to-cart', (payload) => {
const productId = payload.actionConfig.productId;
setCart(prev => [...prev, productId]);
});
useProduckAction('clear-cart', () => {
setCart([]);
});
return <CartView items={cart} />;
}Form Filling
function ContactForm() {
const [formData, setFormData] = useState({});
useProduckAction('fill-contact-form', (payload) => {
setFormData({
name: payload.actionConfig.name,
email: payload.actionConfig.email,
message: payload.actionConfig.message,
});
});
return <form>...</form>;
}