Survey syntax reference
Extended Motivation Form field types, media embedding, conditional logic, and page-based pagination — all via .md frontmatter.
This page covers the extended syntax available in Motivation Form .md files beyond the core 10 field types. Use these when you are building surveys, NPS studies, A/B comparisons, or any form where standard text/choice inputs are not enough.
The core 10 types (text, email, textarea, number, radio, checkbox, select, date, image, file) remain unchanged and are documented in Field types.
Pagination with page:
Add a page: integer to any field to control how fields are grouped into pages. There is no separate "survey mode" to enable — the layout is derived entirely from which fields share a page number.
| What you write | What you get |
|---|---|
No page: on any field | All fields on a single page (classic form) |
Fields sharing the same page: value | Those fields appear together on one page |
Each field has a unique page: value | One field per page (classic survey) |
fields:
# Page 1 — two fields grouped together
- id: name
type: text
label: Your name
page: 1
- id: email
type: email
label: Email address
page: 1
# Page 2 — one field alone
- id: recommend
type: nps
label: How likely are you to recommend us?
page: 2
# Page 3 — another field alone
- id: feedback
type: textarea
label: Any other thoughts?
page: 3Page labels
Name each page with page_labels: (list indexed from 0):
page_labels:
- About you
- Satisfaction
- Open feedbackBackward compatibility: The legacy
step:attribute andtype: surveyare still accepted and will continue to work.step:is a direct alias forpage:.type: surveyis treated as assigning each field a unique page automatically. Migrate topage:for new forms.
Global settings
settings:
progress_bar: true # show a progress bar at the top (default: false)
randomize_options: true # shuffle option order for radio/checkbox/select/ranking (default: false)New field types
All fields share the common properties (id, label, required, help_text, page, show_if).
rating
Star (or emoji) rating. Response value: integer 1–max.
- id: overall_experience
type: rating
label: How would you rate your overall experience?
max: 5 # 1–10 supported; default: 5scale
Labelled numeric scale (Likert-style). Response value: integer.
- id: ease_of_use
type: scale
label: This product is easy to use.
min: 1
max: 7
min_label: Strongly disagree
max_label: Strongly agreenps
Net Promoter Score. Renders as 11 numbered buttons (0–10) with anchors. Response value: integer 0–10.
- id: recommend
type: nps
label: How likely are you to recommend us to a colleague?
low_label: Not at all likely
high_label: Extremely likelyyes_no
Binary choice. Response value: "yes" or "no".
- id: used_before
type: yes_no
label: Have you used a similar product before?ranking
Drag-and-drop ordered list. Response value: ordered array of option strings.
- id: priority_order
type: ranking
label: Rank these features by importance to you.
options:
- Speed
- Price
- Reliability
- Customer support
- Documentationmatrix
Grid of rows × columns. Each row is one question; columns are the shared response options. Response value: { row_id: selected_column } object.
- id: satisfaction_grid
type: matrix
label: Rate each aspect of the product.
rows:
- id: onboarding
label: Onboarding experience
- id: performance
label: Performance
- id: docs
label: Documentation
columns:
- Very dissatisfied
- Dissatisfied
- Neutral
- Satisfied
- Very satisfiedstatement
Read-only informational block. No user input. Use for section headings, instructions, or consent text within a multi-step form. Not included in the response payload.
- id: intro_text
type: statement
label: "Before you begin"
help_text: |
This survey takes about 3 minutes. Your responses are anonymous
and will only be used to improve the product. Thank you.Content widths
Fields use the existing prose column by default. Add width when a question or prompt needs more horizontal space:
width | Layout |
|---|---|
prose | Existing reading column (default) |
wide | Centered breakout up to approximately 880px |
full | Full available form container with 24px viewport gutters |
- id: concept_feedback
type: textarea
label: What stands out in this concept?
width: wide
media:
type: image
url: https://cdn.example.com/concept.png
width: fullPrompt media inherits its field's width unless media.width overrides it. All three modes collapse to the same safe single-column width on narrow screens.
Side-by-side comparisons
Add side_by_side to a question when respondents need to inspect two or more equivalent panes before answering. Panes share the question's width track, render horizontally on suitable desktop widths, and stack in source order on mobile. The comparison is display-only: only the paired question's answer is included in the response payload.
Each pane needs a label plus content, media, or both. content is rendered as safe read-only text. media uses the same validated media schema and rendering behavior as prompt media.
- id: preferred_response
type: radio
label: Which response is better?
width: wide
page: 1
side_by_side:
label: Compare both responses before choosing
panes:
- label: Response A
content: A concise answer with one concrete next step.
media:
type: image
url: https://cdn.example.com/response-a.png
alt: Preview of response A.
- label: Response B
content: A detailed answer without a recommendation.
media:
type: image
url: https://cdn.example.com/response-b.png
alt: Preview of response B.
options:
- Response A
- Response BUse at least two panes. The same syntax works on yes_no questions and inside multi-page or quiz forms. Keep pane labels equivalent and neutral for blind evaluations.
Media embedding
Any field can display rich media above its input with the media: property. Media is for display only — it does not affect the response value.
- id: logo_preference
type: radio
label: Which logo do you prefer?
media:
type: image
url: https://cdn.example.com/logo-options.png
alt: Two logo concepts for the rebrand. # optional
options:
- Option A
- Option B
- NeitherSupported media types
type | url format | How it renders |
|---|---|---|
image | URL (external) or Supabase Storage URL | <img> with lazy loading |
youtube | Full YouTube URL or 11-char video ID | <iframe> nocookie embed |
video | Direct .mp4 / .webm URL | HTML5 <video> player (no autoplay) |
model3d | .glb / .gltf URL | <model-viewer> web component |
code | Inline source (no URL) | Read-only, server-highlighted code with copy command |
Media accepts width: prose | wide | full in addition to the type-specific properties below.
image
media:
type: image
url: https://cdn.example.com/comparison.png
alt: Before and after redesign.youtube
media:
type: youtube
url: https://www.youtube.com/watch?v=dQw4w9WgXcQ
alt: Product walkthrough (2 min).video
media:
type: video
url: https://cdn.example.com/demo.mp4
alt: Click play to watch the demo before answering.model3d
media:
type: model3d
url: https://cdn.example.com/product.glb
alt: Drag to rotate the 3D model.code
Use code media when respondents need to review a snippet before answering. It is display-only and distinct from an editable field with type: code. Highlighting is generated on the server; the public form does not load a browser syntax highlighter.
- id: implementation_verdict
type: radio
label: Is this retry behavior safe?
width: wide
media:
type: code
title: Queue worker
language: typescript
caption: Review the retry boundary before answering. # optional
source: |-
export async function process(job: Job) {
return retry(() => job.run(), { attempts: 3 })
}
options:
- Yes
- Notitle, language, and source are required. caption and width are optional. Long lines scroll inside the code block, the copy icon includes keyboard and screen-reader feedback, and code media may also be used as a side_by_side.panes[].media value in surveys or quizzes. Because the block is display-only, its source is never added to a response payload.
Media on individual options
For visual A/B comparisons, attach media: to individual options. When any option in a radio or checkbox field has a media: key, the field renders as a visual card grid instead of a text list.
- id: design_preference
type: radio
label: Which design direction resonates most?
options:
- label: Minimal
media:
type: image
url: https://cdn.example.com/design-a.png
- label: Bold
media:
type: image
url: https://cdn.example.com/design-b.png
- label: Playful
media:
type: image
url: https://cdn.example.com/design-c.pngConditional logic
Show or hide any field based on the current value of another field with show_if:.
show_if:
field: <other_field_id>
op: eq | neq | gt | lt | gte | lte | includes | excludes
value: <scalar or string>The field is hidden on page load and revealed client-side when the condition is met. Hidden fields are excluded from the submitted payload.
Operators
| Op | Meaning | Works on |
|---|---|---|
eq | equals | any scalar |
neq | does not equal | any scalar |
gt | greater than | number, rating, scale, nps |
lt | less than | number, rating, scale, nps |
gte | greater than or equal | number, rating, scale, nps |
lte | less than or equal | number, rating, scale, nps |
includes | array contains value | checkbox, ranking |
excludes | array does not contain value | checkbox, ranking |
Examples
# Show follow-up only for detractors (NPS 0–6)
- id: detractor_reason
type: textarea
label: What would need to change for you to rate us higher?
show_if:
field: recommend
op: lte
value: 6
# Show feature request field only when "New feature" is checked
- id: feature_detail
type: textarea
label: What feature would you like?
show_if:
field: feedback_type
op: includes
value: New feature
# Show competitor name only when respondent says they use another product
- id: competitor_name
type: text
label: Which product do you use today?
show_if:
field: uses_competitor
op: eq
value: "yes"Complete survey example
Each field gets a unique page: number so they appear one at a time.
---
title: Product Experience Survey
slug: product-survey
settings:
progress_bar: true
branding:
primary_color: "#6366f1"
notifications:
email:
- team@example.com
fields:
- id: recommend
type: nps
label: How likely are you to recommend us to a colleague?
low_label: Not at all likely
high_label: Extremely likely
required: true
page: 1
- id: detractor_reason
type: textarea
label: What would need to change for you to rate us higher?
show_if:
field: recommend
op: lte
value: 6
page: 2
- id: promoter_reason
type: textarea
label: What do you love most about the product?
show_if:
field: recommend
op: gte
value: 9
page: 2
- id: ease_of_use
type: scale
label: The product is easy to use.
min: 1
max: 5
min_label: Strongly disagree
max_label: Strongly agree
page: 3
- id: top_feature
type: ranking
label: Rank these features by how much you rely on them.
options:
- CLI / MCP integration
- Email notifications
- Response dashboard
- Branding options
- File uploads
page: 4
- id: overall
type: rating
label: Overall, how satisfied are you?
max: 5
page: 5
- id: extra_thoughts
type: textarea
label: Anything else you'd like to share?
required: false
page: 6
---
Thank you for taking this survey. Your feedback shapes the roadmap.