React UI Base
Loading...

MessageInput

Compose message text input, staged images, submit/stop controls, and elicitation mode with unstyled parts.

MessageInput

MessageInput in @tambo-ai/react-ui-base owns message-authoring behavior while keeping styling in @tambo-ai/ui-registry or your own UI layer.

Demo

import { MessageInput } from "@tambo-ai/react-ui-base/message-input";import { ArrowUp, Paperclip, Square } from "lucide-react";export function DemoMessageInput() {  return (    <MessageInput.Root className="flex flex-col gap-2">      <MessageInput.Content className="flex flex-col rounded-xl border border-neutral-200 bg-white shadow-sm dark:border-neutral-700 dark:bg-neutral-800">        <MessageInput.Textarea className="min-h-20 w-full resize-none rounded-t-xl bg-transparent px-3 py-2.5 text-sm text-neutral-900 placeholder:text-neutral-400 focus:outline-none dark:text-neutral-100 dark:placeholder:text-neutral-500" />        <MessageInput.Toolbar className="flex items-center gap-2 border-t border-neutral-100 px-2 py-1.5 dark:border-neutral-700">          <MessageInput.FileButton className="flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-xs text-neutral-500 hover:bg-neutral-100 hover:text-neutral-700 dark:text-neutral-400 dark:hover:bg-neutral-700 dark:hover:text-neutral-200">            <Paperclip className="h-3.5 w-3.5" />            Attach          </MessageInput.FileButton>          <div className="ml-auto flex items-center gap-2">            <MessageInput.SubmitButton className="flex h-8 w-8 items-center justify-center rounded-lg bg-neutral-900 text-white hover:bg-neutral-700 disabled:opacity-40 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-neutral-300">              <ArrowUp className="h-4 w-4" />            </MessageInput.SubmitButton>            <MessageInput.StopButton className="flex h-8 w-8 items-center justify-center rounded-lg border border-neutral-200 text-neutral-600 hover:bg-neutral-100 disabled:opacity-40 dark:border-neutral-600 dark:text-neutral-300 dark:hover:bg-neutral-700">              <Square className="h-3.5 w-3.5" fill="currentColor" />            </MessageInput.StopButton>          </div>        </MessageInput.Toolbar>      </MessageInput.Content>      <MessageInput.Elicitation />      <MessageInput.Error className="text-sm text-red-600 dark:text-red-400" />    </MessageInput.Root>  );}

Anatomy

<MessageInput.Root>
  <MessageInput.Content>
    <MessageInput.StagedImages>
      {({ images }) => /* render staged image previews */}
    </MessageInput.StagedImages>
    <MessageInput.Textarea>
      {({ value, setValue, disabled }) => /* render text input */}
    </MessageInput.Textarea>
    <MessageInput.Toolbar>
      <MessageInput.FileButton>
        {({ openFilePicker }) => /* render attach button */}
      </MessageInput.FileButton>
      <MessageInput.SubmitButton>Send</MessageInput.SubmitButton>
      <MessageInput.StopButton>Stop</MessageInput.StopButton>
    </MessageInput.Toolbar>
  </MessageInput.Content>
  <MessageInput.Elicitation />
  <MessageInput.Error />
</MessageInput.Root>

MessageInput.ValueAccess can be placed anywhere inside Root to read or modify the input value without rendering a DOM element.

Examples

Submit/Stop Visibility

<MessageInput.SubmitButton keepMounted />
<MessageInput.StopButton keepMounted />

keepMounted (false by default) keeps the element in the tree and toggles visibility via the state render prop ("hidden" or "visible").

Rendering Custom Elicitation UI

<MessageInput.Elicitation>
  {({ request, onResponse }) => (
    <CustomElicitation request={request} onResponse={onResponse} />
  )}
</MessageInput.Elicitation>

Inserting Text via ValueAccess

<MessageInput.ValueAccess>
  {({ value, setValue }) => (
    <button onClick={() => setValue(value + " appended")}>Append</button>
  )}
</MessageInput.ValueAccess>

API reference

Root

Renders a <form>. Provides context for all child subcomponents.

PropTypeDefaultDescription
inputRefReact.RefObject<TamboEditor | null>undefinedRef for editor-level operations.

Render state

FieldTypeDescription
isDraggingbooleantrue while image files are being dragged over the form
isSubmittingbooleantrue while a submission is in-flight
hasErrorbooleantrue if any error (submit, image, or thread) is set

Content

Renders a <div>. Wraps the input area and hides automatically when an elicitation is active.

PropTypeDefaultDescription
keepMountedbooleanfalseKeep mounted while elicitation is active and hide via data-hidden.

Render state

FieldTypeDescription
isDraggingbooleanWhether files are being dragged over the input
elicitationTamboElicitationRequest | nullCurrent elicitation request, if active
resolveElicitationfunction | nullCallback to resolve the active elicitation

Textarea

Renders a <textarea>. Provides render props for building a custom text editor.

PropTypeDefaultDescription
placeholderstring"What do you want to do?"Placeholder text.
enterKeyBehaviour"newline" | "submit""newline"Whether pressing Enter submits the message or inserts a newline.
disableModifierSubmitbooleanfalseDisables Cmd/Ctrl+Enter submit shortcut in "newline" mode.
resourceProviderResourceProviderundefinedResource provider for @ mentions (MCP resources included by default).
promptProviderPromptProviderundefinedPrompt provider for / commands (MCP prompts included by default).
resourceFormatOptionsResourceFormatOptionsundefinedOptions for formatting MCP resources into ResourceItems.
promptFormatOptionsPromptFormatOptionsundefinedOptions for formatting MCP prompts into PromptItems.

