Quick Start
This guide walks you through creating a fully-featured rich text editor with toolbar, text formatting, headings, lists, and custom fonts.

Minimal Editor
Section titled “Minimal Editor”The simplest setup — a text editor with bold, italic, and underline:
import { createEditor } from '@notectl/core';
const editor = await createEditor({ placeholder: 'Start typing...', autofocus: true,});
document.getElementById('app').appendChild(editor);That’s it. The createEditor factory auto-registers a TextFormattingPlugin with bold, italic, and underline enabled by default.
Full-Featured Editor
Section titled “Full-Featured Editor”For a production editor with a toolbar and all features:
-
Import the plugins you need
import {createEditor,TextFormattingPlugin,HeadingPlugin,ListPlugin,LinkPlugin,BlockquotePlugin,TablePlugin,CodeBlockPlugin,StrikethroughPlugin,TextColorPlugin,AlignmentPlugin,HorizontalRulePlugin,FontPlugin,FontSizePlugin,} from '@notectl/core';import { STARTER_FONTS } from '@notectl/core/fonts'; -
Configure the toolbar layout
The
toolbaroption takes an array of arrays. Each inner array is a visual group separated by dividers:const editor = await createEditor({toolbar: [// Group 1: Text formatting[new TextFormattingPlugin({ bold: true, italic: true, underline: true })],// Group 2: Block types[new HeadingPlugin()],// Group 3: Block elements[new BlockquotePlugin(), new LinkPlugin()],// Group 4: Lists[new ListPlugin()],// Group 5: Tables[new TablePlugin()],// Group 6: Code[new CodeBlockPlugin()],// Group 7: Inline formatting[new HorizontalRulePlugin(), new StrikethroughPlugin(), new TextColorPlugin()],// Group 7: Typography[new AlignmentPlugin(),new FontPlugin({ fonts: [...STARTER_FONTS] }),new FontSizePlugin({ sizes: [12, 16, 24, 32, 48], defaultSize: 16 }),],],placeholder: 'Start typing...',autofocus: true,}); -
Add it to the DOM
document.getElementById('app').appendChild(editor); -
Listen for changes
editor.on('stateChange', ({ newState }) => {console.log('Content changed:', newState.doc);});editor.on('ready', () => {console.log('Editor is ready!');});
HTML Setup
Section titled “HTML Setup”Here’s a complete HTML page:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My Editor</title> <style> #app { max-width: 800px; margin: 2rem auto; } notectl-editor { display: block; border: 1px solid #e0e0e0; border-radius: 8px; min-height: 400px; } </style></head><body> <div id="app"></div> <script type="module" src="./main.ts"></script></body></html>Reading Content
Section titled “Reading Content”notectl provides multiple output formats:
// Get structured JSON (Document model)const json = editor.getJSON();
// Get sanitized HTMLconst html = editor.getHTML();
// Get plain textconst text = editor.getText();
// Check if editor is emptyconst empty = editor.isEmpty();Setting Content
Section titled “Setting Content”// Set content from HTMLeditor.setHTML('<h1>Hello</h1><p>Welcome to <strong>notectl</strong>!</p>');
// Set content from JSON (Document model)editor.setJSON({ children: [ { type: 'paragraph', id: 'block-1', children: [{ type: 'text', text: 'Hello world', marks: [] }], }, ],});Programmatic Commands
Section titled “Programmatic Commands”// Toggle formattingeditor.commands.toggleBold();editor.commands.toggleItalic();editor.commands.toggleUnderline();
// Undo / Redoeditor.commands.undo();editor.commands.redo();
// Execute any registered command by nameeditor.executeCommand('toggleStrikethrough');editor.executeCommand('insertHorizontalRule');Framework Integration
Section titled “Framework Integration”Since notectl is a Web Component, it works in any framework:
import { useEffect, useRef } from 'react';import { createEditor } from '@notectl/core';import type { NotectlEditor } from '@notectl/core';
export function Editor() { const containerRef = useRef<HTMLDivElement>(null); const editorRef = useRef<NotectlEditor | null>(null);
useEffect(() => { let mounted = true; createEditor({ placeholder: 'Start typing...', autofocus: true, }).then((editor) => { if (mounted && containerRef.current) { containerRef.current.appendChild(editor); editorRef.current = editor; } });
return () => { mounted = false; editorRef.current?.destroy(); }; }, []);
return <div ref={containerRef} />;}<script setup lang="ts">import { onMounted, onUnmounted, ref } from 'vue';import { createEditor } from '@notectl/core';import type { NotectlEditor } from '@notectl/core';
const container = ref<HTMLElement>();let editor: NotectlEditor | null = null;
onMounted(async () => { editor = await createEditor({ placeholder: 'Start typing...', autofocus: true, }); container.value?.appendChild(editor);});
onUnmounted(() => { editor?.destroy();});</script>
<template> <div ref="container" /></template><script lang="ts"> import { onMount, onDestroy } from 'svelte'; import { createEditor } from '@notectl/core'; import type { NotectlEditor } from '@notectl/core';
let container: HTMLElement; let editor: NotectlEditor | null = null;
onMount(async () => { editor = await createEditor({ placeholder: 'Start typing...', autofocus: true, }); container.appendChild(editor); });
onDestroy(() => { editor?.destroy(); });</script>
<div bind:this={container}></div><div id="app"></div>
<script type="module"> import { createEditor } from '@notectl/core';
const editor = await createEditor({ placeholder: 'Start typing...', autofocus: true, });
document.getElementById('app').appendChild(editor);</script>