App Builder Components | Inputs
Learn about the specific components you can use in the app builder here
Text Input
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.
Key Features
- Dynamic Data Seeding: Supports initial values derived from system context (e.g.,
{{system.user.email}}) or URL parameters. - State Binding: Automatically syncs user input to the global
pageDataobject using the configuredfield_key. - Validation: Supports required fields and custom error states.
- Hidden Mode: Can be set to
hiddento 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.
Configuration Props
| 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. |
Technical Behavior (Seeding Strategy A)
This component uses Seeding Strategy A. When the component mounts:
- It checks if
pageData[field_key]is currentlyundefinedornull. - If empty, it resolves the
inputValueexpression (e.g.,{{params.id}}->123). - It sets this resolved value into the
pageDataatom. - Note: User edits are preserved; the default value only applies if the field is empty on mount.
Rich Text Input
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.
Key Features
- Markdown Native: Data is stored and retrieved as Markdown strings, making it portable and easy to render elsewhere.
- Prose Styling: Wraps content in a Tailwind
prosecontainer for beautiful typography out of the box. - Context Aware Defaults: Like the standard Text Input, it supports dynamic default values (e.g., pre-filling a standard email template).
Configuration Props
| 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. |
Technical Behavior
- State Handling: It handles both raw string values and legacy markdown objects (
{ markdown: "..." }) gracefully, ensuring backward compatibility. - Builder Experience: In App Builder mode, the editor functions normally, but interactions are wrapped to prevent conflict with the drag-and-drop system.
- Performance: Utilizes
MdxEditorfor a lightweight editing experience that doesn't bloat the bundle size.
Upload File
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.
Key Features
-
Dual Mode Operation:
-
Utility Mode: Uploads files immediately and redirects the user to a file view page (default behavior when no
field_keyis set). -
Input Mode: Uploads files and stores the resulting file records (IDs, URLs, metadata) into the
pageDatastate, 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.
Configuration Props
| 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. |
Technical Behavior
- API Integration: Uses the
useApiHookto POSTFormDatato thev1/filesendpoint. - State Integration (Input Mode): When
field_keyis present, successful uploads trigger an Immer producer that appends the new file records to the existing array atpageData[field_key]. It does not overwrite existing data, allowing users to upload multiple batches of files into the same field. - Navigation Guard: In Input Mode, the automatic navigation (
Maps(...)) is suppressed to ensure the user remains on the form to complete their submission. - Mime-Type Detection: The component inspects file types on selection (e.g.,
file.type.startsWith('video/')) to dynamically reveal compression options only when relevant.
Button / Action Trigger
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.
Key Features
- API Discovery: Integrated directly with the system's OpenAPI specification. Builders can select endpoints from a searchable dropdown (
GET /v1/users) rather than manually typing URL paths. - Smart Context Injection: Supports full access to the Data Taxonomy. You can inject
{{pageData}}values into a JSON request body or use{{record.id}}in a URL path parameter. - JSON Type Safety: The request body editor uses a specialized parsing mode (
jsonSafe: true). This ensures that dynamic variables resolve correctly within a JSON structure (e.g., resolvingundefinedvariables tonullinstead of empty strings to prevent syntax errors). - Visual Feedback: Automatically handles loading states (disabling the button and showing a spinner) and displays success/error toast notifications based on the API response.
Configuration Props
| 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). |
Technical Behavior
-
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
bodystring is processed by the Expression Parser. -
Variables resolving to Objects/Arrays are
JSON.stringify'd automatically. -
Variables resolving to
undefinedbecomenull. -
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.
Phone Input
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.
Key Features
- Structured Data: Automatically splits the input into
country_code,friendly_number, andextension. - Auto-Formatting: As the user types, the "Friendly Number" field automatically applies US formatting
(555) 123-4567for better readability. - Data Normalization: Behind the scenes, it constructs a clean
phone_numberstring (e.g.,+15551234567) optimized for backend storage and searching. - Smart Defaults: Defaults to
+1(US) country code but allows overrides.
Configuration Props
| 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. |
Technical Behavior
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" }
- Seeding: If the field is empty on mount, it hydrates state using the
inputValue. IfinputValueis a simple string (e.g.,+15551234567), the component attempts to parse and format it into the object structure automatically. - Immer Integration: Updates are handled via
immer'sproduce, ensuring that typing in the "Extension" field updates that specific property without overwriting the main number or country code.