> ## Documentation Index
> Fetch the complete documentation index at: https://docs.funeli.top/llms.txt
> Use this file to discover all available pages before exploring further.

# Components: questions & inputs

> Reference for the Funeli questions & inputs components: what each one is for, every setting with its type and default, and a working example.

One per question screen; each writes an answer field. Add these components to a step in the editor and change their settings in the inspector, or ask an agent to; the settings below are the same in both.

## Single choice

Type `SingleSelect`. Collects one answer. Writes option.value to answers\[field]; an empty selection is validated when Continue is pressed, while autoAdvance moves on after a tap. Options may use image, emoji or icon; selectedStyle controls auto/tint/outline/solid and selectionIndicator can hide the check marker. Selected colours use theme tokens and local appearance overrides.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the selected option.value (string). Read by rules, visibleWhen and \{\{field}} placeholders |
| `options` | object\[] | yes | 2-12 choices, order as shown; values must be unique. Each choice can use image, emoji or icon as its visual, in that precedence order. Use \<=6 on a phone screen, \<=4 for tabs |
| `layout` | "list" \| "grid" \| "tabs" | no | list: stacked 56px rows (best for text answers). grid: 2 columns of image/icon tiles. tabs: a segmented switcher (2-4 options, first one pre-selected on mount) that swaps the content below via visibleWhen; set autoAdvance false Default: `"list"`. |
| `autoAdvance` | boolean | no | Go to the next step right after a choice (no Button needed). false = visitor changes their mind freely and a Button advances; required for tabs Default: `true`. |
| `selectedStyle` | "auto" \| "tint" \| "outline" \| "solid" | no | Selected option treatment. auto preserves legacy behaviour (tint for list/grid, solid for tabs); outline keeps the surface and adds a primary ring; solid fills with primary. All colours come from theme tokens and local appearance overrides Default: `"auto"`. |
| `selectionIndicator` | "check" \| "none" | no | Selected marker. check shows the existing round/square check; none removes the marker entirely for minimal reference-style answer cards Default: `"check"`. |
| `size` | "compact" \| "default" \| "large" | no | Option size for list and grid: compact (smaller padding and visual), default, large (taller, bigger label). Tabs keep one size |
| `optionStyle` | object | no | Option card look: selected/unselected \{fill, borderStyle, borderWidth, borderColor, shadow, glow, textColor}; overrides selectedStyle colours where set |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "goal",
>   "layout": "list",
>   "selectedStyle": "outline",
>   "selectionIndicator": "check",
>   "options": [
>     {
>       "value": "lose_weight",
>       "label": "Lose weight",
>       "description": "Build a sustainable calorie deficit.",
>       "icon": "scale"
>     },
>     {
>       "value": "tone",
>       "label": "Tone my body",
>       "description": "Improve strength and definition.",
>       "icon": "dumbbell",
>       "badge": {
>         "text": "POPULAR",
>         "tone": "primary",
>         "style": "soft"
>       }
>     },
>     {
>       "value": "flexibility",
>       "label": "Improve flexibility",
>       "description": "Move more comfortably day to day.",
>       "icon": "yoga"
>     },
>     {
>       "value": "energy",
>       "label": "Feel more energetic",
>       "description": "Build a routine that supports daily energy.",
>       "icon": "lightning"
>     }
>   ]
> }
> ```

## Multiple choice

Type `MultiSelect`. Collects several answers. Writes string\[] of option.value to answers\[field]; minSelected is validated when Continue is pressed and the inline error appears after the first press. Options may use image, emoji or icon; selectedStyle controls auto/tint/outline/solid and selectionIndicator can hide the check marker. Selected colours use theme tokens and local appearance overrides.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores an array of selected option.value strings (selection order). Read by rules, visibleWhen and \{\{field}} placeholders |
| `options` | object\[] | yes | 2-16 choices, order as shown; values must be unique. Each choice can use image, emoji or icon as its visual, in that precedence order. Keep the visible set short enough to fit comfortably on mobile |
| `layout` | "list" \| "grid" | no | list: stacked rows (best for text). grid: 2 columns of image/icon tiles Default: `"list"`. |
| `minSelected` | integer | no | Fewest choices needed to continue; 1 = answer required, 0 = may skip. Cannot exceed the option count Default: `1`. |
| `maxSelected` | integer | no | Most choices allowed; picking more drops the oldest pick. Omit for no cap. Must be >= minSelected and \<= option count |
| `selectedStyle` | "auto" \| "tint" \| "outline" \| "solid" | no | Selected option treatment. auto preserves legacy behaviour (tint for list/grid, solid for tabs); outline keeps the surface and adds a primary ring; solid fills with primary. All colours come from theme tokens and local appearance overrides Default: `"auto"`. |
| `selectionIndicator` | "check" \| "none" | no | Selected marker. check shows the existing round/square check; none removes the marker entirely for minimal reference-style answer cards Default: `"check"`. |
| `size` | "compact" \| "default" \| "large" | no | Option size for list and grid: compact (smaller padding and visual), default, large (taller, bigger label). Tabs keep one size |
| `optionStyle` | object | no | Option card look: selected/unselected \{fill, borderStyle, borderWidth, borderColor, shadow, glow, textColor}; overrides selectedStyle colours where set |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "focus_areas",
>   "layout": "list",
>   "minSelected": 2,
>   "maxSelected": 3,
>   "selectedStyle": "outline",
>   "selectionIndicator": "check",
>   "options": [
>     {
>       "value": "belly",
>       "label": "Core",
>       "description": "Strength and stability.",
>       "icon": "target"
>     },
>     {
>       "value": "arms",
>       "label": "Upper body",
>       "description": "Arms, shoulders and posture.",
>       "icon": "dumbbell"
>     },
>     {
>       "value": "legs",
>       "label": "Lower body",
>       "description": "Leg strength and mobility.",
>       "icon": "footprints"
>     },
>     {
>       "value": "back",
>       "label": "Back",
>       "description": "Comfort and supporting strength.",
>       "icon": "activity"
>     },
>     {
>       "value": "full_body",
>       "label": "Full body",
>       "description": "Balanced all-over routine.",
>       "icon": "person",
>       "badge": {
>         "text": "BALANCED",
>         "tone": "success",
>         "style": "soft"
>       }
>     }
>   ]
> }
> ```

