Feature Overview
Notion's editor is a block-based document system. Every piece of content — a paragraph, a heading, a to-do item, a database, an image — is a "block" with a type and properties. Blocks can be nested, reordered, transformed into other block types, and synced in real time across multiple users.
This architecture is what makes Notion flexible enough to be a doc tool, a project tracker, a wiki, and a database — all in one product.
The Block Data Model
Every Notion page is a tree of blocks:
interface Block {
id: string;
type: 'paragraph' | 'heading_1' | 'heading_2' | 'bulleted_list' |
'numbered_list' | 'todo' | 'toggle' | 'code' | 'image' |
'database' | 'callout' | 'divider';
properties: Record<string, any>; // type-specific content
children: string[]; // ordered list of child block IDs
parent_id: string | null;
created_at: string;
updated_at: string;
}
// Example paragraph block
{
id: "abc123",
type: "paragraph",
properties: {
rich_text: [
{ type: "text", text: "Hello ", bold: false },
{ type: "text", text: "world", bold: true }
]
},
children: [],
parent_id: "page_xyz"
}
The rich_text array within a block handles inline formatting — bold, italic, code, links — without a separate node type for each. This keeps the block tree flat while supporting rich inline content.
Tech Approach
Option 1: Build on BlockNote (Recommended)
BlockNote is an open-source Notion-like editor built on TipTap and ProseMirror. It handles:
- —Block types (paragraph, heading, list, code, image)
- —Slash commands (
/menu to insert block types) - —Drag-and-drop reordering
- —Markdown shortcuts
import { BlockNoteEditor } from "@blocknote/core";
import { BlockNoteView, useBlockNote } from "@blocknote/react";
function Editor() {
const editor = useBlockNote({
onEditorContentChange: (editor) => {
const blocks = editor.topLevelBlocks;
saveToDatabase(blocks);
}
});
return <BlockNoteView editor={editor} />;
}
Building on BlockNote gives you 80% of Notion's editor in 1–2 days.
Option 2: Build on TipTap directly
TipTap is lower-level than BlockNote — you define your own node types, extensions, and commands. More flexibility, more work.
Use TipTap when you need custom block types that BlockNote doesn't support (like Notion's databases, or specialized chart blocks).
Option 3: Build from scratch with ProseMirror
Only if you have specific requirements that existing libraries can't meet. ProseMirror is the foundation under TipTap — extremely powerful, extremely complex. 8–12 weeks minimum to build a production editor.
Real-Time Collaboration
Adding collaborative editing (multiple cursors, conflict-free concurrent edits) requires a CRDT (Conflict-free Replicated Data Type) — specifically Yjs, which both BlockNote and TipTap support natively.
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';
const ydoc = new Y.Doc();
const provider = new WebsocketProvider(
'wss://your-yjs-server.com',
'document-room-id',
ydoc
);
// BlockNote integration
const editor = useBlockNote({
collaboration: {
provider,
fragment: ydoc.getXmlFragment("document-store"),
user: { name: "Alice", color: "#7CFC00" }
}
});
The Yjs provider handles operational transforms — when two users edit the same paragraph simultaneously, both edits are merged correctly without conflicts or lost data.
Slash Commands
The / menu is the UX pattern that makes block editors discoverable. Implementation:
// BlockNote handles this natively
// For TipTap, use the @tiptap/suggestion extension
const slashCommands = [
{ title: "Heading 1", command: ({ editor }) => editor.chain().toggleHeading({ level: 1 }).run() },
{ title: "To-do list", command: ({ editor }) => editor.chain().toggleTaskList().run() },
{ title: "Code block", command: ({ editor }) => editor.chain().toggleCodeBlock().run() },
];
Simplified Steps to Ship
Week 1:
- —Set up BlockNote with your tech stack
- —Implement save/load from your database
- —Add the core block types your use case needs
Week 2:
- —Add real-time collaboration with Yjs (if needed)
- —Implement presence indicators (user avatars on shared docs)
- —Add image upload (Supabase Storage or Cloudinary)
Week 3:
- —Custom block types specific to your product
- —Export functionality (Markdown, PDF)
- —Version history (save snapshots on each edit, allow revert)
Estimated Complexity
| Feature | Complexity | Time | |---|---|---| | Basic rich text editor (BlockNote) | Low | 1–2 days | | Custom block types | Medium | 3–5 days | | Real-time collaboration | High | 1 week | | Full Notion-like system with databases | Very High | 6–10 weeks |
Written by
Michael
Lead Engineer, Greta Agency
Michael has built rich text editors, block-based content systems, and collaborative document tools.