Skip to content
/

Build a component. Render it anywhere.

Export a React component. One command builds it to a single HTML file. Import that file in another app and render it like any other component, sandboxed in its own frame.

Read the docsThe packages are not on npm yet.
  1. 1

    Export a component

    A React component, default export. Props come from the host.

    rating.tsx
    type Props = {
      value?: number;
      onRate: (n: number) => Promise<void>;
    };
    
    export default function Rating({
      value = 0,
      onRate,
    }: Props) {
      return (
        <div className="stars">
          {[1, 2, 3, 4, 5].map((n) => (
            <button key={n} onClick={() => onRate(n)}>
              {n <= value ? "★" : "☆"}
            </button>
          ))}
        </div>
      );
    }
  2. 2

    Build it to one file

    React, your code, CSS and the runtime in one HTML file. No config.

    terminal
    $ npx remote-components build rating.tsx
    dist/rating.html
  3. 3

    Import it in another app

    Pass the file to RemoteComponent. Callbacks are async functions.

    App.tsx
    import { useState } from "react";
    import {
      RemoteComponent,
    } from "@assistant-ui/remote-components-host-react";
    import rating from
      "widgets/dist/rating.html?raw";
    
    export function Review() {
      const [stars, setStars] = useState(3);
      return (
        <RemoteComponent
          html={rating}
          value={stars}
          onRate={async (n) => setStars(n)}
        />
      );
    }
Result
value = 3
fig. 01 · the rating runs in a sandboxed frame. Its value lives on this page.

Plain HTML underneath

The build output is an ordinary HTML page with one script in it. So any page works: a hand-written fragment, a page a model wrote, any built app. Pass it inline with html, or serve it and point src at the URL.

weather.html
<h3>Weather in <span id="city">Berlin</span></h3>
<button>Monday</button>
<script>
  rc.addEventListener("props-changed", () => (city.textContent = rc.props.city));
  document.querySelector("button").onclick = () => rc.props.onSelect("mon");
</script>
App.tsx
<RemoteComponent
  src="https://widgets.example/weather.html"
  city="Lisbon"
  onSelect={async (day) => setDay(day)}
/>

Laid out inline

The frame sizes to its content and takes your theme. Popovers and dialogs render on your page, outside the frame.

Design review
30 min, video call
Thu
Nov 6
Pick a time
fig. 02 · the time slots are a separate document in a sandboxed frame. The card, the popover layer and the Book button belong to this page.

Isolation, set from outside

The remote is untrusted code. Sandbox, origin and network are set by your page and the server.