npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@octanejs/blocknote

v0.1.11

Published

BlockNote for the octane renderer — an Octane binding for @blocknote/react block-based rich text editors.

Readme

@octanejs/blocknote

Headless BlockNote editor for Octane. It covers the editor surface of @blocknote/[email protected] without the default UI, and uses @blocknote/core unchanged.

Install

npm install @octanejs/blocknote
pnpm add @octanejs/blocknote

Usage

import { BlockNoteViewRaw, useCreateBlockNote } from '@octanejs/blocknote';

export function Editor() @{
	const editor = useCreateBlockNote({
		initialContent: [{ type: 'paragraph', content: 'Hello' }],
	});

	<BlockNoteViewRaw editor={editor} onChange={() => console.log(editor.document)} />
}

BlockNoteViewRaw imports @blocknote/core/style.css. Toolbars, menus, and other UI are yours to build. Render them as children, read the editor with useBlockNoteEditor(), and use renderEditor={false} with <BlockNoteViewEditor /> to control where the editable area goes.

Exports

  • BlockNoteViewRaw, BlockNoteViewEditor, BlockNoteViewProps
  • BlockNoteContext, useBlockNoteContext, BlockNoteContextValue
  • useCreateBlockNote, useBlockNoteEditor
  • useEditorChange, useEditorSelectionChange, usePrefersColorScheme
  • PortalElementsMap, PortalTarget

Building UI

This package has no default UI. Each BlockNote menu and toolbar is a framework-neutral @blocknote/core extension that exposes a store. The store has subscribe(listener) and state, so Octane's own useSyncExternalStore can read it without any extra dependency.

Menus and toolbars

import { SuggestionMenu, filterSuggestionItems, getDefaultSlashMenuItems } from '@blocknote/core';
import { useBlockNoteEditor } from '@octanejs/blocknote';
import { useEffect, useSyncExternalStore } from 'octane';

export function SlashMenu() @{
	const editor = useBlockNoteEditor();
	const menu = editor.getExtension(SuggestionMenu)!;

	useEffect(() => {
		menu.addSuggestionMenu({ triggerCharacter: '/' });
		return () => menu.removeSuggestionMenu('/');
	}, [menu]);

	const state = useSyncExternalStore(menu.store.subscribe, () => menu.store.state);
	const items = state?.show
		? filterSuggestionItems(getDefaultSlashMenuItems(editor), state.query)
		: [];

	@if (state?.show) {
		<ul
			class="slash-menu"
			style={{ position: 'fixed', left: state.referencePos.left, top: state.referencePos.bottom }}
		>
			@for (const item of items; key item.key) {
				<li
					onMouseDown={(event: MouseEvent) => {
						event.preventDefault(); // keep focus in the editor
						menu.clearQuery();
						menu.closeMenu();
						item.onItemClick();
					}}
				>
					{item.title}
				</li>
			}
		</ul>
	}
}

// <BlockNoteViewRaw editor={editor}><SlashMenu /></BlockNoteViewRaw>

FormattingToolbar, SideMenu, LinkToolbar, FilePanel, and TableHandles follow the same pattern. For collision-aware placement, position the element from referencePos with a library such as @octanejs/floating-ui.

The extension stores are TanStack Stores. If your app already uses @octanejs/tanstack-store, useStore(menu.store, (state) => state?.query) also works and adds selector-based updates.

Custom blocks

Core's createBlockSpec renders to DOM. To render an Octane component inside a block, mount a root on a node you own and unmount it in destroy:

import { BlockNoteSchema, createBlockSpec, defaultBlockSpecs } from '@blocknote/core';
import { createRoot } from 'octane';

function CalloutIcon(props: { emoji: string }) @{
	<span class="callout-icon">{props.emoji}</span>
}

const Callout = createBlockSpec(
	{ type: 'callout', propSchema: { emoji: { default: '💡' } }, content: 'inline' },
	{
		render: (block) => {
			const dom = document.createElement('div');
			const icon = document.createElement('span');
			const contentDOM = document.createElement('div');
			dom.className = 'callout';
			icon.contentEditable = 'false';
			dom.append(icon, contentDOM);
			const root = createRoot(icon);
			root.render(CalloutIcon, { emoji: block.props.emoji });
			return { dom, contentDOM, destroy: () => root.unmount() };
		},
	},
);

export const schema = BlockNoteSchema.create({
	blockSpecs: { ...defaultBlockSpecs, callout: Callout() },
});

// useCreateBlockNote({ schema })

Each block root is separate from your app tree, so it does not see your contexts (theme, router, stores). Pass what it needs through props or module state. Context-aware block and inline content specs are planned.

Both recipes run as real-browser tests in tests/browser/harness.

Server rendering

Server rendering is supported. The server emits the view shell, and the editor mounts into it on the client after hydration. Document content is not part of the server HTML. Without a theme prop the server renders the light scheme, and the system scheme applies after hydration. Pass theme to fix it on both sides.

Known differences

  • No default UI. BlockNoteView, BlockNoteDefaultUI, ComponentsContext, and the toolbar, menu, side-menu, table-handle, and comment components are not provided. Upstream disables all of them when no components context exists, so BlockNoteViewRaw here matches that upstream configuration.
  • BlockNoteViewRaw does not accept the default UI flags (formattingToolbar, slashMenu, and so on). portalElements reads only default.
  • Custom React block, inline content, and style specs are not provided.
  • Event handlers receive native DOM events, as everywhere in Octane.

Provenance

Independently authored and MIT licensed. See UPSTREAM.md.