## Rating scale

Type `RatingScale`. Rates agreement with a statement. 3-7 numbered buttons write an integer 1..points to answers\[field]; required, error "Please choose a rating" after Continue is pressed. autoAdvance (default) moves on after a tap, so omit the Button. Arrow keys move and select.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the chosen number as an integer from 1 to points. Read by rules, visibleWhen and \{\{field}} placeholders |
| `statement` | string | no | Statement to rate, shown in quotes, keep \<=100 chars, e.g. "I often feel tired". It is the accessible group name: omit only when a Heading above poses the question |
| `points` | integer | no | Number of buttons, 3-7; 5 is the standard Likert scale (odd counts have a neutral middle) Default: `5`. |
| `minLabel` | string | no | Caption under 1, keep \<=14 chars; literal Default: `"Not at all"`. |
| `maxLabel` | string | no | Caption under the last point, keep \<=14 chars; literal Default: `"Totally"`. |
| `autoAdvance` | boolean | no | Go to the next step right after a tap (no Button needed). false = a Button advances. A rating is always required ("Please choose a rating") Default: `true`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "stress_level",
>   "statement": "I often feel tired during the day",
>   "points": 5,
>   "minLabel": "Not at all",
>   "maxLabel": "Very often",
>   "autoAdvance": true
> }
> ```

## Swipe cards

Type `SwipeCards`. Collects choices by swiping cards. Drag a card or tap the tick/cross buttons; writes string\[] of ACCEPTED option.value to answers\[field] (like MultiSelect; \[] if all rejected). Never blocks Continue and auto-advances after the last card, so omit the Button. 2-10 cards, each ideally with an image.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the ACCEPTED option.value strings as an array (rejected cards are not stored; \[] if all rejected), like a MultiSelect answer. Read by rules, visibleWhen and \{\{field}} placeholders |
| `options` | object\[] | yes | 2-10 cards, one per option, in deck order; values must be unique. Give each an image (portrait, \~3:4): label/description sit on a dark gradient over it |
| `acceptLabel` | string | no | Stamp shown while dragging right and accessible name of the tick button; literal, keep \<=8 chars Default: `"Yes"`. |
| `rejectLabel` | string | no | Stamp shown while dragging left and accessible name of the cross button; literal, keep \<=8 chars Default: `"No"`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "liked_workouts",
>   "options": [
>     {
>       "value": "yoga",
>       "label": "Yoga",
>       "image": "https://images.unsplash.com/photo-1506126613408-eca07ce68773?w=600&h=800&fit=crop"
>     },
>     {
>       "value": "running",
>       "label": "Running",
>       "image": "https://images.unsplash.com/photo-1552674605-db6ffd4facb5?w=600&h=800&fit=crop"
>     },
>     {
>       "value": "strength",
>       "label": "Strength training",
>       "image": "https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=600&h=800&fit=crop"
>     }
>   ]
> }
> ```

