Skip to main content
Custom blocks are in private alpha:
  • Do not discuss publicly or share with anyone outside your workspace.
  • Access must be enabled per workspace. Installing the SDK does not grant access.
  • These APIs may experience breaking changes without notice.
A custom block runs in a sandboxed iframe and has no direct access to the page around it. Everything it knows about Notion arrives through a handshake with the host, and everything it does to Notion goes back over the same channel.

The handshake

Three messages establish the connection:
  1. The block sends connect when the SDK loads.
  2. The host replies with init, carrying the block’s context.
  3. The block sends initResult to confirm it accepted the context.
The host supplies the block ID, page, user, and data sources during initialization. The SDK exposes this as a promise, so you rarely deal with the messages directly. When connected to a host, NotionCustomBlock renders children after initialization succeeds. When connected to a host, initialization validates all declared data source bindings. See binding a block to Notion data for how that works and what it means for your code. If the host does not respond within the timeout, initialization rejects. The default is 15 seconds, which is deliberately longer than Notion’s own 10-second watchdog so that a host-reported error reaches you before the SDK’s generic timeout does.

Block and app context

The SDK provides getters, subscriptions, and React hooks for block and app context.

Getter

Getters return the current value, such as the theme, current user, or containing page. They throw if called before initialization completes.

Subscription

Subscriptions deliver the initial value and later changes. You can register them before initialization. They wait for initialization before delivering the initial value, or deliver immediately if initialization is complete.

React hook

In React, use context hooks inside NotionCustomBlock. The wrapper waits for initialization, and the hooks keep components current as values change. React handles subscription cleanup when the component unmounts.
See app context for the available getters, subscriptions, and React hooks.

When initialization fails

Initialization rejects with a CustomBlockInitializationError, carrying a code and an isRetryable flag alongside the message. If you open the block directly in a browser tab, initCustomBlock() rejects with NotInIframeError. Use the dev shell when you need realistic host behavior or data source bindings. Other codes describe what the host could not provide: a missing or invalid manifest, an unsupported protocol version, unavailable context or user, or a data source binding that is missing, unavailable, or does not match the declared schema. See error handling for the full contract.

Debug console

Press \ inside a running block to swap its UI for a log of the messages passing between the block and the host, and \ again to go back. This is the fastest way to see why a block is stuck in its loading state. It is built into NotionCustomBlock, so it is only available to blocks using the React SDK. See previewing before deploy.