OpenUI

Render a dashboard

Show a dashboard with LiveArtifactRenderer, from a tool response or an id.

LiveArtifactRenderer shows a dashboard in the browser. It sends the dashboard's data requests to your interpreter route, and switches to the new version after an edit.

components/dashboard.tsx
"use client";

import { library } from "@openuidev/dashboard-library";
import { LiveArtifactRenderer } from "@openuidev/react-ui/cloud";

export function Dashboard({ response }: { response: string }) {
  return (
    <LiveArtifactRenderer
      response={response}
      library={library}
      interpreter={{ url: "/api/interpreter" }}
    />
  );
}

The renderer must sit inside OpenUICloudProvider, which supplies the user's frontend token.

Props

PropDescription
responseThe response from create or edit, unchanged.
idThe dashboard's id, instead of response. Use it to open a saved dashboard, such as one from the list.
libraryThe component library. Use the same library the spec on the server came from.
interpreterWhere queries run: { url, headers? } for a route, or an async function that takes the request.
previewtrue (default): a card that opens the dashboard. false: the dashboard renders in place.
renderInHtmlElementThe element the dashboard opens in, such as a side panel. Default: under the card.
isOpen, onOpenChange, defaultOpenControl whether the dashboard is open from its card.
actionsButtons in the open dashboard's bar, next to the close button.
onErrorReceives rendering, query, and loading errors.
classNameAdded to the root element.

Layouts

Card in the chat

By default, the renderer shows a card with the dashboard's title and status. Clicking it opens the dashboard under the card.

<LiveArtifactRenderer response={response} library={library} interpreter={interpreter} />

Side panel

Pass the panel's element as renderInHtmlElement. The card stays in the chat, and the dashboard opens in the panel:

const [panel, setPanel] = useState<HTMLElement | null>(null);

<>
  <Chat>
    <LiveArtifactRenderer
      response={response}
      library={library}
      interpreter={interpreter}
      renderInHtmlElement={panel}
    />
  </Chat>
  <aside ref={setPanel} />
</>;

In place

With preview={false}, the dashboard renders where the renderer is, without a card. Use it for a full page or a panel the app manages itself:

<LiveArtifactRenderer id={id} library={library} interpreter={interpreter} preview={false} />

In this mode the renderer draws no frame unless a Chrome slot is passed. See Slots.

Slots

Each part of the renderer is a slot, passed as a child. A slot can be left out to keep the default, given props to change the default's values, or given children to replace its look.

SlotRegionWhat it is
PreviewPreviewThe card that opens the dashboard.
PreviewTitle, PreviewDescription, PreviewLoaderPreviewThe card's parts.
ChromeChromeThe frame around the open dashboard: a bar, then the content.
ChromeTitle, ChromeLoader, ChromeActions, CloseButtonChromeThe bar's parts.
ContentChromeThe dashboard itself.
Loading, ErrorContentShown only while the dashboard loads or after it fails, even with custom children.

For example, a full-height panel with its own header:

<LiveArtifactRenderer id={id} library={library} interpreter={interpreter} preview={false}>
  <LiveArtifactRenderer.Chrome>
    <header className="panel-header">
      <LiveArtifactRenderer.ChromeTitle />
      <StatusBadge />
    </header>
    <LiveArtifactRenderer.Content className="panel-body">
      <LiveArtifactRenderer.Loading label="Loading live data…" />
      <LiveArtifactRenderer.Error>
        <DashboardError />
      </LiveArtifactRenderer.Error>
    </LiveArtifactRenderer.Content>
  </LiveArtifactRenderer.Chrome>
</LiveArtifactRenderer>

Slots render only in their region: ChromeTitle outside Chrome renders nothing.

Renderer state

useArtifactRenderer() returns the renderer's state inside any slot:

import { useArtifactRenderer } from "@openuidev/react-ui/cloud";

function StatusBadge() {
  const { isStreaming, isFetching, error } = useArtifactRenderer();
  if (error) return <span>Couldn't load</span>;
  if (isStreaming) return <span>Generating</span>;
  if (isFetching) return <span>Loading data</span>;
  return <span>Live</span>;
}

function DashboardError() {
  const { error, retry } = useArtifactRenderer();
  return (
    <div role="alert">
      <p>
        {error?.source === "query" ? "Some data didn't load." : "This dashboard couldn't be shown."}
      </p>
      {retry && <button onClick={retry}>Try again</button>}
    </div>
  );
}
FieldDescription
id, titleThe dashboard's id and title.
isStreamingThe dashboard is being generated.
isFetchingIts queries are running.
isLoadingEither of the two.
errorA loading error (source: "artifact") or a query error (source: "query").
retryRetries what failed. Undefined when there's nothing to retry.
isOpen, open, closeThe open state, when the dashboard has a card.
previewWhether the renderer has a card.

Theme

ThemeProvider from @openuidev/dashboard-library sets the components' mode, "light" (default) or "dark", and takes lightTheme and darkTheme overrides. It's optional.

Interpreter as a function

interpreter also takes an async function, such as a server action. It receives the request and returns the result:

<LiveArtifactRenderer
  response={response}
  library={library}
  interpreter={(request) => runQuery(request)}
/>

The request has id, version, name, and arguments. Pass it to dashboards.interpreter.execute() on the server, as in the interpreter route.

On this page