Learn about the specific components you can use in the app builder here
The Text Input component is the standard single-line form field used for capturing strings, numbers, or simple text data. It is deeply integrated with the App Builder's state management system, allowing for dynamic default values and real-time data binding.
{{system.user.email}}) or URL parameters.pageData object using the configured field_key.hidden to pass context or default values to a form submission without user interaction. In the App Builder, hidden inputs remain visible as placeholders for easier editing.| Prop | Type | Description |
|---|---|---|
field_key | string | Required. The unique key in pageData where this input's value will be stored (e.g., user.first_name). |
inputValue | string | The default value configuration. Supports static text or dynamic expressions like {{system.date.today}}. |
label | string | The visual label displayed above the input. |
placeholder | string | Ghost text shown when the input is empty. |
is_required | boolean | If true, adds an asterisk to the label and enforces validation. |
hidden | boolean | If true, the input is not rendered in the runtime UI but still registers its value in state. |
This component uses Seeding Strategy A. When the component mounts:
pageData[field_key] is currently undefined or null.inputValue expression (e.g., {{params.id}} -> 123).pageData atom.The Rich Text Input component provides a full WYSIWYG (What You See Is What You Get) editing experience using Markdown. It is ideal for long-form content, descriptions, or notes where formatting (bold, lists, headers) is required.
prose container for beautiful typography out of the box.| Prop | Type | Description |
|---|---|---|
field_key | string | Required. The key in pageData to store the markdown string. |
inputValue | string | Initial markdown content. Can include dynamic variables (e.g., # Report for {{record.id}}). |
label | string | Label displayed above the editor area. |
editor_container_class_name | string | CSS classes to control the editor's height, border, and background. |
{ markdown: "..." }) gracefully, ensuring backward compatibility.MdxEditor for a lightweight editing experience that doesn't bloat the bundle size.The Upload File component serves a dual purpose: it can act as a standalone utility for immediate file uploads (with redirection) or as a form input that attaches uploaded file records to the current page's data context. It handles file selection, drag-and-drop, video compression options, and API communication seamlessly.
Dual Mode Operation:
Utility Mode: Uploads files immediately and redirects the user to a file view page (default behavior when no field_key is set).
Input Mode: Uploads files and stores the resulting file records (IDs, URLs, metadata) into the pageData state, allowing them to be submitted as part of a larger form.
Video Compression: Automatically detects video files and offers selectable compression profiles (e.g., "Standard", "High Quality 720p", "MMS/Email").
Drag & Drop: Native support for dragging files directly onto the component area.
Privacy Controls: Toggle between "Public" and "Private" upload visibility before the upload begins.
| Prop | Type | Description |
|---|---|---|
field_key | string | Optional. If set, switches the component to Input Mode. The uploaded file records (array) will be saved to this key in pageData (e.g., uploaded_files). If omitted, the component defaults to Utility Mode (redirects after upload). |
onUploadComplete | function | Custom callback function triggered after a successful upload. Receives the array of uploaded file objects. |
default_to_public | boolean | Sets the initial state of the "Public File" toggle. Default is false (Private). |
ask_if_public | boolean | If true, shows the toggle allowing users to choose between Public/Private. If false, the toggle is hidden and uses the default. |
attach_only | boolean | Future feature: Restricts uploads to specific file types or attachment modes. |
class_name | string | CSS classes for the outer container. Defaults to a dashed border style. |
useApiHook to POST FormData to the v1/files endpoint.field_key is present, successful uploads trigger an Immer producer that appends the new file records to the existing array at pageData[field_key]. It does not overwrite existing data, allowing users to upload multiple batches of files into the same field.Maps(...)) is suppressed to ensure the user remains on the form to complete their submission.file.type.startsWith('video/')) to dynamically reveal compression options only when relevant.The Button component is the primary mechanism for triggering logic and data operations within an application. While it visually appears simple, it houses a powerful execution engine capable of interacting with system APIs, triggering automations, and handling complex data payloads dynamically.
GET /v1/users) rather than manually typing URL paths.{{pageData}} values into a JSON request body or use {{record.id}} in a URL path parameter.jsonSafe: true). This ensures that dynamic variables resolve correctly within a JSON structure (e.g., resolving undefined variables to null instead of empty strings to prevent syntax errors).| Prop | Type | Description |
|---|---|---|
text | string | The visible label text on the button (e.g., "Submit", "Delete"). |
action_type | string | Determines the execution logic. Currently supports "Valstorm API Request" (triggers system endpoints). Future support planned for "Automation" and "External API". |
valstorm_api_config | object | A configuration object containing the selected method, path, dynamic params map, and the JSON body template. |
class_name | string | CSS classes for styling the button (colors, padding, rounded corners). |
Path Resolution: The component parses the selected API path (e.g., /users/{id}). It looks for dynamic parameters defined in the settings. If a parameter matches a path segment (like {id}), it replaces it. If it does not match a path segment, it is automatically appended as a Query Parameter (e.g., ?search=...).
Body Parsing: Before the request is sent, the body string is processed by the Expression Parser.
Variables resolving to Objects/Arrays are JSON.stringify'd automatically.
Variables resolving to undefined become null.
The result is then parsed back into a valid JSON object to be sent over the network.
Builder Mode: In the App Builder, clicking the button selects the component for editing rather than executing the API call, preventing accidental data mutations while designing.
The Phone Input component is a specialized form field designed to capture structured phone number data. Unlike a standard text input, it manages three distinct pieces of data—Country Code, Friendly Number (for display), and Extension—and combines them into a single unified object for storage.
country_code, friendly_number, and extension.(555) 123-4567 for better readability.phone_number string (e.g., +15551234567) optimized for backend storage and searching.+1 (US) country code but allows overrides.| Prop | Type | Description |
|---|---|---|
field_key | string | Required. The key in pageData where the phone object will be stored. |
inputValue | string | The default value (e.g., +18005551234). The component parses string defaults into the structured object automatically on mount. |
label | string | The label displayed above the input group. |
is_required | boolean | If true, adds an asterisk to the label. Note: Validation typically checks if the main number is populated. |
hidden | boolean | If true, hides the input group but preserves the data in state. |
This component uses a composite state object. When you bind this input to a key like contact.phone, the data stored in pageData is not a string, but an object:
{ "country_code": "+1", "friendly_number": "(555) 123-4567", "extension": "101", "phone_number": "+15551234567" }
inputValue. If inputValue is a simple string (e.g., +15551234567), the component attempts to parse and format it into the object structure automatically.immer's produce, ensuring that typing in the "Extension" field updates that specific property without overwriting the main number or country code.