A2UI Basics — Customer Onboarding UI for a SaaS Platform

Real-world scenario: building the UI surfaces for a customer onboarding agent. The agent collects customer info via a form, shows account metrics on a dashboard, confirms plan selection, and displays a user table.

Concepts: UI.text / .button / .text_field — component factories UI.bind() — two-way data binding to JSON Pointer paths UI.required() — validation checks | (Row), >> (Col) — layout composition operators UI.surface() — named compilation root compile_surface() — Python tree → flat A2UI JSON messages Presets: UI.form(), UI.dashboard(), UI.confirm(), UI.table(), UI.wizard()

Tip

What you’ll learn How to define and apply reusable configuration presets.

Source: 70_a2ui_basics.py

from adk_fluent._ui import (
    UI,
    UIBinding,
    UICheck,
    UIComponent,
    UISurface,
    compile_surface,
)

# --- 1. Onboarding form — component creation + composition ---

header = UI.text("Welcome to Acme", variant="h2")
name_field = UI.text_field("Full Name", bind=UI.bind("/customer/name"), checks=[UI.required()])
email_field = UI.text_field("Email", bind=UI.bind("/customer/email"), checks=[UI.required(), UI.email()])
company_field = UI.text_field("Company", bind=UI.bind("/customer/company"))

assert header._kind == "Text"
assert name_field._kind == "TextField"
assert isinstance(UI.bind("/customer/name"), UIBinding)
assert isinstance(UI.required(), UICheck)

# Compose layout: header on top, fields stacked vertically
onboarding_layout = header >> name_field >> email_field >> company_field
assert onboarding_layout._kind == "Column"
assert len(onboarding_layout._children) >= 2  # binary >> nests; flatten is internal

# Side-by-side buttons
cancel_btn = UI.button(child=UI.text("Cancel"))
submit_btn = UI.button("primary", child=UI.text("Continue"))
actions = cancel_btn | submit_btn
assert actions._kind == "Row"

# Full form
form_layout = onboarding_layout >> actions
assert form_layout._kind == "Column"

# --- 2. Surface creation + theme + data ---
onboarding_surface = UI.surface("onboarding", form_layout)
assert isinstance(onboarding_surface, UISurface)
assert onboarding_surface.name == "onboarding"

themed = onboarding_surface.with_theme(primaryColor="#2563eb", agentDisplayName="Acme Onboarding")
assert len(themed.theme) == 2

with_defaults = onboarding_surface.with_data(customer={"name": "", "email": "", "company": ""})
assert len(with_defaults.data) == 1

# --- 3. Compilation to A2UI protocol ---
msgs = compile_surface(onboarding_surface)
assert len(msgs) == 2  # createSurface + updateComponents

create_msg = msgs[0]
assert create_msg["createSurface"]["surfaceId"] == "onboarding"

update_msg = msgs[1]
components = update_msg["updateComponents"]["components"]
assert len(components) >= 5  # Column + 4 fields + buttons

# --- 4. Preset: schema-driven feedback form ---
form = UI.form("Customer Feedback", fields={"name": "text", "email": "email", "message": "longText"})
assert isinstance(form, UISurface)

# --- 5. Preset: account metrics dashboard ---
dashboard = UI.dashboard(
    "Account Overview",
    cards=[
        {"title": "Active Users", "bind": "/stats/active_users"},
        {"title": "MRR", "bind": "/stats/mrr"},
        {"title": "Churn Rate", "bind": "/stats/churn"},
    ],
)
assert isinstance(dashboard, UISurface)
dash_msgs = compile_surface(dashboard)
assert any("createSurface" in m for m in dash_msgs)

# --- 6. Preset: plan upgrade confirmation ---
confirm = UI.confirm("Upgrade to Enterprise plan? ($499/mo, billed annually)")
assert isinstance(confirm, UISurface)

# --- 7. Preset: team member table ---
table = UI.table(["Name", "Email", "Role"], data_bind="/team/members")
assert isinstance(table, UISurface)

# --- 8. Preset: onboarding wizard ---
wizard = UI.wizard(
    "Account Setup",
    steps=[
        ("Company Info", UI.text_field("Company Name", bind=UI.bind("/company/name"))),
        (
            "Plan Selection",
            UI.text("Choose your plan") >> (UI.button("Starter") | UI.button("Pro") | UI.button("Enterprise")),
        ),
        ("Confirmation", UI.text("Review your selections and confirm.")),
    ],
)
assert isinstance(wizard, UISurface)

# --- 9. Generic component (escape hatch for custom visualizations) ---
usage_chart = UI.component("BarChart", data="/stats/usage_history", x="month", y="api_calls")
assert usage_chart._kind == "BarChart"

# --- 10. Runnable agent: onboarding assistant with LLM-guided UI ---
from adk_fluent import Agent

_onboarding_builder = (
    Agent("onboarding_agent", "gemini-2.5-flash")
    .instruct(
        "You are a customer onboarding assistant for Acme SaaS. "
        "Collect the customer's name, email, and company. "
        "Show a welcome form, then present plan options. "
        "Use the A2UI tools to create interactive forms and dashboards."
    )
    .ui(llm_guided=True)
)
try:
    root_agent = _onboarding_builder.build()
except Exception:
    root_agent = None  # a2ui-agent not installed — agent runs without UI tools