> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Prompt Bar - Web

> Prompt bars let users compose and submit prompts to an agent.

> [!NOTE]
>
> For development, import `PromptBar` from
> `@servicetitan/anvil2-ai-kit`. For design, find it in the
> [AI Kit Web Component Library file](https://www.figma.com/design/QEIsArLktLeJAaFvcpw3PO/AI-Kit-Web-Component-Library).

ai-kit-prompt-bar

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar?file=src/App.tsx)

```tsx lines theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");

  return (
    <PromptBar value={value} onChange={setValue} placeholder="Ask anything…" />
  );
}

export default App;
```

## Options

PromptBar is controlled — the parent owns `value`, `attachments`, and the `transcription` feed — and derives its mode from them. It supports an attachment row, a voice-transcription entry point, and a customizable disclaimer line.

### With Text

ai-kit-prompt-bar-with-text

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-with-text?file=src/App.tsx)

```tsx lines theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState(
    "What's the weather like in San Francisco today?",
  );

  return <PromptBar value={value} onChange={setValue} onSend={() => {}} />;
}

export default App;
```

Once there's text, the editor rises to its own row above the controls, and the primary action switches from disabled to send.

### Attachments

ai-kit-prompt-bar-attachments

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-attachments?file=src/App.tsx)

```tsx lines theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");
  const [attachments, setAttachments] = useState([
    { id: "1", name: "invoice.pdf" },
    { id: "2", name: "site-photo.jpg" },
  ]);

  return (
    <PromptBar
      value={value}
      onChange={setValue}
      attachments={attachments}
      onAddAttachment={() => {}}
      onRemoveAttachment={(id) =>
        setAttachments((current) =>
          current.filter((attachment) => attachment.id !== id),
        )
      }
    />
  );
}

export default App;
```

Attachments render as thumbnail tiles in a row above the editor; adding one expands the row and reflows the bar. A tile without a `thumbnailUrl` falls back to showing the file name.

### Transcribing

ai-kit-prompt-bar-transcribing

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-transcribing?file=src/App.tsx)

```tsx lines theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");
  const [draftValue, setDraftValue] = useState(
    "What's the weather like in San Francisco today?",
  );

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
      {/* Active transcription with a live waveform */}
      <PromptBar
        value={value}
        onChange={setValue}
        transcription={{
          active: true,
          source: { type: "preset", name: "speaking" },
        }}
        onCancelTranscription={() => {}}
        onConfirmTranscription={() => {}}
      />

      {/* Text in the prompt — no transcription active */}
      <PromptBar
        value={draftValue}
        onChange={setDraftValue}
        onSend={() => {}}
      />

      {/* Transcription error */}
      <PromptBar
        value={value}
        onChange={setValue}
        transcription={{ active: true, error: "Unable to access microphone" }}
        onCancelTranscription={() => {}}
        onConfirmTranscription={() => {}}
      />
    </div>
  );
}

export default App;
```

`transcription.active` switches the bar into a distinct mode: the attach and mic controls are replaced by a Cancel button, an animated waveform, and a Done primary action. Prefer wiring real audio into `transcription.source` (a `MediaStream`, an `AnalyserNode`, or a computed amplitude); fall back to a named preset (`ambient` / `speaking` / `thinking`) only when none is available. Setting `transcription.error` instead swaps in an error layout — an error icon, the message, and Done dismisses the error and exits.

### Disclaimer

ai-kit-prompt-bar-disclaimer

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-disclaimer?file=src/App.tsx)

```tsx lines theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");

  return (
    <PromptBar
      value={value}
      onChange={setValue}
      disclaimer={{ linkHref: "https://example.com" }}
    />
  );
}

export default App;
```

A disclaimer line sits below the bar, with a "Learn more" link out to more information.

### Disabled

ai-kit-prompt-bar-disabled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-disabled?file=src/App.tsx)

```tsx lines theme={null}
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  return (
    <PromptBar
      value="What's the weather like in San Francisco today?"
      onChange={() => {}}
      disabled
    />
  );
}

export default App;
```

`disabled` dims the whole bar and blocks the editor, attachments, and every action.

## Behavior

The editor grows with its content, and the bar reflows as attachments and text come and go.

### Text Wrapping

ai-kit-prompt-bar-draft-reflow

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-draft-reflow?file=src/App.tsx)

```tsx lines theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState(
    "Can you help me draft a detailed message to the customer explaining the delay in their service appointment, the reason for the delay, and what we're doing to make it right? Please keep the tone apologetic but professional.",
  );

  return <PromptBar value={value} onChange={setValue} onSend={() => {}} />;
}

export default App;
```

Once there's text, the editor lifts to its own row above the controls and wraps across multiple lines, growing up to 8 rows before it scrolls internally — it never truncates or clips.

## Usage Guidelines

### When to Use

