npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

v-notion-editor

v0.1.5

Published

A Notion-style Vue 3 editor.

Downloads

114

Readme

Vue Notion Editor

A powerful, feature-rich Vue 3 rich text editor component inspired by Notion, It delivers a complete editing experience with slash commands, context menus, drag-and-drop support, advanced formatting, enhanced tables, Masonry Grids and image upload capabilities.

Features

  • Rich Text Formatting: Bold, italic, underline, strikethrough, code, and color formatting
  • Advanced Block Types: Headings, blockquotes, code blocks, lists, and task lists
  • Table Support: Create, edit, and manipulate tables with advanced cell & row operations, including interactive drag-to-add/remove rows.
  • Masonry Grid Support: Design and control masonry grids with column resizing, grid controls, and item-level customization.
  • Image Handling: Drag-and-drop image uploads with customizable upload functions.
  • File Handler: Smart paste handling for images and rich content
  • Image Resizing & Alignment: Smoothly resize images with interactive handles and align them left, right, or center.
  • Drag & Drop: Reorder content blocks with visual drag handles
  • Slash Commands: Quick content insertion with "/" command menu
  • Link Management: Smart link insertion and editing with bubble menu
  • Text Alignment: Left, center, right, and justify alignment options
  • Bubble Menu: Context-sensitive formatting toolbar
  • Undo/Redo: Full history management with keyboard shortcuts
  • Task Lists: Interactive checkboxes and task management
  • Customizable: Flexible styling with CSS variables and custom themes
  • TypeScript Support: Fully typed for enhanced developer experience

Demo

Live Demo

Installation

# npm
npm install v-notion-editor

# yarn
yarn add v-notion-editor

# pnpm
pnpm add v-notion-editor

Don't forget to follow me on GitHub!

Basic Usage

<script setup>
import { ref } from 'vue'
import { NotionEditor, NotionToolbar } from 'v-notion-editor'
import 'v-notion-editor/style.css'

const content = ref('<p>Start writing your content here...</p>')

const handleImageUpload = async (files) => {
   // Implement your image upload logic
   const fileToBase64 = (file) => {
      return new Promise((resolve, reject) => {
         const reader = new FileReader()
         reader.onload = () => resolve(reader.result)
         reader.onerror = reject
         reader.readAsDataURL(file)
      })
   }

   return await Promise.all(files.map(fileToBase64))
}
</script>

<template>
   <div>
      <NotionToolbar />
      <NotionEditor
         v-model="content"
         :image-upload-options="{
            onUpload: handleImageUpload,
            maxFileSize: 10 * 1024 * 1024, // 10MB
         }" />
   </div>
</template>

📁 Importing Styles

You must import the CSS styles:

import 'v-notion-editor/style.css'

Components

NotionEditor

The main editor component that provides the rich text editing interface.

Props

| Prop | Type | Default | Description | | -------------------- | -------------------- | ------- | --------------------------------------- | | modelValue | string | '' | The HTML content of the editor | | imageUploadOptions | ImageUploadOptions | {} | Configuration for image upload handling |

Events

| Event | Parameters | Description | | ------------------- | --------------- | ------------------------------- | | update:modelValue | value: string | Emitted when content changes | | onFocus | - | Emitted when editor gains focus | | onBlur | - | Emitted when editor loses focus |

Image Upload Options

interface ImageUploadOptions {
   onUpload?: (files: File[]) => Promise<string[]>
   maxFileSize?: number // in bytes, default: 10MB
   allowedTypes?: string[] // default: ['image/png', 'image/jpeg', 'image/gif', 'image/webp']
   onOpenFileDialog?: (multiple: boolean) => Promise<string[] | File[]> // custom file picker
}

NotionToolbar

A comprehensive toolbar component with formatting options.

Props

| Prop | Type | Default | Description | | ----------------- | ---------- | --------- | --------------------------------- | | visibleToolbars | string[] | All tools | Array of toolbar items to display | | slashCommand | boolean | true | Enables the slash command menu | | showBubbleMenu | boolean | true | Enables the bubble menu | | showDragHandle | boolean | true | Enables the drag handle |

