Skip to main content

Schema Format

eSheet uses a JSON schema format identified by the id field. This page documents the complete structure.

Form Definition

The top-level object that describes an entire form:

interface FormDefinition {
/** Unique form identifier */
id: string;
/** Optional form title */
title?: string;
/** Optional form description */
description?: string;
/** When true, enables dangerously embedded JS for calculations and conditions */
dangerouslyAllowJS?: boolean;
/** Pages — every form has at least one page; fields live inside pages */
pages: PageEntry[];
}

interface PageEntry {
/** Unique page identifier */
id: string;
/** Optional display title (shown as tab label in the builder) */
title?: string;
/**
* Reserved: automatically advance to the next page when all fields are answered.
* Not yet active in the renderer.
*/
autoAdvance?: boolean;
/** Fields rendered on this page */
fields?: FieldDefinition[];
}

A form with a single page renders as a normal single-scroll form — no navigation UI is shown. A form with multiple pages shows a Previous / N of total / Next navigation bar in the renderer. See Pages for full details.

Example

{
"id": "patient-intake-form",
"title": "Patient Intake Form",
"description": "Please fill out all required fields",
"pages": [
{
"id": "page_1",
"title": "Demographics",
"fields": [
{
"id": "name",
"fieldType": "text",
"question": "Full Name",
"required": true,
"inputType": "string"
},
{
"id": "dob",
"fieldType": "text",
"question": "Date of Birth",
"inputType": "date"
}
]
}
]
}

Field Definition

Every field in a form is described by a FieldDefinition. This is a "wide" schema -- not every property applies to every field type.

interface FieldDefinition {
/** Unique identifier within the form */
id: string;
/** Determines rendering and behavior */
fieldType: FieldType;
/** The question/label shown to the user */
question?: string;
/** Whether a response is required */
required?: boolean;
/** Conditional rules (see Conditional Logic) */
rules?: ConditionalRule[];
/** Width occupied by the field in the layout grid */
width?: FieldWidth;

// --- Text fields ---
/** Input type variant for text fields */
inputType?: TextInputType;
/** Unit suffix displayed after the input (e.g. "kg", "cm") */
unit?: string;

// --- Choice fields ---
/** Options for radio, check, dropdown, multiselect, multitext, rating, ranking */
options?: FieldOption[];
/** Layout direction for fields that display options */
optionLayout?: OptionLayout;

// --- Matrix fields ---
/** Rows for singlematrix and multimatrix */
rows?: MatrixRow[];
/** Columns for singlematrix and multimatrix */
columns?: MatrixColumn[];

// --- Rich content ---
/** Raw HTML content for html fields */
htmlContent?: string;
/** Image URI for image fields */
imageUri?: string;
/** Alt text for image fields */
altText?: string;
/** Caption for image fields */
caption?: string;
/** Placeholder text on the drawing canvas (signature/diagram) */
padPlaceholder?: string;
/** Markdown-like content for display fields (supports expression interpolation) */
content?: string;

// --- Section (container) ---
/** Section title */
title?: string;
/** Nested child fields */
fields?: FieldDefinition[];
}

Field Layout

FieldWidth

The optional width property controls how much of a row a field occupies:

ValueDescription
fullUses the full row width
halfUses half of the row
thirdUses one third of the row

Text, selection, and rating fields default to third. Rich, matrix, and organization fields default to full.

type FieldWidth = 'full' | 'half' | 'third';

OptionLayout

The optional optionLayout property controls how a field's options are arranged:

ValueDescription
stackDisplays one option per line
wrapDisplays options horizontally and wraps as required

Fields that support optionLayout default to wrap.

type OptionLayout = 'stack' | 'wrap';

Field Types

The 19 built-in field types:

Field TypeCategoryAnswer TypeHas OptionsHas MatrixDescription
texttexttextNoNoSingle-line text input with variants
longtexttexttextNoNoMulti-line textarea
multitexttextmultitextYesNoOne text input per option
radioselectionselectionYesNoSingle-select radio buttons
checkselectionmultiselectionYesNoMulti-select checkboxes
booleanselectionselectionNoNoYes/No toggle
dropdownselectionselectionYesNoSingle-select dropdown
multiselectdropdownselectionmultiselectionYesNoMulti-select dropdown
ratingratingselectionYesNoNumeric scale (1-5, 1-10)
rankingratingmultiselectionYesNoDrag-to-order items
sliderratingselectionYesNoRange slider
singlematrixmatrixmatrixNoYesOne selection per row
multimatrixmatrixmatrixNoYesMultiple selections per row
imagerichdisplayNoNoImage display
htmlrichdisplayNoNoRaw HTML embed
signaturerichmediaNoNoDrawing pad for signatures
diagramrichmediaNoNoDrawing pad for markup
displayrichdisplayNoNoMarkdown + expression content
sectionorganizationcontainerNoNoContainer for nested fields

Text Input Types

The text field supports these input type variants via the inputType property:

Input TypeHTML TypeDescription
stringtextPlain text (default)
numbernumberNumeric input
emailemailEmail address
teltelPhone number (auto-formatted)
datedateDate picker
datetime-localdatetime-localDate and time picker
monthmonthMonth picker
timetimeTime picker
urlurlURL input

Options

Choice fields use FieldOption objects:

interface FieldOption {
/** Unique option identifier */
id: string;
/** Display value */
value: string;
/** Optional tooltip or auxiliary text */
text?: string;
/** Optional numeric score for scored surveys (e.g. PHQ-9, GAD-7). When present on
* any option, the field's answer value is the sum of scores for all selected options. */
score?: number;
}

Matrix Dimensions

Matrix fields use rows and columns:

interface MatrixRow {
id: string;
value: string; // Row label
}

interface MatrixColumn {
id: string;
value: string; // Column header
/** Optional numeric score for this column. Overrides auto-scoring when `scored` is enabled. */
score?: number;
}

Form Response

The response for a submitted form:

/** Maps field IDs to their response values */
type FormResponse = Record<string, FieldResponse>;

interface FieldResponse {
/** Text answer (text, longtext) */
answer?: string;
/**
* Selected option(s):
* - SelectedOption for single-select (radio, dropdown, boolean, rating, slider)
* - SelectedOption[] for multi-select (check, multiselectdropdown, ranking)
* - Record<string, SelectedOption | SelectedOption[]> for matrix (rowId -> column)
*/
selected?:
| SelectedOption
| SelectedOption[]
| Record<string, SelectedOption | SelectedOption[]>;
/** Per-option text for multitext fields */
multitextAnswers?: Record<string, string>;
/** Serialized stroke data (signature) */
signatureData?: string;
/** Base64 PNG image (signature) */
signatureImage?: string;
/** Serialized stroke data (diagram) */
markupData?: string;
/** Base64 PNG image (diagram) */
markupImage?: string;
}

interface SelectedOption {
readonly id: string;
value: string;
}

Example Response

{
"name": { "answer": "Jane Doe" },
"email": { "answer": "jane@example.com" },
"reason": { "selected": { "id": "opt2", "value": "Follow-up" } },
"symptoms": {
"selected": [
{ "id": "s1", "value": "Headache" },
{ "id": "s3", "value": "Fatigue" }
]
},
"pain_matrix": {
"selected": {
"row_head": { "id": "col_mild", "value": "Mild" },
"row_back": { "id": "col_severe", "value": "Severe" }
}
},
"signature_field": {
"signatureData": "[{\"points\":[...]}]",
"signatureImage": "data:image/png;base64,..."
}
}