In the browser

React

Draw an answer with the Answer component in React 18 or later. It reads the text as it grows, so you can pass your streamed text straight in.

View as Markdown
Terminal
npm install @morphscreen/web
Terminal
pnpm add @morphscreen/web
Terminal
yarn add @morphscreen/web
Terminal
bun add @morphscreen/web

<Answer> takes the core and the answer's text so far. When text grows, only the new part is read, so you can set it on every chunk. Set done when the answer is complete.

src/main.tsx
import { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import { loadCore, type Core } from "@morphscreen/web";
import { Answer } from "@morphscreen/web/react";
import "@morphscreen/web/morphscreen.css";
import wasm from "@morphscreen/web/morphscreen.wasm?url";
import { SAMPLE } from "./sample";

function App() {
  const [core, setCore] = useState<Core | null>(null);
  const [text, setText] = useState("");
  useEffect(() => {
    loadCore(wasm).then(setCore);
  }, []);
  // Your server's stream goes here; this grows a saved answer piece by piece.
  useEffect(() => {
    if (!core) return;
    let n = 0;
    const t = setInterval(() => {
      n += 24;
      setText(SAMPLE.slice(0, n));
      if (n >= SAMPLE.length) clearInterval(t);
    }, 10);
    return () => clearInterval(t);
  }, [core]);
  const done = text.length >= SAMPLE.length;
  useEffect(() => {
    if (done) document.body.dataset.done = "yes";
  }, [done]);
  return core ? <Answer core={core} text={text} done={done} /> : null;
}

createRoot(document.getElementById("root")!).render(<App />);

With Vite, ?url turns the package's morphscreen.wasm into an address it serves with your app.

Its props

Prop What it is
core from loadCore
text the answer so far; or stream, an async iterable of chunks
done the answer is complete
packs, custom, components the packs and your own components (Your own components)
onAction what the reader did (Actions and state)
imageHosts, maps, maplibre where pictures and map tiles load from (Pictures, icons and maps)
className your classes for the answer's element

For a whole conversation, use connectAgent in an effect (Quick start), or <Answer> once per message.