Rafi Wirana

Design Engineer

Preview
0%
Back to philosophy
Copy link

Collapsible Composer

A minimalistic message composer that expands from a floating button. Perfect for chat interfaces and AI applications where space is valuable.

Try typing a message and press Cmd/Ctrl + Enter or click Send to submit

Installation

✨ No external dependencies required

Component location

components/craft/CollapsibleComposer/index.tsx

Usage

Basic example

import { CollapsibleComposer } from '@/components/craft/CollapsibleComposer';

export function Example() {
  const handleSubmit = async (message: string) => {
    console.log("Sending:", message);
    await sendMessage(message);
  };

  return (
    <CollapsibleComposer
      onSubmit={handleSubmit}
      placeholder="Type your message..."
      position="bottom-right"
    />
  );
}

More Examples

Basic Usage

Default bottom-right positioned composer

<CollapsibleComposer
  onSubmit={async (message) => {
    console.log("Message:", message);
  }}
  placeholder="Ask me anything..."
/>

Controlled Value

Control the textarea value externally

const [message, setMessage] = useState("");

<CollapsibleComposer
  value={message}
  onValueChange={setMessage}
  onSubmit={handleSubmit}
/>

Controlled Expansion

Control when the composer expands and collapses

const [isOpen, setIsOpen] = useState(false);

<CollapsibleComposer
  expanded={isOpen}
  onExpandedChange={setIsOpen}
  onSubmit={handleSubmit}
/>

Custom Position

Position in different screen corners

<CollapsibleComposer
  onSubmit={handleSubmit}
  position="bottom-left"
  placeholder="Send a message..."
/>

Custom Size

Customize expanded and collapsed dimensions

<CollapsibleComposer
  onSubmit={handleSubmit}
  expandedSize={{ width: 400, height: 200 }}
  collapsedSize={{ width: 140, height: 48 }}
/>

Custom Trigger

Replace default collapsed button content

<CollapsibleComposer
  onSubmit={handleSubmit}
  triggerContent={
    <div className="flex items-center gap-2">
      <PlusCircle className="h-5 w-5" />
      <span>New Chat</span>
    </div>
  }
/>

With Attach Button

Add file attachment functionality

<CollapsibleComposer
  onSubmit={handleSubmit}
  showAttach={true}
  onAttachClick={() => {
    console.log("Attach clicked");
  }}
/>

Static Position

Embed inline instead of floating

<CollapsibleComposer
  onSubmit={handleSubmit}
  position="static"
  initiallyExpanded={true}
/>

API Reference

PropTypeDefaultDescription
onSubmitRequired
(content: string) => Promise<void> | voidCallback fired when the form is submitted with the message content
placeholder
stringPlaceholder text for the textarea
position
"bottom-right" | "bottom-left" | "top-right" | "top-left" | "static""bottom-right"Position of the composer. Use "static" for inline embedding.
value
stringControlled value for the textarea
defaultValue
stringDefault value for uncontrolled textarea
onValueChange
(value: string) => voidCallback fired when textarea value changes
expanded
booleanControlled expansion state
defaultExpanded
booleanDefault expansion state for uncontrolled component
initiallyExpanded
booleanfalseLegacy prop for initial expansion (use defaultExpanded instead)
onExpandedChange
(expanded: boolean) => voidCallback fired when expansion state changes
autoFocusOnExpand
booleantrueWhether to auto-focus textarea when expanded
submitOnModifierEnter
booleantrueSubmit form on Cmd+Enter or Ctrl+Enter
expandedSize
{ width: number; height: number }{ width: 360, height: 152 }Dimensions when expanded (in pixels)
collapsedSize
{ width: number; height: number }{ width: 120, height: 40 }Dimensions when collapsed (in pixels)
triggerContent
React.ReactNodeCustom content for the collapsed trigger button
leftActions
React.ReactNodeCustom actions to show in bottom-left of expanded composer
showAttach
booleantrueWhether to show the attach button (ignored if leftActions provided)
onAttachClick
() => voidCallback fired when attach button is clicked
sendAriaLabel
string"Send message"Accessible label for the send button
className
stringAdditional CSS classes for the root container