Block Types
A detailed guide to 25+ block types
Blocks are the content building units of Surflet pages. Each page can contain any number and combination of blocks.
Common Structure
Every block follows the same top-level structure:
{
"type": "block_type",
"data": {
"title": "Optional title",
"...": "type-specific fields"
}
}
Layout Control
Blocks support multi-column layouts via the layout array, and content organization via group and tab.
layout — Multi-Column Layout
{
"type": "layout",
"data": {
"columns": [
{ "width": "1/2", "blocks": [{ "type": "key_value", "data": { "..." : "..." } }] },
{ "width": "1/2", "blocks": [{ "type": "metric", "data": { "..." : "..." } }] }
]
}
}
group — Grouping
{
"type": "group",
"data": {
"title": "Ticket Details",
"collapsible": true,
"default_collapsed": false,
"blocks": [
{ "type": "key_value", "data": { "..." : "..." } },
{ "type": "text", "data": { "..." : "..." } }
]
}
}
tab — Tabs
{
"type": "tab",
"data": {
"tabs": [
{ "label": "Overview", "blocks": [{ "type": "key_value", "data": { "..." : "..." } }] },
{ "label": "Details", "blocks": [{ "type": "text", "data": { "..." : "..." } }] },
{ "label": "History", "blocks": [{ "type": "timeline", "data": { "..." : "..." } }] }
]
}
}
key_value — Key-Value Pairs
Display structured key-value information. Suitable for ticket overviews, user profiles, order details, and similar use cases.
{
"type": "key_value",
"data": {
"title": "Ticket Overview",
"pairs": [
{"key": "Customer", "value": "Alice Wang"},
{"key": "Order ID", "value": "#ORD-20260321-7891"},
{"key": "Amount", "value": "$127.50", "style": "highlight"},
{"key": "Status", "value": "Pending", "badge": "warning"}
],
"layout": "vertical"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title |
pairs | array | Array of key-value pairs |
pairs[].key | string | Key name |
pairs[].value | string | Value |
pairs[].style | string | Value style (normal / highlight / muted) |
pairs[].badge | string | Badge style (info / warning / error / success) |
layout | string | Layout (vertical / horizontal / grid) |
text — Markdown Rich Text
Renders Markdown-formatted rich text. Supports standard Markdown syntax.
{
"type": "text",
"data": {
"title": "Analysis Report",
"content": "## Summary\n\nCustomer reported a product quality issue. After investigation...\n\n### Recommendations\n\n1. Full refund\n2. Send an apology email\n3. Update QA process"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
content | string | Markdown content |
table — Data Table
Renders a structured data table. Supports sorting and pagination hints.
{
"type": "table",
"data": {
"title": "Order History",
"columns": [
{"key": "date", "label": "Date", "width": "120px"},
{"key": "event", "label": "Event"},
{"key": "amount", "label": "Amount", "align": "right"},
{"key": "status", "label": "Status"}
],
"rows": [
{"date": "2026-03-20", "event": "Order placed", "amount": "$127.50", "status": "completed"},
{"date": "2026-03-22", "event": "Refund requested", "amount": "$127.50", "status": "pending"}
],
"sortable": true,
"max_rows": 100,
"footer": "2 records total"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
columns | array | Column definitions |
columns[].key | string | Column key (maps to a field in each row) |
columns[].label | string | Column display name |
columns[].width | string | Column width (optional) |
columns[].align | string | Alignment (left / center / right) |
rows | array | Row data |
sortable | boolean | Whether the table is sortable |
max_rows | number | Maximum number of rows to display |
footer | string | Footer text |
callout — Callout / Alert Box
Highlights important information such as AI suggestions, risk warnings, and notices.
{
"type": "callout",
"data": {
"message": "Recommendation: Full refund. Good history, first complaint.",
"style": "info",
"icon": "lightbulb",
"collapsible": false
}
}
| Field | Type | Description |
|---|---|---|
message | string | Callout content (supports Markdown) |
style | string | Style (info / warning / error / success) |
icon | string | Icon (optional, defaults based on style) |
collapsible | boolean | Whether the callout is collapsible |
code — Code Block
Display code, logs, config files, etc. Supports syntax highlighting.
{
"type": "code",
"data": {
"title": "Error Log",
"content": "2026-03-22T15:30:00Z ERROR [payment] Transaction failed\n order_id: ORD-7891\n error: insufficient_funds\n amount: 127.50",
"language": "log",
"line_numbers": true,
"highlight_lines": [1],
"max_height": "300px"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
content | string | Code content |
language | string | Language (python / javascript / json / yaml / log, etc.) |
line_numbers | boolean | Whether to show line numbers |
highlight_lines | number[] | Lines to highlight |
max_height | string | Maximum height (scrolls when exceeded) |
divider — Divider
A visual separator for dividing different content sections.
{
"type": "divider",
"data": {
"style": "solid",
"spacing": "large"
}
}
| Field | Type | Description |
|---|---|---|
style | string | Line style (solid / dashed / dotted) |
spacing | string | Spacing (small / medium / large) |
metric — Metric Card
Display key numeric metrics, with support for trend arrows and comparisons.
metric (enhanced: inline sparklines)
Metric items support a sparkline array to render inline trend lines within the card:
{ "type": "metric", "data": { "items": [
{ "label": "Revenue", "value": "$12M", "trend": "up", "trend_value": "+8%",
"sparkline": [8, 9, 10, 9.5, 11, 12] }
]}}
{
"type": "metric",
"data": {
"title": "Monthly Refunds",
"value": "$12,350",
"change": "+15%",
"trend": "up",
"description": "Increase from last month",
"icon": "dollar"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Metric name |
value | string | Metric value |
change | string | Change magnitude (optional) |
trend | string | Trend direction (up / down / flat) |
description | string | Description text (optional) |
icon | string | Icon (optional) |
chart — Chart
Renders data charts. Supports bar, line, pie, area, scatter, and radar types.
chart (enhanced: event annotations)
Annotate key events on bar/line/area charts:
"annotations": [
{ "x": "2026-03-15", "label": "Strategy launched", "color": "#ef4444" },
{ "x": "2026-03-20", "label": "Recovery", "color": "#10b981" }
]
{
"type": "chart",
"data": {
"title": "Monthly Refund Trend",
"chart_type": "line",
"data": {
"labels": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"],
"datasets": [
{
"label": "Refund Amount",
"data": [1200, 1900, 800, 1500, 2200, 1800],
"color": "#3b82f6"
}
]
},
"options": {
"y_axis_label": "Amount ($)",
"show_legend": true
}
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
chart_type | string | Chart type (bar / line / pie / area / scatter / radar) |
data.labels | string[] | X-axis labels |
data.datasets | array | Datasets |
data.datasets[].label | string | Dataset name |
data.datasets[].data | number[] | Data values |
data.datasets[].color | string | Color |
options | object | Chart options |
timeline — Timeline
Display an event timeline. Suitable for event history, process flows, and status changes.
{
"type": "timeline",
"data": {
"title": "Process Flow",
"events": [
{
"time": "2026-03-20T10:00:00Z",
"event": "Ticket created",
"detail": "Customer submitted via online form",
"status": "completed",
"icon": "create"
},
{
"time": "2026-03-20T14:00:00Z",
"event": "Awaiting manager approval",
"detail": "Notification sent",
"status": "current",
"icon": "pending"
}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
events | array | Event list |
events[].time | string | Timestamp (ISO 8601) |
events[].event | string | Event name |
events[].detail | string | Event details |
events[].status | string | Status (completed / current / pending) |
events[].icon | string | Icon (optional) |
buttons — Button Group
Embed a group of buttons within the content area for quick actions.
{
"type": "buttons",
"data": {
"title": "Quick Actions",
"items": [
{"label": "View Ticket", "url": "https://...", "style": "primary"},
{"label": "Contact Customer", "url": "mailto:...", "style": "secondary"},
{"label": "Escalate", "action_id": "act_escalate", "style": "danger"}
],
"layout": "horizontal"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
items | array | Button list |
items[].label | string | Button text |
items[].url | string | Link (optional) |
items[].action_id | string | Associated action_id (optional) |
items[].style | string | Style (primary / secondary / danger) |
layout | string | Layout (horizontal / vertical) |
logs — Log Stream
Display log output with syntax coloring and auto-scroll.
{
"type": "logs",
"data": {
"title": "Build Log",
"lines": [
{"time": "10:00:01", "level": "info", "message": "Starting build..."},
{"time": "10:00:05", "level": "info", "message": "Compiling 42 modules"},
{"time": "10:00:12", "level": "error", "message": "Failed: missing dependency"}
],
"max_height": "400px",
"auto_scroll": true
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
lines | array | Log lines |
lines[].time | string | Timestamp |
lines[].level | string | Level (info / warn / error / debug) |
lines[].message | string | Log message |
max_height | string | Maximum height |
auto_scroll | boolean | Whether to auto-scroll to the bottom |
progress — Progress Bar
Display task progress or completion rate.
{
"type": "progress",
"data": {
"title": "Migration Progress",
"value": 73,
"max": 100,
"label": "73% complete",
"color": "#3b82f6",
"show_percentage": true
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
value | number | Current value |
max | number | Maximum value |
label | string | Display text (optional) |
color | string | Color (optional) |
show_percentage | boolean | Whether to show percentage |
stepper — Step Indicator
Display the current progress through a multi-step process.
{
"type": "stepper",
"data": {
"title": "Approval Process",
"current": 1,
"steps": [
{"label": "Request submitted", "description": "Completed"},
{"label": "Manager approval", "description": "In progress"},
{"label": "Finance confirmation", "description": "Pending"},
{"label": "Execute refund", "description": "Pending"}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
current | number | Current step index (0-based) |
steps | array | Step list |
steps[].label | string | Step name |
steps[].description | string | Step description |
image — Image
Display a single image with an optional title and caption.
{
"type": "image",
"data": {
"title": "System Architecture Diagram",
"url": "https://storage.surflet.app/img/architecture.png",
"alt": "Surflet system architecture",
"width": "100%",
"caption": "v2.0 architecture design"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
url | string | Image URL |
alt | string | Alt text |
width | string | Width (optional) |
caption | string | Caption (optional) |
countdown — Countdown Timer
Display a countdown timer. Suitable for deadlines and time-limited events.
{
"type": "countdown",
"data": {
"title": "Approval Deadline",
"target": "2026-03-25T18:00:00Z",
"expired_message": "Timed out",
"style": "warning"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
target | string | Target time (ISO 8601) |
expired_message | string | Text shown after expiry |
style | string | Style (info / warning / danger) |
comparison — Comparison Card
Display two or more options side by side for comparison.
{
"type": "comparison",
"data": {
"title": "Option Comparison",
"items": [
{
"label": "Option A",
"highlight": true,
"fields": [
{"key": "Cost", "value": "$5,000"},
{"key": "Timeline", "value": "2 weeks"},
{"key": "Risk", "value": "Low"}
]
},
{
"label": "Option B",
"fields": [
{"key": "Cost", "value": "$3,000"},
{"key": "Timeline", "value": "4 weeks"},
{"key": "Risk", "value": "Medium"}
]
}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
items | array | Comparison item list |
items[].label | string | Option name |
items[].highlight | boolean | Whether to highlight as recommended |
items[].fields | array | Field list |
kanban — Kanban Board
Display a kanban board view for task management.
{
"type": "kanban",
"data": {
"title": "Task Board",
"columns": [
{
"label": "Pending",
"cards": [
{"title": "Refund #4821", "description": "Awaiting approval", "badge": "high"}
]
},
{
"label": "In Progress",
"cards": [
{"title": "Refund #4820", "description": "Finance processing"}
]
},
{
"label": "Completed",
"cards": []
}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
columns | array | Column list |
columns[].label | string | Column name |
columns[].cards | array | Card list |
columns[].cards[].title | string | Card title |
columns[].cards[].description | string | Card description |
columns[].cards[].badge | string | Badge |
tree — Tree Structure
Display hierarchical data. Suitable for file trees and org charts.
{
"type": "tree",
"data": {
"title": "Project Structure",
"nodes": [
{
"label": "src/",
"children": [
{"label": "index.ts"},
{"label": "utils/", "children": [
{"label": "helpers.ts"},
{"label": "constants.ts"}
]}
]
},
{"label": "package.json"}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
nodes | array | Node list (recursive structure) |
nodes[].label | string | Node name |
nodes[].children | array | Child nodes (optional) |
nodes[].icon | string | Icon (optional) |
gauge — Gauge
Display a gauge metric. Suitable for health scores and utilization rates.
{
"type": "gauge",
"data": {
"title": "CPU Utilization",
"value": 72,
"max": 100,
"unit": "%",
"thresholds": [
{"value": 60, "color": "#22c55e"},
{"value": 80, "color": "#f59e0b"},
{"value": 100, "color": "#ef4444"}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
value | number | Current value |
max | number | Maximum value |
unit | string | Unit |
thresholds | array | Threshold ranges |
sparkline — Sparkline
An inline mini-chart, ideal for displaying trends alongside metrics.
{
"type": "sparkline",
"data": {
"title": "7-Day Trend",
"values": [12, 15, 8, 22, 18, 25, 20],
"color": "#3b82f6",
"height": "40px"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
values | number[] | Data points |
color | string | Line color |
height | string | Chart height |
funnel — Funnel Chart
Display conversion funnel data.
{
"type": "funnel",
"data": {
"title": "Registration Conversion Funnel",
"steps": [
{"label": "Page visits", "value": 10000},
{"label": "Clicked signup", "value": 3500},
{"label": "Completed form", "value": 2100},
{"label": "Registered", "value": 1200}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
steps | array | Funnel steps |
steps[].label | string | Step name |
steps[].value | number | Step value |
gallery — Image Gallery
Display images, screenshots, attachments, and other media.
{
"type": "gallery",
"data": {
"title": "Damage Evidence",
"items": [
{
"url": "https://storage.surflet.app/img/damage-01.jpg",
"thumbnail_url": "https://storage.surflet.app/img/damage-01-thumb.jpg",
"caption": "Product front — casing crack",
"mime_type": "image/jpeg",
"size_bytes": 245000
},
{
"url": "https://storage.surflet.app/img/damage-02.jpg",
"thumbnail_url": "https://storage.surflet.app/img/damage-02-thumb.jpg",
"caption": "Packaging condition",
"mime_type": "image/jpeg",
"size_bytes": 312000
}
],
"layout": "grid",
"max_columns": 3
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
items | array | Item list |
items[].url | string | File URL |
items[].thumbnail_url | string | Thumbnail URL (optional) |
items[].caption | string | Caption |
items[].mime_type | string | MIME type |
items[].size_bytes | number | File size |
layout | string | Layout (grid / list / carousel) |
max_columns | number | Maximum columns in grid layout |
diff — Diff View
Display a text diff comparison. Suitable for code changes, config edits, and document revisions.
diff (enhanced: numeric comparison)
In addition to text diffs, numeric comparison mode is now supported:
{ "type": "diff", "id": "nd1", "data": {
"items": [
{ "label": "PnL", "old": 1302, "new": 1822, "unit": "$" },
{ "label": "Win Rate", "old": 91.2, "new": 93.5, "unit": "%" }
],
"old_label": "Yesterday", "new_label": "Today"
}}
Changes are calculated automatically: positive values shown in green ↑, negative in red ↓.
{
"type": "diff",
"data": {
"title": "Config Changes",
"old_text": "max_connections = 100\ntimeout = 30\nlog_level = info",
"new_text": "max_connections = 200\ntimeout = 60\nlog_level = debug\nretry_count = 3",
"language": "ini",
"old_label": "Current Config",
"new_label": "New Config",
"view_mode": "split"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
old_text | string | Original text |
new_text | string | New text |
language | string | Language (for syntax highlighting) |
old_label | string | Original version label |
new_label | string | New version label |
view_mode | string | View mode (split / unified) |
form — Interactive Form
Collect user input. Supports multiple field types.
{
"type": "form",
"data": {
"title": "Refund Details",
"fields": [
{
"field_id": "refund_amount",
"type": "number",
"label": "Refund Amount",
"required": true,
"default_value": 127.50,
"validation": {"min": 0, "max": 10000}
},
{
"field_id": "refund_method",
"type": "select",
"label": "Refund Method",
"required": true,
"options": [
{"value": "original", "label": "Original payment method"},
{"value": "credit", "label": "Account credit"},
{"value": "bank", "label": "Bank transfer"}
]
},
{
"field_id": "reason",
"type": "textarea",
"label": "Reason for Refund",
"placeholder": "Please describe the reason for the refund...",
"max_length": 500
}
],
"submit_label": "Submit"
}
}
Supported field types: text / textarea / number / select / multi_select / checkbox / radio / date / datetime / file / email / url
embed — Embedded Content
Embed external content such as PDFs, videos, and iframes.
{
"type": "embed",
"data": {
"title": "Original Invoice",
"url": "https://storage.surflet.app/docs/invoice-4821.pdf",
"embed_type": "pdf",
"width": "100%",
"height": "600px"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
url | string | Content URL |
embed_type | string | Type (pdf / video / iframe / audio) |
width | string | Width |
height | string | Height |
thread — Comment Thread
Display a discussion thread or chat log.
{
"type": "thread",
"data": {
"title": "Customer Support Conversation",
"messages": [
{"author": "Customer", "avatar": "C", "time": "10:00", "content": "Product arrived damaged"},
{"author": "Support AI", "avatar": "A", "time": "10:01", "content": "We're sorry to hear that. I've created a refund request for you."},
{"author": "Customer", "avatar": "C", "time": "10:02", "content": "Thank you, how long will it take?"}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
messages | array | Message list |
messages[].author | string | Sender name |
messages[].avatar | string | Avatar (URL or initials) |
messages[].time | string | Time |
messages[].content | string | Message content (supports Markdown) |
file — File Attachment
Display downloadable file attachments.
{
"type": "file",
"data": {
"title": "Related Files",
"files": [
{
"name": "invoice-4821.pdf",
"url": "https://storage.surflet.app/files/invoice-4821.pdf",
"mime_type": "application/pdf",
"size_bytes": 524288
},
{
"name": "damage-report.docx",
"url": "https://storage.surflet.app/files/damage-report.docx",
"mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"size_bytes": 102400
}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Title (optional) |
files | array | File list |
files[].name | string | Filename |
files[].url | string | Download URL |
files[].mime_type | string | MIME type |
files[].size_bytes | number | File size |
heatmap — Heatmap
A color-coded matrix suitable for calibration data, correlation analysis, and activity heatmaps. Similar to a GitHub contribution graph.
{ "type": "heatmap", "id": "hm1", "data": {
"rows": ["Tier1", "Tier2", "Tier3"],
"columns": ["1h", "4h", "12h"],
"values": [[0.92, 0.85, 0.78], [0.88, 0.91, 0.82], [0.75, 0.80, 0.90]],
"color_scale": "green_red"
}}
| Field | Description |
|---|---|
rows | Array of row labels |
columns | Array of column labels |
values | 2D array of values (rows × columns) |
color_scale | Color scheme: green_red (default), blue_red, gray_blue |
min / max | Optional manual value range (auto-detected by default) |
link — Link Card
Display one or more rich link cards with title, description, URL, and an optional favicon or preview image.
{
"type": "link",
"data": {
"title": "Related Resources",
"links": [
{
"url": "https://example.com/ticket/4821",
"title": "Original Ticket #4821",
"description": "Customer refund request submitted on 2026-03-20",
"image": "https://example.com/og-image.png",
"favicon": "https://example.com/favicon.ico"
},
{
"url": "https://docs.example.com/refund-policy",
"title": "Refund Policy",
"description": "Full refund policy documentation"
}
]
}
}
A single link can also be passed at the top level without the links array:
{
"type": "link",
"data": {
"url": "https://example.com/ticket/4821",
"title": "Ticket #4821",
"description": "View the original support ticket"
}
}
| Field | Type | Description |
|---|---|---|
title | string | Section title (optional) |
links | array | Array of link objects (use instead of top-level url) |
links[].url | string | Link URL (required) |
links[].title | string | Link title (optional, falls back to URL) |
links[].description | string | Short description (optional) |
links[].image | string | Preview image URL (optional, shown as thumbnail) |
links[].favicon | string | Favicon URL (optional) |
url | string | Shorthand for a single link — use links array for multiple |
rich_text — Rich Text (Markdown with Collapse)
Renders Markdown-formatted rich text in a styled card. Supports headings, bold, italic, inline code, and links. Can be made collapsible.
{
"type": "rich_text",
"data": {
"title": "Analysis Report",
"content": "## Summary\n\nCustomer **Alice Wang** reported a damaged product.\n\n### Recommendations\n\n1. Issue a **full refund** of `$127.50`\n2. Send an apology email\n3. File a quality report with supplier",
"collapsible": true
}
}
| Field | Type | Description |
|---|---|---|
title | string | Card title (optional) |
content | string | Markdown content |
collapsible | boolean | Whether the content can be collapsed (default false) |
When collapsible is true, the card renders a collapse/expand toggle next to the title. The initial state is expanded.
Supported Markdown: headings (#, ##, ###), **bold**, *italic*, `inline code`, [text](url) links, and paragraph breaks.
evidence_gallery — Evidence Gallery
Display a mixed-media gallery of evidence items — images, documents, and videos — with captions and uploader attribution. Clicking an image opens a full-screen lightbox.
{
"type": "evidence_gallery",
"data": {
"title": "Submitted Evidence",
"items": [
{
"type": "image",
"url": "https://storage.surflet.app/img/damage-front.jpg",
"caption": "Product front — casing crack",
"uploaded_by": "customer"
},
{
"type": "document",
"url": "https://storage.surflet.app/files/receipt-4821.pdf",
"caption": "Original purchase receipt",
"uploaded_by": "agent:refund-bot"
},
{
"type": "video",
"url": "https://storage.surflet.app/video/unboxing.mp4",
"caption": "Unboxing video",
"uploaded_by": "customer"
}
]
}
}
| Field | Type | Description |
|---|---|---|
title | string | Gallery title (optional) |
items | array | List of evidence items |
items[].type | string | Media type: image, document, or video |
items[].url | string | File URL |
items[].caption | string | Caption text (optional) |
items[].uploaded_by | string | Uploader identity label (optional) |
Rendering behavior:
image— rendered as a thumbnail; clicking opens a full-screen lightbox with captionvideo— rendered as a dark card with a play icon; URL links to the videodocument— rendered as a document icon card with a "View Document" link
The gallery uses a responsive grid: 1 column on mobile, 2 columns on tablet, 3 columns on desktop (for 3+ items). Use the File Uploads guide to upload files and then reference their download URLs here.
SmartRenderer
Surflet uses SmartRenderer to render blocks. If type is not one of the known types listed above, SmartRenderer will attempt automatic rendering — displaying the fields in data in a reasonable way. This means you can use custom type values, and SmartRenderer will do its best to present the content.