Skip to main content
Gremorie

Web Preview

Sandboxed iframe surface for live HTML preview. Includes URL bar, optional console output and a context-shared URL state.

Overview

WebPreview is the surface for running an AI-generated page, snippet or live URL inside the chat. The iframe is sandboxed with allow-scripts allow-same-origin allow-forms allow-popups allow-presentation, so it can execute scripts and forms without breaking out of the parent app.

Internally it ships a WebPreviewContext that carries the current url, a setter that fires onUrlChange, and the consoleOpen state. Composition pieces (WebPreviewUrl, WebPreviewBody, WebPreviewConsole) all subscribe to the same context, so typing a URL in the bar reflects in the iframe without prop drilling.

Pair with Artifact when the preview is part of a generated asset, or use it standalone as a live preview tile in a workspace.

Preview

Basic preview with URL bar

'use client';import {  WebPreview,  WebPreviewBody,  WebPreviewNavigation,  WebPreviewUrl,} from '@gremorie/rx-artifacts';export function WebPreviewPreview() {  return (    <WebPreview defaultUrl="https://gremorie.com" className="max-w-2xl">      <WebPreviewNavigation>        <WebPreviewUrl />      </WebPreviewNavigation>      <WebPreviewBody src="https://gremorie.com" className="h-[240px]" />    </WebPreview>  );}

With console output

'use client';import {  WebPreview,  WebPreviewBody,  WebPreviewConsole,  WebPreviewNavigation,  WebPreviewUrl,} from '@gremorie/rx-artifacts';export function WebPreviewConsolePreview() {  return (    <WebPreview defaultUrl="https://gremorie.com" className="max-w-2xl">      <WebPreviewNavigation>        <WebPreviewUrl />      </WebPreviewNavigation>      <WebPreviewBody src="https://gremorie.com" className="h-[180px]" />      <WebPreviewConsole        logs={[          {            level: 'log',            message: '[ready] dev server on :5020',            timestamp: new Date(),          },          {            level: 'warn',            message: 'Slow render on /components/workflow/canvas',            timestamp: new Date(),          },        ]}      />    </WebPreview>  );}

Installation

bash npx gremorie@latest add rx-web-preview

bash pnpm dlx gremorie@latest add rx-web-preview

bash yarn dlx gremorie@latest add rx-web-preview

bash bunx --bun gremorie@latest add rx-web-preview

Brings in rx-display (for Collapsible), rx-forms (for Input and Button) and rx-overlays (for Tooltip) as registry dependencies.

Usage

import {
  WebPreview,
  WebPreviewBody,
  WebPreviewConsole,
  WebPreviewNavigation,
  WebPreviewUrl,
} from "@gremorie/rx-artifacts";

export function Example({ logs }) {
  return (
    <WebPreview defaultUrl="https://gremorie.com" onUrlChange={track}>
      <WebPreviewNavigation>
        <WebPreviewUrl />
      </WebPreviewNavigation>
      <WebPreviewBody src="https://gremorie.com" />
      <WebPreviewConsole logs={logs} />
    </WebPreview>
  );
}

Angular edition planned for Phase 5h. Use the React edition for now, or open an issue to prioritize this primitive.

API

<WebPreview>

PropTypeDefaultDescription
defaultUrlstring""Initial URL. Seeds the internal url state and WebPreviewUrl.
onUrlChange(url: string) => void-Fires whenever a child calls setUrl (typically on Enter inside WebPreviewUrl).
classNamestring-Extra classes on the root div.

Extends all ComponentProps<"div">. Provides WebPreviewContext to all descendants.

Default chrome: flex size-full flex-col rounded-lg border bg-card.

<WebPreviewNavigation>

Header row hosting the URL bar and navigation buttons. Sits at the top with a border-bottom separator.

Extends all ComponentProps<"div">.

<WebPreviewNavigationButton>

PropTypeDefaultDescription
tooltipstring-Tooltip label. Required for icon-only buttons.
onClick() => void-Click handler.
disabledboolean-Standard Button disabled state.

Renders a 32x32 ghost Button wrapped in a Tooltip. Use for back / forward / reload affordances.

<WebPreviewUrl>

Controlled or uncontrolled URL input. Reads url from WebPreviewContext and calls setUrl on Enter, which propagates to onUrlChange.