Available Toolbar Items

headings, bold, italic, underline, strike, code, color, link, bulletList, orderedList, taskList, blockquote, table, grid, align, image, divider, undo, redo

Usage Examples

1. Custom Toolbar Configuration

<script setup>
import { NotionEditor, NotionToolbar } from 'v-notion-editor'

const customToolbars = ['headings', 'bold', 'italic', 'bulletList', 'orderedList', 'link', 'image']
</script>

<template>
   <div>
      <NotionToolbar :visible-toolbars="customToolbars" />
      <NotionEditor v-model="content" />
   </div>
</template>

2. Editor Instance Access

<script setup>
import { ref, onMounted } from 'vue'
import { NotionEditor } from 'v-notion-editor'

const editorRef = ref(null)

const insertCustomContent = () => {
   const editor = editorRef.value?.editor
   if (editor) {
      editor.chain().focus().insertContent('<p>Custom inserted content!</p>').run()
   }
}

const getEditorContent = () => {
   const editor = editorRef.value?.editor
   if (editor) {
      console.log('HTML:', editor.getHTML())
      console.log('JSON:', editor.getJSON())
      console.log('Text:', editor.getText())
   }
}
</script>

<template>
   <div>
      <button @click="insertCustomContent">Insert Content</button>
      <button @click="getEditorContent">Get Content</button>

      <NotionEditor ref="editorRef" v-model="content" />
   </div>
</template>

3. Read-Only Content Display

To display editor content with the same styling in read-only mode, wrap your content with the v-notion-editor class:

<script setup>
import 'v-notion-editor/style.css'

const savedContent = ref(`
  <h1>My Document Title</h1>
  <p>This content will have the same styling as in the editor.</p>
  <blockquote>Important note with proper formatting</blockquote>
`)
</script>

<template>
   <!-- Read-only content with editor styling -->
   <div class="v-notion-editor">
      <div v-html="savedContent" class="read-mode" />
   </div>
</template>

Keyboard Shortcuts

| Shortcut | Action | | ---------------------- | ------------------ | | Ctrl/Cmd + B | Bold | | Ctrl/Cmd + I | Italic | | Ctrl/Cmd + U | Underline | | Ctrl/Cmd + Shift + S | Strikethrough | | Ctrl/Cmd + E | Code | | Ctrl/Cmd + K | Link | | Ctrl/Cmd + Z | Undo | | Ctrl/Cmd + Shift + Z | Redo | | Ctrl/Cmd + Shift + L | Bullet List | | Ctrl/Cmd + Shift + O | Ordered List | | Ctrl/Cmd + Enter | Hard Break | | / | Slash command menu |

Slash Commands

Type / to access quick content insertion:

Styling

Customize the editor appearance using CSS variables:

:root {
   /* Editor Base */
   --editor-bg: #ffffff;
   --editor-color: #000000;
   --editor-primary: #dbeafe;
   --editor-primary-fg: #1d4ed8;

   --editor-secondary: #f7f6f4;
   --editor-secondary-dark: #c2c2bd;
   --editor-border-color: #ececea;
   --editor-btn-size: 1.7em;
   --editor-btn-icon-size: calc(var(--editor-btn-size) - 0.7em);
   --editor-border-radius: 0.5rem;
   --editor-shadow: 0 4px 80px -4px rgba(0, 0, 0, 0.088);
   --spacing-btn-size: var(--editor-btn-size);
   --radius: var(--editor-border-radius);

   /* Content Colors */
   --editor-content-color-default: #37352f;
   --editor-content-color-gray: #989898;
   --editor-content-color-orange: #ea580c;
   --editor-content-color-yellow: #ecb802;
   --editor-content-color-green: #16a34a;
   --editor-content-color-blue: #2563eb;
   --editor-content-color-purple: #9333ea;
   --editor-content-color-pink: #f64f9f;
   --editor-content-color-red: #e0383e;
   --editor-content-color-white: #ffffff;
   --editor-content-color-heading: #292929;
   --editor-content-color-text: #37352f;
   --editor-content-color-text-secondary: #4f4f4f;
   --editor-content-color-code: #e3342f;
   --editor-content-color-code-block: #272727;
   --editor-content-color-mark: #151413;
   --editor-content-color-primary: #007bff;
   --editor-content-color-primary-dark: #007bff;
   --editor-content-color-primary-dark-light: #007bff;
   --editor-content-color-border: #c8c8c6;
   --editor-content-color-border-dark: #d0d0ce;
   --editor-selection-color: #ecebe7d1;

   /* Content Background Colors */
   --editor-content-bg-default: rgba(55, 53, 47, 0.12);
   --editor-content-bg-gray: #d5d7d8;
   --editor-content-bg-yellow: #ffebb4;
   --editor-content-bg-yellow-light: #f3f1eb;
   --editor-content-bg-green: #cbecbf;
   --editor-content-bg-blue: #b4d6ff;
   --editor-content-bg-purple: rgba(105, 64, 165, 0.4);
   --editor-content-bg-pink: #fecbe2;
   --editor-content-bg-red: #ff9898;
   --editor-content-bg-black: #000000;
   --editor-content-bg-secondary: #f7f6f4;
   --editor-content-bg-mark: #fef08a;
   --editor-content-bg-primary: #007bff;
   --editor-content-bg-primary-dark: #0a77ec;
   --editor-content-bg-primary-light: #eff5fd;
   --editor-content-bg-primary-fg: #ffffff;

   /* Headings */
   --size-font-h1: 2.15em;
   --size-font-h2: 1.65em;
   --size-font-h3: 1.4em;
   --size-font-h4: 1.35em;
   --size-font-h5: 1.3em;
   --size-font-h6: 1.125em;
   --size-line-height-heading: 1.25;

   /* Table */
   --size-font-table: 1.1em;
   --size-padding-table: 8px 12px;
   --size-padding-table-cell: 0.5em 0.75em;
   --size-table-cell-min-width: 100px;
   --z-index-selected-cell: 2;

   /* Code */
   --size-font-code: 0.875em;

   /* Fonts */
   --font-family-base: 'Roboto', Arial, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
   --font-weight-medium: 500;
   --font-weight-semibold: 600;
   --font-weight-bold: 700;
   --size-font-base: 15px;
   --size-font-sm: 0.875em;
   --size-font-xs: 0.75em;

   /* Spacing / Padding / Margin  */
   --size-spacing-sm: 0.25rem;
   --size-spacing-md: 0.5rem;
   --size-spacing-lg: 0.75rem;
   --size-spacing-xl: 1rem;
   --size-spacing-2xl: 1.5rem;
   --size-spacing-3xl: 2rem;

   --size-padding-sm: 0.125rem;
   --size-padding-md: 0.375rem;
   --size-padding-lg: 0.75rem;
   --size-padding-xl: 1rem;

   --size-margin-sm: 0.2em;
   --size-margin-md: 0.58em;
   --size-margin-lg: 0.88em;
   --size-margin-xl: 1.18em;
   --size-margin-2xl: 1.45em;

   /* Borders  */
   --size-border-radius-sm: 0.25rem;
   --size-border-radius-md: 0.375rem;
   --size-border-radius-lg: 0.5rem;
   --size-border-radius-xl: 1px;
   --size-border-width-sm: 0.95px;
   --size-border-width-md: 2px;
   --size-border-width-lg: 3px;

   /* Line Heights */
   --size-line-height-base: 1.6;
   --size-line-height-list: 1.6;
   --size-line-height-list-marker: 1.4;

   /* Misc Sizes */
   --size-cursor-width: 20px;
   --size-resize-handle: 4px;
   --size-drag-handle-width: 1.03rem;
   --size-drag-handle-height: 1.4rem;
}

Reporting Bugs or Requesting Features

If you encounter a bug, miss a feature, or want to suggest an enhancement: Open an issue on GitHub: issues Provide a clear description, steps to reproduce, and screenshots if possible. For feature requests, describe your use case and expected behavior.

License

MIT

Author

safdar-azeem