Skip to content

Forms

Chat composer

A multi-line message input that grows with the text: Enter sends, Shift+Enter adds a line.

import { ChatComposer } from '@jsonresume/ui'Source(opens in a new tab)

Examples

Copilot with voice input

Enter to send · Shift+Enter for a new line

Show code
chat-composer/copilot.tsx
'use client';

import { Button, ChatComposer, Stack, Text } from '@jsonresume/ui';
import { Mic } from 'lucide-react';
import { useState } from 'react';

export default function ChatComposerCopilot() {
  const [sent, setSent] = useState<string[]>([]);
  const [busy, setBusy] = useState(false);
  return (
    <Stack gap={3}>
      {sent.map((m, i) => (
        <Text key={i} tone="muted" size="xs">
          You: {m}
        </Text>
      ))}
      <ChatComposer
        busy={busy}
        hint="Enter to send · Shift+Enter for a new line"
        actions={
          <Button variant="ghost" size="icon" aria-label="Speak your message">
            <Mic aria-hidden="true" />
          </Button>
        }
        onSubmit={(m) => {
          setSent((all) => [...all, m]);
          setBusy(true);
          setTimeout(() => setBusy(false), 1200);
        }}
      />
    </Stack>
  );
}

Accessibility

  • The textarea is labelled; the Send button shows progress while a reply streams.
  • Enter does not send during IME composition.

Props

<ChatComposer>

Also accepts the attributes of the element ChatComposer renders.
PropTypeDefaultDescription
onSubmit required(message: string) => void–
actionsReactNode–Extra controls before Send, e.g. a voice input button.
busybooleanfalseWhile a reply streams: the Send button shows progress and Enter won't send.
hintReactNode–
labelstringMessage
placeholderstringAsk anything about your resume…
sendLabelstringSend