thesys|
OpenUI

The Renderer

Learn how to parse and render OpenUI Lang streams with the Renderer.

The <Renderer /> component is the heart of OpenUI's client side. It parses the OpenUI Lang stream in real-time, resolves component names against your library, and renders the resulting React tree.

It handles incremental parsing, meaning it renders partial UI (like a chart header) while the rest of the data (the chart bars) is still streaming.

Basic Usage

Import the renderer from the core package and pass it your library and the LLM's response text.

import { Renderer } from '@openuidev/lang-react';
import { myLibrary } from './registry';

// Inside your Chat Message component
export function AssistantMessage({ content, isStreaming }) {
  return (
    <div className="prose dark:prose-invert max-w-none">
      <Renderer
        library={myLibrary}
        response={content}
        isStreaming={isStreaming}
      />
    </div>
  );
}

Props API

PropTypeDescription
responsestring | nullThe raw OpenUI Lang text from the LLM. Pass null to render nothing.
libraryLibraryThe component library created via createLibrary.
isStreamingbooleanWhether the LLM is still streaming. Controls form disabling and partial render behavior.
onAction(event: ActionEvent) => voidCallback for button clicks or form submissions inside the rendered UI.
updateMessage(message: string) => voidPersist message content with form state on field change.
onParseResult(result: ParseResult | null) => voidCalled whenever the parse result changes — useful for debugging or inspecting the AST.

Streaming UX

OpenUI is designed for progressive rendering. Because OpenUI Lang is line-oriented, the renderer starts rendering as soon as the first line arrives — no need to wait for the full response.

<Renderer
  library={myLibrary}
  response={streamedText}
  isStreaming={true}
/>

The renderer correctly handles forward references: if the LLM mentions chart = BarChart(...) before the data lines arrive, it will render the component as soon as those lines stream in.

Handling Actions

Unlike static Markdown, OpenUI components are interactive. Buttons, forms, and links trigger actions. You handle these centrally via onAction.

<Renderer
  library={myLibrary}
  response={content}
  onAction={(event) => {
    // Example: a button click
    if (event.type === 'navigate') {
      router.push(event.params.url);
    }

    // Example: a form submission
    if (event.type === 'submit_order') {
      submitOrderToBackend(event.params);
    }
  }}
/>

Custom Component Hooks

If you are building custom components (see Defining Components), you can use these hooks to interact with the Renderer context.

useRenderNode()

Use this to render nested children. If your component accepts a prop that contains other OpenUI components (like a Dashboard accepting cards), you need this hook.

import { defineComponent } from '@openuidev/lang-react';
import { z } from 'zod';

const StatCard = defineComponent({ ... });

const Dashboard = defineComponent({
  name: 'Dashboard',
  description: 'A grid of stat cards.',
  props: z.object({ cards: z.array(StatCard.ref) }),
  component: ({ props, renderNode }) => (
    <div className="grid grid-cols-3 gap-4">
      {props.cards.map((card, i) => renderNode(card))}
    </div>
  ),
});

The renderNode function is passed directly to your component — no need to import a hook separately.

useIsStreaming()

Use this inside a component to disable interactivity while the LLM is still writing.

import { useIsStreaming } from '@openuidev/lang-react';

const SubmitButton = defineComponent({
  name: 'SubmitButton',
  description: 'A submit button.',
  props: z.object({ label: z.string() }),
  component: ({ props }) => {
    const isStreaming = useIsStreaming();
    return (
      <button disabled={isStreaming}>
        {props.label}
      </button>
    );
  },
});

Next Steps

You have now mastered the OpenUI Lang engine. You can define components, generate prompts, and render them.

On this page