## Slider

Type `Slider`. Captures a number on a slider. Writes the number to answers\[field] as soon as it mounts (the start position counts as an answer) and the unit to \<field>\_unit; never blocks Continue. Big live value above a 48px-high track. Add a Button.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the number; the start position is written on mount, so the answer is never empty. With unit also writes \<field>\_unit. Read by rules, visibleWhen and \{\{field}} placeholders |
| `label` | string | no | Caption above the big value, keep \<=30 chars, e.g. "Your height"; the accessible name |
| `min` | number | no | Value at the left end Default: `0`. |
| `max` | number | no | Value at the right end; must exceed min Default: `100`. |
| `step` | number | no | Increment between values; must be \<= max - min. Use 1 for integers, 0.5 or 0.1 for decimals Default: `1`. |
| `defaultValue` | number | no | Starting position, between min and max; it is stored even if the visitor never moves it. Default = the middle of the range, rounded to step |
| `unit` | string | no | Suffix after the value and stored as \<field>\_unit, e.g. "cm" (enables unit/bmi formulas); empty = none Default: `""`. |
| `minLabel` | string | no | Caption under the left end, keep \<=14 chars, e.g. "Low"; literal |
| `maxLabel` | string | no | Caption under the right end, keep \<=14 chars, e.g. "High"; literal |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "height",
>   "label": "Your height",
>   "min": 140,
>   "max": 210,
>   "defaultValue": 168,
>   "unit": "cm"
> }
> ```

## Number input

Type `NumberInput`. Captures numbers with optional units. Writes a number to answers\[field] (unset when empty/invalid) and the unit to \<field>\_unit; feet\_inches mode stores total inches with unit "in". Decimal keyboard, accepts ",". Errors (required, min, max) show inline only after Continue is pressed. Add a Button.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the number (cleared when empty or invalid); with units also writes \<field>\_unit. Read by rules, visibleWhen and \{\{field}} placeholders |
| `label` | string | no | Visible caption above the input, keep \<=30 chars, e.g. "Current weight". It is the accessible name: omit only when a Heading right above names the field |
| `placeholder` | string | no | Hint inside the empty input, keep \<=10 chars, e.g. "70"; disappears when typing, so never put the label here. feet\_inches mode shows its own 5 ft / 8 in hints Default: `""`. |
| `mode` | "number" \| "feet\_inches" | no | number: one decimal field ("," accepted as decimal point). feet\_inches: two fields (ft, in) combined into total inches, e.g. height; \<field>\_unit is always "in" and units is ignored Default: `"number"`. |
| `units` | string\[] | no | Unit options, e.g. \["kg","lb"] (\<=3, each \<=8 chars): the first is preselected and the choice is stored as \<field>\_unit; 2+ units show a toggle, 1 unit a fixed suffix. Ignored when mode is feet\_inches Default: `[]`. |
| `min` | number | no | Smallest accepted value (inclusive); lower input blocks Continue with "Minimum is N". Applies to total inches in feet\_inches mode |
| `max` | number | no | Largest accepted value (inclusive); higher input blocks Continue with "Maximum is N"; must be >= min |
| `required` | boolean | no | true = Continue is blocked while empty ("Please enter a value"). false = may be skipped; the answer then stays unset, so use \{\{field\|fallback}} downstream Default: `true`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "weight",
>   "label": "Current weight",
>   "placeholder": "70",
>   "mode": "number",
>   "units": [
>     "kg",
>     "lb"
>   ],
>   "min": 30,
>   "max": 300,
>   "required": true
> }
> ```

## Text input

