Skip to Content
    QAFKA
    CTRL K
    CTRL K
    • Introduction
      • Quick Start
      • Configuration
      • React Native Widget
      • Theming
      • Context
      • Navigation
      • External Navigation
      • Handling Tools
      • Voice Chat
      • Sub-Projects
      • Error Handling
      • CLI
      • Dashboard
      • Invitations
      • Settings
        • Project
        • Overview
        • Conversations
        • Chat Test
        • Sub-Projects
        • Analysis
        • Configuration
        • Members
        • Documents
        • Tools
        • Action Logs
        • Navigation Rules
        • External Destinations
        • Chat Theme
        • API Keys
      • API Key Security
    • Introduction
      • Quick Start
      • Configuration
      • React Native Widget
      • Theming
      • Context
      • Navigation
      • External Navigation
      • Handling Tools
      • Voice Chat
      • Sub-Projects
      • Error Handling
      • CLI
      • Dashboard
      • Invitations
      • Settings
        • Project
        • Overview
        • Conversations
        • Chat Test
        • Sub-Projects
        • Analysis
        • Configuration
        • Members
        • Documents
        • Tools
        • Action Logs
        • Navigation Rules
        • External Destinations
        • Chat Theme
        • API Keys
      • API Key Security

    On This Page

    • Basic Usage
    • Display Modes
    • Props Reference
    • Event Callbacks
    • Imperative Handle
    • Customizing Components
    • Header Buttons (Back / Close)
    • Navigation Buttons
    • Voice Page Components
    • Tool Response Components
    • Card CTAs
    • SDK-internal actions (no setup needed)
    • Host-bound actions
    • deep_link — internal route navigation
    • tool_trigger — fire another tool from a card
    • CTA telemetry
    Question? Give us feedback Edit this page 
    GuidesReact Native Widget

    React Native Widget

    The Qafka component is a drop-in chat widget with full UI, theming, streaming, and voice support.

    Basic Usage

    import { Qafka } from '@qafka/react-native' export default function ChatScreen() { return <Qafka /> }

    The widget calls useSafeAreaInsets internally, so make sure a <SafeAreaProvider> exists somewhere above it in the tree (Expo Router, React Navigation, and most RN starter templates already include one).

    Set up your project with the Qafka CLI (qafka init) so the widget can find the development key — see Quick Start.

    Responses stream in real-time by default — tokens render in the message bubble as they arrive, with a typing indicator while the AI is thinking.

    Display Modes

    ModeDescription
    fullscreenTakes up the entire screen (default)
    inlineEmbeds within a parent container
    floatingFloating chat bubble overlay
    <Qafka mode="fullscreen" />

    Props Reference

    PropTypeDefaultDescription
    projectIdstringruntime-config defaultPick a specific project when the CLI registered more than one
    apiUrlstringProduction URLBackend API endpoint
    subProjectIdstring—Sub-project identifier for multi-tenant setups
    mode'fullscreen' | 'inline' | 'floating''fullscreen'Display mode
    theme'light' | 'dark' | Theme'light'Theme name or custom theme object
    themeOverrideThemeOverride—Partial theme overrides
    placeholderstring'Type a message...'Input placeholder text
    titlestring—Header title
    showHeaderbooleantrueShow header bar
    maxMessageLengthnumber500Maximum message character count
    greetingMessagestring—Custom greeting (overrides project-configured greeting)
    showTimestampsbooleantrueShow message timestamps
    showMetadatabooleanfalseShow message metadata (handled by, confidence, tokens)
    voiceEnabledbooleantrueAllow voice chat (server must also allow it)
    isAuthenticatedboolean—Hides screens marked auth-only / public-only based on this flag
    contextRecord<string, any>—Runtime context sent with messages and used for tool matching
    contextDescriptionstring—Human description of context keys for the AI
    componentsComponentRegistry—Custom components for tool response rendering. See Customizing Components.
    voiceComponentsVoiceComponents—Custom voice indicator/background/transcript components
    toolRenderMode'upsert' | 'replace''upsert'How voice tool results accumulate on screen
    BackComponentComponentType—Custom back button (replaces default rendering when onBack is set)
    CloseComponentComponentType—Custom close button (replaces default rendering when onClose is set)
    NavigationButtonComponentComponentType<NavigationButtonProps>—Custom navigation suggestion button
    navigationLabelFormat(screenName: string) => string—Format the label shown on navigation buttons (e.g. "Go to Cart")

    Event Callbacks

    CallbackTypeDescription
    onReady() => voidSDK initialized successfully
    onMessageSent(message: string) => voidUser sent a message
    onResponseReceived(response) => voidAI response received
    onError(error: Error) => voidError occurred
    onNavigationSuggest(suggestion) => voidAI suggests navigation (always fires)
    onNavigationAction(suggestion) => voidUser pressed a navigation button. If omitted, the SDK navigates automatically via Expo Router.
    onExternalSuggestion(suggestion) => voidUser pressed an external action (WhatsApp, phone, map, app store…). Defaults to opening the URL via Linking. See External Navigation.
    onToolSuggested(tools, addResponse) => void | Promise<void>Backend matched a tool to the user’s message. Provide a handler to resolve it; otherwise, the widget renders defaults.
    onActionResult(results) => voidBackend action execution finished
    onStepCompleted(result) => voidA step finished inside a multi-step tool flow
    onFileUploadRequest(request) => voidA tool requires a file upload — open your picker and call submit()
    onExtractionResult(result) => voidAI finished extracting structured data from an uploaded file
    onClose() => voidClose button pressed (button only renders when this is set)
    onBack() => voidBack button pressed (button only renders when this is set)
    onCardDeepLink(path: string, action) => voidCard button fired a deep_link action — partner navigates to the route. Required for cards that use deep links; otherwise the action silently skips.
    onCardSuggestMessage(text: string) => voidCard fired suggest_message. Default behavior submits the text as if the user typed it; override only when you want custom routing.
    onCardExternalNavigation(action) => voidCard fired external_navigation (WhatsApp / phone / map / etc.). Falls back to the SDK’s external navigation handler when omitted — you usually rely on the default.
    onCardShare({ text, url }) => voidCard fired share. Default opens the OS share sheet.
    onCardCopy(value: string) => voidCard fired copy. Default copies to the clipboard.
    onCardToolTrigger(toolName, params, meta) => voidCard fired tool_trigger — calls another tool from a button. The partner forwards this to their tool execution path.
    onCardCTAClick(event) => voidTelemetry hook called once per CTA click. Event includes cardTemplateId, cardSlug, actionType, item (full bound record), and itemIndex (only present in list mode). Use it to feed analytics.

    Imperative Handle

    Pass a ref to control the widget programmatically:

    import { useRef } from 'react' import { Qafka, type QafkaHandle } from '@qafka/react-native' const qafkaRef = useRef<QafkaHandle>(null) <Qafka ref={qafkaRef} /> // Send a message as if the user typed it qafkaRef.current?.sendMessage('Show my orders') // Voice session control await qafkaRef.current?.connectVoice() await qafkaRef.current?.disconnectVoice() qafkaRef.current?.pauseMic() qafkaRef.current?.resumeMic() // Voice loading pill (for async work outside onToolSuggested) qafkaRef.current?.setLoading(true, 'Fetching data…') qafkaRef.current?.setLoading(false) // Reset voice tool cards qafkaRef.current?.clearRenderedTools()

    Customizing Components

    The widget renders sensible defaults for every interactive surface, but you can swap any of them out for your own component to match your app’s visual language.

    Header Buttons (Back / Close)

    The back and close buttons appear in the header only when you wire up their callbacks. To replace the default rendering, pass BackComponent / CloseComponent:

    <Qafka onBack={() => navigation.goBack()} onClose={() => navigation.navigate('Home')} BackComponent={() => <MyBackButton />} CloseComponent={() => <MyCloseButton />} />

    Navigation Buttons

    When the AI suggests a navigation target, the widget renders a button. Replace the rendering with NavigationButtonComponent, or just rewrite the label with navigationLabelFormat.

    // Just change the label text <Qafka navigationLabelFormat={(screen) => `Go to ${screen}`} /> // Replace the entire button <Qafka NavigationButtonComponent={({ screenName, onPress, theme, label }) => ( <TouchableOpacity onPress={onPress} style={myButtonStyle(theme)}> <Text>{label}</Text> </TouchableOpacity> )} />

    The component receives { screenName, suggestion, onPress, theme, style, label, icon }.

    Voice Page Components

    The voice page has three slots — animation indicator, background container, and transcript text. Provide any subset; missing ones fall back to defaults.

    import LottieView from 'lottie-react-native' import voiceLottie from './voice-blob.json' <Qafka voiceComponents={{ VoiceIndicator: ({ state, amplitude, theme }) => ( <LottieView source={voiceLottie} autoPlay loop speed={state === 'speaking' ? 1.5 : 1} /> ), VoiceBackground: ({ state, children, theme }) => ( <LinearGradient colors={['#1A1A2E', '#16213E']} style={{ flex: 1 }}> {children} </LinearGradient> ), VoiceTranscript: ({ transcript, userTranscript, state, theme }) => ( <Text style={{ color: theme.colors.text }}> {state === 'listening' ? userTranscript : transcript} </Text> ), }} />

    Each slot receives the live voice state ('idle' | 'connecting' | 'listening' | 'thinking' | 'speaking'), the current audio amplitude (0–1), and the active theme.

    Tool Response Components

    When a tool result comes back, the widget renders it with a default Card / List / Detail / Table component based on the tool’s response.type. Override these by registering your own components and referencing them by name from the dashboard’s tool config.

    import { CampaignCard, ProductRow } from './chat-renderers' <Qafka components={{ CampaignCard, ProductRow, }} />

    Each registered component receives { data, tool, theme, onAction }. In the dashboard, set the tool’s response.itemComponent to the matching key ("CampaignCard", "ProductRow") to use your renderer instead of the default.

    Card CTAs

    When a tool is configured to use the Card renderer in the dashboard (see Tools — UI Rendering), buttons inside the card fire one of seven action types. Five of them have built-in SDK behavior; two cross the SDK boundary and need a host callback.

    SDK-internal actions (no setup needed)

    Action typeDefault behavior
    external_navigationGoes through the SDK’s external nav handler (same path as External Navigation guide).
    suggest_messageSubmits a new message as if the user typed it.
    copyCopies the value to the clipboard.
    dismissHides the card locally.
    shareOpens the OS share sheet.

    Override any of these via onCardSuggestMessage, onCardCopy, onCardShare, onCardExternalNavigation if you want custom behavior (e.g. show a toast on copy).

    Host-bound actions

    deep_link — internal route navigation

    Card author writes the path in the dashboard (with optional {{template}} placeholders that resolve at click time):

    { "component": "QButton", "label": "Detayları Gör", "variant": "primary", "action": { "action": { "type": "deep_link", "path": "/store/{{id}}" } } }

    Partner registers the handler:

    import { useRouter } from 'expo-router' const router = useRouter() <Qafka // ...other props onCardDeepLink={(path) => router.push(path)} />

    When the partner doesn’t provide onCardDeepLink, the action is silently skipped and a dev-mode warning is logged.

    tool_trigger — fire another tool from a card

    For chains where one card spawns another (e.g. “Confirm booking” button calls a bookAppointment tool):

    <Qafka onCardToolTrigger={(toolName, params, meta) => { sdk.invokeTool(toolName, params) }} />

    meta.ctaDisplayMode is 'user_message' (default) or 'silent' — controls whether the trigger appears as a user bubble in chat history.

    CTA telemetry

    onCardCTAClick fires once per button press. Event shape:

    { event: 'cta_click', cardTemplateId: string, cardSlug: string, actionType: 'external_navigation' | 'deep_link' | ..., toolName?: string, messageId?: string, itemIndex?: number, // only set in list mode item: unknown, // the bound record (item in list mode, full result in detail mode) confirmed: boolean, }

    item carries the actual record the partner is acting on, so you don’t need to keep the source array around to look it up by index. Forward it to your analytics service:

    <Qafka onCardCTAClick={(event) => { analytics.track('card_cta_click', { action: event.actionType, template: event.cardTemplateId, itemId: (event.item as any)?.id, }) }} />
    Last updated on June 3, 2026
    ConfigurationTheming

    MIT 2026 QAFKA