Guides
React Native & Expo
formkrafter-react-native renders the same form specs as the web packages
with native components — no WebView, no Web Components. Validation, rules,
option sources and the recap walk all come from formkrafter-core, so a spec
built in the web builder renders on the phone unchanged.
It is a renderer: there is no mobile builder, by design.
Install
Section titled “Install”npx expo install @streamline-pulse/formkrafter-react-nativenpm install @streamline-pulse/formkrafter-react-nativeReact ≥18 and React Native ≥0.76 are peer dependencies. Nothing else is required for the core bricks — the date, file and signature bricks each live behind an opt-in entry point.
First render
Section titled “First render”-
Wrap your app in the theme provider:
import { FkThemeProvider } from '@streamline-pulse/formkrafter-react-native'<FkThemeProvider theme={colorScheme === 'dark' ? 'dark' : 'light'}><App /></FkThemeProvider> -
Render a spec:
import { useRef } from 'react'import { FormRenderer } from '@streamline-pulse/formkrafter-react-native'import type { FormRendererHandle } from '@streamline-pulse/formkrafter-react-native'function Checkout({ spec }) {const form = useRef<FormRendererHandle>(null)return (<FormRendererref={form}spec={spec}onDataChange={(data, isValid) => console.log(data, isValid)}onSubmit={(data, isValid) => isValid && send(data)}/>)} -
Validate on demand through the ref:
const result = form.current?.validate()// { valid: boolean, errors: { fieldKey: message, … } }
A wizard spec (panel:stepper) renders with per-step validation and its own
submit button, which calls your onSubmit — no extra wiring. Grid rows count
in the global verdict.
Theming
Section titled “Theming”FkThemeProvider accepts 'light', 'dark' or a full token table — the
same values the web exposes as CSS custom properties:
import { fkDarkTheme } from '@streamline-pulse/formkrafter-react-native'
<FkThemeProvider theme={{ ...fkDarkTheme, colorPrimary: '#e0662d' }}>Languages
Section titled “Languages”Two independent knobs, identical to the web:
- Chrome strings (stepper buttons, grid actions, select search…) come
from the shared store:
setFkTranslations(frFkTranslations)fromformkrafter-core. - Field labels written as
{ en: 'Full name', fr: 'Nom complet' }resolve through thelocaleprop onFormRenderer.
Switch both without remounting — the entered data stays.
Bricks that need a native module
Section titled “Bricks that need a native module”Metro resolves imports statically, so bricks that depend on a native module live on their own entry points. Install the module, call the registration once at startup; applications that skip a brick never resolve its module.
| Entry point | Native module | Registration |
|---|---|---|
…/date |
@react-native-community/datetimepicker |
registerNativeDateBricks() |
…/file |
expo-document-picker |
registerNativeFileBrick() |
…/signature |
react-native-svg |
registerNativeSignatureBrick() |
import { registerNativeDateBricks } from '@streamline-pulse/formkrafter-react-native/date'
registerNativeDateBricks()File uploads go through core’s fileUploadService — the same injection
point as the web — and the signature stores an image/svg+xml data URL
where the web’s canvas produces PNG.
Custom bricks and UI kits
Section titled “Custom bricks and UI kits”The registry is the extension point. A brick receives data, error,
disabled, resolved configs and onDataChange, and renders whatever it
wants — gluestack, NativeBase, Tamagui or your own components:
import { createNativeBrick, registerNativeBrick,} from '@streamline-pulse/formkrafter-react-native'
registerNativeBrick( createNativeBrick({ type: 'input', id: 'rating', render: (props) => ( <Stars value={props.data} onChange={props.onDataChange} error={props.error} /> ), }),)Register before the first render to replace a built-in — the defaults never clobber an existing registration. Bricks without a native renderer show an explicit placeholder rather than failing.
Next steps
Section titled “Next steps”A project by Streamline Pulse