- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Form
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- Menubar
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Rich Editor
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
- Typography
"use client"
import { EditorProvider } from "../ui/rich-editor"
import { Editor } from "../ui/rich-editor/Editor"
export function RichEditorDemo() {
return (
<div className="mx-auto w-full overflow-y-auto">
<EditorProvider>
<Editor notionBased />
</EditorProvider>
</div>
)
}
Installation
Install the rich-editor component with all dependencies:
pnpm dlx shadcn@latest add https://ui-v4-livid.vercel.app/r/styles/new-york-v4/rich-editor.json
This automatically installs:
- All required shadcn components (button, card, dialog, popover, etc.)
- All npm packages (framer-motion, lucide-react, sonner, next-themes, color, etc.)
- All editor files including hooks and utilities
Configure the theme provider:
The rich editor includes dark mode toggle functionality. To make it work, you need to wrap your app with the ThemeProvider from next-themes.
See Dark Mode Setup for detailed instructions on configuring the theme provider.
Usage
Basic Editor (Empty State)
Start with an empty editor in Notion mode:
import { EditorProvider } from "@/components/ui/rich-editor"
import { Editor } from "@/components/ui/rich-editor/editor"
export default function MyEditor() {
return (
<EditorProvider>
<Editor notionBased />
</EditorProvider>
)
}Editor with Initial Content
Load the editor with pre-existing content:
import { EditorProvider } from "@/components/ui/rich-editor"
import type { ContainerNode } from "@/components/ui/rich-editor"
import { createDemoContent } from "@/components/ui/rich-editor/demo-content"
import { Editor } from "@/components/ui/rich-editor/editor"
export default function MyEditor() {
const initialContainer: ContainerNode = {
id: "root",
type: "container",
children: createDemoContent(),
attributes: {},
}
return (
<EditorProvider initialContainer={initialContainer}>
<Editor notionBased />
</EditorProvider>
)
}Custom Upload Handler
Integrate with your backend for image and video uploads using a single handler:
import { EditorProvider } from "@/components/ui/rich-editor"
import { Editor } from "@/components/ui/rich-editor/editor"
export default function MyEditor() {
const handleUpload = async (file: File): Promise<string> => {
// Upload to your backend API (handles both images and videos)
const formData = new FormData()
formData.append("file", file)
const response = await fetch("/api/upload", {
method: "POST",
body: formData,
})
const data = await response.json()
return data.url // Return the permanent URL
}
return (
<EditorProvider>
<Editor notionBased onUploadImage={handleUpload} />
</EditorProvider>
)
}Features
✨ Version 0.2.0 - New Features!
- 🎨 Notion-Style Mode - Cover images and enhanced first-block styling
- 🖼️ Free-Floating Images - Drag images anywhere with resize handles and z-index control
- 📝 Simplified Lists - Easy-to-use bulleted (li) and numbered (ol) lists
- 🎯 Template Switcher - Blog, Gallery, and Halloween templates with one-click switching
- 🐛 Mobile Drag & Drop - Full touch support with visual feedback
- ✨ Bug Fixes - Selection improvements, better image handling, enhanced video blocks
Core Features
- 📋 Block-Based Architecture - Every element is an independent, draggable block
- ✨ Rich Text Formatting - Bold, italic, underline, strikethrough, and combinations
- 🎨 Inline Element Types - Mix H1-H6 styles and inline code within paragraphs
- 🎨 Color Support - Preset Tailwind colors + custom hex/RGB colors
- 📏 Font Size Control - Preset sizes + custom pixel values
- 🎨 Background Colors - Apply backgrounds to any block with presets or custom colors
- 📊 Advanced Tables - Drag columns/rows, resize, markdown import, and cell formatting
- 🖼️ Image Management - Multi-select with Ctrl+Click, drag & drop, grid layouts, free positioning
- 🔗 Smart Links - Modern link popover with URL management
- ✏️ Custom Tailwind Classes - Apply any Tailwind class with smart class picker
- ⌨️ Keyboard Shortcuts - Full keyboard navigation and formatting shortcuts
- 📱 Mobile Optimized - Sheet drawers, touch-friendly controls, automatic keyboard management
- 🌙 Dark Mode - Full dark mode support out of the box
- 🔄 Undo/Redo - Complete history management with Ctrl+Z/Ctrl+Y
- 📤 HTML Export - Export your content to clean, semantic HTML
- 👁️ Read-Only Mode - Perfect for displaying published content
Examples
Editor with Content
















