@twelvemonday/blog-editor
v1.0.9
Published
WordPress-style block blog editor for React — categories, tags, SEO panel, media library, and HTML serialization
Maintainers
Readme
@twelvemonday/blog-editor
WordPress-style block blog editor for React. Drag-and-drop blocks, rich paste, SEO panel, image resize, custom theming, and HTML serialization.
Published by Twelve Monday.
Install
npm install @twelvemonday/blog-editorPeer dependencies:
npm install react react-dom @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities lucide-reactImage upload (onUploadImage)
Editor kahan upload kare ye tum decide karte ho. Sirf ek function pass karo — editor File bhejega, tum URL return karo.
Works for featured image and image blocks.
<BlogEditor
onUploadImage={async (file) => {
// S3, Cloudinary, apni API — jo marzi
const form = new FormData();
form.append('file', file);
const res = await fetch('https://your-api.com/upload', { method: 'POST', body: form });
const { url } = await res.json();
return url; // public image URL string
}}
// ...
/>Agar onUploadImage nahi doge to file picker hide rahega — user image URL se add kar sakta hai.
uploadFile prop ab deprecated hai — onUploadImage use karo.
onListMedia returns items for your media library modal. Filter by product/site in your API — the editor just renders what you return.
onListMedia={async () => {
const res = await fetch('/api/admin/media?productSlug=12pilot&context=blog');
return res.json();
}}Delete from library (optional):
onDeleteMedia={async (item) => {
await fetch('/api/admin/media', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: item.id }),
});
}}Categories (WordPress-style)
Pass availableCategories for checkbox multi-select in the Post sidebar. Saved on document.categories: string[].
<BlogEditor
availableCategories={[
'Product Development',
'AI & Automation',
'Startups',
'Engineering',
'Design',
'Marketing',
'Company News',
]}
// ...
/>Quick start
'use client';
import '@twelvemonday/blog-editor/styles.css';
import { BlogEditor, type EditorDocument } from '@twelvemonday/blog-editor';
export function AdminEditor({ post }: { post: EditorDocument }) {
return (
<BlogEditor
initial={post}
backHref="/admin/posts"
previewUrl={(slug) => `/blogs/${slug}`}
siteName="My Site"
baseUrl="https://example.com"
theme={{ primary: '#04644A', primaryHover: '#035239' }}
onUploadImage={async (file) => {
const form = new FormData();
form.append('file', file);
const res = await fetch('/api/upload', { method: 'POST', body: form });
const { url } = await res.json();
return url;
}}
onSave={async (doc) => {
await fetch('/api/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(doc),
});
return { slug: doc.slug };
}}
/>
);
}Theming
| Key | Default | Description |
|-----|---------|-------------|
| primary | #04644A | Buttons, accents, focus |
| primaryHover | #035239 | Primary hover |
| primaryMuted | #f0fdf4 | Light backgrounds |
| background | #f0f0f1 | Editor chrome |
| surface | #ffffff | Canvas / panels |
HTML utilities
import { htmlToBlocks, blocksToHtml } from '@twelvemonday/blog-editor';Tailwind
@source "../node_modules/@twelvemonday/blog-editor/dist/**/*.js";Changelog
1.0.9
- Media library: WordPress-style Attachment details sidebar (filename, date, size, dimensions, file URL)
onDeleteMediacallback + delete button in media library- Extended
MediaItemtype (id,bytes,width,height,mimeType,publicId) - Upload refreshes media library tab after success
1.0.8
onDeleteMediaprop onBlogEditor/EditorProvider- Media library grid selection checkmark UI
License
MIT
