# How to Implement Generative UI with Transient Chat Components in Agent-Native

> Learn how to implement generative UI with transient chat components in Agent-Native using render inline extension. Create dynamic, temporary UI elements for interactive chat experiences.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: how-to-guide
- Published: 2026-07-02

---

**Generative UI in Agent-Native enables agents to render sandboxed, interactive widgets directly within chat transcripts using the `render-inline-extension` action, creating temporary UI elements that exist only for the current conversation turn.**

Generative UI transforms static chat into dynamic experiences by allowing agents to create Alpine.js widgets on demand. In the BuilderIO/agent-native framework, **transient chat components** provide temporary, sandboxed interfaces for tasks like threshold tuning or quick calculations, automatically rendering inline without persisting to the Extensions panel.

## Transient vs. Persisted UI Components

Understanding the lifecycle distinction helps you choose the right implementation path.

**Transient components** are created using the `render-inline-extension` tool defined in [`packages/core/src/extensions/actions.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/extensions/actions.ts). These UI elements appear inline within the chat transcript and automatically disappear when the conversation moves forward. They never create entries in the Extensions list, making them ideal for one-off interactions like sliders, calculators, or temporary data visualizations.

**Persisted components** use the `create-extension` action instead, saving the UI to the Extensions view where users can reopen them later with `show-extension-inline`. Reserve this approach for tools the user needs to access repeatedly.

When you only need a quick interactive control for the current turn, always choose the **transient** path to keep the interface clean.

## Architecture Overview

The generative UI system operates through four coordinated layers:

1. **Agent actions**: The `render-inline-extension` tool (lines 60–130 in [`packages/core/src/extensions/actions.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/extensions/actions.ts)) exposes parameters including `name`, `content`, and optional `context`, returning a payload with `chatUI.renderer` set to `"core.inline-extension"`.

2. **Extension sandbox**: The chat client mounts your HTML in an iframe with `sandbox="allow-scripts"` (strictly omitting `allow-same-origin`). This isolation prevents the generated UI from accessing host DOM, cookies, or local storage while allowing execution of Alpine.js logic.

3. **Bridge helpers**: The sandbox exposes `window.slotContext` for receiving initial data, `agentNative.ui.output` for writing temporary state, and `agentNative.chat.send` for injecting messages back into the conversation flow.

