React Prompt Composer

The input looks trivial and isn't. It has to honour the keyboard conventions people reach for without thinking, grow with the content, show the token budget before a request fails, and never let the same key both send a message and interrupt a reply.

0 / 200K

Enter sends, Shift+Enter adds a newline. Sending flips the button to Stop for a moment. The token counter tracks against the selected model’s context window.

Installation

npx shadcn@latest add "https://kelvinui.com/registry/prompt-composer.json"

Props

PropTypeDescription
modelsComposerModel[]{ id, label, contextTokens } for the picker
toolsComposerTool[]{ id, label, icon? } toggle chips
streamingbooleanSwap Send for Stop while a reply is in flight
onSubmit(text, { model, tools }) => voidFires on send
onStop() => voidFires when Stop is pressed
value / onChangestring / (t) => voidOptional controlled value

Keyboard first

Enter sends; Shift+Enter is a newline — the convention every chat app has trained people into, and getting it wrong is instantly felt. IME composition is respected, so composing CJK text and pressing Enter to confirm a candidate doesn't fire the message off half-written. The textarea grows with its content up to about seven lines, then scrolls, so a long paste never swallows the page.

Honest about the budget

The token estimate runs against the selected model's context window and changes colour before it's a problem — amber past 80%, red and send-disabled once the prompt alone would overflow the window. It's an estimate (characters ÷ 4), clearly a guide rather than the tokenizer's word, but it's the difference between a considered edit and a request that fails on submit. Swapping the model re-evaluates against the new budget. While a reply streams, the send affordance becomes Stop: one control, one meaning at a time.

New components every week

Get the week's new Kelvin UI components and templates in one short email. No spam, unsubscribe anytime.