Skip to content

Plugin Overview

import { LinkCard, CardGrid } from ‘@astrojs/starlight/components’;

notectl ships with 19 built-in plugins. Every editor feature — from bold text to tables — is implemented as a plugin. You can use all of them, a subset, or build your own.

Editor with full plugin set

PluginIDDescriptionKeyboard Shortcuts
TextFormattingPlugintext-formattingBold, italic, underlineCtrl+B, Ctrl+I, Ctrl+U
HeadingPluginheadingHeading levels 1-6, Title, SubtitleCtrl+Shift+1-6
ListPluginlistBullet, ordered, and checklistTab, Shift+Tab
LinkPluginlinkHyperlinksCtrl+K
TablePlugintableTables with cell selectionTab, Enter
CodeBlockPlugincode-blockFenced code blocks with syntax highlighting``` (input rule)
BlockquotePluginblockquoteBlock quotesCtrl+Shift+>
ImagePluginimageImage blocks with upload support-
FontPluginfontCustom font families-
FontSizePluginfontSizeFont size controlCtrl+Shift++/-
TextColorPlugintextColorText color picker-
AlignmentPluginalignmentLeft, center, right, justifyCtrl+Shift+L/E/R/J
StrikethroughPluginstrikethroughStrikethrough textCtrl+Shift+X
SuperSubPluginsuper-subSuperscript & subscriptCtrl+., Ctrl+,
HighlightPluginhighlightText highlight (background color)-
HorizontalRulePluginhorizontal-ruleHorizontal divider lines-
HardBreakPluginhard-breakLine breaks within a blockShift+Enter
PrintPluginprintPrint with clean output and HTML exportCtrl+P
ToolbarPlugintoolbarToolbar UI (auto-created)-

Each plugin implements the Plugin interface and registers its capabilities during init():

interface Plugin {
/** Unique identifier. */
readonly id: string;
/** Human-readable name. */
readonly name: string;
/** Plugin initialization order (lower = first). */
readonly priority?: number;
/** Required plugin IDs that must be loaded first. */
readonly dependencies?: readonly string[];
/** Register schema, commands, keymaps, toolbar items. */
init(context: PluginContext): void | Promise<void>;
/** Clean up when the editor is destroyed. */
destroy?(): void | Promise<void>;
/** Called on every state change (for reactive updates). */
onStateChange?(oldState: EditorState, newState: EditorState, tr: Transaction): void;
/** Called when configurePlugin() is used at runtime. */
onConfigure?(config: TConfig): void;
/** Called once after all plugins are initialized. */
onReady?(): void | Promise<void>;
/** Called when the editor's read-only mode changes. */
onReadOnlyChange?(readonly: boolean): void;
/** Returns decorations for the current state. */
decorations?(state: EditorState, tr?: Transaction): DecorationSet;
}

Plugins register through the PluginContext:

MethodWhat it doesExample
registerNodeSpec()New block typesheading, list_item, table, blockquote
registerMarkSpec()New inline marksbold, italic, link, textColor
registerInlineNodeSpec()New inline elementshard_break, emoji, mention
registerCommand()Named commandstoggleBold, insertTable, alignCenter
registerKeymap()Keyboard shortcutsMod-B for bold, Mod-K for link
registerInputRule()Text pattern transforms# to heading, --- to horizontal rule
registerToolbarItem()Toolbar buttons/dropdownsBold button, heading dropdown, color picker
registerBlockTypePickerEntry()Block type dropdown entriesHeading levels, paragraph, title
registerNodeView()Custom block renderersCode block header, image upload UI, table controls
registerMiddleware()Transaction interceptorsPreserve alignment on block type change
registerService()Typed servicesToolbarService, TableSelectionService
registerFileHandler()File paste/drop handlersImage upload on drag-and-drop
registerStyleSheet()Inject plugin CSSTable grid styles, code block theme
announce()Screen reader announcements”Image resized”, “Entered code block”
executeCommand()Execute a registered commandTrigger toggleBold from keyboard handler
getEventBus()Access typed event busEmit/subscribe to plugin events
isReadOnly()Check read-only stateSkip mutations in read-only mode
updateConfig()Push runtime config updatesDynamic plugin reconfiguration

Use plugins with the toolbar config for a visual toolbar:

const editor = await createEditor({
toolbar: [
[new TextFormattingPlugin()], // Group 1: B I U
[new HeadingPlugin()], // Group 2: Heading dropdown
[new ListPlugin(), new BlockquotePlugin()], // Group 3: Lists + blockquote
[new LinkPlugin(), new TablePlugin()], // Group 4: Link + table
[new CodeBlockPlugin()], // Group 5: Code blocks
],
});

Or use plugins for headless mode (no toolbar):

const editor = await createEditor({
plugins: [
new TextFormattingPlugin(),
new HeadingPlugin(),
],
});

TextFormattingPlugin is auto-registered with default settings if not explicitly provided. All other plugins must be added manually.