Documentation Index
Fetch the complete documentation index at: https://mintlify.com/prosekit/prosekit/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Image extension provides support for embedding images in your editor with features like drag-and-drop upload, paste handling, dimension management, and asynchronous upload with progress tracking.
Installation
import { defineImage } from '@prosekit/extensions'
Basic Usage
import { defineImage } from '@prosekit/extensions'
import { createEditor } from '@prosekit/core'
const editor = createEditor({
extensions: [
defineImage(),
// ... other extensions
],
})
Image Attributes
Images support the following attributes:
interface ImageAttrs {
/**
* The image URL.
*/
src?: string | null
/**
* The image width in pixels.
*/
width?: number | null
/**
* The image height in pixels.
*/
height?: number | null
}
Commands
insertImage
Inserts an image node at the current position or specified position.
editor.commands.insertImage({
src: 'https://example.com/image.jpg',
width: 640,
height: 480,
})
Parameters:
src: The image URL (optional, can be null for placeholders)
width: Image width in pixels (optional)
height: Image height in pixels (optional)
uploadImage
Uploads an image file and inserts it with a temporary URL that updates once the upload completes.
editor.commands.uploadImage({
file: imageFile,
uploader: async ({ file }) => {
// Upload logic here
const url = await uploadToServer(file)
return url
},
})
Parameters:
file: The File object to upload
uploader: Function that handles the upload and returns the final URL
pos: Optional position to insert the image
replace: Whether to replace an existing image at pos (default: false)
onError: Optional error handler
Upload Handler
The extension supports automatic file handling for drag-and-drop and paste events.
defineImageUploadHandler
Sets up automatic image upload handling:
import { defineImageUploadHandler } from '@prosekit/extensions'
const extension = defineImageUploadHandler({
uploader: async ({ file }) => {
const formData = new FormData()
formData.append('image', file)
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
const data = await response.json()
return data.url
},
onError: ({ error, file }) => {
console.error('Upload failed:', error, file)
},
})
Options:
uploader: Async function that uploads the file and returns the URL
canDrop: Optional predicate to determine if a dropped file should be handled
canPaste: Optional predicate to determine if a pasted file should be handled
Uploader Function
The uploader function receives options and should return a promise:
type Uploader<T> = (options: UploaderOptions) => Promise<T>
interface UploaderOptions {
file: File
uploadTask: UploadTask<T>
}
Upload Progress Tracking
Track upload progress using the UploadTask class:
import { UploadTask } from '@prosekit/extensions'
const uploadTask = new UploadTask({
file: imageFile,
uploader: async ({ file, uploadTask }) => {
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable) {
const progress = event.loaded / event.total
uploadTask.setProgress(progress)
}
})
// Upload logic...
return uploadedUrl
},
})
// Listen to progress
uploadTask.addEventListener('progress', (event) => {
console.log('Upload progress:', event.detail.progress)
})
Complete Upload Example
import { createEditor } from '@prosekit/core'
import { defineImage, defineImageUploadHandler } from '@prosekit/extensions'
const editor = createEditor({
extensions: [
defineImage(),
defineImageUploadHandler({
uploader: async ({ file, uploadTask }) => {
// Create FormData
const formData = new FormData()
formData.append('file', file)
// Upload with progress tracking
const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
})
if (!response.ok) {
throw new Error('Upload failed')
}
const data = await response.json()
return data.url // Return final URL
},
onError: ({ error, file, uploadTask }) => {
console.error('Upload failed:', error)
// Show error notification to user
alert(`Failed to upload ${file.name}`)
},
}),
],
})
// Manual upload
const fileInput = document.querySelector('input[type="file"]')
fileInput?.addEventListener('change', (event) => {
const file = event.target.files?.[0]
if (file && file.type.startsWith('image/')) {
editor.commands.uploadImage({
file,
uploader: async ({ file }) => {
// Upload and return URL
return uploadedUrl
},
})
}
})
Filtering Files
Control which files can be dropped or pasted:
defineImageUploadHandler({
uploader: async ({ file }) => {
// Upload logic
},
canDrop: (file) => {
// Only allow images under 5MB
return file.type.startsWith('image/') && file.size < 5 * 1024 * 1024
},
canPaste: (file) => {
// Only allow PNG and JPEG
return ['image/png', 'image/jpeg'].includes(file.type)
},
})
Replacing Images
Replace an existing image by specifying position and replace option:
editor.commands.uploadImage({
file: newImageFile,
pos: imagePosition,
replace: true,
uploader: async ({ file }) => uploadedUrl,
})
Error Handling
Handle upload errors gracefully:
interface ImageUploadErrorHandlerOptions {
file: File
error: unknown
uploadTask: UploadTask<string>
}
const errorHandler: ImageUploadErrorHandler = ({ file, error, uploadTask }) => {
console.error('Upload failed:', error)
// Show user-friendly error
if (error instanceof Error) {
if (error.message.includes('network')) {
alert('Network error. Please check your connection.')
} else if (error.message.includes('size')) {
alert('File is too large.')
} else {
alert('Upload failed. Please try again.')
}
}
// Cleanup
uploadTask.abort()
}
editor.commands.uploadImage({
file: imageFile,
uploader: uploadFunction,
onError: errorHandler,
})
Utilities
replaceImageURL
Replace temporary URLs with final URLs after upload:
import { replaceImageURL } from '@prosekit/extensions'
replaceImageURL(editor.view, tempURL, finalURL)
Styling Images
Images render as standard <img> elements and can be styled with CSS:
.ProseMirror img {
max-width: 100%;
height: auto;
border-radius: 4px;
}
.ProseMirror img.ProseMirror-selectednode {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
Image Properties
Images are defined as:
- Block-level nodes in the document structure
- Draggable for repositioning
- Defining (cannot be split or joined)
Advanced Features
Custom Upload Service
class ImageUploadService {
async upload(file: File): Promise<string> {
// Optimize image before upload
const optimized = await this.optimizeImage(file)
// Upload to CDN
const url = await this.uploadToCDN(optimized)
return url
}
private async optimizeImage(file: File): Promise<File> {
// Compression logic
return file
}
private async uploadToCDN(file: File): Promise<string> {
// CDN upload logic
return 'https://cdn.example.com/image.jpg'
}
}
const uploadService = new ImageUploadService()
defineImageUploadHandler({
uploader: async ({ file }) => await uploadService.upload(file),
})
Image Validation
function validateImage(file: File): boolean {
// Check file type
const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
if (!validTypes.includes(file.type)) {
return false
}
// Check file size (max 10MB)
if (file.size > 10 * 1024 * 1024) {
return false
}
return true
}
defineImageUploadHandler({
canDrop: validateImage,
canPaste: validateImage,
uploader: async ({ file }) => uploadedUrl,
})
- Use with
defineFile() for general file handling
- Combine with
defineTable() to embed images in table cells
- Works with drag-and-drop indicators for visual feedback