A modern and beautiful Notion-style rich text editor for Vue 3 with collaborative features.
- Headings (H1, H2, H3) with proper hierarchy
- Text Formatting: Bold, italic, underline, strikethrough, highlight, superscript, subscript
- Lists: Bullet lists, numbered lists, and nested task lists with checkboxes
- Code Blocks: Syntax highlighting for multiple languages (JavaScript, TypeScript, HTML, CSS)
- Blockquotes and horizontal rules
- Text Alignment: Left, center, right, justify
- Typography: Smart quotes, dashes, and ellipses
- Image Upload: Drag & drop or paste images with resize capabilities
- YouTube Embeds: Embed videos with customizable controls
- File Handling: Support for PNG, JPEG, GIF, and WebP images
- Mentions: @mention system with suggestions
- Emojis: GitHub emoji picker with emoticon support
- Slash Commands: Quick access to all editor features via
/commands - Collapsible Details: Expandable content sections
- Mathematics: LaTeX math expressions (inline and block)
- Drag & Drop: Reorder content blocks with drag handles
- Real-time Collaboration: Powered by Yjs for multi-user editing
- Auto-save: Content automatically saved to localStorage
- Persistent State: Content restored on page reload
- Modern Design: Clean, minimal interface
- Dark/Light Theme: Theme toggle with system preference detection
- Responsive: Works seamlessly on desktop and mobile devices
- Keyboard Shortcuts: Full keyboard navigation support
- Floating Toolbar: Context-aware formatting toolbar
- Nuxt 3.0+
- Node.js 16+
npm install notionuxt
# or
yarn add notionuxt
# or
pnpm add notionuxtThat's it! No additional dependencies required - everything is bundled.
📦 Package: npmjs.com/package/notionuxt
The package automatically configures Tailwind CSS for you! Just use the provided helper:
Wrap your Tailwind config with the withNotionUxt helper:
// tailwind.config.js
const { withNotionUxt } = require('notionuxt/lib/tailwind-config')
module.exports = withNotionUxt({
content: [
'./components/**/*.{js,vue,ts}',
'./layouts/**/*.vue',
'./pages/**/*.vue',
// notionuxt paths are automatically added!
],
theme: {
extend: {},
},
plugins: [],
})The @source directive is automatically included in the package styles. Just import the styles:
@import 'tailwindcss';
@import 'notionuxt/styles';
/* notionuxt content paths are automatically included! */<script setup>
import { NotionEditor } from 'notionuxt'
// Styles are automatically imported
</script>
<template>
<div>
<NotionEditor />
</div>
</template><script setup>
import { NotionEditor } from 'notionuxt'
const editorOptions = {
content: '<p>Start writing...</p>',
editable: true,
autofocus: true,
placeholder: 'Type something...'
}
</script>
<template>
<div>
<NotionEditor
:options="editorOptions"
class="my-custom-editor"
/>
</div>
</template><script setup>
import { NotionEditor, useEditor } from 'notionuxt'
import { ref } from 'vue'
const editorRef = ref()
const { editor } = useEditor({
content: '<p>Hello World!</p>',
editable: true
})
function getContent() {
if (editor.value) {
console.log(editor.value.getHTML())
}
}
function setContent() {
if (editor.value) {
editor.value.commands.setContent('<p>New content!</p>')
}
}
</script>
<template>
<div>
<NotionEditor ref="editorRef" />
<button @click="getContent">
Get Content
</button>
<button @click="setContent">
Set Content
</button>
</div>
</template>The package includes default styles that work out of the box. For full functionality, make sure to import the styles:
import 'notionuxt/styles'You can customize the appearance by overriding CSS variables. Important: Define your custom CSS variables after importing notionuxt/styles to ensure they override the defaults:
/* Import notionuxt styles first */
@import 'tailwindcss';
@import 'notionuxt/styles';
/* Then define your custom variables - they will override the defaults */
:root {
--background: #ffffff;
--foreground: #000000;
--card: #ffffff;
--card-foreground: #000000;
--popover: #ffffff;
--popover-foreground: #000000;
--primary: #3b82f6;
--primary-foreground: #ffffff;
--secondary: #f1f5f9;
--secondary-foreground: #000000;
--accent: #f1f5f9;
--accent-foreground: #000000;
--muted: #f8fafc;
--muted-foreground: #64748b;
--border: #e2e8f0;
--input: #e2e8f0;
--ring: #3b82f6;
--destructive: #ef4444;
--destructive-foreground: #ffffff;
--radius: 0.75rem;
}
/* Override dark mode variables */
.dark {
--background: #1a1a1a;
--foreground: #ffffff;
--card: #1a1a1a;
--card-foreground: #ffffff;
--popover: #1a1a1a;
--popover-foreground: #ffffff;
--primary: #3b82f6;
--primary-foreground: #ffffff;
--secondary: #2a2a2a;
--secondary-foreground: #ffffff;
--accent: #2a2a2a;
--accent-foreground: #ffffff;
--muted: #2a2a2a;
--muted-foreground: #a0a0a0;
--border: #3a3a3a;
--input: #3a3a3a;
--ring: #3b82f6;
--destructive: #ef4444;
--destructive-foreground: #ffffff;
}
body {
@apply bg-background text-foreground;
}Alternatively, you can scope variables to a specific class:
<template>
<NotionEditor class="my-editor" />
</template>
<style>
.my-editor {
--background: #ffffff;
--foreground: #000000;
/* Add more custom variables */
}
</style>The editor uses the following CSS variables for theming. Default values are defined in the defaults layer and can be overridden:
:root {
--background: #ffffff;
--foreground: #000000;
--primary: #3b82f6;
--secondary: #f1f5f9;
--border: #e2e8f0;
--muted: #f8fafc;
--muted-foreground: #64748b;
--card: #ffffff;
--card-foreground: #000000;
--popover: #ffffff;
--popover-foreground: #000000;
--primary-foreground: #ffffff;
--secondary-foreground: #000000;
--accent: #f1f5f9;
--accent-foreground: #000000;
--destructive: #ef4444;
--destructive-foreground: #ffffff;
--input: #e2e8f0;
--ring: #3b82f6;
--radius: 0.75rem;
/* ... and more */
}interface EditorOptions {
content?: string // Initial HTML content
editable?: boolean // Whether the editor is editable (default: true)
autofocus?: boolean // Auto-focus on mount (default: true)
placeholder?: string // Placeholder text
}import {
createEditorExtensions,
useEditor,
useEditorActions
} from 'notionuxt'
// Create a custom editor instance
const { editor } = useEditor({
content: '<p>Custom content</p>',
editable: true
})
// Access editor actions
const { toggleBold, toggleItalic } = useEditorActions(editor)
// Create custom extensions
const extensions = createEditorExtensions(editor, lowlight)Ctrl/Cmd + B- BoldCtrl/Cmd + I- ItalicCtrl/Cmd + U- UnderlineCtrl/Cmd + Shift + X- StrikethroughCtrl/Cmd + Shift + 7- Numbered listCtrl/Cmd + Shift + 8- Bullet listCtrl/Cmd + Shift + 9- Blockquote
Type / followed by:
/h1,/h2,/h3- Headings/code- Code block/image- Image upload/youtube- YouTube embed/math- Math expression/todo- Task list/quote- Blockquote/divider- Horizontal rule
import { createEditorExtensions } from '@notion-vue/editor'
import { CustomExtension } from './my-extensions'
const editor = ref(null) // Initialize editor ref first
const lowlight = createLowlight(all)
const customExtensions = createEditorExtensions(editor, lowlight)
customExtensions.push(CustomExtension)
editor.value = new Editor({
extensions: customExtensions,
// ... other options
})The editor supports real-time collaboration via Yjs:
import { NotionEditor } from 'notionuxt'
import { WebrtcProvider } from 'y-webrtc'
import * as Y from 'yjs'
// Set up Yjs document
const ydoc = new Y.Doc()
const provider = new WebrtcProvider('notion-editor-room', ydoc)Then use it in your Vue template:
<NotionEditor :options="{ ydoc }" />The editor is fully responsive and optimized for:
- Desktop: Full feature set with floating toolbar
- Tablet: Adapted touch interactions
- Mobile: Optimized mobile editing experience
We welcome contributions! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes and test thoroughly
- Run linting:
npm run lint:fixorpnpm lint:fix - Commit your changes:
git commit -m 'Add some feature' - Push to the branch:
git push origin feature/your-feature-name - Open a Pull Request
- Follow Vue 3 Composition API patterns
- Use TypeScript for type safety
- Follow the existing code style
- Add tests for new features
- Update documentation as needed
For more details, see the Contributing Guide (if available).
This project is licensed under the MIT License - see the LICENSE file for details.
Nahim Hossain Shohan
- GitHub: @nh-shohan
- LinkedIn: Nahim Hossain Shohan
- TipTap - The headless editor framework
- Vue.js - The progressive JavaScript framework
- Yjs - Real-time collaboration framework
- Nuxt.js - The Vue.js framework
- shadcn-vue - Beautiful Vue UI components
- Tailwind CSS - Utility-first CSS framework
Made with ❤️ and lots of ☕