JDS.

Search docs

Find a page or component

Prompt Input

Composer for chat and agent apps. Enter to send, Shift+Enter for a new line, paste or drop files, stop while streaming.

Installation

pnpm dlx shadcn@latest add @jds/prompt-input

Usage

import {
  PromptInput,
  PromptInputSubmit,
  PromptInputTextarea,
  PromptInputToolbar,
  PromptInputTools,
  PromptInputAttachButton,
  PromptInputAttachments,
} from "@/components/ai/prompt-input"

const { status, sendMessage, stop } = useChat()

<PromptInput status={status} onSubmit={({ text, files }) => sendMessage({ text, files })}>
  <PromptInputAttachments />
  <PromptInputTextarea />
  <PromptInputToolbar>
    <PromptInputTools>
      <PromptInputAttachButton />
    </PromptInputTools>
    <PromptInputSubmit onStop={stop} />
  </PromptInputToolbar>
</PromptInput>

Examples

With context and options

Add context, let the agents manage the rest

Dictation

Dictation for the prompt input. Speech is transcribed into the textarea as you talk, so it can be reviewed before sending.

Installed separately, so the base component stays lean:

pnpm dlx shadcn@latest add @jds/prompt-input-mic
import { PromptInputMic } from "@/components/ai/prompt-input-mic"

<PromptInputToolbar>
  <PromptInputTools />
  <PromptInputMic />
  <PromptInputSubmit />
</PromptInputToolbar>

PromptInputMic

PropTypeDefault
lang

BCP 47 language for recognition, e.g. "it-IT".

stringnavigator.language

useSpeechRecognition

PropTypeDefault
returns

Web Speech API. Chrome, Edge and Safari. Elsewhere supported is false and the mic disables itself.

{ supported, listening, transcript, error, start, stop }–

Mentions

Type @ in the composer to mention files, tools or agents. Keyboard first; picks show as chips and are reported for sending.

Installed separately, so the base component stays lean:

pnpm dlx shadcn@latest add @jds/prompt-input-mentions
import { PromptInputMentions } from "@/components/ai/prompt-input-mentions"

<PromptInput onSubmit={({ text }) => send({ text, mentions })}>
  <PromptInputMentions items={items} onMentionsChange={setMentions} />
  <PromptInputTextarea />
  …
</PromptInput>

PromptInputMentions

PropTypeDefault
items

What can be mentioned. Filtered by the text after @.

{ id, label, type: "file" | "folder" | "tool" | "agent", description? }[]–
onMentionsChange

Current picks. Cleared after submit.

(mentions) => void–

Tools menu

Tools like web search or deep research folded into one toolbar menu, so they fit at any width. The trigger shows the active tool, or how many are on.

Installed separately, so the base component stays lean:

pnpm dlx shadcn@latest add @jds/prompt-input-option-menu
import { PromptInputOptionMenu } from "@/components/ai/prompt-input-option-menu"

<PromptInputTools>
  <PromptInputAttachButton />
  <PromptInputOptionMenu options={tools} value={enabled} onValueChange={setEnabled} />
</PromptInputTools>

PromptInputOptionMenu

PropTypeDefault
options

The tools to offer.

{ id, label, icon?, description? }[]–
value

Enabled ids.

string[]–
onValueChange

Send it with the message.

(ids: string[]) => void–
label

Trigger text when none are on, and the menu heading.

string"Tools"

Scope

Tell the agent which parts of the product it may work on. A context label and a multi-select of modules in the composer footer; nothing selected means everything.

Installed separately, so the base component stays lean:

pnpm dlx shadcn@latest add @jds/prompt-input-scope
import { PromptInputScope } from "@/components/ai/prompt-input-scope"

<PromptInputFrame>
  <PromptInput onSubmit={({ text }) => send({ text, scope })}>…</PromptInput>
  <PromptInputFooter>
    <PromptInputScope
      label="Workspace context"
      icon={<FolderIcon />}
      scopes={modules}
      allLabel="All modules"
      value={scope}
      onValueChange={setScope}
    />
  </PromptInputFooter>
</PromptInputFrame>

PromptInputScope

PropTypeDefault
scopes

What the agent can be limited to.

{ id, label, description? }[]–
value

Selected ids. Empty means all; picking every scope also resets to empty.

string[]–
onValueChange

Send it with the message.

(ids: string[]) => void–
label

Context line on the left, e.g. "Workspace context".

ReactNode–
icon

Icon before the label.

ReactNode–
allLabel

Shown when nothing is selected.

string"All"
menuLabel

Heading at the top of the menu.

string–

API Reference

PromptInput

PropTypeDefault
onSubmit

Called on the send key or the send button. Input clears after.

({ text, files }) => void–
status

Matches AI SDK useChat. Drives the submit button.

"ready" | "submitted" | "streaming" | "error""ready"
value

Controlled value.

string–
onValueChange

Change handler for controlled use.

(value: string) => void–
submitOn

Enter sends and Shift+Enter adds a line, or ⌘/Ctrl+Enter sends and Enter adds a line (better for long prompts and code).

"enter" | "mod-enter""enter"
accept

File types for the picker.

string–

PromptInputSubmit

PropTypeDefault
onStop

Called when pressed while streaming.

() => void–

PromptInputFrame

PropTypeDefault
children

An optional PromptInputHeader, the PromptInput, and an optional PromptInputFooter. Header and footer animate when added or removed.

ReactNode–

PromptInputHeader

PropTypeDefault
icon

Shown before the context text.

ReactNode–

PromptInputOption

PropTypeDefault
icon

Shown before the label.

ReactNode–
pressed

Controlled state. Or use defaultPressed.

boolean–
onPressedChange(pressed: boolean) => void–