Mihe

The widget

The orb, your own trigger, or your own UI entirely.

The widget is a convenience, not a requirement. Pick one of three levels. The examples are React; without React, mountWidget gives you the same widget as a web component, with the same options. See the JavaScript SDK.

1. The orb

<AssistantWidget />

A circle in the corner of your app. Tapping it opens a small menu of chat and the voice modes you allow, or opens chat straight away when voice is off. Chat opens the conversation panel. Voice stays in the orb, so your page stays visible while the assistant navigates and changes it: a ring shows it listening, thinking or speaking, and a confirmation card pops out of the orb when an action needs one.

PropDefaultMeaning
form"orb""classic" for a launcher that opens the panel, with voice inside it.
draggablefalseLet the user drag the orb to either side. It remembers where.
corner"bottom-right"Or "bottom-left", where it sits until dragged.
voiceModesbothWhich of "hands-free" and "hold" the menu offers, when voice is on.
showLaunchertrueRender the orb, or the classic launcher. Turn it off when you have your own trigger.
defaultOpenfalseOpen the panel on first mount.
launcherIconyour uploaded icon, or the Mihe markAny element, drawn inside the circle.
titlethe assistant's name from the dashboardThe panel's header.
placeholder—The input's placeholder.
style—Merged over the panel's own styles.

form, draggable, corner and the voice modes are also settings, under Widget and Voice in the app's settings in the dashboard. They reach the widget when it connects, so changing one needs no deploy. A prop in your code overrides the setting, except voiceModes, which can only narrow it: a mode switched off in the dashboard is refused by the runtime, so the widget never offers it.

The widget is styled inline, so it neither leaks into your page's CSS nor picks it up.

2. Your trigger, our panel

Anything can open the assistant. useAssistantTrigger() returns the props that make an element a proper button, including role, tabIndex and the keyboard handler, since a div with only an onClick is invisible to keyboards and screen readers:

function AskButton() {
  const trigger = useAssistantTrigger();
  if (!trigger) return null; // no assistant configured here
  return <div {...trigger}>Ask the assistant</div>;
}

<AskButton />
<AssistantWidget showLauncher={false} />

For a keyboard shortcut or a command palette, use the controls directly:

const { open, close, toggle, isOpen } = useAssistant();

For suggestion chips and help links, useAssistantPrompt sends a message and opens the panel in one call:

const ask = useAssistantPrompt("How do I add a product?");
return ask && <button onClick={ask}>How do I add a product?</button>;

Both hooks return null when there is no assistant, so shared navigation can render nothing rather than a control that does not work.

3. Your panel too

AssistantPanel is the conversation with no launcher and no fixed position. Put it in a sidebar, a drawer or a tab:

<aside style={{ width: 360 }}>
  <AssistantPanel style={{ height: "100%" }} />
</aside>

Or build your own entirely. Everything the panel renders is on useAssistant(): messages, proposals, busy, connection, lastError, send and respondToProposal.

Render confirmation cards from proposals

If you build your own UI, render each card from the proposals payload, never from the assistant's text. Its label, description and input come from your registry, which is what stops text smuggled into your data from changing what a user thinks they are approving. High-risk actions need that tap, and a typed "yes" is refused for them.

The launcher icon

The launcher shows the Mihe mark until you upload your own under Widget icon in the app's settings. The upload reaches the widget when it connects, so changing it needs no deploy. launcherIcon in code overrides both.

If your app sends a Content-Security-Policy, an uploaded icon needs the storage domain in img-src (*.digitaloceanspaces.com). Without it the image is blocked, and the launcher falls back to the Mihe mark. MiheMark is exported for a trigger of your own that should match.

On this page