"use client"
import { EditorProvider } from "../ui/rich-editor"
import type { ContainerNode } from "../ui/rich-editor"
import { createDemoContent } from "../ui/rich-editor/demo-content"
import { Editor } from "../ui/rich-editor/Editor"
export function RichEditorWithContentDemo() {
const initialContainer: ContainerNode = {
id: "root",
type: "container",
children: createDemoContent(),
attributes: {},
}
return (
<div className="mx-auto w-full overflow-y-auto">
<EditorProvider initialContainer={initialContainer}>
<Editor notionBased />
</EditorProvider>
</div>
)
}
Custom Upload Handler
















"use client"
import type { ContainerNode } from "../ui/rich-editor"
import { EditorProvider } from "../ui/rich-editor"
import { createDemoContent } from "../ui/rich-editor/demo-content"
import { Editor } from "../ui/rich-editor/Editor"
export function RichEditorCustomUploadDemo() {
const initialContainer: ContainerNode = {
id: "root",
type: "container",
children: createDemoContent(),
attributes: {},
}
// Example custom upload handler for both images and videos
const handleUpload = async (file: File): Promise<string> => {
// Simulate upload delay
await new Promise((resolve) => setTimeout(resolve, 1000))
// In production, you would:
// 1. Upload to your backend API
// 2. Return the permanent URL
// Example:
// const formData = new FormData();
// formData.append('file', file);
// const response = await fetch('/api/upload', {
// method: 'POST',
// body: formData
// });
// const data = await response.json();
// return data.url;
// For demo purposes, return a data URL
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result as string)
reader.onerror = reject
reader.readAsDataURL(file)
})
}
return (
<div className="mx-auto w-full overflow-y-auto">
<EditorProvider initialContainer={initialContainer}>
<Editor notionBased onUploadImage={handleUpload} />
</EditorProvider>
</div>
)
}
Read-Only Mode
