Type `TextInput`. Captures a short text answer. Writes the typed string to answers\[field]. required (default) blocks Continue on blank input, error shown inline after the first press. Add a Button. Set label unless a Heading names the field; browsers name an unlabelled input from its placeholder only.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the typed string. Read by rules, visibleWhen and \{\{field}} placeholders |
| `label` | string | no | Visible caption above the input, keep \<=30 chars, e.g. "First name". It is the accessible name: omit only when a Heading right above names the field |
| `placeholder` | string | no | Hint inside the empty input, keep \<=24 chars, e.g. "Your name"; disappears when typing, so never put the label here Default: `""`. |
| `autocomplete` | "off" \| "given-name" \| "name" | no | Browser autofill hint: given-name for a first name, name for a full name, off otherwise Default: `"off"`. |
| `icon` | "activity" \| "alarm" \| "apple" \| "award" \| "baby" \| "battery" \| "bed" \| "bike" \| "brain" \| "calendar" \| "check" \| "chart" \| "clock" \| "coffee" \| "dumbbell" \| "flame" \| "flower" \| "footprints" \| "gift" \| "heart" \| "leaf" \| "lightning" \| "lock" \| "moon" \| "mountain" \| "music" \| "person" \| "ruler" \| "salad" \| "scale" \| "shield" \| "smile" \| "sparkles" \| "star" \| "sun" \| "target" \| "thumbs-up" \| "timer" \| "trophy" \| "upload" \| "users" \| "water" \| "yoga" \| "x" \| "zap" \| "heart-pulse" \| "stethoscope" \| "pill" \| "syringe" \| "thermometer" \| "weight" \| "hand-heart" \| "eye" \| "ear" \| "bone" \| "carrot" \| "cherry" \| "cookie" \| "pizza" \| "sandwich" \| "egg" \| "fish" \| "beef" \| "wheat" \| "cup-soda" \| "wine" \| "candy" \| "soup" \| "milk" \| "waves" \| "medal" \| "flag" \| "goal" \| "bed-double" \| "cloud-moon" \| "sunrise" \| "sunset" \| "wind" \| "feather" \| "brain-circuit" \| "book-open" \| "headphones" \| "glasses" \| "home" \| "briefcase" \| "car" \| "plane" \| "map-pin" \| "globe" \| "dog" \| "cat" \| "shopping-bag" \| "wallet" \| "credit-card" \| "piggy-bank" \| "gamepad" \| "tv" \| "smartphone" \| "laptop" \| "camera" \| "palette" \| "graduation-cap" \| "smile-plus" \| "frown" \| "meh" \| "laugh" \| "angry" \| "heart-crack" \| "party-popper" \| "check-circle" \| "x-circle" \| "alert-triangle" \| "info" \| "help-circle" \| "plus" \| "minus" \| "arrow-right" \| "trending-up" \| "trending-down" \| "bar-chart" \| "pie-chart" \| "percent" \| "tag" \| "badge-check" \| "crown" \| "gem" \| "rocket" \| "key" \| "bell" \| "mail" \| "phone" \| "message-circle" \| "search" \| "settings" \| "sliders" \| "refresh" \| "infinity" \| "hourglass" \| "calendar-check" \| "list-checks" \| "clipboard" \| "file-text" \| "lightbulb" \| "puzzle" \| "magnet" \| "anchor" \| "user-check" \| "user-plus" \| "users-round" \| "handshake" \| "thumbs-down" \| "star-half" | no | Small icon inside the input on the left |
| `required` | boolean | no | true = Continue is blocked while blank ("This field is required"). false = optional; the answer may stay unset Default: `true`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "name",
>   "label": "First name",
>   "placeholder": "Your name",
>   "autocomplete": "given-name"
> }
> ```

## Email input

Type `EmailInput`. Collects email for checkout. Email keyboard + autofill; writes the trimmed address to answers.email by default, which checkout and receipts use. Malformed input blocks Continue (inline error after first press). consentText adds privacy print. Add a Button.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | no | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the trimmed address; the default "email" is what checkout and receipts use, so keep it unless you collect a second address. Read by rules, visibleWhen and \{\{field}} placeholders Default: `"email"`. |
| `label` | string | no | Visible caption above the input, keep \<=30 chars. It is the accessible name: omit only when a Heading right above names the field |
| `placeholder` | string | no | Hint inside the empty input, keep \<=24 chars Default: `"Enter your email"`. |
| `icon` | "activity" \| "alarm" \| "apple" \| "award" \| "baby" \| "battery" \| "bed" \| "bike" \| "brain" \| "calendar" \| "check" \| "chart" \| "clock" \| "coffee" \| "dumbbell" \| "flame" \| "flower" \| "footprints" \| "gift" \| "heart" \| "leaf" \| "lightning" \| "lock" \| "moon" \| "mountain" \| "music" \| "person" \| "ruler" \| "salad" \| "scale" \| "shield" \| "smile" \| "sparkles" \| "star" \| "sun" \| "target" \| "thumbs-up" \| "timer" \| "trophy" \| "upload" \| "users" \| "water" \| "yoga" \| "x" \| "zap" \| "heart-pulse" \| "stethoscope" \| "pill" \| "syringe" \| "thermometer" \| "weight" \| "hand-heart" \| "eye" \| "ear" \| "bone" \| "carrot" \| "cherry" \| "cookie" \| "pizza" \| "sandwich" \| "egg" \| "fish" \| "beef" \| "wheat" \| "cup-soda" \| "wine" \| "candy" \| "soup" \| "milk" \| "waves" \| "medal" \| "flag" \| "goal" \| "bed-double" \| "cloud-moon" \| "sunrise" \| "sunset" \| "wind" \| "feather" \| "brain-circuit" \| "book-open" \| "headphones" \| "glasses" \| "home" \| "briefcase" \| "car" \| "plane" \| "map-pin" \| "globe" \| "dog" \| "cat" \| "shopping-bag" \| "wallet" \| "credit-card" \| "piggy-bank" \| "gamepad" \| "tv" \| "smartphone" \| "laptop" \| "camera" \| "palette" \| "graduation-cap" \| "smile-plus" \| "frown" \| "meh" \| "laugh" \| "angry" \| "heart-crack" \| "party-popper" \| "check-circle" \| "x-circle" \| "alert-triangle" \| "info" \| "help-circle" \| "plus" \| "minus" \| "arrow-right" \| "trending-up" \| "trending-down" \| "bar-chart" \| "pie-chart" \| "percent" \| "tag" \| "badge-check" \| "crown" \| "gem" \| "rocket" \| "key" \| "bell" \| "mail" \| "phone" \| "message-circle" \| "search" \| "settings" \| "sliders" \| "refresh" \| "infinity" \| "hourglass" \| "calendar-check" \| "list-checks" \| "clipboard" \| "file-text" \| "lightbulb" \| "puzzle" \| "magnet" \| "anchor" \| "user-check" \| "user-plus" \| "users-round" \| "handshake" \| "thumbs-down" \| "star-half" | no | Small icon inside the input on the left |
| `consentText` | string | no | Privacy small print under the input with a lock icon, keep \<=120 chars; supports [label](https://...) links |
| `required` | boolean | no | true = Continue is blocked while empty ("Please enter your email") or malformed ("Please enter a valid email"). false = may be skipped, but a typed value must still be valid Default: `true`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "consentText": "We respect your privacy. See our [Privacy Policy](https://example.com/privacy)."
> }
> ```

