Mount and configure the hosted HeroUI Agent from a React application.
HeroUIAgent creates a secure, cross-origin iframe on document.body. The hosted iframe renders the
launcher, conversation, and generated UI; the React component only manages the customer-page
bridge. Render it once near your application root. It does not wrap your component tree.
For websites without React, use agent.heroui.pro/loader.js and
window.HeroUIAgent.mount(). See the vanilla quickstart.
import {HeroUIAgent} from "@heroui/agent";
const getAuthToken = async (context) => {
const response = await fetch("/api/heroui-agent/auth-token", {
body: JSON.stringify(context),
headers: {"Content-Type": "application/json"},
method: "POST",
});
if (!response.ok) throw new Error("Agent authentication failed");
return response.json();
};
export function AppAgent() {
return <HeroUIAgent getAuthToken={getAuthToken} agentId={process.env.HEROUI_AGENT_ID!} />;
}Every visual option below has a counterpart in the dashboard appearance editor, and the embed applies what you saved there. Props override it field by field, so you only write the ones you want pinned in code. See Resolution order.
| Prop | Type | Default | Description |
|---|---|---|---|
getAuthToken | (context: GetAuthTokenContext) => Promise<AgentAuthToken> | Required | Calls your server endpoint for a short-lived browser token. |
agentId | string | Required | Agent identifier. |
appearance | AgentAppearance | — | View mode, launcher placement, and theme. |
capabilities | AgentCapabilities | — | Optional hosted capabilities such as web search. |
componentExports | AgentComponentExportFormat[] | false | ['csv', 'svg', 'png'] | Formats available from generated charts and tables. Pass false to hide export controls. |
composer | AgentComposerOptions | — | Attachments, dictation, disclaimer, and placeholder. |
context | AgentSharedContext | — | Browser-only shared context passed to client tools. The reserved page function supplies turn context. |
markdown | AgentMarkdownOptions | — | Streaming animation and caret preferences. |
thinking | AgentThinkingOptions | {presentation: 'trail'} | In-progress reasoning and tool activity presentation. |
onFeedback | (feedback: AgentResponseFeedback) => void | Promise<void> | — | Receives end-user ratings and optional negative-feedback details. |
onReady | () => void | — | Fires when project configuration and the complete composer are ready for the current conversation. |
permissions | AgentPermissionOptions | — | Default client-tool permission mode and optional end-user picker. |
preload | boolean | true | Warm authentication and the chat runtime while closed. Appearance still resolves on mount when false. |
reopenOnRefresh | boolean | false | Reopen after a refresh at the start screen instead of resuming the previous conversation. |
remoteConfig | boolean | true | Apply the appearance saved for this project in the dashboard. Props still win. Pass false to opt out. |
responseActions | AgentMessageAction[] | false | ['copy', 'feedback', 'retry'] | Actions shown below assistant responses. |
showLauncher | boolean | true | Show the floating launcher after its presentation configuration resolves. |
showBetaBadge | boolean | false | Show a compact Beta chip in the open panel header, next to the assistant title. |
showSources | boolean | true | Show the website and document references used by assistant responses. |
startNewConversationOnOpen | boolean | false | Return to the start screen whenever the panel is reopened within the same page. |
startScreen | AgentStartScreenOptions | — | Greeting, subtitle, suggested prompts, and prompt shortcuts. |
tools | ClientTool[] | [] | Browser client tools the hosted Agent may call. |
Use thinking.presentation to choose how work in progress appears. The default "trail" retains
the complete activity trail. "summary" keeps a two-line overview, "status" shows the current
activity in one line, and "hidden" shows only shimmering “Thinking” text. The compact modes clear
when work finishes; approvals remain available in every presentation.
<HeroUIAgent
thinking={{presentation: "summary"}}
getAuthToken={getAuthToken}
agentId={process.env.HEROUI_AGENT_ID!}
/>The Agent resolves its remote presentation configuration as soon as it mounts. The built-in launcher and panel wait for that snapshot, so controls such as attachments, permissions, model selection, and the disclaimer do not appear later and shift the layout. If the configuration is unavailable, the embed falls back to its defaults after a short, bounded wait.
By default, authentication and the chat runtime warm up in parallel while the panel is closed. Calls
to show(), toggle(), or newConversation() made before presentation configuration resolves are
queued instead of painting a temporary panel. Calling hide() or toggling closed during that window
cancels the pending open.
This is on by default. You do not need to configure anything.
// Warms up on its own — nothing to add.
<HeroUIAgent getAuthToken={getAuthToken} agentId={process.env.HEROUI_AGENT_ID!} />Turn it off when most visitors never open the Agent, or when you would rather choose the moment yourself:
<HeroUIAgent getAuthToken={getAuthToken} preload={false} agentId={process.env.HEROUI_AGENT_ID!} />With preload={false}, the small remote presentation request still runs on mount so the first visible
surface is layout-stable. Authentication and the chat runtime remain deferred until someone opens the
panel or calls preload().
useAgent().preload() runs the same warm-up on demand, without opening the panel. Pair it with preload={false} to warm up at the moment you think someone is about to ask something:
"use client";
import {useAgent} from "@heroui/agent";
export function PricingTable() {
const agent = useAgent();
// Someone comparing plans is likely to have a question.
return <section onMouseEnter={agent.preload}>{/* … */}</section>;
}preload() is safe to call as often as you like — the work happens once.
Use onReady for a lifecycle event, or the reactive useAgent().ready status when a custom entry
point should wait for the complete authenticated runtime. Waiting is optional: controller actions are
safe to call earlier and queue while presentation configuration resolves.
function AgentEntryPoint() {
const agent = useAgent();
return (
<button disabled={!agent.ready} onClick={agent.show}>
Ask the Agent
</button>
);
}
<HeroUIAgent
showLauncher={false}
onReady={() => analytics.track("agent_ready")}
agentId={process.env.HEROUI_AGENT_ID!}
getAuthToken={getAuthToken}
/>;After a page refresh, opening the agent starts at the home screen. Previous conversations remain available from the conversation picker. Closing and reopening the panel within the same page keeps the current conversation.
Set reopenOnRefresh to also preserve whether the panel is open for the current browser tab. When the
visitor refreshes the page, an open panel automatically returns to the start screen as a new chat.
<HeroUIAgent reopenOnRefresh agentId={process.env.HEROUI_AGENT_ID!} getAuthToken={getAuthToken} />Set startNewConversationOnOpen when every launcher open should begin at the start screen. Existing
conversations remain available from the conversation picker.
<HeroUIAgent
startNewConversationOnOpen
agentId={process.env.HEROUI_AGENT_ID!}
getAuthToken={getAuthToken}
/>In appearance.viewMode: "floating", the desktop panel closes when the visitor clicks outside it
unless you opt out with appearance.shouldCloseOnInteractOutside. Omitted or true always closes;
false keeps it open. Escape, the header close button, and other dismiss paths are unchanged.
In appearance.viewMode: "chat-bar", the same option collapses the conversation back to its prompt.
The prompt remains visible. The full-screen action expands the conversation across the viewport;
Escape exits full screen first, then collapses the card.
<HeroUIAgent
appearance={{shouldCloseOnInteractOutside: false}}
getAuthToken={getAuthToken}
agentId={process.env.HEROUI_AGENT_ID!}
/>See Configuration — Appearance for every
appearance option.
<HeroUIAgent
reopenOnRefresh
getAuthToken={getAuthToken}
agentId={process.env.HEROUI_AGENT_ID!}
appearance={{
launcher: {offset: {x: 24, y: 24}, position: "bottom-left"},
theme: {
colorScheme: "system",
colors: {accent: "#7c3aed"},
radius: "round",
typography: {baseSize: 14, fontFamily: "Inter, sans-serif"},
},
viewMode: "sidebar",
}}
capabilities={{webSearch: true}}
composer={{
attachments: ["image/jpeg", "image/png", "application/pdf"],
dictation: true,
disclaimer: "Agent may make mistakes. Verify all outputs.",
placeholder: "Ask about this workspace…",
}}
permissions={{defaultMode: "auto", showPicker: true}}
showSources={true}
preload={true}
startScreen={{
greeting: "Ask about your data",
subtitle: "Live answers with charts, metrics, and tables.",
promptShortcuts: true,
prompts: ["Show this month's revenue", "Find accounts at risk"],
}}
/>See Configuration for the nested option types and defaults.
| Prop | Type | Default |
|---|---|---|
appearance.launcher.iconSize | number (pixels) | 56% of launcher |
appearance.panel.mobile.presentation | "fullscreen" | "sheet" | "fullscreen" |
appearance.panel.mobile.height | number | string | "75dvh" for sheets |
Sheets support floating and sidebar modes. Theme color props accept CSS RGB/RGBA values as well as hex. See Appearance for saved configuration, transparency, and precedence.