@mbs-dev/react-editor
v1.13.6
Published
react editor
Readme
@mbs-dev/react-editor
A lightweight, typed React wrapper around Jodit rich‑text editor, designed for real‑world CRUD forms (blogs, products, CMS pages, etc.).
It provides:
- A simple React component
<ReactEditor /> - A reusable
config()helper for configuration - A reusable
uploaderConfig()helper - A new
onDeleteImagecallback to delete images on server when removed from editor
✨ Features
- 🧩 Simple React API
- ⚙️ Powerful config builder (
config()) - 🖼️ Image upload support
- 🗑️ New: Server‑side image delete support (
onDeleteImage) - 🔥 Fully typed (TS/TSX)
- 📦 Built for npm libraries
📦 Installation
npm install @mbs-dev/react-editor🚀 Quick Start
import React, { useMemo, useState } from "react";
import ReactEditor, { config } from "@mbs-dev/react-editor";
const MyPage = () => {
const [content, setContent] = useState("");
const editorConfig = useMemo(() => config({}), []);
return (
<ReactEditor
config={editorConfig}
value={content}
onChange={setContent}
/>
);
};🔧 Configuration (Full API)
config(params: ConfigParams)
type ConfigParams = {
includeUploader?: boolean;
apiUrl?: string;
imageUrl?: string;
onDeleteImage?: (imageUrl: string) => void | Promise<void>;
};Example
const editorConfig = useMemo(
() =>
config({
includeUploader: true,
apiUrl: `${apiUrl}/upload`,
imageUrl: blogPostImgUrl,
onDeleteImage: handleDeleteImage,
}),
[]
);🖼️ Image Uploading
The backend must return:
{
"success": true,
"data": {
"files": ["uploaded.webp"],
"messages": []
},
"msg": "Upload successful"
}Images get inserted automatically:
<img src="https://domain.com/uploads/images/filename.webp" />🗑️ NEW — Image Delete (Server Sync)
The editor detects removed <img> tags and calls your function:
TSX Implementation
const handleDeleteImage = async (imageSrc: string) => {
const filename = imageSrc.split("/").pop();
if (!filename) return;
await axios.delete(`${apiUrl}/delete/${filename}`);
};🧩 Full TSX Example (Add Blog)
const AddBlog = () => {
const [description, setDescription] = useState("");
const handleDeleteImage = async (imageSrc: string) => {
const filename = imageSrc.split("/").pop();
if (!filename) return;
await axios.delete(`${apiUrl}/delete/${filename}`);
};
const editorConfig = useMemo(
() =>
config({
includeUploader: true,
apiUrl: `${apiUrl}/upload`,
imageUrl: blogPostImgUrl,
onDeleteImage: handleDeleteImage,
}),
[]
);
return (
<ReactEditor
config={editorConfig}
value={description}
onChange={setDescription}
/>
);
};🧩 Symfony API Platform Full Example
1. Upload base directory
config/services.yaml
parameters:
blog_post_images: '%kernel.project_dir%/public/uploads/images_dir'2. Symfony Upload & Delete Service
<?php
final class UploaderService
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly ParameterBagInterface $params
) {}
public function uploadBlogPostImages(array $uploadedFiles): array
{
if ($uploadedFiles === []) {
throw new BadRequestHttpException('Les fichiers sont requis.');
}
$uploadedFileNames = [];
foreach ($uploadedFiles as $file) {
if (!$file instanceof UploadedFile) {
continue;
}
$image = new BlogPostImages();
$image->setImageFile($file);
$this->entityManager->persist($image);
$uploadedFileNames[] = $image->getImage();
}
if ($uploadedFileNames === []) {
throw new BadRequestHttpException('Aucun fichier valide n’a été fourni.');
}
$this->entityManager->flush();
return $uploadedFileNames;
}
public function deleteBlogPostImage(string $filename): void
{
$cleanName = basename($filename);
$imageEntity = $this->entityManager
->getRepository(BlogPostImages::class)
->findOneBy(['image' => $cleanName]);
if (!$imageEntity) {
throw new NotFoundHttpException("L'image demandée n'existe pas.");
}
$fileDir = str_replace('\\', '/', $this->params->get('blog_post_images'));
$filePath = rtrim($fileDir, '/') . '/' . $cleanName;
if (is_file($filePath)) {
unlink($filePath);
}
$this->entityManager->remove($imageEntity);
$this->entityManager->flush();
}
}3. Delete endpoint
public function deleteImage(string $filename): JsonResponse
{
$this->uploaderService->deleteBlogPostImage($filename);
return new JsonResponse([
'success' => true,
'message' => "L'image a été supprimée avec succès.",
]);
}4. React Integration
export const blogPostImgUrl = `${apiUrl}/uploads/post`;const handleDeleteImage = async (src: string) => {
const filename = src.split("/").pop();
if (filename)
await axios.delete(`${apiUrl}/post/delete/${filename}`);
};📘 API Summary
| Feature | Supported | |-------------------|-----------| | Image upload | ✅ | | Image delete sync | ✅ | | TypeScript ready | ✅ | | Config builder | ✅ |
📝 License
MIT — free for commercial and private use.