"use client"
import { EditorProvider } from "../ui/rich-editor"
import type { ContainerNode } from "../ui/rich-editor"
import { createDemoContent } from "../ui/rich-editor/demo-content"
import { Editor } from "../ui/rich-editor/Editor"
export function RichEditorReadonlyDemo() {
const initialContainer: ContainerNode = {
id: "root",
type: "container",
children: createDemoContent(),
attributes: {},
}
return (
<div className="mx-auto w-full overflow-y-auto">
<EditorProvider initialContainer={initialContainer}>
<Editor readOnly notionBased />
</EditorProvider>
</div>
)
}
API Reference
EditorProvider
Wraps your editor and provides the context for all editor operations.
| Prop | Type | Default | Description |
|---|---|---|---|
initialContainer | ContainerNode | - | The initial content structure for the editor |
debug | boolean | false | Show debug panel with state information |
children | ReactNode | - | Editor components to render |
Editor
The main editor component that renders the editing interface.
| Prop | Type | Default | Description |
|---|---|---|---|
readOnly | boolean | false | Enable read-only mode (disables editing) |
notionBased | boolean | true | Enable Notion-style mode with cover images and enhanced styling |
onUploadImage | (file: File) => Promise<string> | - | Custom upload handler for images and videos. Should return the URL of the upload |
ContainerNode
The root node type for editor content.
interface ContainerNode {
id: string
type: "container"
children: EditorNode[]
attributes: Record<string, any>
}EditorNode
Editor nodes can be text blocks, images, tables, flex containers, or nested containers.
type EditorNode = TextNode | ContainerNode
interface TextNode {
id: string
type:
| "p"
| "h1"
| "h2"
| "h3"
| "h4"
| "h5"
| "h6"
| "blockquote"
| "code"
| "li"
| "ol"
| "table"
| "thead"
| "tbody"
| "tr"
| "th"
| "td"
| "img"
| "video"
content?: string
children?: InlineNode[]
attributes?: Record<string, any>
styles?: Record<string, string>
}
interface InlineNode {
content: string
bold?: boolean
italic?: boolean
underline?: boolean
strikethrough?: boolean
code?: boolean
elementType?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "code"
href?: string
className?: string
styles?: Record<string, string>
}Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl/Cmd + B | Toggle bold |
Ctrl/Cmd + I | Toggle italic |
Ctrl/Cmd + U | Toggle underline |
Ctrl/Cmd + Z | Undo |
Ctrl/Cmd + Shift + Z | Redo |
Enter | Create new block after |
Shift + Enter | Create nested block |
Ctrl/Cmd + K | Insert link |
Ctrl + Click | Multi-select images |
Delete/Backspace | Delete block (when empty) |
- (dash space) | Convert to bulleted list |
1. (number period) | Convert to numbered list |
/ | Open command menu (planned) |
Block Types
The editor supports the following block types:
- Paragraph (p) - Standard text content with inline formatting
- Headings (h1-h6) - Six levels of headings for document structure
- Blockquote - Styled quotes and callouts with custom backgrounds
- Code Block - Syntax-friendly code display
- Lists (li/ol) - Bulleted and numbered lists with auto-conversion
- Table - Full-featured tables with drag, resize, and formatting
- Image - Single images, grid layouts, and free-positioned images
- Video - Embedded video with playback controls
- Container - Nested blocks for hierarchical content organization
Customization
Accessing Editor State
Access the editor state and actions through the context:
import {
useEditorState,
useEditorDispatch,
useContainer,
} from "@/components/ui/rich-editor/store/editor-store"
function MyComponent() {
const state = useEditorState()
const dispatch = useEditorDispatch()
const container = useContainer()
// Access current content
console.log(container)
// Access selection
console.log(state.currentSelection)
// Dispatch actions
dispatch({ type: "ADD_BLOCK", payload: { ... } })
}Export to HTML
Export your content to HTML for storage or display:
import { serializeToHTML } from "@/components/ui/rich-editor/utils/serialize-to-html"
const html = serializeToHTML(container)Custom Classes
Apply any Tailwind CSS class to text or blocks using the Custom Class Popover:
- Select text
- Click the pencil icon (✏️) in the selection toolbar
- Search for classes or type custom ones
- Apply gradients, shadows, borders, backgrounds, and more!
Example classes you can apply:
- Gradients:
bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent - Shadows:
drop-shadow-lg text-blue-600 - Borders:
border-2 border-green-600 px-2 rounded - Backgrounds:
bg-yellow-200 dark:bg-yellow-800 px-2 py-1 rounded
Notion-Style Mode
Enable Notion-style features with the notionBased prop:
<Editor notionBased />Features in Notion mode:
- Cover Images - Beautiful header images with drag-to-reposition
- First Block Styling - Automatic large heading for document titles
- Enhanced Spacing - Optimized padding and margins
- Modern Layout - Centered content with proper max-width
Templates
The editor includes pre-designed templates accessible via the Template Switcher button:
Blog Template
Perfect for articles and blog posts with:
- Professional typography
- Metadata sections
- Table of contents structure
- Code examples
Gallery Template
Ideal for photographers and artists:
- Image-focused layout
- Multiple grid configurations
- Caption support
- Portfolio-ready design
Halloween Template
Festive seasonal design with:
- Themed colors and styling
- Special decorative elements
- Holiday-appropriate content
Mobile Optimization
The editor is fully optimized for mobile devices:
- Touch-Friendly Controls - Large tap targets for all buttons
- Sheet Drawers - Mobile-optimized panels for tables, images, and settings
- Drag & Drop - Full touch support with visual feedback
- Keyboard Management - Automatic keyboard handling for inputs
- Responsive Layout - Adapts seamlessly to all screen sizes
Notes
- The editor uses Framer Motion for animations. Make sure it's installed.
- Images are stored as base64 by default. Provide a custom upload handler for production use.
- The editor is mobile-responsive and uses Sheet components on smaller screens.
- All colors and classes use Tailwind CSS and follow shadcn/ui design patterns.
- Built with TypeScript for excellent IntelliSense and type safety.
- State management uses reducers for predictable updates and undo/redo support.
Demo Content
The createDemoContent() function returns a comprehensive example document showcasing:
- All text formatting options
- Every block type
- Color and font size examples
- Image layouts
- Tables
- Nested containers
- Links and custom classes
- Keyboard shortcuts reference
- Feature documentation
Perfect for testing and learning how to structure your own content!
Credits
Created by Mina Massoud
On This Page
InstallationUsageBasic Editor (Empty State)Editor with Initial ContentCustom Upload HandlerFeatures✨ Version 0.2.0 - New Features!Core FeaturesExamplesEditor with ContentCustom Upload HandlerRead-Only ModeAPI ReferenceEditorProviderEditorContainerNodeEditorNodeKeyboard ShortcutsBlock TypesCustomizationAccessing Editor StateExport to HTMLCustom ClassesNotion-Style ModeTemplatesBlog TemplateGallery TemplateHalloween TemplateMobile OptimizationNotesDemo ContentCredits