# Prompt Input



import PromptInputDefault from "@/components/nexus-ui/examples/prompt-input/default";
import PromptInputBasic from "@/components/nexus-ui/examples/prompt-input/basic";
import GeminiInput from "@/components/nexus-ui/examples/prompt-input/gemini-input";
import PromptInputMultipleActions from "@/components/nexus-ui/examples/prompt-input/multiple-actions";

A flexible, composable input component for building chat interfaces. Includes an auto-resizing textarea with scroll support and customizable action slots for buttons like send, attach, and more.

<DemoWithCode src="components/nexus-ui/examples/prompt-input/default.tsx">
  <PromptInputDefault />
</DemoWithCode>

Installation [#installation]

<Tabs items={["CLI", "Manual"]} framed={false}>
  <Tab value="CLI">
    <Tabs items={["npm", "pnpm", "yarn", "bun"]}>
      <Tab value="npm">
        ```bash
        npx shadcn@latest add @nexus-ui/prompt-input
        ```
      </Tab>

      <Tab value="pnpm">
        ```bash
        pnpm dlx shadcn@latest add @nexus-ui/prompt-input
        ```
      </Tab>

      <Tab value="yarn">
        ```bash
        yarn dlx shadcn@latest add @nexus-ui/prompt-input
        ```
      </Tab>

      <Tab value="bun">
        ```bash
        bunx shadcn@latest add @nexus-ui/prompt-input
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab value="Manual">
    <Steps>
      <Step>
        <h3>
          Install the following dependencies:
        </h3>

        <Tabs items={["npm", "pnpm", "yarn", "bun"]}>
          <Tab value="npm">
            ```bash
            npx shadcn@latest add textarea scroll-area tooltip kbd && npm install @radix-ui/react-slot
            ```
          </Tab>

          <Tab value="pnpm">
            ```bash
            pnpm dlx shadcn@latest add textarea scroll-area tooltip kbd && pnpm add @radix-ui/react-slot
            ```
          </Tab>

          <Tab value="yarn">
            ```bash
            yarn dlx shadcn@latest add textarea scroll-area tooltip kbd && yarn add @radix-ui/react-slot
            ```
          </Tab>

          <Tab value="bun">
            ```bash
            bunx shadcn@latest add textarea scroll-area tooltip kbd && bun add @radix-ui/react-slot
            ```
          </Tab>
        </Tabs>
      </Step>

      <Step>
        <h3>
          Copy and paste the following code into your project.
        </h3>

        <ComponentSource src="components/nexus-ui/prompt-input.tsx" title="components/nexus-ui/prompt-input.tsx" />
      </Step>

      <Step>
        <h3>
          Update import paths to match your project setup.
        </h3>
      </Step>
    </Steps>
  </Tab>
</Tabs>

Usage [#usage]

```tsx keepBackground
import {
  PromptInput,
  PromptInputTextarea,
  PromptInputActions,
  PromptInputActionGroup,
  PromptInputAction,
} from "@/components/nexus-ui/prompt-input";
```

```tsx keepBackground
<PromptInput>
  <PromptInputTextarea placeholder="Ask anything..." />
  <PromptInputActions>
    <PromptInputActionGroup>
      {/* Left-aligned actions */}
    </PromptInputActionGroup>
    <PromptInputActionGroup>
      {/* Right-aligned actions */}
    </PromptInputActionGroup>
  </PromptInputActions>
</PromptInput>
```

Examples [#examples]

Basic [#basic]

A minimal prompt input with just a textarea and send button.

<DemoWithCode src="components/nexus-ui/examples/prompt-input/basic.tsx">
  <PromptInputBasic />
</DemoWithCode>

With Multiple Actions [#with-multiple-actions]

Combine multiple action buttons in a single group and add built-in tooltips using `PromptInputAction`'s `tooltip` prop (string or object with `content`, optional `side`, and optional `shortcut`).

<DemoWithCode src="components/nexus-ui/examples/prompt-input/multiple-actions.tsx">
  <PromptInputMultipleActions />
</DemoWithCode>

Vercel AI SDK Integration [#vercel-ai-sdk-integration]

Connect Prompt Input to the [Vercel AI SDK](https://sdk.vercel.ai) for streaming chat interfaces.

<Steps>
  <Step>
    <h3>
      Install the AI SDK
    </h3>

    ```bash
    npm install ai @ai-sdk/react @ai-sdk/openai
    ```
  </Step>

  <Step>
    <h3>
      Create your chat API route
    </h3>

    ```ts title="app/api/chat/route.ts"
    import { convertToModelMessages, streamText, UIMessage } from "ai";
    import { openai } from "@ai-sdk/openai";

    export async function POST(req: Request) {
      const { messages }: { messages: UIMessage[] } = await req.json();

      const result = streamText({
        model: openai("gpt-4o-mini"),
        system: "You are a helpful assistant.",
        messages: await convertToModelMessages(messages),
      });

      return result.toUIMessageStreamResponse();
    }
    ```
  </Step>

  <Step>
    <h3>
      Wire Prompt Input to 

      `useChat`
    </h3>

    Use `onSubmit` for Enter-to-submit. Shift+Enter inserts a new line.

    ```tsx
    "use client";

    import { useState, useCallback } from "react";
    import { useChat } from "@ai-sdk/react";
    import { DefaultChatTransport } from "ai";
    import { Button } from "@/components/ui/button";
    import {
      PromptInput,
      PromptInputActions,
      PromptInputAction,
      PromptInputActionGroup,
      PromptInputTextarea,
    } from "@/components/nexus-ui/prompt-input";
    import { ArrowUp02Icon, SquareIcon } from "@hugeicons/core-free-icons";
    import { HugeiconsIcon } from "@hugeicons/react";

    export default function ChatWithPromptInput() {
      const { sendMessage, status } = useChat({
        transport: new DefaultChatTransport({ api: "/api/chat" }),
      });
      const [input, setInput] = useState("");
      const isLoading = status !== "ready";

      const handleSubmit = useCallback(
        (value?: string) => {
          const trimmed = (value ?? input).trim();
          if (trimmed) {
            sendMessage({ text: trimmed });
            setInput("");
          }
        },
        [input, sendMessage],
      );

      return (
        <form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }} className="w-full">
          <PromptInput onSubmit={handleSubmit}>
            <PromptInputTextarea
              value={input}
              onChange={(e) => setInput(e.target.value)}
              placeholder="Ask anything..."
              disabled={isLoading}
            />
            <PromptInputActions>
              <PromptInputActionGroup />
              <PromptInputActionGroup>
                <PromptInputAction asChild>
                  <Button
                    type="submit"
                    size="icon-sm"
                    className="cursor-pointer rounded-full active:scale-97 disabled:opacity-70"
                    disabled={isLoading || !input.trim()}
                  >
                    {isLoading ? (
                      <HugeiconsIcon icon={SquareIcon} strokeWidth={2.0} className="size-3.5 fill-current" />
                    ) : (
                      <HugeiconsIcon icon={ArrowUp02Icon} strokeWidth={2.0} className="size-4" />
                    )}
                  </Button>
                </PromptInputAction>
              </PromptInputActionGroup>
            </PromptInputActions>
          </PromptInput>
        </form>
      );
    }
    ```
  </Step>
</Steps>

API Reference [#api-reference]

PromptInput [#promptinput]

The root container that wraps the textarea and action bar.

<TypeTable
  type={{
  onSubmit: {
    type: "(value: string) => void (optional)",
    description:
      "Called when Enter is pressed in the textarea (without Shift). Receives the current textarea value. Use with value/onChange on PromptInputTextarea for controlled mode. Shift+Enter inserts a new line.",
  },
  className: {
    type: "string",
    description: "Additional CSS classes to apply to the container.",
  },
  onClick: {
    type: "React.MouseEventHandler<HTMLDivElement>",
    description:
      "Called after the internal click handler. The internal handler focuses the textarea when clicking non-interactive areas.",
  },
}}
/>

PromptInputTextarea [#promptinputtextarea]

An auto-resizing textarea wrapped in a scroll area. Accepts all standard `textarea` props including `disabled` and `onKeyDown`. Use `onSubmit` on PromptInput for Enter-to-submit; Shift+Enter inserts a new line.

<TypeTable
  type={{
  className: {
    type: "string",
    description: "Additional CSS classes to apply to the textarea.",
  },
  disabled: {
    type: "boolean",
    default: "false",
    description: "Disables the textarea (e.g. while the AI is responding).",
  },
  onChange: {
    type: "React.ChangeEventHandler<HTMLTextAreaElement>",
    description: "Called when the value changes. Use with value for controlled mode.",
  },
  onKeyDown: {
    type: "React.KeyboardEventHandler<HTMLTextAreaElement>",
    description: "Called when a key is pressed. Fired after the internal Enter/Shift+Enter handler.",
  },
  placeholder: {
    type: "string",
    default: '"How can I help you today?"',
    description: "Placeholder text displayed when the textarea is empty.",
  },
  ref: {
    type: "React.Ref<HTMLTextAreaElement>",
    description:
      "Forwarded ref to the textarea element. Merged with the internal ref used for click-to-focus.",
  },
  value: {
    type: "string",
    description: "Controlled value. Use with onChange for controlled mode.",
  },
}}
/>

PromptInputActions [#promptinputactions]

A flex container for action buttons. Uses `justify-between` to position child groups at opposite ends.

<TypeTable
  type={{
  className: {
    type: "string",
    description: "Additional CSS classes to apply to the actions container.",
  },
}}
/>

PromptInputActionGroup [#promptinputactiongroup]

Groups related action buttons together with a horizontal layout.

<TypeTable
  type={{
  className: {
    type: "string",
    description: "Additional CSS classes to apply to the group.",
  },
}}
/>

PromptInputAction [#promptinputaction]

A wrapper for individual action buttons. Supports polymorphism via `asChild` and optional built-in tooltip rendering.

<TypeTable
  type={{
  asChild: {
    type: "boolean",
    default: "false",
    description:
      "When true, merges props onto the child element instead of rendering a wrapper div. Uses Radix UI Slot.",
  },
  className: {
    type: "string",
    description: "Additional CSS classes to apply to the action.",
  },
  tooltip: {
    type: 'string | { content?: string; side?: "top" | "right" | "bottom" | "left"; shortcut?: string }',
    description:
      "Tooltip config. A string maps to tooltip content. Object form supports content, side, and keyboard shortcut. If content is not provided, no tooltip is rendered.",
  },
}}
/>
