Notion-like
The Notion-like Editor Template is a fully featured block-based editor that replicates the familiar Notion experience. It supports collaboration, AI assistance, emoji, drag & drop, advanced formatting—and it’s fully customizable.
Start plan required
The Notion-like template requires at least a Start plan subscription to use in production. You can integrate and test it during your trial period. Usage of this template is subject to our Terms of Service and Pro License.
Installation
You'll need a Tiptap account with an active subscription or trial. Create one at cloud.tiptap.dev/register.
For existing projects
npx @tiptap/cli@latest add notion-like-editor
For new projects
npx @tiptap/cli@latest init notion-like-editor
This template requires styling and configuration setup.
Styling
We stay unopinionated about styling frameworks, so you'll need to integrate it with your setup. Follow the style setup guide to ensure the editor displays correctly.
Configuration
Before running the app, configure the required constants inside tiptap-collab-utils.ts
. This is necessary to enable features like AI or collaboration
Environment Variables
Provide values for the following environment variables:
TIPTAP_COLLAB_DOC_PREFIX
- Prefix for identifying collaborative documentsTIPTAP_COLLAB_APP_ID
- Your collaboration App IDTIPTAP_COLLAB_TOKEN
- JWT token for accessing Collaboration servicesTIPTAP_AI_APP_ID
- Your AI App IDTIPTAP_AI_TOKEN
- JWT token for accessing AI services
The above environment variables should be available in the client-side. Depending on your framework, use the following prefixes to expose them to the client:
- Next.js:
NEXT_PUBLIC_
. For example,NEXT_PUBLIC_TIPTAP_COLLAB_DOC_PREFIX
. - Vite + React:
VITE_
. For example,VITE_TIPTAP_COLLAB_DOC_PREFIX
. - Other frameworks: Follow the specific rules of your framework, and define the variables in the
tiptap-collab-utils.ts
file.
JWT Authentication
Collaboration and AI features require a valid server-generated JWT token passed to the editor. See the fetchCollabToken
function in tiptap-collab-utils.ts
for an example.
See the full guide on JWT authentication.
Generate the JWT token
To get started quickly, you can use the example JWT tokens from your Tiptap Cloud account and
store them in the TIPTAP_COLLAB_TOKEN
and TIPTAP_AI_TOKEN
enviroment variables. However,
example JWT tokens are valid for a short time and should not be used in production. In production,
implement an API endpoint that generates JWTs on the server side.
room
prop
Use the room
prop to distinguish collaborative documents. Each session should use a unique room ID.
Image uploads
Image uploads require a server side that is not part of our ui components. You can find an example server that accepts an image, uploads it to s3 and then returns the permanent URL for it below. Note that you may want to implement additional checks when running this in production to prevent abuse.
server side (run with node --experimental-strip-types
)
import {
GetObjectCommand,
PutObjectCommand,
S3Client,
} from "@aws-sdk/client-s3";
import { Hono } from 'hono'
import { serve } from '@hono/node-server'
import { cors } from 'hono/cors'
import { v4 } from 'uuid'
const S3_REGION = 'xxx'
const S3_BUCKET = 'xxx'
const S3_ACCESS_KEY= 'xxx'
const S3_ACCESS_SECRET_KEY='xxx'
const PORT = 1234
const BASE_URL = `http://127.0.0.1:${PORT}`
const app = new Hono()
const client = new S3Client({
region: S3_REGION,
credentials: {
accessKeyId: S3_ACCESS_KEY,
secretAccessKey: S3_ACCESS_SECRET_KEY,
}
});
// in production, make sure to set up restrictive cors rules
app.use('/upload', cors())
// in prod, you should add sensitive rate limits for both endpoints
// you probably want to add content-type and/or filename and filesize validation
app.post('/upload', async (c) => {
const body = await c.req.arrayBuffer()
const remoteFileKey = `upload-${v4()}`
const command = new PutObjectCommand({
Bucket: S3_BUCKET,
Key: remoteFileKey,
Body: body,
ContentType: c.req.header('content-type') ?? ''
});
try{
await client.send(command)
} catch(e) {
console.error(e)
c.status(500)
return c.text('')
}
return c.text(`${BASE_URL}/files/${remoteFileKey}`)
})
app.get('/files/:name', async (c) => {
const { name } = c.req.param()
const remoteFileKey = `${name}`
// you may want to implement caching when using this in production
// or, maybe you want to use pre-signed URLs to the client can directly fetch
// the document from S3.
const command = new GetObjectCommand({
Bucket: S3_BUCKET,
Key: remoteFileKey,
})
try{
const r = await client.send(command)
return c.body(r.Body, 200, {
'content-type': r.ContentType ?? ''
})
} catch(e) {
console.error(e)
c.status(500)
return c.text('')
}
})
serve({...app, port: PORT})
In order to use the endpoint, adjust handleImageUpload
in tiptap-utils.ts
. You will want to remove all code after the validation
and replace it with something similar to this:
const response = await fetch('http://YOUR_URL/upload', {
body: file,
method: 'post',
headers: {
'content-type': file.type,
}
})
if( response.ok ) {
return response.text()
}
throw new Error('upload failed')
Usage
Import and render the NotionEditor
component in your React app:
import { NotionEditor } from '@/components/tiptap-templates/notion/notion-like-editor'
export default function App() {
return <NotionEditor room="my-document-room" placeholder="Start writing..." />
}
Features
The template includes all the essentials of a modern Notion-style editor:
-
Real-time collaboration: Live cursors and user presence
-
AI assistance: Inline AI tools for writing and editing
-
Responsive design: Mobile-ready UI with adaptive toolbars
-
Dark/light mode: Fully themed out of the box
-
Slash commands:
/
menu for quick formatting -
Floating toolbar: Context-aware formatting
-
Drag and drop: Block-level reordering
-
Emoji support: GitHub-style emoji picker
-
Mentions:
@user
autocomplete -
Rich formatting:
- Bold, italic, underline, strikethrough
- Highlight and color
- Superscript / subscript
- Syntax-highlighted code blocks
-
Block types:
- Headings, lists, blockquotes, dividers, math
-
Media support: Drag & drop image upload
-
Link management: With inline previews
-
Text alignment: Left, center, right, justified
-
Undo/Redo: Full editing history
-
Context menus: Right-click enhancements
Component Breakdown
Hooks
use-mobile
use-window-size
use-ui-editor-state
Icons
arrow-left-icon
chevron-right-icon
highlighter-icon
link-icon
more-vertical-icon
Tiptap Extensions
collaboration
,collaboration-cursor
selection-extension
link-extension
trailing-node-extension
ai-extension
emoji-extension
mention-extension
mathematics-extension
unique-id-extension
Libs
tiptap-utils
tiptap-collab-utils
UI Components
ai-menu
blockquote-button
code-block-button
color-highlight-button
,color-highlight-popover
drag-context-menu
emoji-dropdown-menu
heading-button
,heading-dropdown-menu
image-upload-button
link-popover
list-button
,list-dropdown-menu
mark-button
mention-dropdown-menu
slash-dropdown-menu
text-align-button
turn-into-dropdown
undo-redo-button
Nodes
code-block-node
image-node
,image-upload-node
list-node
paragraph-node
Primitives
button
,button-group
dropdown-menu
separator
,spacer
,toolbar
Contexts
app-context
user-context
collab-context
Collaboration
To use collaboration:
- Pass a unique
room
ID toNotionEditor
- Enable JWT auth for each user session
- User presence and cursors are handled automatically
- Operational transformation handles concurrent edits
- Sync and save are managed out-of-the-box
<NotionEditor room="team-notes" placeholder="Share your ideas..." />
AI Integration
The built-in AI tools let you:
- Generate content from prompts
- Improve existing text
- Get smart completions based on context
AI Configuration
Make sure to configure your AI provider. Check the AI Generation extension docs for setup steps.
Extendability
This template is designed to grow with your needs. New Tiptap Cloud features will be seamlessly compatible with the same UI system—no rewrites required.