gridland
Components

PromptInput

A chat input bar with slash commands, file mentions, and AI SDK integration

A chat input bar with slash command autocomplete, file mention suggestions, command history, and direct Vercel AI SDK integration. Supports both a self-contained default layout and a fully composable compound mode.

PromptInput
?

Run demo

Terminal
bunx @gridland/demo prompt-input
Terminal
curl -fsSL https://raw.githubusercontent.com/thoughtfulllc/gridland/main/scripts/run-demo.sh | bash -s prompt-input

Installation

bunx shadcn@latest add @gridland/prompt-input

Usage

import { PromptInput } from "@/components/ui/prompt-input"
import { useKeyboard } from "@gridland/utils"
<PromptInput
  placeholder="Message Claude..."
  useKeyboard={useKeyboard}
  onSubmit={(msg) => console.log("Sent:", msg.text)}
/>

Usage with AI SDKs

onSubmit receives { text: string }. Map it to your SDK of choice.

Vercel AI SDK

Direct pass-through — sendMessage accepts { text } natively.

Vercel AI SDK
import { useChat } from "@ai-sdk/react"

const { status, sendMessage, stop } = useChat({ /* transport config */ })

<PromptInput
  status={status}
  onSubmit={sendMessage}
  onStop={stop}
  useKeyboard={useKeyboard}
/>

Anthropic SDK

Anthropic SDK
const [status, setStatus] = useState<ChatStatus>("ready")

<PromptInput
  status={status}
  onSubmit={async (msg) => {
    setStatus("submitted")
    const stream = anthropic.messages.stream({
      model: "claude-sonnet-4-20250514",
      messages: [...history, { role: "user", content: msg.text }],
    })
    setStatus("streaming")
    for await (const event of stream) { /* handle deltas */ }
    setStatus("ready")
  }}
  onStop={() => { stream.abort(); setStatus("ready") }}
  useKeyboard={useKeyboard}
/>

OpenAI SDK

OpenAI SDK
<PromptInput
  status={status}
  onSubmit={async (msg) => {
    setStatus("submitted")
    const stream = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [...history, { role: "user", content: msg.text }],
      stream: true,
    })
    setStatus("streaming")
    for await (const chunk of stream) { /* handle deltas */ }
    setStatus("ready")
  }}
  useKeyboard={useKeyboard}
/>

Examples

Slash Commands

Provide a commands array for / autocomplete.

Slash commands
<PromptInput
  commands={[
    { cmd: "/help", desc: "Show commands" },
    { cmd: "/model", desc: "Switch model" },
    { cmd: "/clear", desc: "Clear conversation" },
  ]}
  useKeyboard={useKeyboard}
  onSubmit={(msg) => console.log(msg.text)}
/>

File Mentions

Provide a files array for @ mention autocomplete.

File mentions
<PromptInput
  files={["src/index.ts", "src/routes.ts", "package.json"]}
  useKeyboard={useKeyboard}
  onSubmit={(msg) => console.log(msg.text)}
/>

Custom Suggestion Provider

Override the built-in / and @ suggestions with your own logic.

Custom suggestions
<PromptInput
  getSuggestions={(value) => {
    if (value.startsWith("#")) {
      return [
        { text: "#bug", desc: "Bug report" },
        { text: "#feature", desc: "Feature request" },
      ].filter((s) => s.text.startsWith(value))
    }
    return []
  }}
  useKeyboard={useKeyboard}
  onSubmit={(msg) => console.log(msg.text)}
/>

Controlled

Use value and onChange to control the input externally.

Controlled
const [value, setValue] = useState("")

<PromptInput
  value={value}
  onChange={setValue}
  useKeyboard={useKeyboard}
  onSubmit={(msg) => console.log(msg.text)}
/>

Model Label

Display the active model name below the input.

With model
<PromptInput
  model="claude-sonnet-4-20250514"
  useKeyboard={useKeyboard}
  onSubmit={(msg) => console.log(msg.text)}
/>

Disabled (legacy)

When not using status, use disabled and disabledText directly.

Disabled
<PromptInput
  disabled
  disabledText="Generating..."
  useKeyboard={useKeyboard}
/>

Compound Components

For full control over layout, pass children to enter compound mode. Subcomponents read state from PromptInput via context — no prop drilling needed.

Compound usage
import { PromptInput } from "@/components/ui/prompt-input"

