@osforms/react. You can import them directly for TypeScript type safety when building headless forms.
import type {
FormField,
FieldType,
FieldOption,
FieldValidation,
FieldConfig,
} from '@osforms/react';
FieldType
type FieldType =
| 'text'
| 'email'
| 'number'
| 'tel'
| 'url'
| 'textarea'
| 'select'
| 'radio'
| 'checkbox'
| 'date'
| 'file'
| 'rating'
| 'scale'
| 'statement'
| 'divider';
date, file, and divider exist in the type but the SDK doesn’t render
them yet: they’re skipped on screen. Don’t use them in forms you render with
@osforms/react. The other 12 types are fully supported.FormField
interface FormField {
id: string;
type: FieldType;
label: string;
description?: string;
placeholder?: string;
required: boolean;
options?: FieldOption[];
validation?: FieldValidation;
conditionalLogic?: ConditionalLogic;
config?: FieldConfig;
}
FieldOption
Used byselect, radio, and checkbox fields.
interface FieldOption {
id: string; // nanoid(6)
label: string; // display text shown to user
value: string; // value submitted (defaults to label if not set)
}
FieldValidation
interface FieldValidation {
minLength?: number; // text, textarea
maxLength?: number; // text, textarea
min?: number; // number, scale, rating
max?: number; // number, scale, rating
pattern?: string; // regex
patternError?: string; // custom error on pattern mismatch
fileTypes?: string[]; // not enforced yet
maxFileSize?: number; // not enforced yet
}
FieldConfig
Type-specific configuration options.interface FieldConfig {
// rating
maxRating?: number;
ratingIcon?: 'star' | 'heart' | 'thumb'; // not applied yet: always stars
// scale
scaleMin?: number;
scaleMax?: number;
scaleLowLabel?: string; // e.g. "Not likely"
scaleHighLabel?: string; // e.g. "Very likely"
// textarea
rows?: number; // default: 4 in classic, 3 in conversational
// select
searchable?: boolean; // not applied yet: native <select>
// checkbox / radio
layout?: 'vertical' | 'horizontal' | 'grid'; // not applied yet: always vertical
// conversational mode: auto-advance after selection
// default: true for radio, rating, and scale. select always advances.
autoAdvance?: boolean;
}
Building a form from a local schema
import { OSForm } from '@osforms/react';
import type { FormSchema } from '@osforms/react';
const schema: FormSchema = {
mode: 'conversational',
fields: [
{
id: 'name',
type: 'text',
label: "What's your name?",
required: true,
validation: { minLength: 2 },
},
{
id: 'role',
type: 'radio',
label: "What's your role?",
required: true,
options: [
{ id: 'dev', label: 'Developer', value: 'developer' },
{ id: 'design', label: 'Designer', value: 'designer' },
{ id: 'pm', label: 'Product Manager', value: 'pm' },
{ id: 'other', label: 'Other', value: 'other' },
],
},
{
id: 'role_other',
type: 'text',
label: 'Please specify',
required: false,
conditionalLogic: {
action: 'show',
match: 'all',
conditions: [{ fieldId: 'role', operator: 'equals', value: 'other' }],
},
},
{
id: 'nps',
type: 'scale',
label: 'How likely are you to recommend us?',
required: false,
config: {
scaleMin: 0,
scaleMax: 10,
scaleLowLabel: 'Not likely',
scaleHighLabel: 'Very likely',
},
},
],
welcomeScreen: {
enabled: true,
title: 'Quick survey',
description: 'Takes less than 2 minutes.',
buttonLabel: 'Start',
},
thankYouScreen: {
enabled: true,
title: 'Thanks!',
description: 'Your response has been recorded.',
},
};
export default function SurveyPage() {
return (
<OSForm
schema={schema}
endpoint="https://osforms.com/api/v1/f/your-slug"
mode="conversational"
fullScreen
onComplete={() => console.log('done')}
/>
);
}