## Date picker

Type `DatePicker`. Captures a date from month/day/year selects (no calendar popup). Writes "YYYY-MM-DD" to answers\[field] and the year to \<field>\_year only when the date is real (Feb 30 shows an error). Years run newest first between minYear and maxYear (defaults 1940-2012, adults). Add a Button.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the date as "YYYY-MM-DD" and the year as \<field>\_year (use years\_since on it for age); both stay unset until day, month and year form a real date. Read by rules, visibleWhen and \{\{field}} placeholders |
| `label` | string | no | Caption above the three selects, keep \<=30 chars, e.g. "Your date of birth"; groups them for screen readers |
| `order` | "mdy" \| "dmy" | no | Order of the month / day / year selects: mdy for US audiences, dmy elsewhere Default: `"mdy"`. |
| `minYear` | integer | no | Oldest selectable year (inclusive). The default suits adult birthdays; widen it for other audiences Default: `1940`. |
| `maxYear` | integer | no | Newest selectable year (inclusive); must be >= minYear. Years list newest first. The default 2012 suits adult audiences Default: `2012`. |
| `required` | boolean | no | true = Continue is blocked until a real date is picked ("Please pick a date"; Feb 30 gives "This date does not exist"). false = may be skipped Default: `true`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "birth_date",
>   "label": "Your date of birth"
> }
> ```

## File upload

Type `FileUpload`. Collects a photo as an answer. Uploads an image (HEIC ok) and writes its public URL to answers\[field]. States: empty tinted area (add a dashed border with appearance borderStyle), uploading spinner (blocks Continue), 4:3 preview with "Choose another photo", inline error (too large / failed). onUpload next advances immediately, so omit the Button; stay waits for one.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | yes | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the uploaded file URL (string), e.g. "photo\_url"; usable in step hooks (hook.send) and \{\{placeholders}}. Read by rules, visibleWhen and \{\{field}} placeholders |
| `label` | string | no | Guidance shown inside the empty area and used as the accessible name, keep \<=60 chars, e.g. "Upload a clear photo of your face"; hidden when identical to buttonLabel |
| `buttonLabel` | string | no | Bold title inside the empty area, keep \<=24 chars; literal Default: `"Upload a photo"`. |
| `hint` | string | no | Format/size note under the title, keep \<=60 chars; update it if you lower maxSizeMb Default: `"JPG, PNG, WEBP or HEIC, up to 10 MB"`. |
| `capture` | "none" \| "user" \| "environment" | no | Open the camera directly on phones: user = front (selfie), environment = back; none = choose from gallery or camera Default: `"none"`. |
| `maxSizeMb` | number | no | Largest accepted file in MB (1-10); bigger files fail inline with a size message. The server also caps uploads at config uploads.max\_size\_mb Default: `10`. |
| `onUpload` | "next" \| "stay" | no | Outcome after a successful upload: next validates the step and advances at once (no Button needed); stay keeps the visitor here, for steps with a ConsentCheckbox or a Button. Not allowed inside checkoutSheet Default: `"next"`. |
| `required` | boolean | no | true = Continue is blocked until a photo is uploaded ("Please upload a photo") or while an upload is running. false = may be skipped Default: `true`. |
| `flagField` | string | no | Also write "yes" to this answer once a file is uploaded, e.g. "has\_photo"; use it in conditions such as Button disabledUntil \[\{"field":"has\_photo","op":"eq","value":"yes"}] or visibleWhen |
| `icon` | "activity" \| "alarm" \| "apple" \| "award" \| "baby" \| "battery" \| "bed" \| "bike" \| "brain" \| "calendar" \| "check" \| "chart" \| "clock" \| "coffee" \| "dumbbell" \| "flame" \| "flower" \| "footprints" \| "gift" \| "heart" \| "leaf" \| "lightning" \| "lock" \| "moon" \| "mountain" \| "music" \| "person" \| "ruler" \| "salad" \| "scale" \| "shield" \| "smile" \| "sparkles" \| "star" \| "sun" \| "target" \| "thumbs-up" \| "timer" \| "trophy" \| "upload" \| "users" \| "water" \| "yoga" \| "x" \| "zap" \| "heart-pulse" \| "stethoscope" \| "pill" \| "syringe" \| "thermometer" \| "weight" \| "hand-heart" \| "eye" \| "ear" \| "bone" \| "carrot" \| "cherry" \| "cookie" \| "pizza" \| "sandwich" \| "egg" \| "fish" \| "beef" \| "wheat" \| "cup-soda" \| "wine" \| "candy" \| "soup" \| "milk" \| "waves" \| "medal" \| "flag" \| "goal" \| "bed-double" \| "cloud-moon" \| "sunrise" \| "sunset" \| "wind" \| "feather" \| "brain-circuit" \| "book-open" \| "headphones" \| "glasses" \| "home" \| "briefcase" \| "car" \| "plane" \| "map-pin" \| "globe" \| "dog" \| "cat" \| "shopping-bag" \| "wallet" \| "credit-card" \| "piggy-bank" \| "gamepad" \| "tv" \| "smartphone" \| "laptop" \| "camera" \| "palette" \| "graduation-cap" \| "smile-plus" \| "frown" \| "meh" \| "laugh" \| "angry" \| "heart-crack" \| "party-popper" \| "check-circle" \| "x-circle" \| "alert-triangle" \| "info" \| "help-circle" \| "plus" \| "minus" \| "arrow-right" \| "trending-up" \| "trending-down" \| "bar-chart" \| "pie-chart" \| "percent" \| "tag" \| "badge-check" \| "crown" \| "gem" \| "rocket" \| "key" \| "bell" \| "mail" \| "phone" \| "message-circle" \| "search" \| "settings" \| "sliders" \| "refresh" \| "infinity" \| "hourglass" \| "calendar-check" \| "list-checks" \| "clipboard" \| "file-text" \| "lightbulb" \| "puzzle" \| "magnet" \| "anchor" \| "user-check" \| "user-plus" \| "users-round" \| "handshake" \| "thumbs-down" \| "star-half" | no | Icon in the empty zone (default upload) |
| `iconSize` | integer | no | Icon size, px (default 28) |
| `iconColor` | string | no | Icon colour; default the primary colour |
| `illustration` | string | no | Image shown in the empty zone instead of the icon, e.g. an example selfie |
| `illustrationHeight` | integer | no | Illustration height, px (default 96) |
| `uploadedLabel` | string | no | Status when the uploaded file cannot be previewed (default "Photo added"); literal |
| `replaceLabel` | string | no | Link under the preview to pick another file (default "Choose another photo"); literal |
| `requireOneFace` | boolean | no | Check in the browser that the photo shows exactly one face before uploading; no face or several faces fail inline. Lenient: small faces (a person standing further away) are looked for in enlarged crops. Runs on the device (the photo is not sent anywhere for it); if the check cannot run, the photo is accepted |
| `minImageSize` | integer | no | Smallest accepted photo side in px, e.g. 512 for face search; smaller images fail inline before upload. A format the browser cannot decode (some HEIC) is not checked |

> [accordion] Example
>
> ```json theme={null}
> {
>   "field": "photo_url",
>   "label": "Upload a clear photo of your face",
>   "capture": "user"
> }
> ```

## Consent checkbox

Type `ConsentCheckbox`. Captures consent or an optional opt-in. Writes "yes" to answers\[field] when ticked (cleared when unticked); defaultChecked controls the initial state, while required controls whether Continue is blocked until checked. Label links open a new tab without toggling. Add a Button.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | no | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores "yes" when ticked and clears the answer when unticked. Read by rules, visibleWhen and \{\{field}} placeholders Default: `"consent"`. |
| `text` | string | yes | Label next to the box, keep \<=140 chars; supports [label](https://...) links (they open a new tab without toggling the box) |
| `defaultChecked` | boolean | no | Initial checkbox state on first render. true writes "yes" to answers\[field] once; the visitor can still untick it afterwards Default: `false`. |
| `required` | boolean | no | true = Continue is blocked until ticked and errorText appears. false = optional opt-in (e.g. marketing emails) Default: `true`. |
| `errorText` | string | no | Error under the box when required and unticked, shown only after Continue is pressed; literal Default: `"Please accept to continue"`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "text": "I agree to the [Terms](https://example.com/terms) and [Privacy Policy](https://example.com/privacy)"
> }
> ```

