Proteus
Proteus is a JSON-based UI specification for building interactive tool interfaces. You define your UI as a JSON document, and the Proteus renderer (@optiaxiom/proteus) turns it into interactive Axiom components.
#
Document structure
#
#
Minimal document
#
A Proteus document is a JSON object with $type: "Document". At minimum it needs a body. Every element has a $type that identifies the component. The children property holds nested content: a string, number, another element, or an array of elements.
Welcome to your dashboard
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
body: [
{
$type: "Text",
children:
"Proteus turns a JSON description into a fully interactive Axiom UI.",
},
],
title: "Welcome to your dashboard",
}}
/>
</Box>
);
}#
Document properties
#
Documents also support title, subtitle, appName, appIcon, and actions (buttons rendered at the bottom).
Opal
Create your test plan
Select how you'd like to define the page or experience.
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [data, setData] = useState<Record<string, unknown>>({});
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={data}
element={{
$type: "Document",
actions: [
{
$type: "Action",
appearance: "primary-opal",
children: "Create Test Plan",
},
],
appName: "Opal",
blocking: true,
body: [
{
$type: "Field",
children: {
$type: "Input",
name: "url",
placeholder: "Add a URL",
},
label: "URL",
},
{
$type: "Field",
children: {
$type: "Textarea",
name: "test_idea",
placeholder:
"e.g., Add quantity badges to product thumbnails to show how many of each item they're buying",
},
label: "Test Idea",
},
],
subtitle: "Select how you'd like to define the page or experience.",
title: "Create your test plan",
}}
onDataChange={setData}
/>
</Box>
);
}#
Components
#
#
Layout and structure
#
Use Group as a flexbox container to arrange child elements. It supports flexDirection, gap, alignItems, and justifyContent. Card, CardHeader, and CardLink provide card-based layouts. Separator adds a visual divider.
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
body: {
$type: "Group",
border: "1",
borderColor: "border.tertiary",
children: [
{
$type: "Group",
children: [
{
$type: "Text",
children: "Add quantity badges to product thumbnails",
fontWeight: "600",
},
{
$type: "Text",
children: "TSK-8526",
color: "fg.tertiary",
fontSize: "sm",
},
],
flexDirection: "column",
gap: "4",
p: "12",
},
{ $type: "Separator", borderColor: "border.tertiary" },
{
$type: "Group",
children: [
{
$type: "Text",
children: "D-Congress 2026 - Digital screen content",
fontWeight: "600",
},
{
$type: "Text",
children: "TSK-9102",
color: "fg.tertiary",
fontSize: "sm",
},
],
flexDirection: "column",
gap: "4",
p: "12",
},
],
flexDirection: "column",
rounded: "md",
},
}}
/>
</Box>
);
}#
Typography
#
Heading renders section headings, Text renders inline or block text, and Link renders a hyperlink with an href prop.
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
body: [
{ $type: "Heading", children: "Key Insight", level: "4" },
{
$type: "Text",
children:
"Initial performance metrics reveal a 12% drop in user retention post-update. Immediate deep-dive into Android Scrum Project's V2.2 onboarding flow is critical to reverse the trend and secure Q3 engagement goals.",
color: "fg.secondary",
fontSize: "sm",
},
{
$type: "Link",
children: "View task TSK-98",
href: "https://example.com/task/tsk-98",
},
],
}}
/>
</Box>
);
}#
Data display
#
DataTable renders structured data with columns, sorting, and pagination. Chart renders bar or line charts. Image and ImageCarousel display images. Avatar, Badge, and Time handle user avatars, status tags, and formatted timestamps.
Salesforce CRM
Q4 2024 Sales Performance
October 1 - December 31, 2025
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
appName: "Salesforce CRM",
body: {
$type: "DataTable",
columns: [
{ accessorKey: "rank", header: "Rank", size: 80 },
{ accessorKey: "representative", header: "Representative" },
{ accessorKey: "deals", header: "Deals Closed", size: 120 },
{ accessorKey: "revenue", header: "Revenue", size: 120 },
],
data: [
{
deals: "47",
rank: "1",
representative: "Sarah Chen",
revenue: "$2.8M",
},
{
deals: "42",
rank: "2",
representative: "Michael Wong",
revenue: "$2.5M",
},
{
deals: "39",
rank: "3",
representative: "Jenn Taylor",
revenue: "$2.3M",
},
{
deals: "35",
rank: "4",
representative: "David Smith",
revenue: "$2.1M",
},
{
deals: "33",
rank: "5",
representative: "Emily Johnson",
revenue: "$2.0M",
},
],
},
subtitle: "October 1 - December 31, 2025",
title: "Q4 2024 Sales Performance",
}}
/>
</Box>
);
}#
Form controls
#
Wrap inputs with Field to add a label. Available inputs include Input, Textarea, Select (composed from SelectTrigger and SelectContent), Switch, Range, and Question (a multi-question dynamic form).
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [data, setData] = useState<Record<string, unknown>>({});
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={data}
element={{
$type: "Document",
body: [
{
$type: "Field",
children: {
$type: "Select",
children: [
{ $type: "SelectTrigger", w: "full" },
{ $type: "SelectContent" },
],
name: "target_by",
options: [
{ label: "URL", value: "url" },
{ label: "Saved Pages", value: "page" },
],
},
label: "Target by",
},
{
$type: "Field",
children: {
$type: "Input",
name: "url",
placeholder: "Add a URL",
},
label: "URL",
},
{
$type: "Field",
children: {
$type: "Textarea",
name: "test_idea",
placeholder:
"e.g., Add quantity badges to product thumbnails to show how many of each item they're buying",
},
label: "Test Idea",
},
{
$type: "Field",
children: { $type: "Switch", name: "include_metadata" },
label: "Include Metadata",
},
],
}}
onDataChange={setData}
/>
</Box>
);
}#
Actions
#
Action renders a button with an onClick handler (see Interactions below). Use appearance (primary, danger, subtle, etc.) to style the button.
Content Marketing Platform
Version 2.2 Performance Optimization
Android Scrum Campaign / TSK-98
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
actions: [
{ $type: "Action", children: "Comment" },
{
$type: "Action",
appearance: "primary",
children: "View task",
},
],
appName: "Content Marketing Platform",
body: [
{
$type: "Text",
children:
"Initial performance metrics reveal a 12% drop in user retention post-update.",
},
],
subtitle: "Android Scrum Campaign / TSK-98",
title: "Version 2.2 Performance Optimization",
}}
/>
</Box>
);
}#
Styling props
#
All components accept Axiom styling props for spacing, colors, layout, and typography:
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
body: {
$type: "Group",
border: "1",
borderColor: "border.tertiary",
children: [
{
$type: "Text",
children: "TOTAL REVENUE",
color: "fg.tertiary",
fontSize: "xs",
textTransform: "uppercase",
},
{
$type: "Text",
children: "$204M",
fontSize: "2xl",
fontWeight: "700",
},
{
$type: "Text",
children: "+12% vs last quarter",
color: "fg.success.strong",
fontSize: "sm",
},
],
flexDirection: "column",
gap: "4",
p: "12",
rounded: "xl",
},
}}
/>
</Box>
);
}#
Data binding
#
#
Value
#
Reads a field from the tool response using a JSON Pointer path. Paths starting with / are absolute (from the root of the tool response). Paths without a leading / are relative (resolved from the current context, like inside a Map).
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={{
label: "TOTAL REVENUE",
trend: "+12% vs last quarter",
trendColor: "fg.success.strong",
value: "$204M",
}}
element={{
$type: "Document",
body: {
$type: "Group",
border: "1",
borderColor: "border.tertiary",
children: [
{
$type: "Text",
children: { $type: "Value", path: "label" },
color: "fg.tertiary",
fontSize: "xs",
textTransform: "uppercase",
},
{
$type: "Text",
children: { $type: "Value", path: "value" },
fontSize: "2xl",
fontWeight: "700",
},
{
$type: "Text",
children: { $type: "Value", path: "trend" },
color: { $type: "Value", path: "trendColor" },
fontSize: "sm",
},
],
flex: "1",
flexDirection: "column",
gap: "4",
p: "12",
rounded: "xl",
},
}}
/>
</Box>
);
}#
Map
#
Iterates over an array, rendering children once per item. Inside a Map, relative paths resolve against the current item:
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={{
metrics: [
{
label: "TOTAL REVENUE",
trend: "+12% vs last quarter",
trendColor: "fg.success.strong",
value: "$204M",
},
{
label: "TOTAL CLOSE RATE",
trend: "-3% vs last quarter",
trendColor: "fg.error.strong",
value: "83%",
},
{
label: "TOTAL DEALS CLOSED",
trend: "+18 vs last quarter",
trendColor: "fg.success.strong",
value: "230",
},
],
}}
element={{
$type: "Document",
body: {
$type: "Group",
children: {
$type: "Map",
children: {
$type: "Group",
border: "1",
borderColor: "border.tertiary",
children: [
{
$type: "Text",
children: { $type: "Value", path: "label" },
color: "fg.tertiary",
fontSize: "xs",
textTransform: "uppercase",
},
{
$type: "Text",
children: { $type: "Value", path: "value" },
fontSize: "2xl",
fontWeight: "700",
},
{
$type: "Text",
children: { $type: "Value", path: "trend" },
color: { $type: "Value", path: "trendColor" },
fontSize: "sm",
},
],
flex: "1",
flexDirection: "column",
gap: "4",
p: "12",
rounded: "xl",
},
path: "/metrics",
},
gap: "16",
},
}}
/>
</Box>
);
}#
Other expressions
#
MapIndex gives you the current iteration index inside a Map. Concat takes a children array and joins each resolved value into a single string. Zip takes a sources object — each key becomes a property name, and the values (arrays or expressions resolving to arrays) are zipped row-wise into an array of objects, useful as Chart.data or DataTable.data.
UTM Creation
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box, toaster } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={{
parameters: [
{ name: "Campaign Name", value: "spring-sale-2024" },
{ name: "Landing Page URL", value: "https://example.com/landing" },
{ name: "Channel", value: null },
],
}}
element={{
$type: "Document",
actions: [
{
$type: "Action",
appearance: "primary-opal",
children: "Run agent",
onClick: {
message: {
$type: "Map",
children: {
$type: "Concat",
children: [
{ $type: "Value", path: "name" },
": ",
{
$type: "Show",
children: "[Not specified]",
when: { "!": { $type: "Value", path: "value" } },
},
{
$type: "Show",
children: { $type: "Value", path: "value" },
when: { "!!": { $type: "Value", path: "value" } },
},
],
},
path: "/parameters",
separator: "\n",
},
},
},
],
body: [
{
$type: "Text",
children:
"Click the button to see how Concat formats the parameter values into a single message.",
},
],
title: "UTM Creation",
}}
onMessage={(msg) => {
toaster.create(typeof msg === "string" ? msg : JSON.stringify(msg));
}}
/>
</Box>
);
}#
Putting it together
#
This template renders a list of search results dynamically from a tool response:
Content Marketing Platform
2 results found
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={{
table_data: [
{
cmp_url: "https://example.com/task/id-123",
owner: "John Doe",
reference: "TSK-8526",
status: "Overdue",
title: "Add quantity badges to product thumbnails",
},
{
cmp_url: "https://example.com/task/id-345",
owner: "Jane Doe",
reference: "TSK-9102",
status: "In Progress",
title: "D-Congress 2026 - Digital screen content",
},
],
total_results: 2,
}}
element={{
$type: "Document",
appName: "Content Marketing Platform",
body: {
$type: "Map",
children: {
$type: "Card",
children: {
$type: "CardHeader",
children: {
$type: "CardLink",
children: { $type: "Value", path: "title" },
href: { $type: "Value", path: "cmp_url" },
},
description: { $type: "Value", path: "reference" },
},
},
path: "/table_data",
},
title: [{ $type: "Value", path: "/total_results" }, " results found"],
}}
/>
</Box>
);
}#
Conditional rendering
#
#
Show
#
Use Show to render content only when a condition is met:
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [data, setData] = useState<Record<string, unknown>>({
target_by: "url",
});
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={data}
element={{
$type: "Document",
body: [
{
$type: "Field",
children: {
$type: "Select",
children: [
{ $type: "SelectTrigger", w: "full" },
{ $type: "SelectContent" },
],
name: "target_by",
options: [
{ label: "URL", value: "url" },
{ label: "Saved Pages", value: "page" },
],
},
label: "Target by",
},
{
$type: "Show",
children: {
$type: "Field",
children: {
$type: "Input",
name: "url",
placeholder: "Add a URL",
},
label: "URL",
},
when: {
"==": [{ $type: "Value", path: "/target_by" }, "url"],
},
},
{
$type: "Show",
children: {
$type: "Field",
children: {
$type: "Select",
children: [
{ $type: "SelectTrigger", w: "full" },
{ $type: "SelectContent" },
],
name: "saved_page",
options: [
{ label: "Home page", value: "home" },
{ label: "Marketplace", value: "marketplace" },
{ label: "Product Details", value: "product_details" },
],
},
label: "Saved Page",
},
when: {
"==": [{ $type: "Value", path: "/target_by" }, "page"],
},
},
],
}}
onDataChange={setData}
/>
</Box>
);
}#
Operators
#
Supported operators: ==, !=, <, <=, >, >=, !! (truthy), ! (falsy). Combine conditions with and and or:
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
import { useState } from "react";
export function App() {
const [data, setData] = useState<Record<string, unknown>>({
target_by: "url",
url: "https://example.com",
});
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={data}
element={{
$type: "Document",
body: [
{
$type: "Field",
children: {
$type: "Select",
children: [
{ $type: "SelectTrigger", w: "full" },
{ $type: "SelectContent" },
],
name: "target_by",
options: [
{ label: "URL", value: "url" },
{ label: "Saved Pages", value: "page" },
],
},
label: "Target by",
},
{
$type: "Show",
children: {
$type: "Field",
children: {
$type: "Textarea",
name: "test_idea",
placeholder:
"e.g., Add quantity badges to product thumbnails",
},
label: "Test Idea",
},
when: {
or: [
{
and: [
{
"==": [{ $type: "Value", path: "/target_by" }, "url"],
},
{ "!!": { $type: "Value", path: "/url" } },
],
},
{
and: [
{
"==": [{ $type: "Value", path: "/target_by" }, "page"],
},
{ "!!": { $type: "Value", path: "/saved_page" } },
],
},
],
},
},
],
}}
onDataChange={setData}
/>
</Box>
);
}#
Interactions
#
#
Interaction handler
#
Sends the named interaction back to your server, where it can be handled and responded to:
Content Marketing Platform
Version 2.2 Performance Optimization
Android Scrum Campaign / TSK-98
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
actions: [
{
$type: "Action",
children: "Comment",
onClick: { interaction: "comment_on_task" },
},
{
$type: "Action",
appearance: "primary",
children: "View task",
onClick: { interaction: "view_task" },
},
],
appName: "Content Marketing Platform",
body: [
{
$type: "Text",
children:
"Initial performance metrics reveal a 12% drop in user retention post-update.",
},
],
subtitle: "Android Scrum Campaign / TSK-98",
title: "Version 2.2 Performance Optimization",
}}
/>
</Box>
);
}#
Message handler
#
Sends a text message back to the LLM:
Content Marketing Platform
Version 2.2 Performance Optimization
Android Scrum Campaign / TSK-98
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
element={{
$type: "Document",
actions: [
{
$type: "Action",
children: "Comment",
onClick: {
message: "I'd like to leave a comment on this task.",
},
},
{
$type: "Action",
appearance: "primary",
children: "View task",
onClick: {
message: "Show me the full details for TSK-98.",
},
},
],
appName: "Content Marketing Platform",
body: [
{
$type: "Text",
children:
"Initial performance metrics reveal a 12% drop in user retention post-update.",
},
],
subtitle: "Android Scrum Campaign / TSK-98",
title: "Version 2.2 Performance Optimization",
}}
/>
</Box>
);
}#
Download handler
#
Triggers a file download:
Strategic Pitch Presentation
Google Slides
"use client";
import { ProteusDocumentRenderer } from "@optiaxiom/proteus";
import { Box } from "@optiaxiom/react";
export function App() {
return (
<Box maxW="md" w="full">
<ProteusDocumentRenderer
data={{
download_url:
"https://example.com/files/strategic-pitch-presentation.pptx",
}}
element={{
$type: "Document",
actions: [
{
$type: "Action",
appearance: "primary",
children: "Download",
onClick: {
action: "download",
url: { $type: "Value", path: "/download_url" },
},
},
],
body: [
{
$type: "Image",
alt: "Slide preview of Strategic Pitch Presentation",
src: "https://placehold.co/560x315",
},
],
subtitle: "Google Slides",
title: "Strategic Pitch Presentation",
}}
/>
</Box>
);
}#
Proteus Designer
#
#
Visual editor
#
The Proteus Designer is a visual editor for building Proteus documents. You can build a component tree, edit properties in the inspector, see a live preview of how the document renders, and copy the final JSON into your resource handler. You can also provide sample data to test how dynamic values resolve.
#
Element reference
#
#
Document
#
actionsActions available for this document
NodeappearanceVisual treatment of the document shell. 'default' renders the document as a card with background, border, and padding. 'inline' strips the surrounding chrome so the document blends into its host container.
"default" | "inline" | ExpressionappIconURL or data URI for the application icon (e.g., 'https://example.com/icon.png' or 'data:image/svg+xml,...'). Rendered as an <img> element.
stringappNameThe official name of the application
stringblockingIf true, hides chat prompt and forces user interaction with document. User can press ESC or close to abandon.
booleanbody*The main content of the document.
NodecompactIf true, constrains the body to a max height and makes it scrollable when content overflows.
booleandataInitial data for the document. Not used by the renderer directly — intended for the outer component managing state to use as the starting data.
objectmetaAdditional metadata not directly consumed by Proteus. Use this to pass along any extra data.
anysubtitleA brief description or tagline that provides additional context about the Proteus document's purpose.
NodetitleA concise heading that encapsulates the essence of the Proteus document's content or intended action.
NodetitleIconURL or data URI for an icon displayed alongside the title in a block-style header.
string#
Layout
#
#
Group
#
alignItemsSet the element's align-items CSS property. Defaults to center when
flexDirection='row', and stretch when flexDirection='column'.
"stretch" | "center" | "end" | "start" | "normal" | ExpressionchildrenNodeflexDirectionSet the element's flex-direction CSS property.
Default: 'row' (CSS standard)
"column" | "column-reverse" | "row" | "row-reverse" | Expression#
Card
#
childrenNode#
CardHeader
#
addonAfterDisplay content inside the header after children.
NodeaddonBeforeDisplay content inside the header before children.
NodechildrenNodedescriptionAdd secondary text after the primary title.
NodelineClampTruncate the text at specific number of lines.
"2" | "4" | "1" | "3" | Expression#
CardLink
#
childrenNodehrefThe link href.
string | ExpressiononClickAction triggered when link is clicked
EventHandler#
Separator
#
Accepts only $type plus the standard styling props.
#
Typography
#
#
Heading
#
childrenNodelevelHeading level (1-4) that controls both the semantic HTML tag and font size.
- level="1": renders <h1> with fontSize="4xl" (default)
- level="2": renders <h2> with fontSize="3xl"
- level="3": renders <h3> with fontSize="2xl"
- level="4": renders <h4> with fontSize="xl"
Use asChild to decouple the semantic level from visual appearance.
"2" | "4" | "1" | "3" | Expression#
Text
#
childrenNodelineClampTruncate the text at specific number of lines.
"2" | "4" | "1" | "3" | ExpressiontruncateWhether to truncate the text and add an ellipsis at the end.
boolean | Expression#
Link
#
childrenNodehrefThe link href.
string | Expression#
Data display
#
#
DataTable
#
columnsArray | ExpressiondataArray | Expression | Zip#
Chart
#
dataChart data records, either inline, a ProteusExpression, or a ProteusZip transformation
Array | Expression | ZiplayoutChart layout direction
"horizontal" | "vertical"seriesArray | ExpressiontypeChart type
"bar" | "line"xAxisKeyKey in data records for x-axis labels
string#
Image
#
altAlternative text for the image
Expression | stringsrcThe image source URL
Expression | string#
ImageCarousel
#
images*Array of image data to display in the carousel
Array | ExpressiontitleAccessible label for the carousel region.
Expression | string#
Avatar
#
childrenNodecolorSchemeControl the avatar fallback background and text colors.
"purple" | "neutral" | ExpressionfallbackThe fallback icon to display when no name or image is given.
"opal" | "team" | "user" | ExpressionnameUse name to generate initials to show inside the avatar.
string | ExpressionsizeControl the size of the avatar.
"xs" | "sm" | "md" | "lg" | "xl" | "3xl" | "2xs" | ExpressionsrcRender the image inside the avatar.
string | Expression#
Badge
#
childrenNodeintentControl the appearance by selecting between the different badge types.
"information" | "success" | "warning" | "danger" | "neutral" | "primary" | Expression#
Time
#
date*The date to display. Can be a Date object or an ISO 8601 string.
string | ExpressionshowDateWhether to show the date part of the value. Defaults to true.
boolean | ExpressionshowTimeWhether to show the time part of the value. Defaults to false.
boolean | Expression#
Form controls
#
#
Field
#
childrenNodedescriptionProvide description and help text for the field.
NodeinfoDisplay a help icon with additional context for the input.
NodelabelThe label of the field.
NoderequiredDisplay an asterisk for required inputs.
boolean | Expression#
Input
#
addonAfterDisplay content inside the input at the end.
NodeaddonBeforeDisplay content inside the input at the start.
NodeappearanceControl the appearance of the input.
"number" | "default" | ExpressionautoFocusWhether the input should be focused on mount.
boolean | ExpressionnameThe name of the form control element.
string | ExpressionplaceholderThe placeholder text to use when control has no value.
string | ExpressionrequiredWhether selecting this input is required.
boolean | ExpressiontypeThe input type.
"number" | "color" | "button" | "checkbox" | "radio" | "hidden" | "text" | "reset" | "range" | "search" | "time" | "image" | "tel" | "url" | "email" | "date" | "submit" | "month" | "datetime-local" | "week" | "file" | "password" | string | Expression#
Textarea
#
maxRowsLimits the height of the textarea when resize=auto is used.
1 | 2 | 3 | 4 | 5 | ExpressionnameThe name of the form control element.
string | ExpressionplaceholderThe placeholder text to use when control has no value.
string | ExpressionrequiredWhether selecting this input is required.
boolean | ExpressionresizeControl whether resizing mode is manual, automatic, or disabled.
"none" | "auto" | "vertical" | ExpressionrowsThe number of rows to display.
number | Expression#
Select
#
childrenNodenameThe name of the inner select element.
string | Expressionoptions*The select items/options we want to render.
ArrayrequiredWhether the select value is required.
boolean | Expression#
SelectTrigger
#
childrenNode#
SelectContent
#
Accepts only $type plus the standard styling props.
#
Switch
#
childrenNodedescriptionAdd secondary text after the label.
NodenameThe name of the form control element.
string | ExpressionrequiredWhether selecting this input is required.
boolean | Expression#
Range
#
marksThe marks to display on the range steps.
ArraymaxThe maximum value for the range.
number | ExpressionminThe minimum value for the range.
number | ExpressionstepThe stepping interval for the range.
number | Expression#
Question
#
questions*Array of questions data
Expression#
Actions
#
#
Action
#
appearanceControl the appearance by selecting between the different button types.
"default" | "danger" | "primary" | "subtle" | "danger-outline" | "default-opal" | "inverse" | "primary-opal" | ExpressionchildrenNodetypeThe default behavior of the button.
"button" | "reset" | "submit" | ExpressiononClickAction triggered when button is clicked
EventHandler#
Button
#
appearanceControl the appearance by selecting between the different button types.
"default" | "danger" | "primary" | "subtle" | "danger-outline" | "default-opal" | "inverse" | "primary-opal" | ExpressionchildrenNodetypeThe default behavior of the button.
"button" | "reset" | "submit" | ExpressiononClickAction triggered when button is clicked
EventHandler#
Data binding
#
#
Value
#
formatterOptional formatter to apply to the resolved value. Can be a string shorthand or an object with type and options for Intl formatters.
"DateTime" | "Number" | objectpath*Path to a value in the data. Absolute paths start with '/' and resolve from the root (e.g., '/title', '/options/0/label'). Inside a Map template, paths without a leading '/' are relative to the current item (e.g., 'title' resolves to each item's 'title' field).
string#
Map
#
childrenTemplate object to render for each item in the array. Value paths inside this template are relative to the current item (e.g., path='title' resolves to each item's 'title' field). Use a leading '/' to reference top-level data (e.g., path='/title' resolves to the root data's 'title').
NodeflatWhen true, flattens the result array by one level. Useful when each mapped item resolves to an array and you want a single flat list.
booleanpath*JSON pointer path to the source array in the data (e.g., '/results')
stringseparatorOptional separator to render between items. Can be a string or a ProteusNode for more complex separators.
Node#
MapIndex
#
Accepts only $type plus the standard styling props.
#
Concat
#
childrenArray of values to concatenate into a single string. Each item is resolved and joined together.
Array#
Zip
#
sources*Map of output property names to array sources. Each source should resolve to an array of the same length.
object#
Show
#
childrenContent to show when condition is true
NodewhenSingle condition or array of conditions (AND logic). Each condition is an object with one operator key.
Condition | Array#
IconCalendar
#
Accepts only $type plus the standard styling props.
#
Bridge
#
fallbackContent rendered on platforms without iframe support (Teams, Slack, mobile). If omitted, a default 'View in Opal web' message is shown.
NodeheightHeight of the iframe in pixels
numberresource*Resource URI identifying the MCP app to render (e.g., 'ui://sample-widget')
string