Assistant launcher

A round button in the corner of a page that opens a small chat panel, for help or support widgets.

Loading preview…

Install

  1. 1

    Run this command in your Tailwind CSS project. It adds components/ai/assistant-launcher.tsx and installs its dependencies.

    $ npx shadcn@latest add https://gradientdeck.com/ai-components/r/assistant-launcher.json

Usage & API

Example

assistant-launcher.tsx
import { AssistantLauncher } from "@/components/ai/assistant-launcher"

<AssistantLauncher
  open={true}
  onOpenChange={() => {}}
  children={children}
/>

Props AssistantLauncher

PropType & descriptionRequired
openbooleanRequired
onOpenChange(open: boolean) => voidRequired
hintstring | nullSmall note shown beside the closed button.Optional
titlestringOptional
childrenReactNodeRequired
containedbooleanPin inside the nearest positioned parent instead of the viewport.Optional

Dependencies

  • React
  • Tailwind CSS
  • motion
  • lucide-react
Implementation notes

Corner button exports AssistantLauncher. open is controlled. The hint button and the round button both call onOpenChange(true) when the panel is closed. The round button toggles. The header Close button and Escape call onOpenChange(false). title is both the header text and the dialog's accessible name, and it defaults to Assistant. The preview overrides it to Help. hint is a string or null. The preview starts with null, then sets Questions? Ask here, and clears it once the panel has been opened. children fill the space under the header. In the preview that is a scrolling list plus a form whose field is labelled Message and whose button is labelled Send. The send handler is demo code: it appends the user's line and, after 1400ms, a reply. The launcher does not contain that timer.

Choosing a layout

Use it for help or support on a page that already has a job, where a full thread would replace the page. The preview sets title to Help, passes the hint Questions? Ask here after a short delay, and puts a small transcript in children: a greeting about orders, returns, and sizing, then a reply that returns are free within 30 days. That transcript is not inside AssistantLauncher. You can put any node in children. The launcher will still move focus to the first field it finds.

Do not use it as the main chat. A product whose whole screen is the conversation wants Conversation, Empty state, Thread header, and Thread list, not a 320px dialog. Do not use it for a menu. It is a dialog with a message area, and the button swaps between a message icon and an X. If you need the panel to push the page instead of floating, this is the wrong component. contained only changes fixed to absolute. It does not dock. Thread list's rail is the pattern that pushes, and it is a list of chats, not a widget.

Focus moves somewhere unexpected when the panel opens. What is it looking for?

The first input, textarea, or button in the panel, via querySelector, in that order in the selector list. A Close button in the header is a button, so if your children have no field, focus lands on Close. Put the composer in children and the field will win, because it is an input and the selector asks for input first. On close, focus returns to the round button only if the panel had been open. The hint button is outside the dialog. It is not the focus target.

Why is the panel clipped or the page underneath scrolling?

Without contained, the root is position fixed, bottom 4, right 4, and the panel height uses the viewport. A transformed or filtered ancestor can trap fixed elements. Set contained so the root is absolute and the height is 292px, and make sure the parent is positioned and tall enough. The preview does this inside a 380px frame with overflow hidden. The launcher does not lock body scroll. If the page scrolls under the dialog, that is the page, not a prop you missed. There is no modal prop.