hashbrown

Getting Started

Guide

  1. 1. Basics of AI
  2. 2. System Instructions
  3. 3. Message History
  4. 4. Skillet Schema
  5. 5. Streaming
  6. 6. Tool Calling
  7. 7. Structured Output
  8. 8. Generative UI
  9. 9. JavaScript Runtime

Recipes

  1. Natural Language Forms
  2. UI Chatbot with Tools
  3. UI Kits
  4. Predictive Suggestions
  5. Remote MCP
  6. Threads
  7. Magic Text
  8. JSON Parser
  9. CopilotKit
  10. Local Models

Platforms

Magic Text in React

Magic Text is Hashbrown's optimistic Markdown parser and renderer for streaming LLM output. It renders partial Markdown while text is still arriving, then converges to the final structure when the stream completes.

Use it when you want:

  • Stable streaming Markdown without handing arbitrary HTML to the browser.
  • Inline citations that can be rendered as trusted UI.
  • Per-node renderer overrides for your design system.
  • Segment-level rendering for animation.
  • A renderer you can expose to generative UI with exposeMarkdown().

1. Render Streaming Markdown

MagicTextRenderer renders Markdown from a string. Set isComplete when the model is done so open Markdown constructs can finalize.

import { MagicTextRenderer } from '@hashbrownai/react';

export function AssistantMarkdown({
  text,
  isComplete,
}: {
  text: string;
  isComplete: boolean;
}) {
  return (
    <MagicTextRenderer
      isComplete={isComplete}
      caret
      options={{ segmenter: { granularity: 'word' } }}
    >
      {text}
    </MagicTextRenderer>
  );
}

caret shows a streaming cursor while the deepest open node is still incomplete. options.segmenter controls how text nodes are split for rendering and animation.


Magic Text supports normal Markdown links and citation references:

  • Inline reference: [^source-id]
  • Definition: [^source-id]: Source title https://example.com
import { MagicTextRenderer } from '@hashbrownai/react';

export function AssistantMarkdown({ text }: { text: string }) {
  return (
    <MagicTextRenderer
      isComplete
      onLinkClick={(event, url) => {
        if (url.startsWith('http')) {
          return;
        }

        event.preventDefault();
      }}
      onCitationClick={(_event, citation) => {
        console.log('citation selected', citation);
      }}
    >
      {text}
    </MagicTextRenderer>
  );
}

Give the model a prompt pattern like this when you want citations:

Write in Markdown.

When you make a factual claim that needs a source, add an inline citation like [^id].
At the end, add a definition line for each citation:
[^id]: Short source title https://full-url

Do not invent URLs. Omit citations if you are unsure.

3. Customize Node Rendering

Use nodeRenderers when Markdown should match your product UI. The node renderer is a fallback for every node type that does not have a specific override.

import {
  MagicTextRenderer,
  createMagicTextNodeRenderers,
} from '@hashbrownai/react';

const nodeRenderers = createMagicTextNodeRenderers({
  heading: ({ node, children }) => {
    const Heading = `h${node.level}` as 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';

    return <Heading className="assistant-heading">{children}</Heading>;
  },
  citation: ({ citation }) => (
    <sup className="assistant-citation">
      {citation?.number ? `[${citation.number}]` : '[?]'}
    </sup>
  ),
  node: ({ defaultNode }) => defaultNode,
});

export function AssistantMarkdown({ text }: { text: string }) {
  return (
    <MagicTextRenderer isComplete nodeRenderers={nodeRenderers}>
      {text}
    </MagicTextRenderer>
  );
}

4. Expose Markdown to Generative UI

Use exposeMarkdown() when the model should generate Markdown inside a trusted component instead of generating arbitrary HTML.

import { exposeMarkdown, useUiKit } from '@hashbrownai/react';

export function useAssistantUiKit() {
  return useUiKit({
    components: [
      exposeMarkdown({
        name: 'Markdown',
        description: 'Render Markdown content for the user.',
        citations: true,
        options: { segmenter: { granularity: 'word' } },
        caret: true,
        className: 'assistant-markdown',
      }),
    ],
  });
}

Pass the UI kit to useUiChat() or useUiCompletion():

import { useUiChat } from '@hashbrownai/react';
import { useAssistantUiKit } from './ui-kit';

export function Assistant() {
  const uiKit = useAssistantUiKit();
  const { messages, sendMessage } = useUiChat({
    model: 'gpt-5',
    components: [uiKit],
  });

  return (
    <ChatView
      messages={messages}
      onSubmit={(message) => sendMessage(message)}
    />
  );
}

5. Use the Parser Directly

If you need the Magic Text AST instead of React elements, use the parser helpers from @hashbrownai/core.

import {
  createMagicTextParserState,
  finalizeMagicText,
  parseMagicTextChunk,
} from '@hashbrownai/core';

let state = createMagicTextParserState({
  segmenter: { granularity: 'word' },
});

state = parseMagicTextChunk(state, 'Hello **wor');
state = parseMagicTextChunk(
  state,
  'ld** [^docs]\n\n[^docs]: Docs https://hashbrown.dev',
);
state = finalizeMagicText(state);

The parser state includes the Markdown AST, citation metadata, warnings, and completion state.

Magic Text in React 1. Render Streaming Markdown 2. Handle Links and Citations 3. Customize Node Rendering 4. Expose Markdown to Generative UI 5. Use the Parser Directly