PropTypeDefaultDescription
valuestringcontext urlOverride the context value to make the input fully controlled.
onChangeChangeEventHandler<HTMLInputElement>-Optional override of the change handler.
onKeyDownKeyboardEventHandler<HTMLInputElement>-Called alongside the internal Enter handler.

Extends all Input props from @gremorie/rx-forms.

<WebPreviewBody>

The sandboxed iframe.

PropTypeDefaultDescription
srcstringcontext urlOverride the iframe source. Falls back to the context URL.
loadingReactNode-Optional skeleton or spinner rendered alongside the iframe.
classNamestring-Extra classes on the iframe.

Hardcoded sandbox: allow-scripts allow-same-origin allow-forms allow-popups allow-presentation. Title: "Preview".

<WebPreviewConsole>

PropTypeDefaultDescription
logsArray<{ level, message, timestamp }>[]Log entries. level is "log" | "warn" | "error", timestamp is a Date.
childrenReactNode-Extra content rendered below the logs (raw HTML console, debug controls, etc.).
classNamestring-Extra classes on the Collapsible root.

A Collapsible that opens or closes via the shared consoleOpen state. Empty logs surface a "No console output" placeholder. The trigger + content are rendered internally - do NOT compose with separate WebPreviewConsoleTrigger / WebPreviewConsoleContent, those subcomponents do not exist.

Composition

  1. <WebPreview> is the wrapper that provides WebPreviewContext. Hold the URL state here.
  2. <WebPreviewNavigation> is the address-bar row. Drop WebPreviewNavigationButtons for back / forward / reload, then WebPreviewUrl as the flex-1 input.
  3. <WebPreviewBody> fills the remaining height with the iframe.
  4. <WebPreviewConsole> docks to the bottom and expands upward when opened. The "console open" state is shared, so a custom trigger elsewhere can also expand it.

Variations

Read-only preview

When the URL is fixed (an MDX demo, a generated asset), skip the nav row entirely.

<WebPreview defaultUrl="/preview/landing">
  <WebPreviewBody src="/preview/landing" className="h-[480px]" />
</WebPreview>

Browser chrome with back / forward

Add WebPreviewNavigationButtons before the URL input for a more browser-like feel.

<WebPreview defaultUrl="/preview/landing">
  <WebPreviewNavigation>
    <WebPreviewNavigationButton tooltip="Back" onClick={back}>
      <ArrowLeftIcon className="size-4" />
    </WebPreviewNavigationButton>
    <WebPreviewNavigationButton tooltip="Forward" onClick={forward}>
      <ArrowRightIcon className="size-4" />
    </WebPreviewNavigationButton>
    <WebPreviewNavigationButton tooltip="Reload" onClick={reload}>
      <RotateCwIcon className="size-4" />
    </WebPreviewNavigationButton>
    <WebPreviewUrl />
  </WebPreviewNavigation>
  <WebPreviewBody src="/preview/landing" />
</WebPreview>

Live console for debugging generated code

Stream logs from the iframe (e.g. via postMessage) and pass them through logs. Errors render in destructive color, warnings in yellow, regular logs in foreground.

<WebPreview defaultUrl="about:blank">
  <WebPreviewBody srcDoc={generatedHtml} className="h-[280px]" />
  <WebPreviewConsole logs={consoleLogs} />
</WebPreview>

Accessibility

  • Iframe title: The <iframe> always carries title="Preview" so screen readers announce it. Override via the spread props if you want a more specific title per surface.
  • Sandbox: The strict sandbox keeps the iframe from breaking out of the parent app. If you need a relaxed sandbox (e.g. allowing top navigation), pass sandbox explicitly to override.
  • Keyboard: Focus enters the URL input via Tab. Enter submits the new URL. The console trigger is a real Button, so it activates on Enter / Space.
  • Console color: Errors are not conveyed by color alone. Each entry retains its raw text plus the timestamp, so the level can be inferred from the message even on monochrome displays. If you want full a11y for the level, prepend the level to the message text.
  • Screen readers: Empty console state announces "No console output", so users do not get a silent collapsed surface.
  • Artifact - card chrome that often hosts a WebPreview
  • Code Block - the source code that this preview renders
  • Open in Chat - hand a generated page back to the chat for iteration

On this page