21st

Input Bar

AI chats 56

About

Chat composer with auto-resizing textarea, attachment chips for staged images/files, send/stop button, and slot-based left/right toolbar actions for mode pickers, model selectors, etc. Self-contained port from 21st.dev Agent Elements.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Input Bar to your project and fits it to your theme.

Code

How Input Bar is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import { useState } from "react";
import { InputBar, type AttachedImage, type AttachedFile } from "@/components/ui/input-bar";

const PLACEHOLDER_IMG =
  "data:image/svg+xml;utf8," +
  encodeURIComponent(
    `<svg xmlns='http://www.w3.org/2000/svg' width='128' height='128' viewBox='0 0 128 128'>
       <defs>
         <linearGradient id='g' x1='0' y1='0' x2='1' y2='1'>
           <stop offset='0%' stop-color='#a78bfa'/>
           <stop offset='100%' stop-color='#3b82f6'/>
         </linearGradient>
       </defs>
       <rect width='128' height='128' fill='url(#g)'/>
       <circle cx='44' cy='52' r='14' fill='#fff' opacity='0.85'/>
       <path d='M16 110 L52 70 L80 96 L112 60 L112 120 L16 120 Z' fill='#fff' opacity='0.9'/>
     </svg>`,
  );

export default function WithAttachmentsDemo() {
  const [images, setImages] = useState<AttachedImage[]>([
    { id: "img-1", filename: "hero.png", url: PLACEHOLDER_IMG, size: 482_133 },
  ]);
  const [files, setFiles] = useState<AttachedFile[]>([
    { id: "file-1", filename: "spec.pdf", size: 1_240_500 },
  ]);

  return (
    <div className="min-h-screen w-full flex items-center justify-center p-6 bg-white dark:bg-neutral-950">
      <div className="w-full max-w-md">
        <InputBar
          onSend={() => {}}
          onStop={() => {}}
          status="ready"
          onAttach={() => {}}
          attachedImages={images}
          attachedFiles={files}
          onRemoveImage={(id) =>
            setImages((prev) => prev.filter((i) => i.id !== id))
          }
          onRemoveFile={(id) =>
            setFiles((prev) => prev.filter((f) => f.id !== id))
          }
        />
      </div>
    </div>
  );
}

Dependencies

More by 21st

View all
  • Spline Scene

    Spline Scene

    9.4K
  • Testimonials with Marquee

    Testimonials with Marquee

    2.3K
  • Gradient Button

    Gradient Button

    1.9K
  • Pixel Canvas

    Pixel Canvas

    898
  • Animated Blur Number

    Animated Blur Number

    830
  • Empty State

    Empty State

    772
  • Balloons

    Balloons

    753
  • Status Badge

    Status Badge

    639

More in AI chats

View all
  • AI Actions

    AI Actions

    317
  • Color Picker

    Color Picker

    73
  • Agent Chat

    Agent Chat

    17
  • Prompt with Model Picker

    Prompt with Model Picker

    2
  • Approval Card

    Approval Card

    12
  • System Message

    System Message

    53
  • Message

    Message

    48
  • AI Task List

    AI Task List

    158