npm package: @question-forms/react-native
Description: React Native renderer for Question Forms SDK
Author: question-forms
Latest version: 0.5.4

# @question-forms/react-native

React Native renderer for the [Question Forms SDK](https://jolly-dune-009ebe90f.4.azurestaticapps.net/) — render schema-driven forms natively on iOS and Android, build forms visually, and customize everything.

## Installation

```bash
npm install @question-forms/core @question-forms/react-native
```

### Peer Dependencies

```bash
npm install react react-native react-native-vector-icons \
  react-native-image-picker react-native-signature-canvas \
  @react-native-community/datetimepicker
```

#### iOS

```bash
cd ios && pod install
```

#### Android

Follow the [react-native-vector-icons setup guide](https://github.com/oblador/react-native-vector-icons#installation) to configure fonts.

## Quick Start

```tsx
import { useState } from 'react';
import { QuestionForm } from '@question-forms/react-native';
import type { FormSchema, FormValues } from '@question-forms/core';

const schema: FormSchema = {
    id: 'my-form',
    sections: [
        {
            id: 'section-1',
            questions: [
                {
                    id: 'q1',
                    key: 'name',
                    type: 'TEXT',
                    subtype: 'short',
                    label: 'Your Name',
                    required: true,
                },
            ],
        },
    ],
};

const MyForm = () => {
    const [values, setValues] = useState<FormValues>({});

    return (
        <QuestionForm
            schema={schema}
            value={values}
            onChange={setValues}
            onSubmit={(v) => console.log('Submitted:', v)}
        />
    );
};
```

## Features

- **10 question types** — text, date, time, select, rating, image, handwriting, radio grid, checkbox grid
- **Visual form builder** — mobile-optimized form designer
- **11 locales** — en, es, pt, fr, de, ar, ja, ko, zh, it, uk
- **Custom renderers** — override any question type with your own component
- **Theming** — customize colors, spacing, typography, and border radius using numeric values
- **Three form modes** — `edit`, `readonly`, `preview`
- **Action bar** — built-in submit/reset buttons (can be hidden)
- **Native controls** — platform date/time pickers, native alerts, image picker
- **Conditional logic** — visibility and enabled state driven by `@question-forms/core`
- **Validation** — required, min/max, pattern, custom — with per-question error display

## Components

### QuestionForm

The main form renderer.

```tsx
import { QuestionForm } from '@question-forms/react-native';

<QuestionForm
    schema={schema}
    value={values}
    onChange={setValues}
    onSubmit={handleSubmit}
    onReset={handleReset}
    mode="edit"
    locale="en"
    theme={{ primaryColor: '#6366f1' }}
    showActionBar={true}
    rendererOverrides={{ TEXT: MyCustomTextRenderer }}
/>
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `schema` | `FormSchema` | required | Form definition object |
| `value` | `FormValues` | required | Current form values |
| `onChange` | `(values: FormValues) => void` | required | Called when any value changes |
| `onSubmit` | `(values: FormValues) => void` | — | Called on valid submit |
| `onReset` | `() => void` | — | Called on reset |
| `mode` | `'edit' \| 'readonly' \| 'preview'` | `'edit'` | Form interaction mode |
| `locale` | `Locale` | `'en'` | UI translation locale |
| `theme` | `Partial<IFormTheme>` | — | Theme token overrides |
| `showActionBar` | `boolean` | `true` | Show submit/reset buttons |
| `rendererOverrides` | `Partial<Record<QuestionType, Component>>` | — | Custom renderers per type |
| `licenseKey` | `string` | — | Required for production use |

### FormBuilder

Mobile-optimized visual form designer.

```tsx
import { useState, useCallback } from 'react';
import { FormBuilder } from '@question-forms/react-native';
import type { FormSchema } from '@question-forms/core';

const MyBuilder = () => {
    const [schema, setSchema] = useState<FormSchema | null>(null);

    const handleChange = useCallback((s: FormSchema) => {
        setSchema(s);
    }, []);

    return (
        <FormBuilder
            initialSchema={schema ?? undefined}
            onChange={handleChange}
            locale="en"
            licenseKey="your-license-key"
        />
    );
};
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `initialSchema` | `FormSchema` | — | Pre-populate the builder |
| `onChange` | `(schema: FormSchema) => void` | — | Called on every schema change |
| `allowedTypes` | `string[]` | all types | Restrict available question types |
| `colors` | `IBuilderColors` | `DEFAULT_COLORS` | Builder canvas colors |
| `locale` | `Locale` | `'en'` | Builder UI locale |
| `licenseKey` | `string` | — | Required for production use |

## Custom Renderers

Override any question type with your own component:

```tsx
import { View, TextInput, Text } from 'react-native';
import type { IQuestionRendererProps } from '@question-forms/react-native';

const MyTextRenderer = ({ question, value, onChange, disabled, errors, theme }: IQuestionRendererProps) => (
    <View>
        <Text style={{ color: theme.labelColor }}>{question.label}</Text>
        <TextInput
            value={(value as string) ?? ''}
            onChangeText={onChange}
            editable={!disabled}
            style={{
                borderWidth: 1,
                borderColor: errors.length > 0 ? theme.errorColor : theme.borderColor,
                borderRadius: theme.borderRadius,
                padding: theme.inputPadding,
            }}
        />
        {errors.map((err, i) => (
            <Text key={i} style={{ color: theme.errorColor }}>{err}</Text>
        ))}
    </View>
);

<QuestionForm
    schema={schema}
    value={values}
    onChange={setValues}
    rendererOverrides={{ TEXT: MyTextRenderer }}
/>
```

### IQuestionRendererProps

| Prop | Type | Description |
|------|------|-------------|
| `question` | `QuestionSchema` | The question definition |
| `value` | `unknown` | Current value for this question |
| `onChange` | `(value: unknown) => void` | Update the value |
| `disabled` | `boolean` | Whether the input is disabled |
| `mode` | `'edit' \| 'readonly' \| 'preview'` | Current form mode |
| `errors` | `string[]` | Validation error messages |
| `theme` | `IFormTheme` | Resolved theme object |

## Theme Customization

Theme values use **numbers** (not strings) for spacing and sizing — consistent with React Native's style system.

```tsx
<QuestionForm
    schema={schema}
    value={values}
    onChange={setValues}
    theme={{
        fontFamily: 'System',
        fontSize: 14,
        primaryColor: '#6366f1',
        errorColor: '#ef4444',
        borderColor: '#e5e7eb',
        borderRadius: 8,
        inputPadding: 14,
        sectionGap: 32,
        questionGap: 20,
        labelColor: '#111827',
        disabledOpacity: 0.5,
    }}
/>
```

### Default Theme

```typescript
{
    fontFamily: 'System',
    fontSize: 14,
    primaryColor: '#2563eb',
    errorColor: '#dc2626',
    borderColor: '#d1d5db',
    borderRadius: 6,
    inputPadding: 12,
    sectionGap: 24,
    questionGap: 16,
    labelColor: '#1f2937',
    disabledOpacity: 0.6,
}
```

### Key Difference from `@question-forms/react`

| Aspect | React (Web) | React Native |
|--------|-------------|--------------|
| Theme values | Strings (`'14px'`, `'8px 12px'`) | Numbers (`14`, `12`) |
| Theme delivery | Context-based | Passed as prop to renderers |
| UI framework | Material UI | Native components |
| Icons | `@mui/icons-material` | `react-native-vector-icons` |
| Date/time pickers | `@mui/x-date-pickers` | `@react-native-community/datetimepicker` |
| Image picker | HTML file input | `react-native-image-picker` |
| Handwriting | HTML Canvas | `react-native-signature-canvas` |

## Internationalization

11 built-in locales with 70+ translation strings.

```tsx
import { QuestionForm, LOCALE_LABELS } from '@question-forms/react-native';

<QuestionForm
    schema={schema}
    value={values}
    onChange={setValues}
    locale="es"
/>
```

**Supported locales:** `en`, `es`, `pt`, `fr`, `de`, `ar`, `ja`, `ko`, `zh`, `it`, `uk`

## Icons

Built-in icon components wrapping `react-native-vector-icons`:

```tsx
import { MaterialIcon, MaterialCommunityIcon } from '@question-forms/react-native';

<MaterialIcon name="check" size={24} color="#000" />
<MaterialCommunityIcon name="heart" size={24} color="red" />
```

## Builder Utilities

```typescript
import {
    useBuilderState,
    DEFAULT_COLORS,
    QUESTION_TYPE_LABELS,
    QUESTION_TYPE_GROUPS,
    createDefaultQuestion,
    createDefaultSection,
    createDefaultSubSection,
    generateId,
} from '@question-forms/react-native';
```

## Exports Summary

| Export | Description |
|--------|-------------|
| `QuestionForm` | Main form renderer component |
| `FormBuilder` | Visual form designer component |
| `defaultTheme` | Default theme values (numeric) |
| `TranslationProvider` | i18n context provider |
| `useTranslation` | Hook — access translation strings |
| `getTranslations` | Get translations for a locale |
| `fmt` | String formatter with `{0}`, `{1}` placeholders |
| `LOCALE_LABELS` | Map of locale codes to display names |
| `MaterialIcon` | Material Design icon component |
| `MaterialCommunityIcon` | Material Community icon component |
| `useBuilderState` | Hook — builder state management |
| `DEFAULT_COLORS` | Builder canvas colors |
| `QUESTION_TYPE_LABELS` | Question type display names |
| `QUESTION_TYPE_GROUPS` | Grouped question type menu config |
| `createDefaultQuestion` | Factory — new question with defaults |
| `createDefaultSection` | Factory — new section with defaults |
| `createDefaultSubSection` | Factory — new subsection with defaults |
| `generateId` | Generate unique IDs |
| `getTypeOptionKey` | Get composite type key |
| `getLocalizedGroups` | Localized question type groups |
| `getLocalizedTypeLabels` | Localized question type labels |

## License

MIT — [Question Forms SDK](https://jolly-dune-009ebe90f.4.azurestaticapps.net/)