Render state

FieldTypeDescription
valuestringCurrent input value
setValue(value: string) => voidUpdate the input value
submitMessage() => Promise<void>Submit the message programmatically
handleSubmit(e: React.FormEvent) => Promise<void>Form submit handler
disabledbooleanWhether the input is disabled
placeholderstringResolved placeholder text
editorRefReact.RefObject<TamboEditor | null>Reference to the editor instance
addImage(file: File) => Promise<void>Stage a single image
imagesStagedImage[]Currently staged images
setImageError(error: string | null) => voidSet an image error message
resourceItemsResourceItem[]Combined resource items (MCP + provider)
setResourceSearch(query: string) => voidUpdate the resource search query
promptItemsPromptItem[]Combined prompt items (MCP + provider)
setPromptSearch(query: string) => voidUpdate the prompt search query

StagedImages

Renders a <div>. Only mounts when images are staged (or when a render prop is provided).

Render state

FieldTypeDescription
imagesStagedImageState[]Array of staged images with per-image helpers
removeImage(id: string) => voidRemove an image by ID
expandedImageIdstring | nullCurrently expanded image ID
setExpandedImageId(id: string | null) => voidSet the expanded image

Each item in images provides:

FieldTypeDescription
imageStagedImageThe staged image data
displayNamestringDisplay name for the image
indexnumberIndex of the image
isExpandedbooleanWhether this image is expanded
onToggle() => voidToggle expanded state
onRemove() => voidRemove this image

Toolbar

Renders a <div>. A simple container for action controls like submit, stop, and file buttons.

No additional props beyond standard HTML div attributes and the render prop.

FileButton

Renders a <button> with a hidden <input type="file">.

PropTypeDefaultDescription
acceptstring"image/*"Accept attribute for the file input.
multiplebooleantrueAllow multiple file selection.
inputVisiblebooleanfalseWhen true, removes visually-hidden styles from the file input.

Render state

FieldTypeDescription
openFilePicker() => voidTrigger the file picker dialog
fileInputRefReact.RefObject<HTMLInputElement | null>Ref to the hidden file input

Elicitation

Renders a <div>. Shows the default Elicitation form or custom children when an elicitation is active.

PropTypeDefaultDescription
keepMountedbooleanfalseKeep mounted while no elicitation is active and hide via data-hidden.

Render state

FieldTypeDescription
state"hidden" | "visible"Visibility state
elicitationTamboElicitationRequest | nullCurrent elicitation request
onResponsefunction | nullCallback to resolve the elicitation

SubmitButton

Renders a <button type="submit">. Hidden while a run is active; shows when idle.

PropTypeDefaultDescription
keepMountedbooleanfalseKeep mounted while hidden and toggle via render state.

Render state

FieldTypeDescription
disabledbooleanWhether the button is disabled
loadingbooleanWhether in a loading state
state"hidden" | "visible"Visibility state

StopButton

Renders a <button>. Hidden while idle; shows during an active run.

PropTypeDefaultDescription
keepMountedbooleanfalseKeep mounted while hidden and toggle via render state.

Render state

FieldTypeDescription
disabledbooleanWhether the button is disabled
state"hidden" | "visible"Visibility state

Error

Renders a <p>. Shows automatically when any error is present (submit, image, or thread error). Falls back to displaying children if provided.

PropTypeDefaultDescription
keepMountedbooleanfalseKeep mounted in the DOM when there is no error.

Render state

FieldTypeDescription
errorMessagestring | nullResolved error message to display
errorError | nullOriginal thread error object, if any
submitErrorstring | nullSubmit-specific error message
imageErrorstring | nullImage upload-specific error message

ValueAccess

Renders no DOM element. Provides a render-prop child with access to the input value and editor ref.

Render state (child function argument)

FieldTypeDescription
valuestringCurrent input value
setValue(value: string) => voidUpdate the input value
editorRefReact.RefObject<TamboEditor | null>Reference to the editor instance

Accessibility

  • Use semantic controls in render-prop children (button, textarea, input).
  • Provide clear labels for custom submit/stop controls when replacing defaults.
  • Keep keyboard submit/stop behavior consistent with your app input model.

Styling Hooks

data-slot values

Subcomponentdata-slot value
Rootmessage-input-root
Contentmessage-input-content
Textareamessage-input-textarea
StagedImagesmessage-input-staged-images
Toolbarmessage-input-toolbar
FileButtonmessage-input-file-button
Elicitationmessage-input-elicitation
SubmitButtonmessage-input-submit
Errormessage-input-error

StopButton does not emit a data-slot.

State attributes

AttributeSubcomponent(s)Description
data-stateRoot, ErrorRoot: "dragging" while files drag over. Error: "visible" or "hidden".
data-pendingRootPresent while a submission is in-flight.
data-draggingContentPresent while files are being dragged over the input.
data-elicitationContent"active" when an elicitation request is active.
data-countStagedImagesNumber of currently staged images.
data-emptyStagedImagesPresent when no images are staged.
data-disabledTextareaPresent when the textarea is disabled.
data-hiddenSubmitButton, StopButton, Content, ElicitationPresent when the element is kept mounted but logically hidden (via keepMounted).