<PromptInput
  status={status}
  onSubmit={handleSubmit}
  onStop={stop}
  useKeyboard={useKeyboard}
>
  <PromptInput.Divider />
  <PromptInput.Suggestions />
  <PromptInput.Textarea />
  <PromptInput.Model />
  <PromptInput.StatusText />
  <PromptInput.Submit />
  <PromptInput.Divider />
</PromptInput>

Subcomponents

ComponentDescription
PromptInput.TextareaPrompt character + text with cursor
PromptInput.SuggestionsAutocomplete dropdown (slash commands, @mentions)
PromptInput.SubmitStatus indicator: ⏎ ready, ◐ submitted, ■ streaming, ✕ error
PromptInput.DividerHorizontal rule ()
PromptInput.StatusTextError text shown when status is "error"
PromptInput.ModelMuted model label shown below the input

Provider

Wrap your app in PromptInputProvider to lift input state outside of PromptInput. This lets you read or modify the input value and suggestions from sibling components.

Provider usage
import { PromptInputProvider, usePromptInputController } from "@/components/ui/prompt-input"

<PromptInputProvider initialInput="">
  <Sidebar />
  <PromptInput useKeyboard={useKeyboard} onSubmit={handleSubmit} />
</PromptInputProvider>

usePromptInputController

Access lifted state from anywhere inside the provider tree.

Controller hook
const controller = usePromptInputController()

controller.textInput.value       // current text
controller.textInput.setValue(v)  // set text
controller.textInput.clear()      // clear text

controller.suggestions.suggestions      // current suggestions
controller.suggestions.selectedIndex    // selected index
controller.suggestions.setSuggestions(s) // set suggestions
controller.suggestions.setSelectedIndex(i)
controller.suggestions.clear()

usePromptInput

Access rendering state from within any compound subcomponent.

Custom subcomponent
import { usePromptInput } from "@/components/ui/prompt-input"

function MyCustomStatus() {
  const { status, value, disabled } = usePromptInput()
  return <text>{status}: {value.length} chars</text>
}

Controls

  • Enter: Submit message (or accept suggestion)
  • Tab: Cycle through suggestions
  • ↑/↓: Navigate suggestions or command history
  • Escape: Dismiss suggestions, or stop generation when streaming
  • /: Trigger slash command suggestions
  • @: Trigger file mention suggestions

API Reference

PromptInput

PropTypeDefaultDescription
valuestring-Controlled input value
defaultValuestring""Default value for uncontrolled mode
onSubmit(message: { text: string }) => void | Promise<void>-Called on submit. Clears on resolve, preserves on reject.
onChange(text: string) => void-Called when input value changes
placeholderstring"Type a message..."Placeholder text when empty
promptstring"❯ "Prompt character before input
promptColorstringtheme.mutedColor of the prompt character
statusChatStatus-AI chat status. Drives disabled state and hint text.
onStop() => void-Called when Escape is pressed during streaming
submittedTextstring"Thinking..."Hint text when status is "submitted"
streamingTextstring"Generating..."Hint text when status is "streaming"
errorTextstring"An error occurred. Try again."Text shown when status is "error"
disabledbooleanfalseDisable input. Ignored when status is provided.
disabledTextstring"Generating..."Text shown when disabled. Ignored when status is provided.
commands{ cmd: string; desc?: string }[][]Slash commands for autocomplete
filesstring[][]File paths for @ mention autocomplete
getSuggestions(value: string) => Suggestion[]-Custom suggestion provider — overrides commands/files
maxSuggestionsnumber5Max visible suggestions
enableHistorybooleantrueEnable command history with ↑/↓
modelstring-Model name displayed below the input
showDividersbooleantrueShow horizontal dividers above and below input
autoFocusbooleanfalseAuto-focus the canvas on mount for keyboard events
useKeyboard(handler: (event: any) => void) => void-Keyboard hook from @opentui/react
childrenReactNode-When provided, enables compound mode

PromptInputProvider

PropTypeDefaultDescription
initialInputstring""Initial text input value
childrenReactNode-Components that can access the provider context

Suggestion

FieldTypeDescription
textstringSuggestion text
descstring?Optional description

ChatStatus

ValueDescription
"ready"Input enabled, accepts user input
"submitted"Input disabled, shows submitted text
"streaming"Input disabled, shows streaming text, Escape calls onStop
"error"Input enabled, shows error indicator