Skip to content

Prompt Composer

The box where people write to an AI: a growing text area, pill controls for attachments and modes, and a send button, edged with a glow in the brand's colours.

Preview

Installation

pnpm add @syntara/react @syntara/tokens
import { PromptComposer, ComposerButton, ComposerSelect } from '@syntara/react';

Usage

import { ComposerButton, ComposerSelect, PromptComposer } from '@syntara/react';
import { IconPlus } from '@syntara/icons';

export function Chat() {
  return (
    <PromptComposer
      onSubmit={(text) => console.log(text)}
      startActions={
        <>
          <ComposerButton aria-label="Attach a file" icon={<IconPlus />} />
          <ComposerSelect
            label="Reasoning"
            options={[
              { id: 'quick', label: 'Quick answer' },
              { id: 'deep', label: 'DeepThink' },
            ]}
          />
        </>
      }
    />
  );
}

Examples

Brand glow

glow="brand": the edge and halo in the brand's primary and accent colours, on a solid face.

No glow

glow={false} for a quiet composer: the same keyboard behaviour without the light.

Accessibility

KeysAction
EnterSends. Does nothing while an input method is still composing a word.
ShiftEnterAdds a new line.
TabMoves from the text box through the toolbar controls to send.
  • The send button is disabled while the box is empty, and becomes a Stop button while a reply is pending.
  • Focus in the text box draws the 2px focus ring around the whole composer; the brighter glow is decoration.
  • The glow colours swap sides and the send arrow mirrors on right-to-left pages.
  • On narrow composers pills with an icon show only the icon; the label stays as visually hidden text, so the name is unchanged.

Guidelines

Do

  • Keep the toolbar to a few controls; put the rest in the attach menu.

Don’t

  • Don't use the glow on more than one element in a view.

API reference

PromptComposer

onSubmit
(value: string) => void

Called with the trimmed text on Enter or the send button. Shift+Enter adds a line.

isPending
boolean

A reply is being written: the send button becomes stop and the glow breathes.

Default false

onStop
() => void

Called by the stop button.

startActions
ReactNode

Toolbar controls at the start: attach, pickers.

endActions
ReactNode

Toolbar controls before the send button.

glow
boolean | 'brand' | 'spectrum'

The two-colour edge and halo. brand: primary and accent. spectrum: warning to info, warm to cool, the same in every brand's palette. false turns it off.

Default 'brand'

surface
  • soliddefault
  • glass

glass is a frosted face for a composer floating over content; text keeps 4.5:1 over any backdrop.

label
string

Accessible name of the text box.

Default 'Message'

ComposerButton

icon
ReactNode

Leading icon. With no children the pill becomes a circle and needs aria-label.

ComposerSelect

labelRequired
string

What is being chosen. Read together with the choice, e.g. "Reasoning: DeepThink".

optionsRequired
ComposerOption[]

{ id, label, icon?, description? } for each choice.

Tokens

The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.

Colour14
action.primary.bgaction.primary.fgaction.primary.hoveraccent.bgsurface.raisedsurface.selectedborder.defaultborder.strongtext.defaulttext.subtletext.disabledfocus.ringfeedback.warning.solidfeedback.info.solid
Type3
font.size.mdfont.size.lgfont.weight.medium
Space and size2
control.heightcontrol.padding.inline
Shape2
radius.containerradius.button
Depth4
shadow.raisedhairlineglass.opacityglass.blur
Motion2
motion.springmotion.duration.spring