SurveyJS Compatibility

Copy Markdown View Source

DynamicForm can create a form from data. One way to define form data is with JSON.

DynamicForm's data format is compatible with SurveyJS form definitions: JSON produced for SurveyJS — by hand, by a WYSIWYG builder, or exported from SurveyJS Creator — decodes the JSON and renders with DynamicForm's own LiveView components. The compatibility is with the definition format, not the SurveyJS JavaScript runtime: rendering, validation, and submission are all server-side using Phoenix LiveView components and Ecto.

Pass SurveyJS JSON directly into the form component:

{
  "title": "Contact Form",
  "elements": [
    {"type": "text", "name": "name", "inputType": "text"},
    {"type": "text", "name": "email", "inputType": "email"}
  ]
}
<DynamicForm.form id="contact-form" json={@json} />

Supported

DynamicForm does not attempt to implement the entire SurveyJS functionality. Instead, it targets the most important and useful parts for building dynamic forms.

Question types

SurveyJS typeRenders asNotes
text<input>inputType passes through (email, number, ...); number casts to decimal
comment<textarea>
dropdown<select>
radiogroupRadio buttons
checkboxCheckbox groupArray-valued
tagboxMulti-selectArray-valued
booleanSingle checkbox
ratingNumeric radio rowrateMin/rateMax/rateStep (defaults 1–5)
fileDirect-to-cloud uploadConfigured via metadata (a DynamicForm extension) — see Usage: File uploads
paneldynamicRepeating child form with add/removeValue is a list of maps; each entry validates against templateElements with its own changeset — see the Nested Forms guide

Element types

SurveyJS typeRenders asNotes
htmlRaw HTML block
panelTitled containerNesting supported
image<img>imageLink, imageWidth, imageHeight, imageFit

Question properties

name, type, inputType, title, description, placeholder, defaultValue, choices, validators, isRequired, requiredIf, readOnly, enableIf, visibleIf, and rateMin/rateMax/rateStep.

For paneldynamic: templateElements (alias questions), templateTitle (with {panelIndex}), panelCount, minPanelCount, maxPanelCount, allowAddPanel, allowRemovePanel, addPanelText (alias panelAddText), removePanelText (alias panelRemoveText), noEntriesText, confirmDelete, confirmDeleteText, keyName, keyDuplicationError, and defaultPanelValue. Template expressions support the {panel.field} scope prefix.

Carry forward: choicesFromQuestion builds a choice question's options from a paneldynamic's entries — with choiceValuesFromQuestion and choiceTextsFromQuestion selecting the value and label — or from another choice question's options, narrowed by choicesFromQuestionMode (all/selected/unselected). See Nested forms. Not supported: displayMode/renderMode variants other than the default list (carousel, tab), templateVisibleIf, {parentPanel.*}/{prevPanel.*} references, and file uploads inside templates.

choices accepts plain strings (["a", "b"]), value/text objects ([{"value": "v", "text": "Label"}]), and integers.

Validators

SurveyJS validatorFields
textminLength, maxLength
numericminValue, maxValue
email
regexregex

Each accepts a custom error message via text. All validation runs server-side through an Ecto changeset.

Conditional expressions

visibleIf, requiredIf, and enableIf support: =, ==, <>, !=, >, <, >=, <=, empty, notempty, contains, notcontains, anyof, allof, noneof, combined with and, or, and parentheses. Field references use braces ({field}); literals are 'strings', numbers, booleans, and ['lists']. Hidden required questions are excluded from validation automatically.

Form-level

title and description decode onto the instance. Multi-page definitions (pages) are supported by flattening: all pages merge into a single form, with page titles preserved as headings.

Not Supported

Not exhaustive — the major and common SurveyJS features DynamicForm does not implement. Unknown question and element types don't fail the form: they are skipped (nothing renders — obvious in testing, not broken-looking in production) and the rest of the form works normally. Applications can also register their own types — see Usage: Custom field types.

FeatureNotes
Matrix types (matrix, matrixdropdown, matrixdynamic)matrixdynamic is a natural follow-up — it shares paneldynamic's data shape and validation machinery, rendered as a table
multipletextMultiple inputs in one question
signaturepad, imagepicker, ranking, sliderSpecialized input widgets
expression questions, calculated values, triggersNo expression evaluation beyond the conditional operators above (setValueIf, runexpression, quiz scoring, ...)
expression and answercount validatorsUse on_change for cross-field validation
Multi-page navigationPages are flattened into one form — no page-by-page navigation, progress bar, or per-page validation
Choice loading (choicesByUrl, lazy loading)Provide choices in the definition
showOtherItem / showNoneItem / showSelectAllItemChoice extras
Input masks
Element layout (startWithNewLine, width/minWidth/maxWidth, gridLayoutColumns/colSpan)SurveyJS arranges rows per element — startWithNewLine: false joins the previous element's row. DynamicForm arranges per container instead: put the fields in a panel and set its groupType. A Creator export's layout settings are ignored, not honored
Localization objectsMulti-locale strings ({"default": ..., "de": ...}) aren't decoded; error messages translate via Gettext instead
SurveyJS themes and CSS customizationStyling is Tailwind via DynamicForm's components

DynamicForm extensions

Beyond the SurveyJS format, definitions can carry:

  • metadata — per-question extension point: file upload configuration, "style" ("horizontal"/"vertical") for radiogroup/checkbox layout.
  • requiredLabel — the mark beside a required question's label, defaulting to "*"; blank (null, false, "") shows none while the question stays required. SurveyJS's requiredMark is accepted as an alias on decoding, but it is a survey-level property there, so reading it per question is ours. See Styling: the required mark.
  • groupType — on a panel: how it lays its members out, "horizontal" (default) or "vertical". SurveyJS has no panel-level layout property, so this is ours; applications can add their own types — see Styling: custom group types.
  • generateIds — on by default for paneldynamic: every entry carries a stable dynamic_form_id, copied from the entry's id when the data came from a stored record. See Nested forms: Entry ids.
  • choiceTextsFromQuestion as a template — beyond naming one member field, it can interpolate several ("{min} - {max}", "{panelIndex}"), which SurveyJS's single-question form can't express.
  • Declarative mode — the same instances can be defined with <:field> slots in HEEx, including custom markup via slot bodies (in-memory only; dropped on JSON encoding). See Usage: Defining forms.