Build a playground
If you already have Agent output, or want to edit JSON and inspect the result, use the hosted GenUI Playground resources to build your own preview app. You do not need to install GenUI or compile a renderer. This quick start uses the same A2UI messages to preview a Web page and a page in a native Lynx container.
To try generation, component sets, and examples first, open the GenUI Playground.
You can also open the renderer preview to switch between A2UI and OpenUI, edit source, and render it immediately or simulate streaming. The page uses the renderer and bundles from the same Playground deployment.
Choose preview resources
render.html includes the Lynx for Web runtime. The native .lynx.js resource is
a Lynx bundle that a Lynx container loads. The two bundles target different runtimes.
1. Prepare A2UI messages
Preview input is an array of A2UI v0.9 messages: createSurface creates a surface,
updateComponents describes its component tree, and updateDataModel updates
data bound to components. The component tree starts at root.
The complete HTML example below loads a product card with an image, rating,
review count, price, original price, and an Add to Cart button. Its component tree
uses Card, Column, Row, Image, Text, and Button, with updateDataModel
providing the product data. The
hosted example JSON, shaped
as { "name": "...", "description": "...", "messages": [...] }. The preview accepts
both message arrays and these messages wrapper objects.
The hosted bundle contains the Playground's built-in Catalog. Agent-generated
component names and props must match that Catalog; setting catalogId does not
download or register custom components. For your own business components, build
your renderer following Catalogs and components.
2. Build a Web preview page
Save the following as index.html and open it in a browser, or deploy it to your
static site. Edit the JSON and click Preview to see the updated page. Open preview
opens the current preview in a separate tab. The initial example is fetched from
the hosted JSON, so an internet connection is required.
Use URL.searchParams.set to preserve Unicode and special characters without
manually concatenating or double-encoding parameters. instant=1 displays the
final UI immediately; remove it to watch messages play back one at a time.
For larger messages, publish the JSON at a device-accessible HTTPS URL and use
messagesUrl instead of messages. Try this
example preview.
The JSON service must allow CORS reads from https://lynx-stack.dev. Temporary
browser blob: URLs cannot be used for sharing across devices or native previews.
3. Preview in a native Lynx container
Load the hosted a2ui.lynx.js in your existing Lynx host or LynxExplorer. The host
must support the renderer's components and network requests. This loading
configuration maps to your host's bundle URL and globalProps interfaces; it is
not an Android / iOS SDK call.
If the data is already in host memory, pass the message array from step 1 as
globalProps.messages and remove messagesUrl. Native configuration uses
instant: true; the Web query uses instant=1. Loading only the bundle without
passing messages will not display your page.
For hosts that map bundle URL query parameters to globalProps, generate a
preview URL like this:
Pass this URL to LynxExplorer or your host's page-opening entry point. The host
owns the conversion from query parameters to globalProps. If it does not
support that conversion, use the explicit loading configuration above.
4. Connect your Agent and interactions
Preview resources provide rendering and mock playback. Your app collects prompts, calls your Agent, and delivers the resulting A2UI messages to the preview. Keep model credentials on your backend, outside preview URLs.
After receiving A2UI_RENDER_READY with runtimeReady === true from the preview
iframe, a Web app can append messages with
frame.contentWindow.postMessage({ type: 'A2UI_LIVE_MESSAGES', messages }, 'https://lynx-stack.dev'). With liveAction=1, the parent receives
{ type: 'A2UI_USER_ACTION', action }, calls its Agent, and sends back
{ type: 'A2UI_ACTION_RESPONSE', messages }. Check both
event.origin === 'https://lynx-stack.dev' and
event.source === frame.contentWindow when receiving events.
Native hosts use the same message types through global events with [messages]
as their arguments. Enabling globalProps.liveAction: true requires implementing
NativeModules.bridge.call and handling A2UI_USER_ACTION.
A2UI_RUNTIME_READY confirms that the renderer is ready. Static previews do not
require action forwarding. The product card's Add to Cart button emits the
addToCart action; supply actionMocks for a simulated response or connect your
Agent to implement it.
Other preview formats
- OpenUI: Use
render.html?protocol=openui&demoUrl=./openui.web.jswith OpenUI Lang text inrawTextorrawTextUrl. Native hosts loadopenui.lynx.jsand pass text inglobalProps.rawText. The native renderer does not readrawTextUrl; the host must fetch the text first. For mixed chat replies, addinlineMode=1to the Web URL or setglobalProps.inlineMode: truein the native host. The Playground adapter inlynx-src/openui/renders ordinary Markdown components and independent OpenUI blocks in response order. Markdown is not registered in the OpenUI catalog. Each labelledopenui/openui-langfence needs its ownrootand all its referenced declarations; state, queries and actions belong to that block. Closed fences become interactive while later Markdown or UI is still streaming. The coreOpenUiRendereraccepts standard OpenUI Lang only. - ReactLynx: Use
render.html?protocol=reactlynx&bundleUrl=...with a published Web bundle atbundleUrl. Native hosts load the corresponding Native bundle; see Source and builds. - Lynx XML: Use
render.html?protocol=lynx-xml&sourceUrl=...with a complete, executable.lynxmlartifact atsourceUrl. Native hosts load that artifact directly; see Artifacts and validation.
These sourceUrl, bundleUrl, and rawTextUrl resources also need device-accessible URLs and
CORS for Web previews. Hosted resources update with the website; build and publish
your own resources when you need a pinned renderer version or custom components.