## Signature pledge

Type `SignaturePledge`. Signs a pledge with a name. Pledge statement in quotes plus a typed-name signature line; writes the name to answers\[field]; blank blocks with "Please sign to continue". Has its own full-width button (do not add a Button) and cannot be used in checkoutSheet.

| Setting | Type | Required | Description |
| - | - | - | - |
| `field` | string | no | Answer key (snake\_case, unique in the funnel; two inputs sharing one overwrite each other). Stores the typed name once signed. Read by rules, visibleWhen and \{\{field}} placeholders Default: `"pledge_name"`. |
| `title` | string | no | Heading above the pledge, keep \<=30 chars Default: `"Your commitment"`. |
| `text` | string | yes | The pledge statement shown in quotes, keep \<=140 chars, e.g. "I will stick to my 15-minute daily routine" |
| `nameLabel` | string | no | Caption above the signature line, keep \<=30 chars; literal Default: `"Type your name to sign"`. |
| `placeholder` | string | no | Hint shown on the empty signature line, keep \<=20 chars; literal Default: `"Your name"`. |
| `buttonLabel` | string | no | Label of the built-in full-width button that advances (the step needs no other Button), keep \<=24 chars Default: `"Sign & continue"`. |

> [accordion] Example
>
> ```json theme={null}
> {
>   "text": "I will show up for myself and follow my plan, one day at a time."
> }
> ```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.