Use the Prompt Bar when:

* **Composing a message to the agent** – The user types a free-form prompt and sends it. Place the Prompt Bar as the composer pinned below the conversation in the [Chat Layout](https://anvil.servicetitan.com/docs/components/chat-layout/web.md).
* **Attaching files to a prompt** – Attachments show as thumbnail tiles above the editor.
* **Dictating a prompt** – Transcription mode replaces the editor with a waveform, Cancel, and Done.
* **Stopping a response in progress** – While a response is in flight, the bar withholds sending. Provide a stop action to switch the primary action to Stop.

### When not to use

Avoid using the Prompt Bar for:

* **Asking a clarifying question with a set of options** – Use the [Agent Prompt](https://anvil.servicetitan.com/docs/components/agent-prompt/web.md) instead, which replaces the Prompt Bar until the user answers.
* **Offering follow-up prompts after an agent response** – Use the [Suggestion](https://anvil.servicetitan.com/docs/components/suggestion/web.md) instead.

## Keyboard Interaction

Users can compose, send, and control transcription using standard keyboard controls.

| Key | Description |
| - | - |
| Enter | Sends the draft (draft mode) or confirms the transcript (transcribing mode) |
| Shift / Ctrl / Cmd + Enter | Inserts a newline in the editor instead of sending |
| Escape | Cancels transcription and returns to the editor |
| Tab | Moves focus among the attach, editor, mic, and primary controls |

## Code

**Implementation**

ai-kit-prompt-bar

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar?file=src/App.tsx)

```tsx lines expandable theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");

  return (
    <PromptBar value={value} onChange={setValue} placeholder="Ask anything…" />
  );
}

export default App;
```

## Common Examples

To use the `PromptBar`, pass controlled `value` and `onChange`. Mode is derived from those inputs: idle when the draft is empty, draft when text or attachments are present, and transcribing when `transcription.active` is true.

```tsx theme={null}
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function ExampleComponent() {
  const [value, setValue] = useState("");

  return (
    <PromptBar
      value={value}
      onChange={setValue}
      onSend={() => send(value)}
    />
  );
}
```

### Draft and send

Enter sends when sending is allowed. Sending is allowed when the draft has trimmed text or attachments, unless you override `canSend`. While `isSending` is true, sending is withheld. Wire `onStop` to surface Stop instead of Send.

ai-kit-prompt-bar-with-text

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-with-text?file=src/App.tsx)

```tsx lines expandable theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState(
    "What's the weather like in San Francisco today?",
  );

  return <PromptBar value={value} onChange={setValue} onSend={() => {}} />;
}

export default App;
```

ai-kit-prompt-bar-draft-reflow

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-draft-reflow?file=src/App.tsx)

```tsx lines expandable theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState(
    "Can you help me draft a detailed message to the customer explaining the delay in their service appointment, the reason for the delay, and what we're doing to make it right? Please keep the tone apologetic but professional.",
  );

  return <PromptBar value={value} onChange={setValue} onSend={() => {}} />;
}

export default App;
```

### Attachments

Pass `attachments` and wire `onAddAttachment` and `onRemoveAttachment`. Each attachment needs a stable `id` and `name`. `thumbnailUrl` fills the tile; without it, the tile shows the name.

ai-kit-prompt-bar-attachments

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-attachments?file=src/App.tsx)

```tsx lines expandable theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");
  const [attachments, setAttachments] = useState([
    { id: "1", name: "invoice.pdf" },
    { id: "2", name: "site-photo.jpg" },
  ]);

  return (
    <PromptBar
      value={value}
      onChange={setValue}
      attachments={attachments}
      onAddAttachment={() => {}}
      onRemoveAttachment={(id) =>
        setAttachments((current) =>
          current.filter((attachment) => attachment.id !== id),
        )
      }
    />
  );
}

export default App;
```

### Transcription

Wire `onStartTranscription` for the mic control. Set `transcription.active` to enter transcribing mode. Prefer a real `MediaStream` for the waveform:

```tsx theme={null}
<PromptBar
  value={value}
  onChange={setValue}
  onStartTranscription={startMic}
  transcription={{
    active: true,
    source: { type: "stream", stream },
  }}
  onCancelTranscription={cancel}
  onConfirmTranscription={confirm}
/>
```

Set `transcription.error` to replace the waveform with an error message. Confirm then dismisses the error and exits transcription.

ai-kit-prompt-bar-transcribing

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-transcribing?file=src/App.tsx)

