Noteverse uses Novel.sh, a modern WYSIWYG editor built on TipTap, to provide a rich text editing experience with extensive formatting capabilities, slash commands, and media embedding.
Novel.sh integration
The editor is built using Novel’s EditorRoot and EditorContent components:
import {
EditorRoot ,
EditorCommand ,
EditorCommandItem ,
EditorCommandEmpty ,
EditorContent ,
type JSONContent ,
EditorCommandList ,
EditorBubble ,
} from 'novel'
This provides a complete editing environment with bubble menus, command palettes, and rich formatting options.
Editor extensions
Noteverse includes a comprehensive set of TipTap extensions for advanced editing features:
Core extensions
export const defaultExtensions = [
starterKit ,
placeholder ,
tiptapLink ,
tiptapImage ,
updatedImage ,
taskList ,
taskItem ,
horizontalRule ,
aiHighlight ,
codeBlockLowlight ,
youtube ,
twitter ,
mathematics ,
characterCount ,
TiptapUnderline ,
MarkdownExtension ,
HighlightExtension ,
TextStyle ,
Color ,
CustomKeymap ,
GlobalDragHandle
]
Text formatting
StarterKit : Provides bold, italic, strike, headings, lists, and blockquotes
TiptapUnderline : Adds underline formatting
TextStyle & Color : Enables text color customization
HighlightExtension : Allows text highlighting with custom colors
Lists and tasks
TaskList & TaskItem : Interactive checkboxes for to-do lists with nested support:
const taskItem = TaskItem . configure ({
HTMLAttributes: {
class: cx ( 'flex gap-2 items-start my-4' )
},
nested: true
})
Code and mathematics
CodeBlockLowlight : Syntax-highlighted code blocks supporting 37+ languages
const codeBlockLowlight = CodeBlockLowlight . configure ({
lowlight: createLowlight ( common )
})
Mathematics : LaTeX math rendering with KaTeX:
const mathematics = Mathematics . configure ({
HTMLAttributes: {
class: cx ( 'text-foreground rounded p-1 hover:bg-accent cursor-pointer' )
},
katexOptions: {
throwOnError: false
}
})
TiptapImage & UpdatedImage : Image upload and display with drag-and-drop support
Youtube : Embed YouTube videos directly in notes
Twitter : Embed tweets from X/Twitter
const youtube = Youtube . configure ({
HTMLAttributes: {
class: cx ( 'rounded-lg border border-muted' )
},
inline: false
})
Slash commands
Type / anywhere in your note to open the command palette with quick access to formatting options.
Available commands
To-do List Track tasks with interactive checkboxes
Heading 1-3 Structure your content with headings
Bullet List Create unordered lists
Numbered List Create ordered lists
Quote Highlight important quotes
Code Insert syntax-highlighted code blocks
Image Upload images from your computer
YouTube Embed YouTube videos
Command implementation
Each command is defined with a title, description, icon, and editor action:
{
title : 'Heading 1' ,
description : 'Big section heading.' ,
searchTerms : [ 'title' , 'big' , 'large' ],
icon : < Heading1 size ={ 18 } />,
command : ({ editor , range }) => {
editor
. chain ()
. focus ()
. deleteRange ( range )
. setNode ( 'heading' , { level: 1 })
. run ()
},
}
Image upload
The image command opens a file picker and uploads the selected image:
{
title : 'Image' ,
description : 'Upload an image from your computer.' ,
searchTerms : [ 'photo' , 'picture' , 'media' ],
icon : < ImageIcon size ={ 18 } />,
command : ({ editor , range }) => {
editor . chain (). focus (). deleteRange ( range ). run ()
const input = document . createElement ( 'input' )
input . type = 'file'
input . accept = 'image/*'
input . onchange = async () => {
if ( input . files ?. length ) {
const file = input . files [ 0 ]
const pos = editor . view . state . selection . from
uploadFn ( file , editor . view , pos )
}
}
input . click ()
},
}
When you select text, a bubble menu appears with formatting options:
< EditorBubble
tippyOptions = {{
placement : 'top' ,
zIndex : 10 ,
}}
className = "flex w-fit max-w-[90vw] overflow-hidden rounded-md border border-muted bg-background shadow-xl"
>
< Separator orientation = "vertical" />
< NodeSelector open = { openNode } onOpenChange = { setOpenNode } />
< Separator orientation = "vertical" />
< LinkSelector open = { openLink } onOpenChange = { setOpenLink } />
< Separator orientation = "vertical" />
< TextButtons />
< Separator orientation = "vertical" />
< ColorSelector open = { openColor } onOpenChange = { setOpenColor } />
</ EditorBubble >
Node selector
Convert the selected block to different node types:
Paragraph
Heading 1, 2, or 3
Bullet list
Numbered list
Quote
Code block
Link selector
Add, edit, or remove hyperlinks:
const tiptapLink = TiptapLink . configure ({
HTMLAttributes: {
class: cx (
'text-muted-foreground underline underline-offset-[3px] hover:text-primary transition-colors cursor-pointer'
)
}
})
Text buttons
Quick access to basic text formatting:
Bold
Italic
Strikethrough
Underline
Code (inline)
Color selector
Change text and highlight colors with a color picker.
Markdown support
The editor includes markdown shortcuts for faster writing:
Type markdown syntax (like **bold** or # heading) and it will automatically convert to formatted text.
Image handling
Drag and drop or paste images directly into the editor:
editorProps = {{
handleDOMEvents : {
keydown : ( _view , event ) => handleCommandNavigation ( event ),
},
handlePaste : ( view , event ) => handleImagePaste ( view , event , uploadFn ),
handleDrop : ( view , event , _slice , moved ) =>
handleImageDrop ( view , event , moved , uploadFn ),
attributes : {
class : `prose prose-sm dark:prose-invert prose-headings:font-title font-default focus:outline-none max-w-full p-0` ,
},
}}
You can resize images after inserting them using the ImageResizer component that appears when you select an image.
Character count
Track the length of your notes with the built-in character counter:
const characterCount = CharacterCount . configure ()
Custom keyboard shortcuts
The CustomKeymap extension provides additional keyboard shortcuts for power users: