React Runtime and Hooks
Detailed reference for the React runtime, adapters, provider, hooks, and rendering helpers.
This is where ToonUI stops being only a protocol and becomes a React integration.
createToonReactRuntime()
Creates the React runtime.
const runtime = createToonReactRuntime({ adapter });The returned runtime includes:
- protocol APIs from
@toon-ui/core - adapter metadata
- layout configuration
- rendering utilities
createToonReactAdapter()
Creates an adapter contract around your component registry.
const adapter = createToonReactAdapter({
level: 'default',
components: {
button: MyButton,
},
});Adapter levels
minimal
Only the components you provide are registered.
Use it when:
- you are building from scratch
- you do not want ToonUI defaults
default
Merges the built-in preset with your overrides.
Use it when:
- you want the fastest customization path
- you only need to replace a few slots
strict
Requires full adapter coverage.
If any adapter key is missing, assertToonReactAdapter() throws.
Use it when:
- your design system must own the entire rendering surface
- incomplete registration should fail fast
Adapter metadata
Every adapter exposes meta:
levelincludesDefaultsisCompleteprovidedKeysmissingKeys
This is valuable in code review because it makes integration completeness inspectable.
getToonAdapterCoverage()
Computes coverage metadata for a registry.
Use it when you want to evaluate:
- what was registered
- what is still missing
- whether strict coverage is satisfied
mergeToonComponentRegistry()
Merges multiple partial registries into one.
Useful for:
- preset + design-system overrides
- composition across internal UI modules
ToonProvider
Provides runtime and rendering context.
It owns:
- form state
- block resolution state
- interaction dispatch
<ToonProvider runtime={runtime} onReply={...} onSubmit={...}>
{children}
</ToonProvider>interactive
ToonProvider accepts interactive?: boolean.
When disabled:
- blocks are treated as already resolved
- interactions no longer behave as active input surfaces
That can be useful for:
- replay views
- read-only history rendering
Hooks
useToonUI()
Returns the current ToonReactRuntime.
Use it when a child component needs direct runtime access.
useToonReply()
Returns the reply dispatcher from render context.
useToonSubmit()
Returns the submit dispatcher from render context.
useToonAction()
Returns:
eventsmessages
This hook mirrors the protocol mental model and is useful when your component wants the interaction APIs without manually threading them through props.
Rendering helpers
getToonFieldId()
Builds a deterministic field id from field name and source line.
Useful for:
- label/input association
- stable form wiring
getToonButtonProps()
Returns normalized button props:
typedisabledonClick
It centralizes the logic for:
- reply actions
- submit actions
- disabled handling
getToonInputProps()
Returns normalized props for input-like controls.
It handles:
idnametyperequireddisabledplaceholdervalueonChange
It also normalizes numeric input behavior.
getToonTextareaProps()
Equivalent helper for textarea wiring.
getToonCheckboxProps()
Equivalent helper for checkbox wiring.
extractToonMarkdown()
Strips toon-ui fenced blocks from mixed markdown content.
Use it when you want to render narrative prose separately from the structured UI.
ToonRenderer
Renders parsed ToonUI structure.
Use it when:
- you already separated the content
- you want direct control over renderer placement
ToonMessage
High-level component that renders assistant content containing normal markdown plus ToonUI blocks.
Use it when you want the best default integration path.
Quick path
import { ToonMessage, createToonClient } from '@toon-ui/toon-ui';
const toon = createToonClient();
<ToonMessage
content={assistantContent}
runtime={toon}
onReply={(payload) => append(toon.messages.toUIMessage(payload))}
onSubmit={(payload) => append(toon.messages.toUIMessage(payload))}
/>This component does two jobs for you:
- renders normal markdown from the assistant
- renders fenced
toon-uiblocks in the same message
renderMarkdown
ToonMessage also accepts renderMarkdown?: (markdown: string) => ReactNode.
Use it when:
- you want a custom markdown renderer
- you want your own typography or prose styles
- you want to plug ToonUI into another chat shell
<ToonMessage
content={assistantContent}
runtime={toon}
renderMarkdown={(markdown) => <MyMarkdownRenderer>{markdown}</MyMarkdownRenderer>}
onReply={(payload) => append(toon.messages.toUIMessage(payload))}
onSubmit={(payload) => append(toon.messages.toUIMessage(payload))}
/>Practical rule:
- keep
ToonMessageif you want mixed markdown + ToonUI in one place - switch only
renderMarkdownwhen the chat shell changes
Interaction reinjection
When a user clicks a ToonUI button or submits a ToonUI form, the payload is typed.
For UI/chat state:
onReply={(payload) => append(toon.messages.toUIMessage(payload))}
onSubmit={(payload) => append(toon.messages.toUIMessage(payload))}For model-loop integration:
const next = toon.messages.toModelMessage(payload);Use toUIMessage() when the next step is showing a user-visible message.
Use toModelMessage() when the next step is feeding the interaction back into your LLM loop.
basicPreset()
Returns the built-in default React component registry used by the default adapter path.
Use it when you want:
- reference behavior
- default fallback rendering
- a base layer for selective overrides
Layout options
createToonReactRuntime() accepts:
messageGapblockGapnodeGap
These control spacing without forcing you to rewrite the rendering logic.