React Native Configuration
Complete prop reference for <Qafka /> (SDK @qafka/react-native 2.7.0). For install steps see Quick Start; for visual customization and callback walkthroughs see React Native Widget.
Required props
| Prop | Type | Description |
|---|---|---|
projectId | string | The Qafka project this SDK instance talks to. Sent with device attestation in production builds and used to select the development key in dev builds. Missing it sets an error state and calls onError instead of initializing — it does not throw. An id that isn’t registered in devConfig logs a console.warn and the SDK continues without a dev key — it does not fall back to a different project silently. Find it in the dashboard project URL or in qafka.config.d.ts after qafka init. |
endUserId | string | number | Stable identifier for the current user. Groups conversations, action logs, and analytics per user. Use a real user id once signed in, or a constant placeholder (e.g. "anonymous") before login. Empty, whitespace-only, or values over 256 characters throw. Numbers are coerced to strings. Never forwarded to the LLM prompt — reach it in dashboards/templates via {{endUser.id}}. |
Development vs. production credentials
- Development: the SDK needs a key before device attestation is available. Pass
devConfig={__DEV__ ? require('../.qafka/qafka-runtime') : undefined}— the fileqafka init/qafka sync/qafka refreshwrite for you. Gating therequirebehind__DEV__means Metro dead-code-eliminates it from release builds, so no key reaches production. - Production: no key ships in the bundle. The backend authenticates the app via device attestation (App Attest on iOS, hardware-backed Android Key Attestation on Android), resolves your project from
projectId— sent alongside the attestation — and checks that the attested app is registered on it. See API Key Security for how key types, attestation, and rate limits fit together.
devConfig shape
qafka init / qafka sync / qafka refresh generate .qafka/qafka-runtime.js for you — most integrations never need to write this by hand. If you’re installing manually (no CLI), build the same shape yourself using a TEST key: create one from the dashboard’s API Keys page and save the value shown — it’s displayed only once, at creation:
{
defaultProjectId: 'proj_abc123', // not used by <Qafka> — projectId is a required prop
apiUrl: 'https://api.qafka.com', // optional: omit to use the production default
projects: {
proj_abc123: { developmentKey: 'your-test-key' },
// one entry per registered project
},
}<Qafka> always uses its own projectId prop — it never falls back to defaultProjectId. That prop’s value looks itself up in projects to resolve the development key and, together with apiUrl, the backend URL. An explicit projectId with no matching entry in projects logs a console.warn (“Dev config missing or invalid”) and the SDK proceeds without a dev key — this is the case described in the projectId row above. This file carries a live TEST key: keep it out of version control (gitignore it, same as the CLI-generated file) and never let it reach a production build — gate the require/import behind __DEV__ the same way the CLI-scaffolded code does.
Connection & identity
| Prop | Type | Default | Description |
|---|---|---|---|
apiUrl | string | production URL | Backend API endpoint. Leave unset unless you’re pointing at a non-default deployment. |
devConfig | RuntimeConfig | null | — | Dev-only runtime config (see below and “Development vs. production credentials” above). Ignored in production. When omitted, the SDK falls back to a legacy node_modules-bundled config for backward compatibility. |
subProjectId | string | — | Routes the SDK to a sub-project under projectId. See Sub-Projects. |
locale | string | — | BCP 47 locale (e.g. "tr", "en-US") forwarded to the backend as sdkContext.locale. When set, the assistant answers in this language unless the project’s own instructions demand another one. The SDK never reads the device locale for this — it’s explicit only, and it does not change the SDK’s own UI strings. |
isAuthenticated | boolean | — | Controls which navigation screens are suggested: true surfaces authenticated screens and hides unauthenticated ones, false is the reverse. Leaving it undefined doesn’t suggest all screens — the SDK reports its version on every request, which puts the backend in strict mode, so only public screens (and screens with no navigation rules at all) are eligible. Pass this prop explicitly if you want auth-gated screens suggested. |
endUserData | Record<string, unknown> | — | Structured profile data for operator dashboards and tool action templates ({{endUser.data.<key>}}). Never forwarded to the LLM (unless a tool’s Description references it with {{endUser.*}}) — put anything the AI should see in context instead. Dropped from the request (with a console.error) if it exceeds 8KB serialized, or isn’t a plain object; chat keeps working either way. |
context | Record<string, any> | — | Runtime facts the assistant can use to personalize answers (first name, selected store, account state, …). Every key here is visible to the AI. See Context for the full context vs. endUserData split. |
contextDescription | string | — | One sentence describing the screen the user is currently on. Placed above the context lines in the prompt. |
Behavior
| Prop | Type | Default | Description |
|---|---|---|---|
enableStreaming | boolean | true | Streams responses token-by-token instead of waiting for the full reply. Keep it on if the project has Server, Static Data or Calendar tools — those only run on the streaming path; with false the assistant can’t execute them. |
voiceEnabled | boolean | true | Opt out of voice mode client-side. The project must also allow voice on the backend. |
voiceTranscript | 'centered' | 'chat' | 'off' | 'centered' | How the voice-mode transcript area renders: a single current line, a scrolling chat-style history, or hidden entirely. |
toolRenderMode | 'upsert' | 'replace' | 'upsert' | How voice tool results accumulate on screen: upsert keeps one card per tool key, replace clears previous results on every new tool result. |
showTimestamps | boolean | true | Show timestamps on chat messages. |
placeholder | string | 'Type a message...' | Input field placeholder text. |
maxMessageLength | number | 500 | Maximum characters accepted in the input field. |
greetingMessage | string | project greeting | Overrides the greeting configured in the dashboard. |
Appearance
| Prop | Type | Description |
|---|---|---|
theme | 'light' | 'dark' | Theme | Base theme. |
themeOverride | ThemeOverride | Partial overrides merged onto the base/dashboard theme. |
customTheme | Theme | Full theme object that replaces the default theme entirely. Combine with themeOverride only if you also need partial adjustments on top of it. |
components | ComponentRegistry | Custom components for Tool Registry responses (e.g. { ProductCard: MyProductCard }). |
voiceComponents | VoiceComponents | Overrides for the voice indicator, background, or transcript components. |
style | ViewStyle | Style applied to the widget’s outer container. |
CloseComponent | React.ComponentType | Replaces the default close button. The header row shows a close/back area when either onClose or CloseComponent (or onBack/BackComponent) is set. |
BackComponent | React.ComponentType | Replaces the default back button. Same visibility rule as CloseComponent. |
navigationLabelFormat | (screenName: string) => string | Formats the label on navigation suggestion buttons. |
NavigationButtonComponent | React.ComponentType<NavigationButtonProps> | Custom navigation suggestion button. |
See Theming for the full token list and React Native Widget for layout and component-override examples.
Callbacks
| Prop | Signature | Description |
|---|---|---|
onReady | () => void | SDK finished initializing. |
onError | (error: Error) => void | An error occurred (mount failure, request failure, etc.). |
onUnavailable | () => void | The project is suspended. The chat screen stays mounted showing a neutral message instead of the normal chat surface; use this to hide your own entry point. A project that’s already suspended when the widget mounts fires this in both streaming modes; onError does not also fire for that transition. With enableStreaming={false}, a suspension hit mid-session by a send arrives through onError (plus the default error bubble) instead. See Error Handling. |
onClose | () => void | Close button pressed. Providing this shows the close button. |
onBack | () => void | Back button pressed. Providing this shows the back button. |
onMessageSent | (message: string) => void | A message was sent. |
onResponseReceived | (response: any) => void | A response was received. |
onNavigationSuggest | (suggestion: NavigationSuggestion) => void | A navigation suggestion arrived (always fires). |
onNavigationAction | (suggestion: NavigationSuggestion) => void | The navigation button was pressed. If omitted, the SDK navigates automatically via Expo Router. |
onExternalSuggestion | (suggestion: ExternalSuggestion) => void | An external suggestion button (WhatsApp, phone, map, app store, …) was pressed. If omitted, the SDK opens suggestion.url via Linking.openURL, falling back to fallbackUrl. See External Navigation. |
onToolSuggested | (tools, addResponse) => void | Promise<void> | Tool Registry matched one or more tools. See Handling Tools. |
onToolDataRequested | (tool: { key, name }) => Record<string, unknown> | Promise<...> | Resolver for {{tooldata.X}} tokens referenced by a suggested tool’s actions. Values are never sent to the LLM or stored in the Qafka database. |
onActionResult | (results: Array<{ actionType, success, message }>) => void | Backend action execution results (email/webhook, etc.) arrived. |
onStepCompleted | (result) => void | A step side-effect completed during a multi-step tool flow. |
onFileUploadRequest | (request: { toolId, fileInput, submit, cancel }) => void | A tool requires a file upload; open your own picker and call submit(). |
onExtractionResult | (result: { fileId, toolId, data, status, incompleteFields? }) => void | AI document extraction finished after a file upload. status is 'success' | 'partial' | 'failed'. |
onCardDeepLink | (path: string, action?: any) => void | Promise<void> | A rendered card’s button requested internal navigation. |
onCardSuggestMessage | (text: string) => void | Promise<void> | A card button suggested a chat message. |
onCardExternalNavigation | (action: any) => void | Promise<void> | A card button requested external navigation. |
onCardShare | (payload: { text?, url? }) => void | Promise<void> | A card button requested a share sheet. Defaults to RN Share.share if omitted. |
onCardCopy | (value: string) => void | Promise<void> | A card button requested a copy-to-clipboard. Defaults to the RN clipboard if omitted. |
onCardToolTrigger | (toolName, params, meta) => void | Promise<void> | A card button triggered a tool. |
onCardCTAClick | (event) => void | Telemetry hook fired once per card button click. |
Props with no effect
mode, title, and showHeader exist on older integrations but are not read by <Qafka> in 2.7.0 — setting them has no effect. The floating-launcher layout some of these implied is provided by QafkaProvider, documented in React Native Widget.
Example
import React from 'react'
import { Qafka } from '@qafka/react-native'
const qafkaDevConfig = __DEV__ ? require('../.qafka/qafka-runtime') : undefined
export default function ChatScreen() {
return (
<Qafka
projectId="proj_abc123"
endUserId={currentUser?.id ?? 'anonymous'}
devConfig={qafkaDevConfig}
locale="en"
context={{ firstName: currentUser?.firstName ?? null }}
isAuthenticated={!!currentUser}
onClose={() => setChatOpen(false)}
/>
)
}