mds components
mds:composer
A chat / prompt input with an action bar — an open version of a Flux Pro component.
A textarea that grows with what you type, an action bar around it, and Ctrl/⌘ + Enter to submit the surrounding form. The preview is live — type a few lines into it.
<form wire:submit="send">
<mds:composer wire:model="prompt" label="Message" label:sr-only placeholder="How can I help you today?">
<x-slot name="actionsLeading">
<flux:button size="sm" variant="subtle" square><mds:icon icon="paper-clip" class="size-4" /></flux:button>
<flux:button size="sm" variant="subtle" square><mds:icon icon="adjustments-horizontal" class="size-4" /></flux:button>
</x-slot>
<x-slot name="actionsTrailing">
<flux:button size="sm" variant="filled" square><mds:icon icon="microphone" class="size-4" /></flux:button>
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
</form>
flux:buttons, so they follow your accent colour. square plus an mds:icon slot keeps the buttons icon-only while drawing Hugeicons rather than heroicons.With a header
The header slot sits above the input — attachments, a reply-to line, a model picker.
<mds:composer placeholder="Write a caption for this image...">
<x-slot name="header">
<mds:file-item heading="galaxy-phone.jpg" image="https://picsum.photos/seed/phone/80/80" :size="162400" class="w-full max-w-64">
<x-slot name="actions">
<mds:file-item.remove label="Remove image" />
</x-slot>
</mds:file-item>
</x-slot>
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
Inline
One row: the actions sit beside the input instead of under it. Pair it with rows="1".
<mds:composer rows="1" inline placeholder="Write your message...">
<x-slot name="actionsLeading">
<flux:button size="sm" variant="ghost" square><mds:icon icon="plus" class="size-4" /></flux:button>
</x-slot>
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
Input variant
variant="input" trades the pill radius for the same corners as the rest of your form controls, so a composer can sit in a field stack without looking like a visitor.
<div class="w-full max-w-md space-y-4">
<flux:input label="Name" placeholder="Your name" />
<mds:composer variant="input" label="Message" placeholder="What's on your mind?">
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary">Send</flux:button>
</x-slot>
</mds:composer>
</div>
Height
rows is the height it starts at, max-rows the height it stops growing at — past that the input scrolls.
<mds:composer rows="4" max-rows="8" placeholder="Four rows to start, eight at most..." class="max-w-md" />
Submit behavior
By default Ctrl/⌘ + Enter submits and Enter makes a new line. submit="enter" promotes the bare Enter to sending, and Shift + Enter takes over the new line. Ctrl/⌘ + Enter sends either way.
<form wire:submit="send">
<mds:composer wire:model="prompt" submit="enter" rows="1" inline placeholder="Enter sends this one...">
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
</form>
form.requestSubmit(), so wire:submit and native validation behave exactly as they would on a click. An open IME candidate window keeps its Enter.Character counter
Not in Flux: maxlength caps the message and counter shows how much of it is used.
<mds:composer :maxlength="280" counter rows="2" placeholder="280 characters at most..." value="Hi! Every character of this message is counted.">
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
A different input
The input slot replaces the textarea. Flux fills it with its Pro editor; anything that collects text works — here a plain flux:textarea keeps its own resize handle.
<mds:composer class="max-w-md">
<x-slot name="input">
<flux:textarea rows="3" resize="none" placeholder="Your own control goes here..." class="border-0! bg-transparent! px-2! py-1.5! shadow-none!" />
</x-slot>
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
submit="enter" all belong to the built-in textarea, so a replacement brings its own. Ctrl/⌘ + Enter keeps working either way.Disabled and invalid
A disabled composer is inert — the whole box, action buttons included, stops taking input. Validation errors come from the error bag for name, or from an explicit error.
<div class="w-full space-y-4">
<mds:composer disabled rows="1" inline placeholder="Sign in to start writing">
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
<mds:composer name="prompt" label="Message" error="A message is required." rows="1" inline>
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
</div>
Persian output
config('mds.persian_digits') — on by default in a real app, off in these docs — switches the digits and the built-in strings to Persian; :fa="true" does it for a single composer, visible here in the counter («۰ / ۲۸۰»). The transcript above the composer is your own markup — the shape most Livewire chat pages end up with; the date in the reply is mds:jalali-date.
<div class="w-full max-w-lg space-y-4">
<div class="space-y-3">
<div class="flex justify-start">
<div class="max-w-[80%] rounded-2xl rounded-ss-sm bg-zinc-100 px-3 py-2 text-sm dark:bg-white/10">
سلام! سفارش من کِی میرسد؟
</div>
</div>
<div class="flex justify-end">
<div class="max-w-[80%] rounded-2xl rounded-se-sm bg-accent px-3 py-2 text-sm text-accent-foreground">
مرسوله شما <mds:jalali-date :date="'2026-08-25'" :fa="true" /> به دستتان میرسد.
</div>
</div>
</div>
<form wire:submit="send">
<mds:composer wire:model="message" label="پیام" label:sr-only rows="1" max-rows="6" :maxlength="280" counter submit="enter" :fa="true" placeholder="پیام خود را بنویسید...">
<x-slot name="actionsLeading">
<flux:button size="sm" variant="ghost" square><mds:icon icon="paper-clip" class="size-4" /></flux:button>
</x-slot>
<x-slot name="actionsTrailing">
<flux:button type="submit" size="sm" variant="primary" square><mds:icon icon="paper-airplane" class="size-4" /></flux:button>
</x-slot>
</mds:composer>
</form>
</div>
Reference
mds:composer
| Prop | Description |
|---|---|
| name | Field name for a plain form post, and the key the error bag is read with. |
| value | Initial text. The default slot works too. |
| placeholder | Placeholder for the input. |
| label | Label text. Wraps the composer in a flux:field. |
| label:sr-only | Keeps the label for screen readers only. label-sr-only also works. |
| description | Help text under the composer. |
| description:sr-only | Same, for screen readers only. |
| rows | Height the input starts at, in lines. Default: 2. |
| max-rows | Height it stops growing at. Unbounded when absent. |
| maxlength | Character cap on the input. |
| counter | Shows the character count. Default: false. |
| inline | Puts the actions beside the input instead of under it. Default: false. |
| variant | input matches the corner radius of other form controls. |
| submit | Key that submits: cmd+enter (default) or enter. |
| autofocus | Focuses the input on load. |
| dir | Direction of the input. auto follows what is being typed. |
| disabled | Makes the whole composer inert. |
| invalid | Applies error styling. |
| error | Validation message. Falls back to the bag for name. |
| fa | Persian digits in the counter. Default: config('mds.persian_digits'). |
| wire:model | Binds the input to a Livewire property. |
| Slot | Description |
|---|---|
| input | Replaces the built-in textarea. |
| header | Above the input — attachments, a reply-to line. |
| footer | Under the action bar, sharing its row with the counter. |
| actionsLeading | Actions at the start of the action bar. |
| actionsTrailing | Actions at the end, usually the submit button. |