Documentation Index
Fetch the complete documentation index at: https://docs.fanfare.io/llms.txt
Use this file to discover all available pages before exploring further.
React to Solid Migration
This guide covers migrating from custom React hook implementations to SolidJS-based web components. This migration is optional but recommended for simpler integration and smaller bundle size.
Why Migrate?
| Aspect | React Hooks | Solid Widgets |
|---|
| Bundle Size | Larger (React dependency) | Smaller (standalone) |
| Customization | Full control | Theme + slots |
| Development Time | More code required | Drop-in solution |
| Maintenance | Custom UI maintenance | Automatic updates |
| Consistency | Varies by implementation | Consistent UX |
When to Migrate
Consider migrating when:
- You want to reduce custom code maintenance
- Your custom UI closely matches the default widgets
- You want automatic updates to widget behavior
- Bundle size is a concern
Keep using React hooks when:
- You need highly custom UI designs
- You have complex business logic in the UI layer
- You want complete control over rendering
Installation
# Add Solid SDK alongside React SDK
npm install @waitify-io/fanfare-sdk-solid
Basic Migration
React Hook Implementation
import { useQueue, useFanfareAuth } from "@waitify-io/fanfare-sdk-react";
function CustomQueueUI() {
const { isAuthenticated, guest } = useFanfareAuth();
const { queue, status, position, enter, leave, isLoading } = useQueue("queue_123");
const handleEnter = async () => {
if (!isAuthenticated) await guest();
await enter();
};
if (isLoading) return <div className="loading">Loading...</div>;
if (status === "admitted") {
return (
<div className="admitted">
<h2>You are In!</h2>
<a href="/checkout">Continue</a>
</div>
);
}
return (
<div className="queue-ui">
{status === "queued" ? (
<>
<p>Position: {position}</p>
<button onClick={leave}>Leave Queue</button>
</>
) : (
<button onClick={handleEnter}>Enter Queue</button>
)}
</div>
);
}
// Setup: Register once at app startup
import { registerWebComponents } from "@waitify-io/fanfare-sdk-solid";
registerWebComponents({
organizationId: "org_xxx",
publishableKey: "pk_live_xxx",
});
// Component: Just use the web component
function QueuePage() {
const widgetRef = useRef<HTMLElement>(null);
useEffect(() => {
const widget = widgetRef.current;
if (!widget) return;
const handleAdmitted = (e: CustomEvent<{ token: string }>) => {
window.location.href = `/checkout?token=${e.detail.token}`;
};
widget.addEventListener("fanfare-queue-admitted", handleAdmitted);
return () => widget.removeEventListener("fanfare-queue-admitted", handleAdmitted);
}, []);
return <fanfare-queue-widget ref={widgetRef} queue-id="queue_123" />;
}
Event Handling Migration
React Hooks (Callback-based)
function QueueWithCallbacks() {
const { status, position, admittanceToken } = useQueue("queue_123");
useEffect(() => {
if (status === "admitted" && admittanceToken) {
analytics.track("queue_admitted");
window.location.href = `/checkout?token=${admittanceToken}`;
}
}, [status, admittanceToken]);
useEffect(() => {
if (position !== null) {
analytics.track("queue_position_change", { position });
}
}, [position]);
return <QueueUI />;
}
function QueueWithEvents() {
const widgetRef = useRef<HTMLElement>(null);
useEffect(() => {
const widget = widgetRef.current;
if (!widget) return;
const handleEnter = (e: CustomEvent) => {
analytics.track("queue_entered", { queueId: e.detail.queueId });
};
const handlePositionChange = (e: CustomEvent) => {
analytics.track("queue_position_change", { position: e.detail.position });
};
const handleAdmitted = (e: CustomEvent<{ token: string }>) => {
analytics.track("queue_admitted");
window.location.href = `/checkout?token=${e.detail.token}`;
};
widget.addEventListener("fanfare-queue-enter", handleEnter);
widget.addEventListener("fanfare-queue-position-change", handlePositionChange);
widget.addEventListener("fanfare-queue-admitted", handleAdmitted);
return () => {
widget.removeEventListener("fanfare-queue-enter", handleEnter);
widget.removeEventListener("fanfare-queue-position-change", handlePositionChange);
widget.removeEventListener("fanfare-queue-admitted", handleAdmitted);
};
}, []);
return <fanfare-queue-widget ref={widgetRef} queue-id="queue_123" />;
}
Styling Migration
React (Custom CSS)
function StyledQueue() {
return (
<div className="my-queue-wrapper">
<div className="queue-header">
<h2>Join Our Queue</h2>
</div>
<div className="queue-content">{/* Custom UI */}</div>
<button className="queue-button">Enter</button>
</div>
);
}
function StyledWidget() {
return (
<>
<style>
{`
fanfare-queue-widget {
--fanfare-primary: #8b5cf6;
--fanfare-primary-hover: #7c3aed;
--fanfare-radius: 1rem;
max-width: 400px;
}
`}
</style>
<fanfare-queue-widget queue-id="queue_123" />
</>
);
}
Partial Migration (Hybrid Approach)
Keep using hooks for some flows while adopting widgets for others:
import { useExperienceJourney } from "@waitify-io/fanfare-sdk-react";
import { registerWebComponents } from "@waitify-io/fanfare-sdk-solid";
// Register widgets
registerWebComponents({
organizationId: "org_xxx",
publishableKey: "pk_live_xxx",
});
function HybridPage() {
// Use hook for complex journey logic
const { status, start } = useExperienceJourney("exp_123");
if (status === "idle") {
// Custom start UI with hooks
return <CustomStartButton onStart={start} />;
}
if (status === "ready") {
// Use widget for the queue UI
return <fanfare-queue-widget queue-id="queue_123" />;
}
return <LoadingState />;
}
TypeScript Migration
Add Type Declarations
// global.d.ts
declare namespace JSX {
interface IntrinsicElements {
"fanfare-queue-widget": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
"queue-id": string;
"show-header"?: string;
"show-actions"?: string;
ref?: React.Ref<HTMLElement>;
},
HTMLElement
>;
"fanfare-draw-widget": React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
"draw-id": string;
"show-header"?: string;
"show-countdown"?: string;
ref?: React.Ref<HTMLElement>;
},
HTMLElement
>;
// Add other widgets as needed
}
}
Event Type Definitions
// types/fanfare-events.ts
interface QueueEnterEventDetail {
queueId: string;
}
interface QueueAdmittedEventDetail {
queueId: string;
token: string;
}
interface QueuePositionChangeEventDetail {
queueId: string;
position: number;
}
// Usage
const handleAdmitted = (e: CustomEvent<QueueAdmittedEventDetail>) => {
window.location.href = `/checkout?token=${e.detail.token}`;
};
Migration Checklist
Rollback Strategy
If issues arise, you can easily revert:
- Keep both implementations during migration
- Use feature flags to toggle between them
- Gradual rollout with monitoring
function QueuePage() {
const useWidgets = useFeatureFlag("use-solid-widgets");
if (useWidgets) {
return <fanfare-queue-widget queue-id="queue_123" />;
}
return <CustomQueueUI queueId="queue_123" />;
}