Mount and configure the hosted HeroUI Agent in React or Next.js.
HeroUIAgent renders the launcher and chat in an independent React root on document.body. Render it once near your application root; it does not wrap your component tree.
import {HeroUIAgent} from "@heroui/agent/next";
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.NEXT_PUBLIC_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, model picker, disclaimer, and placeholder. |
context | AgentSharedContext | — | Browser-only shared context passed to client tools. The reserved page function supplies turn context. |
markdown | AgentMarkdownOptions | — | Streaming animation, caret, and fenced-code renderers. |
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. |
startNewConversationOnOpen | boolean | false | Open on the start screen instead of restoring the most recently active conversation. |
startScreen | AgentStartScreenOptions | — | Greeting, suggested prompts, and prompt shortcuts. |
tools | ClientTool[] | [] | Browser client tools the hosted Agent may call. |
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.NEXT_PUBLIC_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.NEXT_PUBLIC_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.NEXT_PUBLIC_HEROUI_AGENT_ID!}
getAuthToken={getAuthToken}
/>;Set reopenOnRefresh to preserve whether the panel is open for the current browser tab. When the
visitor refreshes the page, an open panel returns to the start screen as a new chat; it does not
resume the conversation that was active before the refresh.
<HeroUIAgent
reopenOnRefresh
agentId={process.env.NEXT_PUBLIC_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.NEXT_PUBLIC_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 never closes on outside interact; a predicate receives the outside element and returns
whether to close (React Aria Popover semantics). Escape, the header close button, and other dismiss
paths are unchanged.
<HeroUIAgent
appearance={{shouldCloseOnInteractOutside: false}}
getAuthToken={getAuthToken}
agentId={process.env.NEXT_PUBLIC_HEROUI_AGENT_ID!}
/>
<HeroUIAgent
appearance={{shouldCloseOnInteractOutside: () => false}}
getAuthToken={getAuthToken}
agentId={process.env.NEXT_PUBLIC_HEROUI_AGENT_ID!}
/>
<HeroUIAgent
appearance={{
shouldCloseOnInteractOutside: (element) => !element.closest("[data-keep-agent-open]"),
}}
getAuthToken={getAuthToken}
agentId={process.env.NEXT_PUBLIC_HEROUI_AGENT_ID!}
/>See Configuration — Appearance for every
appearance option.
<HeroUIAgent
reopenOnRefresh
getAuthToken={getAuthToken}
agentId={process.env.NEXT_PUBLIC_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"],
defaultModel: "google/gemini-3.7-flash",
dictation: true,
disclaimer: "Agent may make mistakes. Verify all outputs.",
modelPicker: true,
placeholder: "Ask about this workspace…",
}}
permissions={{defaultMode: "auto", showPicker: true}}
preload={true}
startScreen={{
greeting: "Ask about your data",
promptShortcuts: true,
prompts: ["Show this month's revenue", "Find accounts at risk"],
}}
/>See Configuration for the nested option types and defaults.