4. **Chat renderer**: When the client detects `chatUI.renderer === "core.inline-extension"`, it automatically creates the iframe via [`InlineExtensionFrame.tsx`](https://github.com/BuilderIO/agent-native/blob/main/InlineExtensionFrame.tsx) and injects the application's Tailwind CSS theme variables.

## How to Implement a Transient Component

Follow these steps to create a self-contained, interactive widget that renders inline.

### Step 1: Configure the Agent Action

In your agent configuration, call the `render-inline-extension` tool with a self-contained HTML snippet:

```json
{
  "tool": "render-inline-extension",
  "arguments": {
    "name": "Threshold tuner",
    "description": "Adjust the model threshold",
    "content": "<div x-data='thresholdTuner'><label class='text-sm font-medium text-foreground'>Threshold</label><input type='range' min='0' max='100' x-model.number='threshold' class='w-full' /><p class='text-sm text-muted-foreground' x-text='`Current: ${threshold}`'></p></div><script>Alpine.data('thresholdTuner',()=>({threshold:50,init(){this.threshold=Number(window.slotContext?.threshold??50);window.onSlotContext?.(ctx=>{if(ctx.threshold!==undefined) this.threshold=Number(ctx.threshold);});}}));</script>"
  }
}

```

The action generates a unique extension ID and returns metadata that triggers inline rendering.

### Step 2: Build the Alpine.js UI Snippet

Your `content` field must contain valid HTML with embedded Alpine.js and Tailwind classes. Access initial context through `window.slotContext` and listen for updates via `window.onSlotContext`:

```html
<div x-data="thresholdTuner">
  <label class="text-sm font-medium text-foreground">Threshold</label>
  <input type="range"
         min="0"
         max="100"
         x-model.number="threshold"
         class="w-full"/>
  <p class="text-sm text-muted-foreground"
     x-text="`Current: ${threshold}`"></p>

  <!-- Passive output – writes to application state -->
  <button class="mt-2 rounded-md bg-primary px-3 py-1 text-primary-foreground"
          @click="agentNative.ui.output({threshold}, {label:'Threshold'})">
    Save value
  </button>

  <!-- Explicit chat message -->
  <button class="mt-2 rounded-md bg-secondary px-3 py-1 text-secondary-foreground"
          @click="agentNative.chat.send(`Use threshold ${threshold}`, {context: JSON.stringify({threshold}), submit:true})">
    Apply in chat
  </button>
</div>

<script>
Alpine.data('thresholdTuner', () => ({
  threshold: 50,
  init() {
    // Load initial context passed by the agent
    this.threshold = Number(window.slotContext?.threshold ?? 50);
    // React to later updates from the agent
    window.onSlotContext?.(ctx => {
      if (ctx.threshold !== undefined) this.threshold = Number(ctx.threshold);
    });
  },
}));
</script>

```

This pattern ensures the component initializes with agent-provided data and can react to subsequent context changes.

### Step 3: Retrieve Output Data

To access values submitted via `agentNative.ui.output`, the agent reads from a temporary state key following the pattern `inline-ui:<extensionId>:output`:

```json
{
  "tool": "read-app-state",
  "arguments": {
    "key": "inline-ui:{{extensionId}}:output"
  }
}

```

The `read-app-state` tool returns the JSON object passed to `agentNative.ui.output`, allowing the agent to continue processing without requiring the user to type values manually.

## Security Model and Sandboxing

Security is enforced by [`InlineExtensionFrame.tsx`](https://github.com/BuilderIO/agent-native/blob/main/InlineExtensionFrame.tsx) through strict iframe sandboxing. The generated UI runs with:

- `sandbox="allow-scripts"` permits JavaScript execution
- Absence of `allow-same-origin` prevents access to host cookies, storage, or DOM
- Tailwind CSS variables are injected safely via the frame's style context, not the host document

Only explicitly exposed bridge functions (`agentNative.ui.output`, `agentNative.chat.send`, `appAction`, `appFetch`) can communicate with the host application, ensuring malicious code cannot exfiltrate sensitive data.

## Key Implementation Files

Reference these source files when building generative UI features:

- [`packages/core/src/extensions/actions.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/extensions/actions.ts) (lines 60–130): Defines the `render-inline-extension` tool parameters and return shape
- [`packages/core/src/client/extensions/InlineExtensionFrame.tsx`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/extensions/InlineExtensionFrame.tsx): React component handling iframe creation and Tailwind injection
- `packages/core/docs/content/generative-ui.mdx`: Design documentation covering lifetimes and security models
- [`.agents/skills/generative-ui/SKILL.md`](https://github.com/BuilderIO/agent-native/blob/main/.agents/skills/generative-ui/SKILL.md): Agent-facing skill guide for determining when to use transient UI
- [`packages/core/src/extensions/actions.spec.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/extensions/actions.spec.ts): Unit tests verifying the transient UI action's `readOnly` flag and renderer output

## Summary

- **Transient components** use `render-inline-extension` for temporary UI that disappears after the current chat turn, while persisted components use `create-extension` for permanent Extensions panel entries.
- Implementation requires calling the action with a self-contained Alpine.js + Tailwind HTML snippet that reads `window.slotContext` for initialization.
- The sandboxed iframe runs at [`packages/core/src/client/extensions/InlineExtensionFrame.tsx`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/extensions/InlineExtensionFrame.tsx) with strict `allow-scripts` isolation and no same-origin access.
- Use `agentNative.ui.output` to write temporary state to `inline-ui:<id>:output` and `agentNative.chat.send` to post explicit messages back to the transcript.
- Retrieve saved values later using the `read-app-state` tool with the constructed key pattern.

## Frequently Asked Questions

### What is the difference between transient and persisted generative UI in Agent-Native?

**Transient UI renders inline using `render-inline-extension` and exists only for the current conversation turn**, automatically cleaning up when the chat advances. **Persisted UI uses `create-extension`**, saves to the Extensions view, and remains accessible across sessions. Choose transient UI for one-off controls like sliders or calculators, and persisted UI for tools users need to reopen repeatedly.

### How do I pass initial data to a transient chat component?

Pass a JSON string via the optional `context` argument when calling `render-inline-extension`. Inside your Alpine.js component, access this data through `window.slotContext` during initialization. For reactive updates, implement `window.onSlotContext` to handle subsequent context changes from the agent.

### Can transient UI components access the host application's local storage or cookies?

**No.** The iframe uses `sandbox="allow-scripts"` without `allow-same-origin`, strictly isolating the generated UI from the host document's cookies, local storage, and DOM. Communication is restricted to the `agentNative` bridge helpers exposed by the sandbox, preventing data exfiltration while permitting controlled interaction with the chat system.

### How do I retrieve values submitted by the user in a transient component?

Use `agentNative.ui.output` within your component to write data, which stores it under the key `inline-ui:<extensionId>:output`. Then call the `read-app-state` tool from your agent with that specific key to retrieve the JSON payload. Alternatively, use `agentNative.chat.send` to post the value as a visible message in the chat transcript.