Inline Embeds

SkillMedia

Lets your agent show interactive previews of app screens right inside its replies instead of links or screenshots.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Inline Embeds skill

About this skill

How to render interactive previews of app screens inline in agent chat via sandboxed iframes. Covers the `embed` fence syntax, the `postNavigate` pop-out helper, and when to prefer an embed over prose / a static image.

What this skill tells your AI

The instructions your AI receives, as published by builderio/agent-native in templates/assets/.agents/skills/inline-embeds/SKILL.md and read by ahel’s review.

The chat renderer lets you embed a sandboxed iframe pointing at any same-origin route of this app, rendered inline with your reply. Use this when a live, interactive preview communicates more than prose or a static screenshot — a chart, an email thread, a slide, a form, a ticket.

The embed fence

Emit a fenced code block with the language embed:

```embed
src: /email?id=msg_123
aspect: 4/3
title: Re: Q4 planning
```

Keys:

KeyRequiredNotes
srcyesSame-origin path beginning with /. Cross-origin URLs are blocked
aspectno16/9 (default), 4/3, 3/2, 2/1, 21/9, 1/1
titlenoAccessible label + hover tooltip
heightnoFixed pixel height when aspect ratio doesn't fit

The rendered iframe is sandboxed (allow-scripts allow-same-origin allow-forms allow-popups) and referrerpolicy="same-origin". The browser enforces that the iframe can't navigate the parent window.

When to reach for an embed

  • A chart or visualization that benefits from tooltips / hover.
  • A detail view the user might want to scan (email, ticket, event, slide).
  • Anything where "here's a link to it" would force an extra click.

Don't embed when plain prose, a bulleted list, or a small table is enough. Don't embed external sites — the renderer blocks cross-origin URLs.

"Open in main window" button — postNavigate

Every embed route should include a small "Open" button so the user can pop the view out of the chat and into the full app. Import the helper from @agent-native/core/client:

import { postNavigate, isInAgentEmbed } from "@agent-native/core/client";

export function OpenButton({ path }: { path: string }) {
  if (!isInAgentEmbed()) return null;
  return (
    <button onClick={() => postNavigate(path)}>Open</button>
  );
}

postNavigate sends a postMessage to the parent chat window. The chat renderer validates the message is same-origin, same-iframe, and the path is relative, then updates the parent URL via history.pushState so react-router navigates without a page reload.

When the page is loaded directly (not in an iframe), postNavigate falls back to a same-window navigation, so the button works both ways.

Adding an embed route to a template

  1. Create a chromeless route (e.g. app/routes/email.tsx) that renders the detail view without the app shell.
  2. Mark the route path as bare in app/components/layout/Layout.tsx so the sidebar / header don't render inside the iframe.
  3. Render the existing detail component with a transparent background so it blends into the chat theme.
  4. Include an <OpenButton> that calls postNavigate("/path/to/detail").
  5. Document the embed URL in this template's AGENTS.md so the agent knows what's embeddable.

The framework-level fence renderer and security enforcement apply automatically — no per-template wiring beyond the route and layout bypass.

Signals

GitHub stars
7k
Forks
613
Last commit
Sep 2026
Advanced
Item type
skill
Key
inline-embeds
Source
github.com/builderio/agent-native