For Developers

Widget System Architecture

How widgets work under the hood — the Blueprint protocol, expression engine, and multi-platform rendering.

Overview

Every widget in Widge is powered by the Blueprint protocol — a platform-agnostic JSON format that describes what to render, not how. The server sends a Blueprint, and each client (Web, Mobile, TV, CLI) interprets it using its own renderer.

Server (Blueprint) ──► Web (Tamagui renderer)
                   ──► Mobile (React Native renderer)
                   ──► TV (Focus-aware renderer)
                   ──► Embed (DOM renderer)
                   ──► CLI (ASCII renderer)

Blueprint Response

Every widget request returns a BlueprintResponse:

{
    "meta": {
        "id": "wdg_abc123",
        "title": "Revenue",
        "update_policy": { "mode": "poll", "ttl": 30 }
    },
    "state": {
        "selectedPeriod": "week"
    },
    "root": {
        "type": "Container",
        "id": "root",
        "props": { "direction": "column", "gap": 8 },
        "children": [
            {
                "type": "Text",
                "id": "title",
                "props": { "content": "${config.title}", "variant": "h2" }
            },
            {
                "type": "Chart",
                "id": "chart",
                "props": {
                    "chartType": "line",
                    "data": "${config.data}"
                }
            }
        ]
    }
}

Meta

FieldDescription
idUnique widget instance ID 
titleDisplay title
update_policy.modepoll (client refreshes) or push (server pushes)
update_policy.ttlSeconds between refreshes
designSizeOptional base dimensions for scaling (default 624×400)

State

Initial local state for the widget. Updated via set_state actions without server round-trips.

Root

The UI tree — a recursive structure of Blueprint Nodes.

Node Types

TypeDescriptionKey Props
ContainerFlex layoutdirection, alignMain, alignCross, gap
TextRich text with markdown and expressionscontent, variant, maxLines, align
ImageRemote image with fallbackurl, fallbackIcon, contentMode
IconSF Symbol or Material iconname, size, color
Chart20+ chart typeschartType, data, color, showAxes
TableData tablecolumns, data, showHeader
NumberFormatted numeric displayvalue, format, prefix, suffix
ClockAnalog or digital clockclockType, timezone, showSeconds
WeatherWeather displaytemperature, city, unit, weatherCode
SpacerLayout spacingsize
LottieAnimation playbackurl, loop, autoplay

Chart Types

Line, Bar, Pie, Donut, Area, Gauge, Horizontal Bar, Stacked Bar, Scatter, Bubble, Radar, Heatmap, Treemap, Funnel, Waterfall, Candlestick, Histogram, Progress Bar, Progress Ring, Goal Tracker.

Expression Engine

Blueprint props support expression bindings — a safe, sandboxed language evaluated at render time. Wrap expressions in dollar-brace syntax inside any string prop.

Basics

${config.title}                     → property access
${state.count + 1}                  → arithmetic
${state.active ? 'Yes' : 'No'}     → ternary
${config.data.length}               → array length

Supported Operations

CategoryExamples
Arithmetic+, -, *, /, %
Comparison>, <, >=, <=, ===, !==
Logical&&, ||, !
Ternarycondition ? a : b
Array methods.map(), .filter(), .find(), .slice(), .join()
String methods.trim(), .toUpperCase(), .split(), .replace()
MathMath.round(), Math.floor(), Math.max()
UtilitiesparseInt(), String(), Number(), JSON.parse()

Context Variables

VariableDescription
configWidget configuration (title, data, colors, etc.)
stateCurrent local state
dataData source records

Security

The expression engine uses a recursive descent parser — no eval() or new Function(). Access to window, document, process, require, import, constructor, and prototype is blocked.

Actions

Nodes can respond to taps via the interactions.on_tap array:

{
    "type": "Text",
    "id": "counter",
    "props": { "content": "Count: ${state.count}" },
    "interactions": {
        "on_tap": [
            { "type": "set_state", "payload": { "count": "${state.count + 1}" } }
        ]
    }
}

Action Types

ActionDescription
set_stateUpdate local widget state
mutationSend an intent to the server (with optional optimistic UI)
open_urlOpen a URL in browser or deep link
copy_clipboardCopy text to clipboard
present_actionsShow an action sheet
api_callMake an HTTP request
run_animationTrigger shake, pulse, fade
hapticTrigger haptic feedback (mobile)

Conditional Actions

Every action supports an if expression:

{
    "type": "set_state",
    "if": "state.count < 10",
    "payload": { "count": "${state.count + 1}" }
}

Theme Tokens

Styles use semantic tokens that resolve to CSS variables (web) or hex values (native):

TokenCSS VariableUsage
$widget_bg--widget-bgWidget background
$widget_text--widget-textPrimary text
$widget_accent--widget-accentAccent color
$widget_border--widget-borderBorder color
$chart_color_1$chart_color_5--chart-color-*Chart series
$text_primary--color-text-primaryPrimary text
$text_secondary--color-text-secondarySecondary text
$status_success--color-status-successSuccess state
$status_error--color-status-errorError state

Capabilities

When requesting a Blueprint, the client declares its capabilities:

{
    "platform": "web",
    "capabilities": {
        "mutations": true,
        "present_actions": true,
        "optimistic_ui": true
    }
}

The server adapts the Blueprint based on what the client supports. A read-only embed might set mutations: false to receive a simpler tree without interactive actions.

Widget Definitions

Each widget type is defined by a Widget Definition containing:

  • Config schema — JSON Schema describing the configuration fields
  • Template — Blueprint tree with expression bindings
  • Size constraints — min/max width and height in grid units
  • Variants — layout variations for different sizes

When a user adds a widget to a dashboard, the server evaluates the template against the config and data source to produce the final Blueprint.

Responsive Grid

Widgets live on a responsive grid:

BreakpointColumnsRow Height
Desktop (>1200px)12100px
Tablet (>768px)880px
Mobile (<768px)260px
TV 16120px

Widget positions: x (column), y (row), w (width in columns), h (height in rows).

Package Structure

The Blueprint protocol is published as @wdg/blueprint — a zero-dependency TypeScript package:

@wdg/blueprint
├── types       — BlueprintNode, BlueprintResponse, BlueprintAction, ...
├── expressions — Safe expression engine (recursive descent parser)
├── template    — Template processing and config defaults
└── tokens      — Theme token resolution (CSS vars / native hex)

Import it in any environment without pulling in UI framework dependencies:

import type { BlueprintResponse, BlueprintNode } from '@wdg/blueprint'
import { safeEvaluate, evaluateExpression } from '@wdg/blueprint'