React bindings for the OpenBlock rich text editor.
npm install @labbs/openblock-react @labbs/openblock-core
# or
pnpm add @labbs/openblock-react @labbs/openblock-coreimport { useOpenBlock, OpenBlockView, SlashMenu, BubbleMenu } from '@labbs/openblock-react';
import '@labbs/openblock-core/styles/editor.css';
function MyEditor() {
const editor = useOpenBlock({
initialContent: [
{
type: 'heading',
props: { level: 1 },
content: [{ type: 'text', text: 'Hello World', styles: {} }],
},
{
type: 'paragraph',
content: [{ type: 'text', text: 'Start editing...', styles: {} }],
},
],
});
if (!editor) return <div>Loading...</div>;
return (
<div className="editor-container">
<OpenBlockView editor={editor} />
<SlashMenu editor={editor} />
<BubbleMenu editor={editor} />
</div>
);
}Creates and manages an OpenBlockEditor instance.
const editor = useOpenBlock({
initialContent: [...],
editable: true,
autoFocus: 'end',
onUpdate: (blocks) => console.log(blocks),
deps: [someValue], // Recreate editor when deps change
});Subscribe to document changes.
const blocks = useEditorContent(editor);
// blocks updates whenever the document changesSubscribe to selection changes.
const selectedBlocks = useEditorSelection(editor);
// selectedBlocks updates when selection changesTrack editor focus state.
const isFocused = useEditorFocus(editor);Renders the editor view.
<OpenBlockView
editor={editor}
className="my-editor"
/>With ref for imperative access:
const viewRef = useRef<OpenBlockViewRef>(null);
<OpenBlockView
ref={viewRef}
editor={editor}
/>
// Later: viewRef.current?.focus()Command palette triggered by typing /.
<SlashMenu editor={editor} />See SlashMenu Customization for advanced usage.
Floating toolbar for text formatting.
<BubbleMenu editor={editor} />See BubbleMenu Customization for advanced usage.
Color picker for text and background colors.
import { ColorPicker } from '@labbs/openblock-react';
<ColorPicker
editor={editor}
currentTextColor={textColor}
currentBackgroundColor={bgColor}
/>See ColorPicker Customization for advanced usage.
Row/column manipulation handles for tables.
<TableHandles editor={editor} />Menu for media blocks (images, videos, files).
<MediaMenu editor={editor} />The BubbleMenu supports extensive customization through props.
interface BubbleMenuProps {
editor: OpenBlockEditor | null;
customItems?: BubbleMenuItem[]; // Add custom buttons
itemOrder?: string[]; // Control order (use '---' for dividers)
hideItems?: string[]; // Hide default items
className?: string;
children?: React.ReactNode;
}- Block type:
blockType - Alignment:
alignLeft,alignCenter,alignRight - Formatting:
bold,italic,underline,strikethrough - Style:
code,link,color
import { BubbleMenu, BubbleMenuItem } from '@labbs/openblock-react';
const translateButton: BubbleMenuItem = {
id: 'translate',
label: 'Translate',
icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10" />
</svg>
),
action: async (editor, state) => {
const text = editor.pm.state.doc.textBetween(state.from, state.to);
const translated = await translateAPI(text);
// Replace selection...
},
};
<BubbleMenu
editor={editor}
customItems={[translateButton]}
/><BubbleMenu
editor={editor}
itemOrder={[
'bold', 'italic', 'underline',
'---', // Divider
'link', 'color',
'---',
'translate', // Custom item
]}
/><BubbleMenu
editor={editor}
itemOrder={['bold', 'italic', '---', 'link']}
/><BubbleMenu
editor={editor}
hideItems={['strikethrough', 'code', 'alignLeft', 'alignCenter', 'alignRight']}
/>The SlashMenu supports customization similar to BubbleMenu.
interface SlashMenuProps {
editor: OpenBlockEditor | null;
items?: SlashMenuItem[]; // Replace all defaults
customItems?: SlashMenuItem[]; // Add to defaults
itemOrder?: string[]; // Control order (only listed items shown)
hideItems?: string[]; // Hide specific items
renderItem?: (item: SlashMenuItem, isSelected: boolean) => React.ReactNode;
className?: string;
}- Text:
paragraph,heading - Lists:
bulletList,numberedList,checkList - Blocks:
blockquote,codeBlock,callout,divider - Tables:
table - Media:
image,video,audio,file
import { SlashMenu, SlashMenuItem } from '@labbs/openblock-react';
const customItems: SlashMenuItem[] = [
{
id: 'emoji',
title: 'Emoji',
description: 'Insert an emoji picker',
icon: '😀',
action: (editor) => {
// Show emoji picker...
},
},
{
id: 'template',
title: 'Template',
description: 'Insert a predefined template',
icon: '📄',
action: (editor) => {
// Insert template...
},
},
];
<SlashMenu
editor={editor}
customItems={customItems}
/><SlashMenu
editor={editor}
itemOrder={['paragraph', 'heading', 'bulletList', 'emoji', 'template']}
/><SlashMenu
editor={editor}
hideItems={['table', 'video', 'audio', 'file']}
/>The ColorPicker component allows custom color palettes.
interface ColorPickerProps {
editor: OpenBlockEditor;
currentTextColor: string | null;
currentBackgroundColor: string | null;
textColors?: ColorOption[]; // Custom text color palette
backgroundColors?: ColorOption[]; // Custom background color palette
textColorLabel?: string; // Label for text color section
backgroundColorLabel?: string; // Label for background section
onClose?: () => void;
}
interface ColorOption {
value: string; // CSS color value ('' for default/remove)
label: string; // Display label
}import { DEFAULT_TEXT_COLORS, DEFAULT_BACKGROUND_COLORS } from '@labbs/openblock-react';
// DEFAULT_TEXT_COLORS includes:
// Default, Gray, Red, Orange, Yellow, Green, Blue, Purple, Pink
// DEFAULT_BACKGROUND_COLORS includes:
// Default, Gray, Red, Orange, Yellow, Green, Blue, Purple, Pink (lighter versions)import { ColorPicker, ColorOption } from '@labbs/openblock-react';
const brandTextColors: ColorOption[] = [
{ value: '', label: 'Default' },
{ value: '#1a1a1a', label: 'Black' },
{ value: '#0066cc', label: 'Primary' },
{ value: '#00994d', label: 'Success' },
{ value: '#cc3300', label: 'Error' },
];
const brandBackgroundColors: ColorOption[] = [
{ value: '', label: 'None' },
{ value: '#e6f2ff', label: 'Blue' },
{ value: '#e6ffe6', label: 'Green' },
{ value: '#ffe6e6', label: 'Red' },
];
<ColorPicker
editor={editor}
currentTextColor={textColor}
currentBackgroundColor={bgColor}
textColors={brandTextColors}
backgroundColors={brandBackgroundColors}
textColorLabel="Text Color"
backgroundColorLabel="Highlight"
/>The editor instance provides full ProseMirror access:
function MyEditor() {
const editor = useOpenBlock({...});
const handleBold = () => {
editor.toggleBold();
};
const handleCustomAction = () => {
// Direct ProseMirror access
const tr = editor.pm.createTransaction();
tr.insertText('Custom text');
editor.pm.dispatch(tr);
};
return (
<>
<button onClick={handleBold}>Bold</button>
<button onClick={handleCustomAction}>Insert</button>
<OpenBlockView editor={editor} />
</>
);
}OpenBlockView- Main editor viewSlashMenu- Command paletteBubbleMenu- Floating toolbarColorPicker- Color selectionTableMenu- Table manipulation menuTableHandles- Table row/column handlesMediaMenu- Media block menuLinkPopover- Link editing popover
useOpenBlock- Create editor instanceuseEditorContent- Subscribe to contentuseEditorSelection- Subscribe to selectionuseEditorFocus- Track focus state
BUBBLE_MENU_ITEMS- Default bubble menu items mapDEFAULT_BUBBLE_MENU_ORDER- Default bubble menu item orderDEFAULT_TEXT_COLORS- Default text color paletteDEFAULT_BACKGROUND_COLORS- Default background color palette
OpenBlockViewProps,OpenBlockViewRefSlashMenuProps,SlashMenuItemBubbleMenuProps,BubbleMenuItemColorPickerProps,ColorOptionTableMenuProps,TableHandlesPropsMediaMenuProps,LinkPopoverProps
For complete documentation, see:
Apache-2.0