Skip to content

PATTERN Cited by 1 source

Platform-adaptive component rendering

Definition

A cross-platform UI-composition pattern in which structured content (tables, cards, modals, buttons) is declared once using a high-level component API (e.g. JSX), and each platform adapter renders the component using that platform's native primitive — with graceful fallback when the platform doesn't support the primitive at all.

The developer writes:

<Table
  headers={["Name", "Status", "Region"]}
  rows={[
    ["api-prod", "healthy", "iad1"],
    ["api-staging", "degraded", "sfo1"],
  ]}
/>

Each platform adapter emits what that platform actually renders well:

Platform Native rendering for <Table>
Slack Block Kit table blocks
Microsoft Teams GFM markdown tables
Discord GFM markdown tables
Google Chat Monospace text widgets
Telegram Code blocks
GitHub Existing markdown pipeline
Linear Existing markdown pipeline

(Source: sources/2026-04-21-vercel-chat-sdk-brings-agents-to-your-users.)

Structure

Three pieces:

  1. Component API — a small set of structured primitives (Table, Card, Modal, Button) with platform-neutral props. Developer writes once.
  2. Per-platform renderer — each adapter package implements render(Component) → PlatformNativeMessage. Renderer picks the most expressive native construct available.
  3. Fallback chain — if the platform doesn't support any native construct for the component, "it will fall back gracefully" — usually to formatted text or a pared-down text summary.

Why this is worth a named pattern

The default cross-platform approach is to lowest-common-denominator everything to markdown text — which wastes each platform's real UI affordances (Slack's Block Kit, Discord's rich embeds, WhatsApp's interactive reply buttons) and produces ugly output on platforms that render some markdown poorly.

The inverse approach — ship one specialised renderer per platform — puts the platform-specific branching in the agent's handler, defeating the whole single-shared-agent factoring.

Platform-adaptive component rendering sits between the two: the component choice carries the developer's intent ("I want to show a 2-column tabular comparison"), and each adapter translates that intent into the best native form it can manage.

Platform-specific examples from the Chat SDK post

Tables

As per the matrix above — five distinct native renderings and a markdown fallback for GitHub / Linear.

Cards on WhatsApp

Cards with ≤3 actions render as WhatsApp interactive reply buttons; cards with >3 actions fall back to formatted text.

"Cards render as interactive reply buttons with up to three options, falling back to formatted text where needed."

This illustrates capacity-limited fallback: the adapter knows WhatsApp's reply-button cap and downgrades gracefully rather than truncating silently or erroring.

Buttons, modals

"Cards, modals, and buttons work similarly. You write the element once using JSX, and each adapter renders them in whatever format the platform supports natively. If a platform doesn't support a given element, it will fall back gracefully."

Relation to streaming

Platform-adaptive component rendering is distinct from streaming markdown-to-native conversion. Component rendering operates on structured content (JSX primitives); streaming conversion operates on unstructured token streams from an LLM. Both live in the adapter layer but solve different problems:

  • Component rendering: agent emits "here's a table, render it natively".
  • Streaming conversion: agent emits "here's a markdown token stream, don't let the user see literal **bold** while it's streaming".

A real agent typically uses both — streaming text narration plus a structured <Table> summary at the end.

Trade-offs

  • Expressiveness ceiling. The component API is fixed; a platform's premium-tier feature (e.g. Slack's Canvas blocks, Discord's polls) isn't reachable unless the abstraction grows to name it.
  • Fallback surprise. The developer doesn't see at design time how a component degrades on the weakest platform. The Telegram table-as-code-block fallback is functional but visually different from the Slack Block Kit rendering — testing matrix grows with every new adapter.
  • Platform-cap awareness has to live in the adapter. The WhatsApp 3-reply-button cap is platform knowledge the SDK embeds; when a platform changes its caps, the adapter must be updated.

Seen in

Last updated · 476 distilled / 1,218 read