```tsx lines expandable theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");
  const [draftValue, setDraftValue] = useState(
    "What's the weather like in San Francisco today?",
  );

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
      {/* Active transcription with a live waveform */}
      <PromptBar
        value={value}
        onChange={setValue}
        transcription={{
          active: true,
          source: { type: "preset", name: "speaking" },
        }}
        onCancelTranscription={() => {}}
        onConfirmTranscription={() => {}}
      />

      {/* Text in the prompt — no transcription active */}
      <PromptBar
        value={draftValue}
        onChange={setDraftValue}
        onSend={() => {}}
      />

      {/* Transcription error */}
      <PromptBar
        value={value}
        onChange={setValue}
        transcription={{ active: true, error: "Unable to access microphone" }}
        onCancelTranscription={() => {}}
        onConfirmTranscription={() => {}}
      />
    </div>
  );
}

export default App;
```

### Disclaimer

Omit `disclaimer` for the default Atlas notice. Pass `{ message, linkHref }` to customize it, wrapping the linked span in `<link>…</link>`. Pass `null` to hide the line.

ai-kit-prompt-bar-disclaimer

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-disclaimer?file=src/App.tsx)

```tsx lines expandable theme={null}
import { useState } from "react";
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  const [value, setValue] = useState("");

  return (
    <PromptBar
      value={value}
      onChange={setValue}
      disclaimer={{ linkHref: "https://example.com" }}
    />
  );
}

export default App;
```

### Disabled

`disabled` disables the whole bar.

ai-kit-prompt-bar-disabled

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-kit-prompt-bar-disabled?file=src/App.tsx)

```tsx lines expandable theme={null}
import { PromptBar } from "@servicetitan/anvil2-ai-kit";

function App() {
  return (
    <PromptBar
      value="What's the weather like in San Francisco today?"
      onChange={() => {}}
      disabled
    />
  );
}

export default App;
```

## Internationalization

`PromptBar` owns these localized strings:

* `ai-kit.promptBar.placeholder`
* `ai-kit.promptBar.add`
* `ai-kit.promptBar.removeAttachment`
* `ai-kit.promptBar.speak`
* `ai-kit.promptBar.cancel`
* `ai-kit.promptBar.send`
* `ai-kit.promptBar.stop`
* `ai-kit.promptBar.done`
* `ai-kit.promptBar.dismiss`
* `ai-kit.promptBar.disclaimer`
* `ai-kit.promptBar.opensInNewTab`

Override `placeholder` or `disclaimer.message` on the component, or mount `AiKitIntlProvider` inside `CartoTheme`. `removeAttachment` receives `{name}`. Disclaimer messages parse as ICU, so escape `{`, `}`, and `'` if they are literal.

## React Accessibility

* Controls expose localized accessible names for add, remove, speak, send, stop, cancel, done, and dismiss.
* Attachment remove labels include the attachment name.
* Disclaimer links that open in a new tab include a localized announcement.

**PromptBar Props**

```tsx theme={null}
<PromptBar
  value={value}
  onChange={setValue}
  onSend={send}
  attachments={attachments}
  onAddAttachment={add}
  onRemoveAttachment={remove}
/>
```

## `PromptBar` Props

The `PromptBar` accepts the following props.

**onChange** `(value: string) =\> void` *required*

Fired on every edit with the next draft value.

**value** `string` *required*

Current draft text. Controlled.

**attachments** `PromptAttachment[]`

Attachments shown in the expanding attachment row.

**canSend** `boolean`

Override for whether sending is allowed. Defaults to whether the draft
has trimmed text or attachments.

**className** `string`

Class merged onto the root element.

**disabled** `boolean`

Disables the whole bar.

**disclaimer** `PromptBarDisclaimer | null`

Disclaimer line. Omit for the default notice; pass `null` to hide it.

**isSending** `boolean`

Whether a response is in flight. Withholds send and can surface Stop.

**onAddAttachment** `() =\> void`

Fired when the add-attachment control is pressed.

**onCancelTranscription** `() =\> void`

Fired when transcription cancel is pressed.

**onConfirmTranscription** `() =\> void`

Fired when transcription confirm is pressed.

**onRemoveAttachment** `(id: string) =\> void`

Fired when an attachment's remove control is pressed.

**onSend** `() =\> void`

Fired when the send action fires from the button or Enter.

**onStartTranscription** `() =\> void`

Fired when the transcribe control is pressed.

**onStop** `() =\> void`

Fired when Stop is pressed. Surfaces only while `isSending` is true.

**placeholder** `string`

Placeholder for the empty editor. Defaults to the localized "Ask
anything…".

**transcription** `PromptBarTranscription`

Live transcription feed. `active` selects transcribing mode.

**PromptAttachment**

```tsx theme={null}
const attachment: PromptAttachment = {
  id: "roof-photo",
  name: "roof.jpg",
  thumbnailUrl: "/previews/roof.jpg",
};
```

## `PromptAttachment`

**id** `string` *required*

Stable identity used for removal and React keys.

**name** `string` *required*

Display name used for alt text, the remove label, and the fallback tile.

**thumbnailUrl** `string`

Optional preview image. Omit to show the name in the tile.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.