Reference
Editor SDK
NotrEditor is a block-based, always-editable rich text editor in the spirit of
Notion. It owns the collaboration plumbing — you give it a noteId, it gives you a
live multiplayer document.
$lib. A standalone @notr/editor npm package is planned and will keep the same
API.Usage
<script>
import { NotrEditor } from '$lib'
let status = $state('connecting')
</script>
<NotrEditor
noteId="8a1f3c0e-…"
user={{ name: 'Ada Lovelace', color: '#c8401a' }}
bind:status
/>
<p>connection: {status}</p> Props
| Prop | Type | Default | Description |
|---|---|---|---|
noteId | string | required | The note to open. Treat it as immutable — remount the component (e.g. with {#key}) to switch notes. |
user | { name, color } | random | Shown on this user's caret to other collaborators. |
token | string | "session" | Collab credential — the default authenticates with the console session cookie; pass a service key from server-side clients. |
url | string | /collab on the current host | Override the collaboration endpoint. |
slashCommands | SlashCommandItem[] | [] | Domain-specific commands appended after the built-ins. |
onUpload | UploadHandler | — | Host uploader for images & files. Enables paste, drag-drop, and the Image command. Omit to disable uploads. See Images & attachments. |
status | bindable string | — | connecting · connected · disconnected. |
class | string | — | Classes for the editor container. Pairs well with Tailwind's prose. |
Built-in blocks
Paragraphs, headings (1–3), bullet / numbered / check lists, quotes, code blocks, dividers, and
tables — all reachable by typing /. Bold, italic, strikethrough, inline code, and
links work with the usual shortcuts and markdown-style input rules.
Custom slash commands
The slash menu is a framework: Notr provides the trigger, filtering, and keyboard handling; you
provide domain commands. Each item gets the Tiptap editor and the range of the typed query to replace.
<script>
import { NotrEditor } from '$lib'
const meetingSummary = {
title: 'Meeting summary',
description: 'Insert the summary template',
keywords: ['template', 'recap'],
command: ({ editor, range }) => {
editor
.chain()
.focus()
.deleteRange(range)
.insertContent('<h2>Summary</h2><p></p><h2>Action items</h2>')
.run()
}
}
</script>
<NotrEditor noteId={note.id} slashCommands={[meetingSummary]} /> Commands are plain objects, so they can do anything — insert templates, reference entities from your application, call your API and insert the result.
Images & attachments
Notr is an engine, not a file manager. A note stores only a reference to a piece of media
— a URL plus a little metadata — never the bytes. Where the bytes live, how they got there, and
who may read them are entirely yours. You wire that in through one callback: onUpload.
Pass it and the editor lights up three gestures: paste an image, drag-drop a file, or run the Image slash command. Each hands your function the File; you store the bytes wherever you like and return a URL. Omit onUpload entirely and there are no upload affordances at all — the capability is opt-in.
<script>
import { NotrEditor } from '$lib'
// Store the bytes wherever you like, return a URL.
async function onUpload(file) {
const body = new FormData()
body.append('file', file)
const res = await fetch('/my-app/uploads', { method: 'POST', body })
const { url } = await res.json()
return url // shorthand for { url }
}
</script>
<NotrEditor noteId={note.id} {onUpload} /> For finer control, resolve an object instead of a bare string. mode decides how it
renders — 'inline' draws an <img>, 'link' draws a download
chip that works for any file. It is stored on the node, so a user can keep an image as a plain link
instead of rendering it. When you return only a URL, Notr infers the mode from the file type (images
inline, everything else as a link).
async function onUpload(file) {
const url = await store(file)
return {
url,
mode: 'inline', // 'inline' | 'link'
alt: file.name,
meta: { id: '…' } // opaque, round-tripped on the node
}
} Two guarantees. Notr makes no promise the URL resolves — link rot, access control, and deletion are the host's concern. But it makes a stronger one: it never breaks on a bad URL. A dead, cross-origin, or hostile link degrades to a placeholder that still surfaces the reference; the editor never throws and the document is never lost.
Returning UploadResult never uploads anything from Notr's side — storage, auth, and
cleanup stay with you. That's the same boundary as the rest of Notr: it stores notes, not files.
Styling
The editor renders semantic HTML and inherits your typography. The component ships minimal
structural CSS (collaboration carets, checklist layout); everything else is yours via the class prop. The console uses class="prose max-w-none" with the Tailwind typography plugin.
How the document actually syncs: Collaboration.
