WhatsApp Flow Component Reference
Complete technical reference for all UI components available in WhatsApp Flows. Use this guide when building Flow JSON definitions to understand component properties, validation rules, and usage patterns.
Overview
WhatsApp Flows support a rich set of UI components for data collection and user interaction. Each component is defined in JSON format within your Flow definition.
Component Categories:
- Layout Components - Container structures
- Text Components - Headings, body text, captions
- Input Components - Form fields for data entry
- Navigation Components - Buttons and actions
- Advanced Components - Conditional logic, carousels, rich text
Usage:
- All components are defined within a screen's
layoutproperty - Most components go inside a
Formwrapper - Use the exact
typevalues shown below - All field
namevalues must be unique within a screen
Layout Components
SingleColumnLayout
Container for vertically stacked components. This is the primary layout type for Flow screens.
Properties:
type(required):"SingleColumnLayout"children(required): Array of component objects
Example:
{
"type": "SingleColumnLayout",
"children": [
{
"type": "TextHeading",
"text": "Welcome"
},
{
"type": "TextBody",
"text": "Please complete the form below."
}
]
}
Text Components
TextHeading
Large, bold text for screen titles and section headers.
Properties:
type(required):"TextHeading"text(required): String content
Example:
{
"type": "TextHeading",
"text": "Contact Information"
}
TextBody
Regular text for descriptions and instructions.
Properties:
type(required):"TextBody"text(required): String content
Example:
{
"type": "TextBody",
"text": "Please provide your contact details so we can reach you."
}
TextCaption
Small text for hints, footnotes, or disclaimers.
Properties:
type(required):"TextCaption"text(required): String content
Example:
{
"type": "TextCaption",
"text": "Your information is kept confidential."
}
Input Components
TextInput
Single-line or multi-line text entry field.
Properties:
type(required):"TextInput"name(required): Unique field identifierlabel(required): Display labelinput-type(required): One of:"text","email","number","phone","password","passcode"required(optional): Boolean, defaultfalsehelper-text(optional): Help text shown below fieldmin-chars(optional): Minimum character countmax-chars(optional): Maximum character count
Example:
{
"type": "TextInput",
"name": "customer_name",
"label": "Full Name",
"input-type": "text",
"required": true,
"helper-text": "Enter your first and last name",
"min-chars": 2,
"max-chars": 100
}
Input Types:
text- General text inputemail- Email with built-in validationnumber- Numeric keyboard, validates numbersphone- Phone number inputpassword- Masked text inputpasscode- Numeric passcode input
TextArea
Multi-line text input for longer responses.
Properties:
type(required):"TextArea"name(required): Unique field identifierlabel(required): Display labelrequired(optional): Boolean, defaultfalsehelper-text(optional): Help text shown below fieldmax-length(optional): Maximum character count
Example:
{
"type": "TextArea",
"name": "feedback",
"label": "Your Feedback",
"required": false,
"helper-text": "Share your thoughts (optional)",
"max-length": 500
}
Dropdown
Select one option from a dropdown list.
Properties:
type(required):"Dropdown"name(required): Unique field identifierlabel(required): Display labelrequired(optional): Boolean, defaultfalsedata-source(required): Array of option objects withidandtitle
Example:
{
"type": "Dropdown",
"name": "preferred_location",
"label": "Preferred Location",
"required": true,
"data-source": [
{ "id": "loc1", "title": "Downtown Office" },
{ "id": "loc2", "title": "Westside Clinic" },
{ "id": "loc3", "title": "Northside Branch" }
]
}
RadioButtonsGroup
Select one option from a vertical list of radio buttons.
Properties:
type(required):"RadioButtonsGroup"name(required): Unique field identifierlabel(required): Display labelrequired(optional): Boolean, defaultfalsedata-source(required): Array of option objects withidandtitle
Example:
{
"type": "RadioButtonsGroup",
"name": "appointment_type",
"label": "Appointment Type",
"required": true,
"data-source": [
{ "id": "consultation", "title": "Consultation" },
{ "id": "followup", "title": "Follow-up" },
{ "id": "emergency", "title": "Emergency" }
]
}
When to use RadioButtons vs Dropdown:
- Use RadioButtons for 2-5 options that users should see at a glance
- Use Dropdown for 5+ options or when space is limited
CheckboxGroup
Select multiple options from a list.
Properties:
type(required):"CheckboxGroup"name(required): Unique field identifierlabel(required): Display labelrequired(optional): Boolean, defaultfalsedata-source(required): Array of option objects withidandtitle
Example:
{
"type": "CheckboxGroup",
"name": "services",
"label": "Services Needed",
"required": false,
"data-source": [
{ "id": "cleaning", "title": "Cleaning" },
{ "id": "repair", "title": "Repair" },
{ "id": "maintenance", "title": "Maintenance" }
]
}
Note: The submitted value will be an array of selected IDs.
DatePicker
Select a date from a calendar interface.
Properties:
type(required):"DatePicker"name(required): Unique field identifierlabel(required): Display labelrequired(optional): Boolean, defaultfalsehelper-text(optional): Help text shown below fieldmin-date(optional): Minimum selectable date (ISO 8601 format)max-date(optional): Maximum selectable date (ISO 8601 format)
Example:
{
"type": "DatePicker",
"name": "appointment_date",
"label": "Preferred Date",
"required": true,
"helper-text": "Select your preferred appointment date",
"min-date": "2026-02-01",
"max-date": "2026-12-31"
}
Note: Submitted date format is YYYY-MM-DD.
OptIn
Checkbox for terms, consent, or agreements.
Properties:
type(required):"OptIn"name(required): Unique field identifierlabel(required): Display text for checkboxrequired(optional): Boolean, defaultfalseon-click-action(optional): Navigation action when clicked
Example:
{
"type": "OptIn",
"name": "terms_accepted",
"label": "I agree to the terms and conditions",
"required": true,
"on-click-action": {
"name": "navigate",
"next": {
"type": "screen",
"name": "TERMS_SCREEN"
}
}
}
Use Cases:
- Terms and conditions acceptance
- Privacy policy consent
- Marketing opt-ins
- Legal disclaimers
PhotoPicker
Upload a photo from device camera or gallery.
Properties:
type(required):"PhotoPicker"name(required): Unique field identifierlabel(required): Display labeldescription(optional): Additional context textrequired(optional): Boolean, defaultfalse
Example:
{
"type": "PhotoPicker",
"name": "id_photo",
"label": "Upload ID Photo",
"description": "Take a photo of your ID document",
"required": true
}
Important:
- Photo is base64-encoded in submission
- Maximum file size varies by WhatsApp limits
- Useful for KYC, verification, proof of purchase, damage reports
Navigation Components
Footer
Action button at the bottom of each screen for navigation or submission.
Properties:
type(required):"Footer"label(required): Button texton-click-action(required): Action object (see below)
Action Types:
1. Navigate to Another Screen:
{
"type": "Footer",
"label": "Continue",
"on-click-action": {
"name": "navigate",
"next": {
"type": "screen",
"name": "NEXT_SCREEN"
},
"payload": {
"field_name": "${form.field_name}"
}
}
}
2. Complete Flow (Terminal Screen):
{
"type": "Footer",
"label": "Submit",
"on-click-action": {
"name": "complete",
"payload": {
"customer_name": "${form.customer_name}",
"email": "${form.email}"
}
}
}
Payload Syntax:
- Use
${form.field_name}to reference current screen's form fields - Use
${data.field_name}to reference data passed from previous screens
Advanced Components
If (Conditional Logic)
Show components conditionally based on form values.
Properties:
type(required):"If"condition(required): Expression to evaluatethen(required): Array of components to show if trueelse(optional): Array of components to show if false
Example:
{
"type": "If",
"condition": "${form.appointment_type} == 'emergency'",
"then": [
{
"type": "TextBody",
"text": "For emergencies, please call our hotline: 1-800-URGENT"
}
],
"else": [
{
"type": "TextBody",
"text": "Standard appointments available within 48 hours."
}
]
}
Supported Operators:
==(equals)!=(not equals)>,<,>=,<=(comparison)&&(and),||(or)
Image
Display a static image using base64 encoding or URL.
Properties:
type(required):"Image"src(required): Base64-encoded image data or URLalt-text(optional): Alternative text for accessibilityscale-type(optional):"cover"or"contain"
Example (Base64):
{
"type": "Image",
"src": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
"alt-text": "Company Logo",
"scale-type": "contain"
}
Example (URL):
{
"type": "Image",
"src": "https://example.com/banner.jpg",
"alt-text": "Promotional Banner"
}
ImageCarousel
Display multiple images in a horizontal scrollable carousel.
Properties:
type(required):"ImageCarousel"images(required): Array of image objects withsrcandalt-text
Example:
{
"type": "ImageCarousel",
"images": [
{
"src": "https://example.com/image1.jpg",
"alt-text": "Product Image 1"
},
{
"src": "https://example.com/image2.jpg",
"alt-text": "Product Image 2"
},
{
"src": "https://example.com/image3.jpg",
"alt-text": "Product Image 3"
}
]
}
Use Cases:
- Product galleries
- Before/after photos
- Multi-angle views
- Feature showcases
RichText
Formatted text with markdown support.
Properties:
type(required):"RichText"text(required): Markdown-formatted text
Example:
{
"type": "RichText",
"text": "**Bold text**, *italic text*, and [links](https://example.com)\n\n- Bullet point 1\n- Bullet point 2\n\n1. Numbered item\n2. Another item"
}
Supported Markdown:
**bold**→ bold*italic*→ italic[link text](url)→ clickable links- Bullet lists with
-or* - Numbered lists with
1.,2., etc. - Line breaks with
\n
EmbeddedLink
Inline link within text that navigates to another screen.
Properties:
type(required):"EmbeddedLink"text(required): Link texton-click-action(required): Navigation action
Example:
{
"type": "EmbeddedLink",
"text": "Read our Terms of Service",
"on-click-action": {
"name": "navigate",
"next": {
"type": "screen",
"name": "TERMS_SCREEN"
}
}
}
Use Cases:
- Link to terms/privacy screens
- Show additional information screens
- Navigate to help/FAQ screens
Form Wrapper
Most input components must be wrapped in a Form component.
Properties:
type(required):"Form"name(required): Unique form identifierchildren(required): Array of input components and Footer
Example:
{
"type": "Form",
"name": "contact_form",
"children": [
{
"type": "TextInput",
"name": "name",
"label": "Your Name",
"input-type": "text",
"required": true
},
{
"type": "TextInput",
"name": "email",
"label": "Email Address",
"input-type": "email",
"required": true
},
{
"type": "Footer",
"label": "Submit",
"on-click-action": {
"name": "complete",
"payload": {
"name": "${form.name}",
"email": "${form.email}"
}
}
}
]
}
Data Passing Between Screens
Using Payload
When navigating between screens, pass data via the payload property:
{
"type": "Footer",
"label": "Continue",
"on-click-action": {
"name": "navigate",
"next": {
"type": "screen",
"name": "SCREEN_2"
},
"payload": {
"first_name": "${form.first_name}",
"last_name": "${form.last_name}"
}
}
}
Accessing Data on Next Screen
Define data schema on receiving screen and reference with ${data.field_name}:
{
"id": "SCREEN_2",
"title": "Confirm Details",
"data": {
"first_name": {
"type": "string",
"__example__": "John"
},
"last_name": {
"type": "string",
"__example__": "Doe"
}
},
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "TextBody",
"text": "Hello ${data.first_name} ${data.last_name}!"
}
]
}
}
Best Practices
Field Naming
- Use descriptive, lowercase names with underscores:
customer_email,preferred_date - Avoid special characters except underscores
- Keep names consistent across similar Flows
- Use meaningful names that indicate the data type
Validation
- Set
required: truefor mandatory fields - Use appropriate
input-typefor built-in validation (email, number, phone) - Add
helper-textto clarify expected format - Use
min-charsandmax-charsfor text length validation - Use
min-dateandmax-datefor date restrictions
User Experience
- Keep screens focused (5-7 fields maximum per screen)
- Use clear, concise labels
- Provide helpful
helper-textfor complex fields - Group related fields on the same screen
- Use RadioButtons for 2-5 options, Dropdown for more
- Show progress indicators in screen titles ("Step 1 of 3")
Mobile Optimization
- Keep text concise (small screens)
- Test all Flows in mobile preview
- Ensure images are optimized for mobile data
- Avoid very long dropdown lists
- Use appropriate keyboard types via
input-type
Complete Example
Here's a complete two-screen Flow demonstrating multiple components:
{
"version": "7.3",
"screens": [
{
"id": "CONTACT_INFO",
"title": "Contact Information",
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "TextHeading",
"text": "Let's Get Started"
},
{
"type": "TextBody",
"text": "Please provide your contact details."
},
{
"type": "Form",
"name": "contact_form",
"children": [
{
"type": "TextInput",
"name": "full_name",
"label": "Full Name",
"input-type": "text",
"required": true,
"helper-text": "First and last name"
},
{
"type": "TextInput",
"name": "email",
"label": "Email Address",
"input-type": "email",
"required": true
},
{
"type": "TextInput",
"name": "phone",
"label": "Phone Number",
"input-type": "phone",
"required": true
},
{
"type": "Footer",
"label": "Continue",
"on-click-action": {
"name": "navigate",
"next": {
"type": "screen",
"name": "PREFERENCES"
},
"payload": {
"full_name": "${form.full_name}",
"email": "${form.email}",
"phone": "${form.phone}"
}
}
}
]
}
]
}
},
{
"id": "PREFERENCES",
"title": "Your Preferences",
"terminal": true,
"data": {
"full_name": { "type": "string", "__example__": "John Doe" },
"email": { "type": "string", "__example__": "john@example.com" },
"phone": { "type": "string", "__example__": "+15551234567" }
},
"layout": {
"type": "SingleColumnLayout",
"children": [
{
"type": "TextHeading",
"text": "Almost Done, ${data.full_name}!"
},
{
"type": "Form",
"name": "preferences_form",
"children": [
{
"type": "RadioButtonsGroup",
"name": "contact_method",
"label": "Preferred Contact Method",
"required": true,
"data-source": [
{ "id": "email", "title": "Email" },
{ "id": "phone", "title": "Phone" },
{ "id": "whatsapp", "title": "WhatsApp" }
]
},
{
"type": "CheckboxGroup",
"name": "interests",
"label": "Areas of Interest",
"data-source": [
{ "id": "products", "title": "New Products" },
{ "id": "promotions", "title": "Promotions" },
{ "id": "events", "title": "Events" }
]
},
{
"type": "OptIn",
"name": "newsletter",
"label": "Subscribe to newsletter",
"required": false
},
{
"type": "Footer",
"label": "Submit",
"on-click-action": {
"name": "complete",
"payload": {
"full_name": "${data.full_name}",
"email": "${data.email}",
"phone": "${data.phone}",
"contact_method": "${form.contact_method}",
"interests": "${form.interests}",
"newsletter": "${form.newsletter}"
}
}
}
]
}
]
}
}
]
}
Next Steps
- Flow Examples - See these components in action
- Creating Flows with Connect UI - Build Flows using the visual editor
- Flow API Reference - Create Flows programmatically
- Best Practices - Design and validation guidelines