Skip to content
Fields

Composer ​

Erag\InertiaForms\Fields\Composer is a message box like the one at the bottom of a chat. It grows as the user types, sends the form with Enter, can take file attachments, and offers ready-made replies as chips. Its value holds the message and the files together. Like every other field, it is built into the package with Tailwind CSS and needs no extra library.

When to use: support replies, comments, chat messages, notes on a record, and any form whose main job is sending a short message, often with a file.

php
use Erag\InertiaForms\Fields\Composer;
use Erag\InertiaForms\Form;

class ReplyForm extends Form
{
    protected ?string $actionRoute = 'tickets.replies.store';

    protected bool $resetOnSuccess = true;

    public function fields(): array
    {
        return [
            Composer::make('reply')
                ->placeholder('Write a reply…')
                ->required()
                ->accept(['pdf', 'png', 'jpg'])
                ->maxFiles(3)
                ->maxSize(5120)
                ->quickReplies(['Thanks, looking into it now.', 'Could you send a screenshot?']),
        ];
    }
}

Try it on the Support chat form in the Live Demo.

Examples ​

Each example is a live form built from the PHP below it. Fill it in and press submit to see the data your controller would receive.

Basic ​

A one-line message box. Enter sends it and Shift+Enter adds a line. required() blocks empty messages.

php
use Erag\InertiaForms\Fields\Composer;

Composer::make('message')
    ->placeholder('Write a message…')
    ->required();

Comment box ​

A taller box with a character limit. submitOnEnter(false) makes Enter add a line, so only the renamed button sends.

php
use Erag\InertiaForms\Fields\Composer;

Composer::make('comment')
    ->placeholder('Add a comment…')
    ->rows(3)
    ->maxLength(500)
    ->submitOnEnter(false)
    ->sendLabel('Post comment')
    ->required();

Advanced: attachments and quick replies ​

accept() turns attachments on: attach files with the paperclip or drop them on the box. Clicking a quick reply fills in the message. The data holds both, with files shown by name.

php
use Erag\InertiaForms\Fields\Composer;

Composer::make('reply')
    ->label('Reply to customer')
    ->placeholder('Write a reply…')
    ->accept(['pdf', 'png', 'jpg'])
    ->maxFiles(3)
    ->maxSize(5120)
    ->maxLength(2000)
    ->quickReplies([
        'Thanks, looking into it now.',
        'Could you send a screenshot?',
        'This is fixed in the latest release.',
    ])
    ->required();

How it works ​

  • The text area starts at rows() lines and grows as the user types.
  • Enter sends the form and Shift+Enter adds a new line. Turn this off with submitOnEnter(false); then Enter adds a line and only the button sends.
  • With attachments on, a paperclip button opens the file picker, and files can also be dropped onto the composer. Each attached file shows as a chip with a × button to remove it.
  • Quick replies appear as chips next to the box. Clicking one fills in the message, ready to edit or send.
  • The composer has its own Send button (the sendLabel()), which submits the form. A form with a Composer doesn't get the default "Submit" button, so you don't need a Submit field.

Clear the box after sending

Turn on resetOnSuccess for composer forms. The message and attachments are then cleared after each successful send, like in a chat app.

Methods ​

All common field methods are available, plus:

attachments(bool $attachments = true) ​

Let users attach files. Off by default. When it is on, the whole form is submitted as multipart, like a form with a file upload.

accept(array|string $extensions) ​

Allowed file extensions, as an array or a comma-separated string ('pdf,png'). Leading dots are removed and case is ignored. Also turns on attachments.

maxFiles(?int $count) ​

The most files per message. Defaults to 5; null removes the limit.

maxSize(?int $kilobytes) ​

The largest file allowed, in kilobytes, like Laravel's max rule for files.

maxLength(?int $characters) ​

The longest message allowed.

submitOnEnter(bool $submitOnEnter = true) ​

Send with Enter (and add lines with Shift+Enter). On by default.

sendLabel(string $label) ​

Text of the send button. Defaults to "Send".

rows(int $rows) ​

The starting height in lines. Defaults to 1.

quickReplies(array $replies) ​

Ready-made messages shown as chips.

Validation rules ​

AttributeRules
replynullable (or required), array:message,attachments, plus a content check when required
reply.messagenullable, string, max:<maxLength>
reply.attachmentsnullable, array, max:<maxFiles>; prohibited when attachments are off
reply.attachments.*file, extensions:<accept>, max:<maxSize>
  • With required(), the user must write a message or attach a file. An empty send fails with "Write a message or attach a file."
  • A file with the wrong type or size fails on its own item, like reply.attachments.0, and the message names the field: "Reply attachment".
  • Files sent to a composer without attachments() are rejected.

Value ​

In the browser the value is an object with the message and the selected files:

json
{ "message": "Here is the invoice.", "attachments": [] }

$form->validated() trims the message and returns the files as UploadedFile instances:

php
$reply = $form->validated('reply');
// ['message' => 'Here is the invoice.', 'attachments' => [UploadedFile, ...]]

foreach ($reply['attachments'] as $file) {
    $file->store('ticket-attachments');
}

Empty value: ['message' => '', 'attachments' => []]. A bound string (or an array with a message key) fills in the message; files are never filled in from a model.

Standalone use ​

vue
<Composer v-model="reply" :field="replyField" id="reply" :disabled="false" />
tsx
<Composer field={replyField} id="reply" value={reply} disabled={false} onChange={setReply} />
svelte
<Composer field={replyField} id="reply" bind:value={reply} disabled={false} />

Start with { message: '', attachments: [] } as the value. Enter and the Send button submit the surrounding <Form>, so on its own the component only edits the value; send it with your own code. See Standalone Components.