Text Editor - Image

Insert images into the editor via URL or local file upload.

#Usage

import { TextEditor } from '@primeui/vue-texteditor';
<TextEditor.Root v-model="value">
    <TextEditor.Content />
    <TextEditor.ImageUpload :handler="uploadHandler" @reject="onFileReject">
        <TextEditor.ImageUploadDropzone>
            <ImageUploadDropzone />
        </TextEditor.ImageUploadDropzone>
        <TextEditor.ImageUploadProgress>
            <ImageUploadProgress />
        </TextEditor.ImageUploadProgress>
    </TextEditor.ImageUpload>
</TextEditor.Root>

Use image upload to drop images into the content from a URL or a local file through the TextEditor.ImageUpload overlay. TextEditor.ImageUpload is the overlay the runtime opens when commands.uploadImages() runs or a file is dropped onto the content. Nested widgets read upload state from useUploadContext(), and commands.insertImage(src, attrs?) places an image directly from a URL.

The overlay accepts handler (the upload transport, signature (file, { onProgress, signal }) => Promise<string>), maxFileCount, maxFileSize, allowedTypes, and accept. For maxFileSize, use the named constants from @primeui/vue-texteditor: TEXT_EDITOR_FILE_SIZE.ONE_MB, FIVE_MB, TEN_MB, TWENTY_FIVE_MB, FIFTY_MB.

The handler receives an AbortSignal as options.signal. Forward it to fetch/XMLHttpRequest so that cancelling or dismissing the upload aborts the in-flight network request:

const handler = (file, { onProgress, signal }) => uploadToServer(file, { signal, onProgress }); // pass signal to fetch

The overlay emits reject with { files, reason } when client-side validation fails (reason is 'max-file-count' | 'max-file-size' | 'file-type'), error with { file, error } when the handler rejects (network/server failure), and complete when uploads finish. The client-side allowedTypes check trusts the browser-reported file type and is a UX guard only — the server must re-validate; see Security.

Two sub-parts split the rendering: <TextEditor.ImageUploadDropzone> renders while no upload is in flight, and <TextEditor.ImageUploadProgress> renders during upload. Each hosts a widget that reads its state from the upload context.

<TextEditor.ImageUpload :handler="uploadHandler" :maxFileCount="3" :maxFileSize="TEXT_EDITOR_FILE_SIZE.ONE_MB" @reject="onFileReject">
    <TextEditor.ImageUploadDropzone>
        <ImageUploadDropzone />
    </TextEditor.ImageUploadDropzone>
    <TextEditor.ImageUploadProgress>
        <ImageUploadProgress />
    </TextEditor.ImageUploadProgress>
</TextEditor.ImageUpload>

The widgets pull uploads, selectFiles, onDrop, and dismiss from the upload context. The surface is the same for both dropzone and progress, since both descend from the same Upload part.

<!-- ImageUploadDropzone.vue -->
<script setup lang="ts">
import { useUploadContext } from '@primeui/vue-texteditor';

const { selectFiles, onDrop } = useUploadContext();
</script>

<template>
    <div @dragover.prevent @drop="onDrop">
        <button @click="selectFiles">Choose image</button>
    </div>
</template>

<!-- ImageUploadProgress.vue -->
<script setup lang="ts">
import { useUploadContext } from '@primeui/vue-texteditor';

const { uploads, dismiss } = useUploadContext();
</script>

<template>
    <div v-for="entry in uploads" :key="entry.id">{{ entry.fileName }} — {{ entry.progress }}%</div>
    <button @click="dismiss">Cancel</button>
</template>

useUploadContext() exposes kind: 'image' | 'document', uploads, selectFiles(), onDrop(event), and dismiss().

#Alternative: inline with slot props

The same state is also surfaced as slot props for inline cases: <TextEditor.ImageUploadDropzone v-slot="{ selectFiles, onDrop }"> and <TextEditor.ImageUploadProgress v-slot="{ uploads, dismiss }">.

<TextEditor.ImageUpload :handler="uploadHandler" @reject="onFileReject">
    <TextEditor.ImageUploadDropzone v-slot="{ selectFiles, onDrop }">
        <div @dragover.prevent @drop="onDrop">
            <button @click="selectFiles">Choose image</button>
        </div>
    </TextEditor.ImageUploadDropzone>
    <TextEditor.ImageUploadProgress v-slot="{ uploads, dismiss }">
        <div v-for="u in uploads" :key="u.id">{{ u.fileName }} — {{ u.progress }}%</div>
        <button @click="dismiss">Cancel</button>
    </TextEditor.ImageUploadProgress>
</TextEditor.ImageUpload>

#Example

Loading Demo...

#API

useUploadContext() carries the upload surface — uploads, selectFiles, onDrop, dismiss — and commands.insertImage(src, attrs?) inserts from a URL. Slots and Contexts maps the dropzone and progress outlets; API lists each signature.