React Integration
Web Components work seamlessly in React. This guide covers best practices and TypeScript integration.
Basic Usage
Import the SDK once at your app's entry point:
// src/main.tsx or src/index.tsx
// Set the config BEFORE importing the SDK: elements read it when they are
// constructed, so a config assigned later (e.g. in a useEffect) can be missed.
window.QariybConfig = { publishableKey: 'pk_live_...' };
import '@qariyb/js';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
Then use components in JSX:
// src/components/TicketSection.tsx
export function TicketSection({ eventId }: { eventId: string }) {
return (
<section>
<h2>Get Your Tickets</h2>
<qariyb-checkout event-id={eventId}></qariyb-checkout>
</section>
);
}
TypeScript Declarations
Add type declarations for the web components:
// src/types/qariyb.d.ts
import type { ThemePreset } from '@qariyb/js';
declare namespace JSX {
interface IntrinsicElements {
'qariyb-checkout': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
'event-id': string;
theme?: ThemePreset;
'accent-color'?: string;
'prefill-email'?: string;
'prefill-name'?: string;
'on-success'?: string;
'on-error'?: string;
},
HTMLElement
>;
'qariyb-button': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
'event-id': string;
theme?: ThemePreset;
'accent-color'?: string;
size?: 'sm' | 'md' | 'lg';
},
HTMLElement
>;
'qariyb-tickets': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
'event-id': string;
theme?: ThemePreset;
'accent-color'?: string;
},
HTMLElement
>;
'qariyb-event-card': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
'event-id': string;
theme?: ThemePreset;
'accent-color'?: string;
'show-image'?: string;
'show-price'?: string;
'show-features'?: string;
},
HTMLElement
>;
'qariyb-event-list': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
'organization-id': string;
theme?: ThemePreset;
'accent-color'?: string;
limit?: string;
layout?: 'grid' | 'list';
'show-past'?: string;
},
HTMLElement
>;
}
}
Event Handling with Refs
Use refs to attach event listeners:
import { useRef, useEffect } from 'react';
import type { CheckoutSuccessDetail, QariybCustomEvent } from '@qariyb/js';
interface Props {
eventId: string;
onSuccess?: (order: CheckoutSuccessDetail) => void;
}
export function Checkout({ eventId, onSuccess }: Props) {
const checkoutRef = useRef<HTMLElement>(null);
useEffect(() => {
const element = checkoutRef.current;
if (!element) return;
const handleSuccess = (e: Event) => {
const customEvent = e as QariybCustomEvent<CheckoutSuccessDetail>;
onSuccess?.(customEvent.detail.data);
};
element.addEventListener('qariyb:checkout:success', handleSuccess);
return () => {
element.removeEventListener('qariyb:checkout:success', handleSuccess);
};
}, [onSuccess]);
return (
<qariyb-checkout
ref={checkoutRef}
event-id={eventId}
></qariyb-checkout>
);
}
Global Event Listener
For app-wide event handling, use a custom hook:
// src/hooks/useQariybEvents.ts
import { useEffect } from 'react';
import type { QariybCustomEvent, CheckoutSuccessDetail } from '@qariyb/js';
export function useQariybSuccessEvent(
callback: (data: CheckoutSuccessDetail) => void
) {
useEffect(() => {
const handler = (e: Event) => {
const customEvent = e as QariybCustomEvent<CheckoutSuccessDetail>;
callback(customEvent.detail.data);
};
window.addEventListener('qariyb:checkout:success', handler);
return () => window.removeEventListener('qariyb:checkout:success', handler);
}, [callback]);
}
Usage:
import { useQariybSuccessEvent } from './hooks/useQariybEvents';
function App() {
useQariybSuccessEvent((data) => {
console.log('Order completed:', data.order.id);
// Update app state, redirect, etc.
});
return <qariyb-checkout event-id="01HW..."></qariyb-checkout>;
}
Configuration with Context
Manage SDK configuration at the app level:
// src/context/QariybContext.tsx
import { createContext, useContext, useEffect, ReactNode } from 'react';
import type { QariybConfig } from '@qariyb/js';
const QariybContext = createContext<QariybConfig | null>(null);
interface Props {
config: QariybConfig;
children: ReactNode;
}
export function QariybProvider({ config, children }: Props) {
useEffect(() => {
window.QariybConfig = config;
}, [config]);
return (
<QariybContext.Provider value={config}>
{children}
</QariybContext.Provider>
);
}
export function useQariybConfig() {
return useContext(QariybContext);
}
Usage:
// src/App.tsx
import { QariybProvider } from './context/QariybContext';
function App() {
return (
<QariybProvider
config={{
publishableKey: 'pk_live_...',
theme: 'dark',
variables: {
colorPrimary: '#8b5cf6',
},
}}
>
<TicketSection eventId="01HW..." />
</QariybProvider>
);
}
Wrapper Component Pattern
Create reusable wrapper components with typed callbacks:
// src/components/QariybCheckout.tsx
import { useRef, useEffect, forwardRef, useImperativeHandle } from 'react';
import type {
ThemePreset,
CheckoutSuccessDetail,
CheckoutErrorDetail,
} from '@qariyb/js';
interface Props {
eventId: string;
theme?: ThemePreset;
accentColor?: string;
prefillEmail?: string;
prefillName?: string;
onSuccess?: (data: CheckoutSuccessDetail) => void;
onError?: (data: CheckoutErrorDetail) => void;
onStepChanged?: (from: string, to: string) => void;
}
export const QariybCheckout = forwardRef<HTMLElement, Props>(function QariybCheckout(
{ eventId, theme, accentColor, prefillEmail, prefillName, onSuccess, onError, onStepChanged },
ref
) {
const elementRef = useRef<HTMLElement>(null);
useImperativeHandle(ref, () => elementRef.current!);
useEffect(() => {
const element = elementRef.current;
if (!element) return;
const handlers: Array<[string, EventListener]> = [];
if (onSuccess) {
const handler = (e: Event) => {
onSuccess((e as CustomEvent).detail.data);
};
handlers.push(['qariyb:checkout:success', handler]);
element.addEventListener('qariyb:checkout:success', handler);
}
if (onError) {
const handler = (e: Event) => {
onError((e as CustomEvent).detail.data);
};
handlers.push(['qariyb:checkout:error', handler]);
element.addEventListener('qariyb:checkout:error', handler);
}
if (onStepChanged) {
const handler = (e: Event) => {
const { from, to } = (e as CustomEvent).detail.data;
onStepChanged(from, to);
};
handlers.push(['qariyb:checkout:step-changed', handler]);
element.addEventListener('qariyb:checkout:step-changed', handler);
}
return () => {
handlers.forEach(([event, handler]) => {
element.removeEventListener(event, handler);
});
};
}, [onSuccess, onError, onStepChanged]);
return (
<qariyb-checkout
ref={elementRef}
event-id={eventId}
theme={theme}
accent-color={accentColor}
prefill-email={prefillEmail}
prefill-name={prefillName}
></qariyb-checkout>
);
});
Usage:
<QariybCheckout
eventId="01HW..."
theme="light"
onSuccess={(data) => {
console.log('Order:', data.order.id);
navigate('/confirmation');
}}
onError={(data) => {
toast.error(data.error);
}}
/>
Server-Side Rendering (Next.js)
For SSR frameworks, load the SDK only on the client:
Client Component
// src/components/QariybCheckoutClient.tsx
'use client';
import { useEffect, useState } from 'react';
interface Props {
eventId: string;
}
export function QariybCheckoutClient({ eventId }: Props) {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
import('@qariyb/js').then(() => {
setIsLoaded(true);
});
}, []);
if (!isLoaded) {
return <div>Loading checkout...</div>;
}
return <qariyb-checkout event-id={eventId}></qariyb-checkout>;
}
Dynamic Import
// src/app/tickets/page.tsx
import dynamic from 'next/dynamic';
const QariybCheckout = dynamic(
() => import('@/components/QariybCheckoutClient').then(mod => mod.QariybCheckoutClient),
{ ssr: false, loading: () => <div>Loading...</div> }
);
export default function TicketsPage() {
return <QariybCheckout eventId="01HW..." />;
}
App Router Layout
// src/app/layout.tsx
'use client';
import { useEffect } from 'react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Configure before SDK loads
window.QariybConfig = {
publishableKey: 'pk_live_...',
theme: 'light',
variables: {
colorPrimary: '#4f46e5',
},
};
// Load SDK
import('@qariyb/js');
}, []);
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
Vue Integration
For Vue, the approach is similar:
<template>
<qariyb-checkout :event-id="eventId"></qariyb-checkout>
</template>
<script setup>
import { onMounted } from 'vue';
const props = defineProps(['eventId']);
onMounted(() => {
import('@qariyb/js');
});
</script>
Common Pitfalls
Self-closing tags don't work
// Won't work
<qariyb-checkout event-id="01HW..." />
// Works
<qariyb-checkout event-id="01HW..."></qariyb-checkout>
Attributes are strings
// Doesn't work - boolean
<qariyb-event-card show-image={false}></qariyb-event-card>
// Works - string
<qariyb-event-card show-image="false"></qariyb-event-card>
TypeScript errors
If TypeScript complains about unknown elements, ensure your qariyb.d.ts file is included in your tsconfig.json.