Guide

Composer and rich text

Connect application-owned editors to structured attachments and tokenized rich-text images.

Neither Composer nor RichText ships an editor. They define value and upload behavior; your application connects its preferred text area, message composer, or rich-text editor through the matching controller.

FieldSubmitted value
Composer without attachmentsstring or null
Composer with attachments{ text: string, attachments: array }
RichTextHTML string; optional images use a separate token array
The managed attachment and image strategies below require the opt-in upload routes. Register Route::inertiaFormUploads() as shown in the uploads guide.

Composer attachments

Enable attachments and configure them with the same upload options as a File field.

use Inertify\Form\Fields\Composer;

Composer::make('message')
    ->maxLength(5_000)
    ->allowAttachments()
    ->acceptedFileTypes(['image/png', 'application/pdf'])
    ->maxFileSize(5 * 1024)
    ->reorderable();

useComposer(name, form) exposes text, attachments, attachmentUploadState, setText(), setAttachments(), uploadAttachments(), removeAttachment(), and reorderAttachment().

resources/js/components/form/ComposerField.vue
<script setup lang="ts">
import { useComposer, type FormFieldSlotProps } from '@inertify/form-vue'

const props = defineProps<FormFieldSlotProps>()
const composer = useComposer(props.name, props.form)

function attach(event: Event) {
  const input = event.currentTarget as HTMLInputElement

  if (input.files) {
    void composer.uploadAttachments(input.files)
  }
}
</script>

<template>
  <textarea v-model="composer.text.value" @blur="blur" />
  <input v-if="composer.allowAttachments.value" type="file" multiple @change="attach">
</template>

After server validation, validated(files: false) retains message.text and removes message.attachments for every upload strategy. Resolve token-based attachments using their qualified message.attachments upload path. For native attachments configured with storeWithForm(), use validated() with its default files: true value or read Laravel's request file bag.

Rich-text image uploads

RichText always submits an HTML string. imageUploads() adds a companion array named <field>_images; for body, the path is body_images.

use Inertify\Form\Fields\RichText;
use Inertify\Form\Fields\UploadConfig;

RichText::make('body')
    ->maxLength(50_000)
    ->imageUploads(
        fn (UploadConfig $images): UploadConfig => $images
            ->maxSize(2 * 1024)
            ->directToStorage('private'),
    );

Use useRichText(name, form) to access html, images, imageUploadState, setHtml(), clearContent(), uploadImages(), removeImage(), and reorderImage().

When the controller returns an uploaded file, insert an image into the editor with its token in data-inertia-forms-upload. Keep the companion token list synchronized; the controller does this when uploading and removing images.

<img
  src="blob:https://example.test/local-preview"
  alt="Diagram"
  data-inertia-forms-upload="encrypted-upload-token"
>

The server compares unique token sets: every HTML marker token must appear in the companion array and vice versa. Ordering and duplicate entries are ignored.

Store and rewrite images

After form validation, store each submitted image and replace its temporary marker. Passing deleteTemporary: false lets RichTextUploads delete every token only after the complete rewrite succeeds.

use Inertify\Form\RichText\RichTextImage;
use Inertify\Form\RichText\RichTextUploads;
use Inertify\Form\Uploads\SubmittedUpload;

$data = $form->validated(files: false);
$data['body'] = RichTextUploads::from(request(), 'body')
    ->storeImagesUsing(function (
        SubmittedUpload $upload,
        RichTextImage $image,
    ): RichTextImage {
        $path = $upload->store(
            'post-images',
            'private',
            deleteTemporary: false,
        );

        return $image->identifier($path, ['disk' => 'private']);
    })
    ->keepTokenized()
    ->toHtml();

keepTokenized() removes the temporary src and stores a versioned data-inertia-forms-image marker containing your identifier and metadata. If public URLs are stable, return $image->src($url) and omit keepTokenized() instead. storeImagesInMediaLibrary() is the optional Media Library shortcut.

Resolve stored images

For tokenized HTML, replace stored markers with fresh URLs before sending content to the browser:

use Illuminate\Support\Facades\Storage;
use Inertify\Form\RichText\RichTextContent;
use Inertify\Form\RichText\RichTextImage;
use Inertify\Form\RichText\RichTextStoredImage;

$body = RichTextContent::from($post->body)
    ->replaceImagesUsing(function (
        RichTextStoredImage $stored,
        RichTextImage $image,
    ): RichTextImage {
        $disk = (string) $stored->meta('disk', 'private');
        $url = Storage::disk($disk)->temporaryUrl(
            $stored->identifier(),
            now()->addMinutes(5),
        );

        return $image->src($url);
    })
    ->toHtml();
Rich-text helpers verify upload markers and rewrite image attributes, but they do not sanitize HTML. Apply your application's HTML allowlist before rendering untrusted content.
Copyright © 2026 Inertify · Released under the MIT License