This is the full developer documentation for Portable Text # Portable Text > A JSON-based specification for structured block content import '@/styles/globals.css' import { Card, CardGrid, LinkCard, TabItem, Tabs, } from '@astrojs/starlight/components' import PortableTextEditor from '../../components/editor/editor.astro' Portable Text is an open specification for structured block content. Rich text, images, code blocks, and any custom type you define, stored as JSON and renderable anywhere. [Get started →](/introduction/) · [Render PT content →](/rendering/) · [Build an editor →](/editor/getting-started/) ## Try the editor Type, format, and see the Portable Text output in real time. This is the [Portable Text Editor](/editor/getting-started/): a fully customizable block content editor you can embed in any React application. ## Get started ## What does Portable Text look like? The same content as an HTML string and as Portable Text: ```html

Read the documentation for Portable Text.

```
```json [ { "_type": "block", "style": "normal", "children": [ { "_type": "span", "text": "Read the " }, { "_type": "span", "text": "documentation", "marks": ["a1b2c3"] }, { "_type": "span", "text": " for " }, { "_type": "span", "text": "Portable Text", "marks": ["strong"] }, { "_type": "span", "text": "." } ], "markDefs": [ { "_key": "a1b2c3", "_type": "link", "href": "/docs" } ] } ] ```
Because content is structured data, you can render it as HTML, React components, Markdown, PDFs, or any other format. [Learn more about Portable Text →](/introduction/) ## Learn more # defineBehavior > **defineBehavior**\<`TPayload`, `TBehaviorEventType`, `TGuardResponse`\>(`behavior`): [`Behavior`](/api/behaviors/type-aliases/behavior/) Defined in: behavior.types.behavior.ts:56 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TPayload `TPayload` *extends* `Record`\<`string`, `unknown`\> ### TBehaviorEventType `TBehaviorEventType` *extends* `` `custom.${string}` `` \| `"*"` \| `"annotation.add"` \| `"annotation.remove"` \| `"block.set"` \| `"block.unset"` \| `"child.set"` \| `"child.unset"` \| `"decorator.add"` \| `"decorator.remove"` \| `"delete"` \| `"history.redo"` \| `"history.undo"` \| `"insert"` \| `"insert.block"` \| `"insert.child"` \| `"insert.text"` \| `"move.backward"` \| `"move.forward"` \| `"remove.text"` \| `"select"` \| `"set"` \| `"unset"` \| `"annotation.set"` \| `"annotation.toggle"` \| `"decorator.toggle"` \| `"delete.backward"` \| `"delete.block"` \| `"delete.child"` \| `"delete.forward"` \| `"delete.text"` \| `"deserialize"` \| `"deserialize.data"` \| `"deserialization.success"` \| `"deserialization.failure"` \| `"insert.blocks"` \| `"insert.break"` \| `"insert.inline object"` \| `"insert.soft break"` \| `"insert.span"` \| `"list item.add"` \| `"list item.remove"` \| `"list item.toggle"` \| `"move.block"` \| `"move.block down"` \| `"move.block up"` \| `"select.block"` \| `"select.previous block"` \| `"select.next block"` \| `"serialize"` \| `"serialize.data"` \| `"serialization.success"` \| `"serialization.failure"` \| `"split"` \| `"style.add"` \| `"style.remove"` \| `"style.toggle"` \| `"clipboard.copy"` \| `"clipboard.cut"` \| `"clipboard.paste"` \| `"drag.dragstart"` \| `"drag.drag"` \| `"drag.dragend"` \| `"drag.dragenter"` \| `"drag.dragover"` \| `"drag.dragleave"` \| `"drag.drop"` \| `"input.*"` \| `"keyboard.keydown"` \| `"keyboard.keyup"` \| `"mouse.click"` \| `"delete.*"` \| `"insert.*"` \| `"select.*"` \| `"set.*"` \| `"unset.*"` \| `"deserialize.*"` \| `"serialize.*"` \| `"split.*"` \| `"annotation.*"` \| `"remove.*"` \| `"block.*"` \| `"child.*"` \| `"decorator.*"` \| `"history.*"` \| `"move.*"` \| `"deserialization.*"` \| `"list item.*"` \| `"serialization.*"` \| `"style.*"` \| `"clipboard.*"` \| `"drag.*"` \| `"keyboard.*"` \| `"mouse.*"` = `` `custom.${string}` `` ### TGuardResponse `TGuardResponse` = `true` ## Parameters ### behavior [`Behavior`](/api/behaviors/type-aliases/behavior/)\<`TBehaviorEventType`, `TGuardResponse`, `ResolveBehaviorEvent`\<`TBehaviorEventType`, `TPayload`\>\> ## Returns [`Behavior`](/api/behaviors/type-aliases/behavior/) ## Example ```tsx const noLowerCaseA = defineBehavior({ on: 'insert.text', guard: ({event, snapshot}) => event.text === 'a', actions: [({event, snapshot}) => [{type: 'insert.text', text: 'A'}]], }) ``` # effect > **effect**(`effect`): `object` Defined in: behavior.types.action.ts:203 Performs a side effect. Use `effect` for logging, analytics, async operations, or other side effects. **Note:** Using `effect` alone (without `forward`) will stop event propagation. To perform a side effect while allowing the default Behavior to continue, combine `effect` with `forward`. The effect callback receives a `send` function that can be used to send events back to the editor asynchronously. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### effect (`payload`) => `void` ## Returns `object` ### effect() > **effect**: (`payload`) => `void` #### Parameters ##### payload ###### send (`event`) => `void` Send a Behavior Event back into the Editor. **Example** ```ts defineBehavior({ on: '...', actions: [ () => [ effect(({send}) => { doSomethingAsync() .then(() => { send({ type: '...', }) }) }) ], ], }) ``` #### Returns `void` ### type > **type**: `"effect"` ## Example ```ts // Log events while preserving default Behavior defineBehavior({ on: 'insert.text', actions: [({event}) => [effect(() => console.log(event)), forward(event)]], }) // Effect alone stops propagation (native event is cancelled) defineBehavior({ on: 'keyboard.keydown', actions: [() => [effect(() => console.log('key pressed'))]], }) // Async effect that sends an event later defineBehavior({ on: 'custom.save', actions: [ () => [ effect(async ({send}) => { await saveDocument() send({type: 'custom.saved'}) }), ], ], }) ``` # execute > **execute**(`event`): `object` Defined in: behavior.types.action.ts:73 Directly executes an event, bypassing all Behavior matching. Use `execute` when you want to perform an action without triggering any Behaviors. The event is executed immediately as a direct operation. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### event [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) ## Returns `object` ### event > **event**: [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) ### type > **type**: `"execute"` ## Example ```ts defineBehavior({ on: 'insert.text', guard: ({event}) => event.text === 'a', actions: [() => [execute({type: 'insert.text', text: 'b'})]], }) ``` # forward > **forward**(`event`): `object` Defined in: behavior.types.action.ts:116 Forwards an event to the next Behavior(s) in the current chain. Use `forward` to pass an event to succeeding Behaviors without starting a fresh lookup. This is useful for intercepting events, performing side effects, and then letting the default handling continue. **Key rule:** When forwarding to a different event type, only Behaviors that were already in the remaining chain AND match the new type will run. This means cross-type `forward` is mostly useful for falling through to default Behaviors, not for triggering user-defined Behaviors of a different type. To trigger all Behaviors for a different event type, use [raise](/api/behaviors/functions/raise/) instead. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### event [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) | [`NativeBehaviorEvent`](/api/behaviors/type-aliases/nativebehaviorevent/) | [`CustomBehaviorEvent`](/api/behaviors/type-aliases/custombehaviorevent/) ## Returns `object` ### event > **event**: [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) \| [`NativeBehaviorEvent`](/api/behaviors/type-aliases/nativebehaviorevent/) \| [`CustomBehaviorEvent`](/api/behaviors/type-aliases/custombehaviorevent/) ### type > **type**: `"forward"` ## Example ```ts // Intercept and forward same event type defineBehavior({ on: 'insert.text', actions: [({event}) => [effect(logEvent), forward(event)]], }) // Forward to default handling of different event type defineBehavior({ on: 'clipboard.paste', actions: [ ({event}) => { const text = event.originEvent.dataTransfer?.getData('text/plain') return text ? [forward({type: 'insert.text', text})] : [] }, ], }) ``` # raise > **raise**(`event`): `object` Defined in: behavior.types.action.ts:155 Raises an event, triggering a fresh lookup of all Behaviors. Use `raise` when you want to trigger an event "from scratch", including all Behaviors that match the event type. This is the appropriate action when you want to trigger Behaviors for a different event type. If no Behavior matches the raised event, synthetic events will fall through to their default operation. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### event [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) | [`CustomBehaviorEvent`](/api/behaviors/type-aliases/custombehaviorevent/) ## Returns `object` ### event > **event**: [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) \| [`CustomBehaviorEvent`](/api/behaviors/type-aliases/custombehaviorevent/) ### type > **type**: `"raise"` ## Example ```ts // Raise a custom event that triggers other Behaviors defineBehavior({ on: 'insert.text', guard: ({event}) => event.text === 'a', actions: [() => [raise({type: 'custom.specialInsert'})]], }) // Raise a different event type (fresh lookup includes all Behaviors) defineBehavior({ on: 'clipboard.paste', actions: [ ({event}) => { const text = event.originEvent.dataTransfer?.getData('text/plain') return text ? [raise({type: 'insert.text', text})] : [] }, ], }) ``` # @portabletext/editor ## Type Aliases - [Behavior](/api/behaviors/type-aliases/behavior/) - [BehaviorAction](/api/behaviors/type-aliases/behavioraction/) - [BehaviorActionSet](/api/behaviors/type-aliases/behavioractionset/) - [BehaviorEvent](/api/behaviors/type-aliases/behaviorevent/) - [BehaviorGuard](/api/behaviors/type-aliases/behaviorguard/) - [CustomBehaviorEvent](/api/behaviors/type-aliases/custombehaviorevent/) - [InsertPlacement](/api/behaviors/type-aliases/insertplacement/) - [NativeBehaviorEvent](/api/behaviors/type-aliases/nativebehaviorevent/) - [SyntheticBehaviorEvent](/api/behaviors/type-aliases/syntheticbehaviorevent/) ## Functions - [defineBehavior](/api/behaviors/functions/definebehavior/) - [effect](/api/behaviors/functions/effect/) - [execute](/api/behaviors/functions/execute/) - [forward](/api/behaviors/functions/forward/) - [raise](/api/behaviors/functions/raise/) # Behavior > **Behavior**\<`TBehaviorEventType`, `TGuardResponse`, `TBehaviorEvent`\> = `object` Defined in: behavior.types.behavior.ts:13 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TBehaviorEventType `TBehaviorEventType` *extends* `"*"` \| `` `${BehaviorEventTypeNamespace}.*` `` \| [`BehaviorEvent`](/api/behaviors/type-aliases/behaviorevent/)\[`"type"`\] = `"*"` \| `` `${BehaviorEventTypeNamespace}.*` `` \| [`BehaviorEvent`](/api/behaviors/type-aliases/behaviorevent/)\[`"type"`\] ### TGuardResponse `TGuardResponse` = `true` ### TBehaviorEvent `TBehaviorEvent` *extends* `ResolveBehaviorEvent`\<`TBehaviorEventType`\> = `ResolveBehaviorEvent`\<`TBehaviorEventType`\> ## Properties ### actions > **actions**: [`BehaviorActionSet`](/api/behaviors/type-aliases/behavioractionset/)\<`TBehaviorEvent`, `TGuardResponse`\>[] Defined in: behavior.types.behavior.ts:39 Array of Behavior Action sets. Each set represents a step in the history stack. *** ### guard? > `optional` **guard**: [`BehaviorGuard`](/api/behaviors/type-aliases/behaviorguard/)\<`TBehaviorEvent`, `TGuardResponse`\> Defined in: behavior.types.behavior.ts:34 Predicate function that determines if the Behavior should be executed. Returning a non-nullable value from the guard will pass the value to the actions and execute them. *** ### on > **on**: `TBehaviorEventType` Defined in: behavior.types.behavior.ts:28 Editor Event that triggers this Behavior. # BehaviorAction > **BehaviorAction** = \{ `event`: [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/); `type`: `"execute"`; \} \| \{ `event`: [`NativeBehaviorEvent`](/api/behaviors/type-aliases/nativebehaviorevent/) \| [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) \| [`CustomBehaviorEvent`](/api/behaviors/type-aliases/custombehaviorevent/); `type`: `"forward"`; \} \| \{ `event`: [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) \| [`CustomBehaviorEvent`](/api/behaviors/type-aliases/custombehaviorevent/); `type`: `"raise"`; \} \| \{ `effect`: (`payload`) => `void`; `type`: `"effect"`; \} Defined in: behavior.types.action.ts:14 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BehaviorActionSet > **BehaviorActionSet**\<`TBehaviorEvent`, `TGuardResponse`\> = (`payload`, `guardResponse`) => [`BehaviorAction`](/api/behaviors/type-aliases/behavioraction/)[] Defined in: behavior.types.action.ts:212 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TBehaviorEvent `TBehaviorEvent` ### TGuardResponse `TGuardResponse` ## Parameters ### payload #### dom `EditorDom` #### event `TBehaviorEvent` #### snapshot `EditorSnapshot` ### guardResponse `TGuardResponse` ## Returns [`BehaviorAction`](/api/behaviors/type-aliases/behavioraction/)[] # BehaviorEvent > **BehaviorEvent** = [`SyntheticBehaviorEvent`](/api/behaviors/type-aliases/syntheticbehaviorevent/) \| [`NativeBehaviorEvent`](/api/behaviors/type-aliases/nativebehaviorevent/) \| [`CustomBehaviorEvent`](/api/behaviors/type-aliases/custombehaviorevent/) Defined in: behavior.types.event.ts:20 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BehaviorGuard > **BehaviorGuard**\<`TBehaviorEvent`, `TGuardResponse`\> = (`payload`) => `TGuardResponse` \| `false` Defined in: behavior.types.guard.ts:7 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TBehaviorEvent `TBehaviorEvent` ### TGuardResponse `TGuardResponse` ## Parameters ### payload #### dom `EditorDom` #### event `TBehaviorEvent` #### snapshot `EditorSnapshot` ## Returns `TGuardResponse` \| `false` # CustomBehaviorEvent > **CustomBehaviorEvent**\<`TPayload`, `TType`, `TInternalType`\> = `object` & `TPayload` Defined in: behavior.types.event.ts:771 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration ### type > **type**: `TInternalType` ## Type Parameters ### TPayload `TPayload` *extends* `Record`\<`string`, `unknown`\> = `Record`\<`string`, `unknown`\> ### TType `TType` *extends* `string` = `string` ### TInternalType `TInternalType` *extends* `CustomBehaviorEventType`\<`"custom"`, `TType`\> = `CustomBehaviorEventType`\<`"custom"`, `TType`\> # InsertPlacement > **InsertPlacement** = `"auto"` \| `"after"` \| `"before"` Defined in: behavior.types.event.ts:331 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # NativeBehaviorEvent > **NativeBehaviorEvent** = `ClipboardBehaviorEvent` \| `DragBehaviorEvent` \| `InputBehaviorEvent` \| `KeyboardBehaviorEvent` \| `MouseBehaviorEvent` Defined in: behavior.types.event.ts:636 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # SyntheticBehaviorEvent > **SyntheticBehaviorEvent** = \{ `annotation`: \{ `_key?`: `string`; `name`: `string`; `value`: \{\[`prop`: `string`\]: `unknown`; \}; \}; `at?`: `NonNullable`\<`EditorSelection`\>; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"annotation.add"`\>; \} \| \{ `annotation`: \{ `name`: `string`; \}; `at?`: `NonNullable`\<`EditorSelection`\>; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"annotation.remove"`\>; \} \| \{ `at`: `BlockPath`; `props`: `Record`\<`string`, `unknown`\>; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"block.set"`\>; \} \| \{ `at`: `BlockPath`; `props`: `string`[]; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"block.unset"`\>; \} \| \{ `at`: `ChildPath`; `props`: \{\[`prop`: `string`\]: `unknown`; \}; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"child.set"`\>; \} \| \{ `at`: `ChildPath`; `props`: `string`[]; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"child.unset"`\>; \} \| \{ `at?`: `NonNullable`\<`EditorSelection`\>; `decorator`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"decorator.add"`\>; \} \| \{ `at?`: `NonNullable`\<`EditorSelection`\>; `decorator`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"decorator.remove"`\>; \} \| \{ `at?`: `NonNullable`\<`EditorSelection`\>; `direction?`: `"backward"` \| `"forward"`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"delete"`\>; `unit?`: `"character"` \| `"word"` \| `"line"` \| `"block"` \| `"child"`; \} \| \{ `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"history.redo"`\>; \} \| \{ `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"history.undo"`\>; \} \| \{ `at`: `Path`; `position`: `"before"` \| `"after"`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert"`\>; `value`: `PortableTextTextBlock` \| `PortableTextObject` \| `PortableTextSpan`; \} \| \{ `at?`: `NonNullable`\<`EditorSelection`\>; `block`: `BlockWithOptionalKey`; `placement`: [`InsertPlacement`](/api/behaviors/type-aliases/insertplacement/); `select?`: `"start"` \| `"end"` \| `"none"`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.block"`\>; \} \| \{ `child`: `ChildWithOptionalKey`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.child"`\>; \} \| \{ `at?`: `Path`; `offset?`: `number`; `text`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.text"`\>; \} \| \{ `distance`: `number`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"move.backward"`\>; \} \| \{ `distance`: `number`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"move.forward"`\>; \} \| \{ `at`: `Path`; `offset`: `number`; `text`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"remove.text"`\>; \} \| \{ `at`: `EditorSelection`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"select"`\>; \} \| \{ `at`: `Path`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"set"`\>; `value`: `unknown`; \} \| \{ `at`: `Path`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"unset"`\>; \} \| `AbstractBehaviorEvent` Defined in: behavior.types.event.ts:102 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration \{ `annotation`: \{ `_key?`: `string`; `name`: `string`; `value`: \{\[`prop`: `string`\]: `unknown`; \}; \}; `at?`: `NonNullable`\<`EditorSelection`\>; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"annotation.add"`\>; \} ### annotation > **annotation**: `object` #### annotation.\_key? > `optional` **\_key**: `string` #### annotation.name > **name**: `string` #### annotation.value > **value**: `object` ##### Index Signature \[`prop`: `string`\]: `unknown` ### at? > `optional` **at**: `NonNullable`\<`EditorSelection`\> ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"annotation.add"`\> \{ `annotation`: \{ `name`: `string`; \}; `at?`: `NonNullable`\<`EditorSelection`\>; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"annotation.remove"`\>; \} ### annotation > **annotation**: `object` #### annotation.name > **name**: `string` ### at? > `optional` **at**: `NonNullable`\<`EditorSelection`\> ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"annotation.remove"`\> \{ `at`: `BlockPath`; `props`: `Record`\<`string`, `unknown`\>; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"block.set"`\>; \} ### at > **at**: `BlockPath` ### props > **props**: `Record`\<`string`, `unknown`\> ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"block.set"`\> \{ `at`: `BlockPath`; `props`: `string`[]; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"block.unset"`\>; \} ### at > **at**: `BlockPath` ### props > **props**: `string`[] ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"block.unset"`\> \{ `at`: `ChildPath`; `props`: \{\[`prop`: `string`\]: `unknown`; \}; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"child.set"`\>; \} ### at > **at**: `ChildPath` ### props > **props**: `object` #### Index Signature \[`prop`: `string`\]: `unknown` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"child.set"`\> \{ `at`: `ChildPath`; `props`: `string`[]; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"child.unset"`\>; \} ### at > **at**: `ChildPath` ### props > **props**: `string`[] ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"child.unset"`\> \{ `at?`: `NonNullable`\<`EditorSelection`\>; `decorator`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"decorator.add"`\>; \} ### at? > `optional` **at**: `NonNullable`\<`EditorSelection`\> ### decorator > **decorator**: `string` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"decorator.add"`\> \{ `at?`: `NonNullable`\<`EditorSelection`\>; `decorator`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"decorator.remove"`\>; \} ### at? > `optional` **at**: `NonNullable`\<`EditorSelection`\> ### decorator > **decorator**: `string` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"decorator.remove"`\> \{ `at?`: `NonNullable`\<`EditorSelection`\>; `direction?`: `"backward"` \| `"forward"`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"delete"`\>; `unit?`: `"character"` \| `"word"` \| `"line"` \| `"block"` \| `"child"`; \} ### at? > `optional` **at**: `NonNullable`\<`EditorSelection`\> ### direction? > `optional` **direction**: `"backward"` \| `"forward"` Defaults to forward deletion. ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"delete"`\> ### unit? > `optional` **unit**: `"character"` \| `"word"` \| `"line"` \| `"block"` \| `"child"` Defaults to character deletion. \{ `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"history.redo"`\>; \} ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"history.redo"`\> \{ `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"history.undo"`\>; \} ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"history.undo"`\> \{ `at`: `Path`; `position`: `"before"` \| `"after"`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert"`\>; `value`: `PortableTextTextBlock` \| `PortableTextObject` \| `PortableTextSpan`; \} ### at > **at**: `Path` ### position > **position**: `"before"` \| `"after"` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert"`\> Primitive: insert a node into an array. The last segment of `at` resolves the insertion point: - A keyed `{_key}` segment inserts relative to that sibling. - A numeric index inserts relative to that slot. `position` ('before' or 'after') is always meaningful: `before: [2]` inserts at index 2, `after: [2]` inserts at index 3. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Example ```ts raise({ type: 'insert', at: [{_key: 'list'}, 'items', {_key: 'item3'}], value: newItem, position: 'after', }) ``` ### value > **value**: `PortableTextTextBlock` \| `PortableTextObject` \| `PortableTextSpan` \{ `at?`: `NonNullable`\<`EditorSelection`\>; `block`: `BlockWithOptionalKey`; `placement`: [`InsertPlacement`](/api/behaviors/type-aliases/insertplacement/); `select?`: `"start"` \| `"end"` \| `"none"`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.block"`\>; \} ### at? > `optional` **at**: `NonNullable`\<`EditorSelection`\> ### block > **block**: `BlockWithOptionalKey` ### placement > **placement**: [`InsertPlacement`](/api/behaviors/type-aliases/insertplacement/) ### select? > `optional` **select**: `"start"` \| `"end"` \| `"none"` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.block"`\> \{ `child`: `ChildWithOptionalKey`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.child"`\>; \} ### child > **child**: `ChildWithOptionalKey` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.child"`\> \{ `at?`: `Path`; `offset?`: `number`; `text`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.text"`\>; \} ### at? > `optional` **at**: `Path` ### offset? > `optional` **offset**: `number` ### text > **text**: `string` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"insert.text"`\> Inserts text into a span. Without `at`/`offset`, text is inserted at the current caret position. This is the form used by typing handlers. With `at` and `offset`, text is inserted at the explicit position. Recommended for plugin behaviors and collaborative-edit contexts. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Example ```ts // Caret form raise({type: 'insert.text', text: 'x'}) // Primitive form (@alpha) raise({ type: 'insert.text', at: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 5, text: 'world', }) ``` \{ `distance`: `number`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"move.backward"`\>; \} ### distance > **distance**: `number` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"move.backward"`\> \{ `distance`: `number`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"move.forward"`\>; \} ### distance > **distance**: `number` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"move.forward"`\> \{ `at`: `Path`; `offset`: `number`; `text`: `string`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"remove.text"`\>; \} ### at > **at**: `Path` ### offset > **offset**: `number` ### text > **text**: `string` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"remove.text"`\> Primitive: remove text from a span at the given offset. The `text` field carries the exact text being removed (matches the apply-layer shape so the inverse can be computed without re-reading the span). Recommended for collaborative-edit contexts (concurrent edits compose cleanly under operational transform). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Example ```ts raise({ type: 'remove.text', at: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 5, text: 'world', }) ``` \{ `at`: `EditorSelection`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"select"`\>; \} ### at > **at**: `EditorSelection` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"select"`\> \{ `at`: `Path`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"set"`\>; `value`: `unknown`; \} ### at > **at**: `Path` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"set"`\> Primitive: set a property on a node, or replace a node wholesale. The last segment of `at` is the property name (a string) for property updates, OR a keyed/indexed segment for full-node replacement. Note: `set` on span text (`{at: [...spanPath, 'text'], value: '...'}`) is legal but not recommended in collaborative-edit contexts. Use `insert.text` and `remove.text` for text edits that compose under operational transform. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Example ```ts // Set a block's style raise({type: 'set', at: [{_key: 'b1'}, 'style'], value: 'h1'}) // Replace a block wholesale raise({type: 'set', at: [{_key: 'b1'}], value: newBlock}) ``` ### value > **value**: `unknown` \{ `at`: `Path`; `type`: `StrictExtract`\<`SyntheticBehaviorEventType`, `"unset"`\>; \} ### at > **at**: `Path` ### type > **type**: `StrictExtract`\<`SyntheticBehaviorEventType`, `"unset"`\> Primitive: unset a property on an object, OR remove a node from an array. When the last segment of `at` is a string, the property is removed. When the last segment is a keyed `{_key}` segment or a numeric index, the node at that array position is removed. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Example ```ts // Remove a property raise({type: 'unset', at: [{_key: 'b1'}, 'level']}) // Remove a node from an array raise({type: 'unset', at: [{_key: 'list'}, 'items', {_key: 'item3'}]}) ``` `AbstractBehaviorEvent` # PortableTextEditor Defined in: packages/editor/src/editor/PortableTextEditor.tsx:33 :::caution[Deprecated] Use `useEditor()` instead ``` import {useEditor} from '@portabletext/editor' // Get the editor instance const editor = useEditor() // Send events to the editor editor.send(...) // Derive state from the editor const state = useEditorSelector(editor, snapshot => ...) ``` ::: ## Constructors ### Constructor > **new PortableTextEditor**(`config`): `PortableTextEditor` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:43 #### Parameters ##### config ###### editable `EditableAPI` ###### editorActor `ActorRef` #### Returns `PortableTextEditor` ## Properties ### ~~schemaTypes~~ > **schemaTypes**: `Schema` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:37 A lookup table for all the relevant schema types for this portable text type. ## Methods ### ~~setEditable()~~ > **setEditable**(`editable`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:48 #### Parameters ##### editable `EditableAPI` #### Returns `void` *** ### ~~activeAnnotations()~~ > `static` **activeAnnotations**(`editor`): [`PortableTextObject`](/api/editor/interfaces/portabletextobject/)[] Defined in: packages/editor/src/editor/PortableTextEditor.tsx:65 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isActive = useEditorSelector(editor, selectors.getActiveAnnotations) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns [`PortableTextObject`](/api/editor/interfaces/portabletextobject/)[] *** ### ~~addAnnotation()~~ > `static` **addAnnotation**\<`TSchemaType`\>(`editor`, `type`, `value?`): [`AddedAnnotationPaths`](/api/editor/type-aliases/addedannotationpaths/) \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:105 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'annotation.add', annotation: { name: '...', value: {...}, } }) ``` ::: #### Type Parameters ##### TSchemaType `TSchemaType` *extends* `object` #### Parameters ##### editor `PortableTextEditor` ##### type `TSchemaType` ##### value? #### Returns [`AddedAnnotationPaths`](/api/editor/type-aliases/addedannotationpaths/) \| `undefined` *** ### ~~blur()~~ > `static` **blur**(`editor`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:123 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'blur', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns `void` *** ### ~~delete()~~ > `static` **delete**(`editor`, `selection`, `options?`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:141 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'delete', at: {...}, direction: '...', unit: '...', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### selection [`EditorSelection`](/api/editor/type-aliases/editorselection/) ##### options? [`EditableAPIDeleteOptions`](/api/editor/interfaces/editableapideleteoptions/) #### Returns `void` *** ### ~~findByPath()~~ > `static` **findByPath**(`editor`, `path`): \[[`PortableTextObject`](/api/editor/interfaces/portabletextobject/) \| [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/) \| [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/)\<[`PortableTextObject`](/api/editor/interfaces/portabletextobject/) \| [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/)\> \| `undefined`, [`Path`](/api/editor/type-aliases/path/) \| `undefined`\] Defined in: packages/editor/src/editor/PortableTextEditor.tsx:154 #### Parameters ##### editor `PortableTextEditor` ##### path [`Path`](/api/editor/type-aliases/path/) #### Returns \[[`PortableTextObject`](/api/editor/interfaces/portabletextobject/) \| [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/) \| [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/)\<[`PortableTextObject`](/api/editor/interfaces/portabletextobject/) \| [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/)\> \| `undefined`, [`Path`](/api/editor/type-aliases/path/) \| `undefined`\] *** ### ~~findDOMNode()~~ > `static` **findDOMNode**(`editor`, `element`): `Node` \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:147 #### Parameters ##### editor `PortableTextEditor` ##### element [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) | [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/) | [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/)\<[`PortableTextObject`](/api/editor/interfaces/portabletextobject/) \| [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/)\> #### Returns `Node` \| `undefined` *** ### ~~focus()~~ > `static` **focus**(`editor`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:169 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'focus', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns `void` *** ### ~~focusBlock()~~ > `static` **focusBlock**(`editor`): [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/) \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:183 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const focusBlock = useEditorSelector(editor, selectors.getFocusBlock) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/) \| `undefined` *** ### ~~focusChild()~~ > `static` **focusChild**(`editor`): [`PortableTextChild`](/api/editor/type-aliases/portabletextchild/) \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:197 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const focusChild = useEditorSelector(editor, selectors.getFocusChild) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns [`PortableTextChild`](/api/editor/type-aliases/portabletextchild/) \| `undefined` *** ### ~~getFragment()~~ > `static` **getFragment**(`editor`): [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:485 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const selectedValue = useEditorSelector(editor, selectors.getSelectedValue) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` *** ### ~~getSelection()~~ > `static` **getSelection**(`editor`): [`EditorSelection`](/api/editor/type-aliases/editorselection/) Defined in: packages/editor/src/editor/PortableTextEditor.tsx:213 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const selection = useEditorSelector(editor, selectors.getSelection) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns [`EditorSelection`](/api/editor/type-aliases/editorselection/) *** ### ~~getValue()~~ > `static` **getValue**(`editor`): [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:227 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const value = useEditorSelector(editor, selectors.getValue) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` *** ### ~~hasBlockStyle()~~ > `static` **hasBlockStyle**(`editor`, `blockStyle`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:241 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isActive = useEditorSelector(editor, selectors.isActiveStyle(...)) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### blockStyle `string` #### Returns `boolean` *** ### ~~hasListStyle()~~ > `static` **hasListStyle**(`editor`, `listStyle`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:255 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isActive = useEditorSelector(editor, selectors.isActiveListItem(...)) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### listStyle `string` #### Returns `boolean` *** ### ~~insertBlock()~~ > `static` **insertBlock**\<`TSchemaType`\>(`editor`, `type`, `value?`): [`Path`](/api/editor/type-aliases/path/) \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:343 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'insert.block object', blockObject: { name: '...', value: {...}, }, placement: 'auto' | 'after' | 'before', }) ``` ::: #### Type Parameters ##### TSchemaType `TSchemaType` *extends* `object` #### Parameters ##### editor `PortableTextEditor` ##### type `TSchemaType` ##### value? #### Returns [`Path`](/api/editor/type-aliases/path/) \| `undefined` *** ### ~~insertBreak()~~ > `static` **insertBreak**(`editor`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:362 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'insert.break', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns `void` *** ### ~~insertChild()~~ > `static` **insertChild**\<`TSchemaType`\>(`editor`, `type`, `value?`): [`Path`](/api/editor/type-aliases/path/) \| `undefined` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:319 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'insert.span', text: '...', annotations: [{name: '...', value: {...}}], decorators: ['...'], }) editor.send({ type: 'insert.inline object', inlineObject: { name: '...', value: {...}, }, }) ``` ::: #### Type Parameters ##### TSchemaType `TSchemaType` *extends* `object` #### Parameters ##### editor `PortableTextEditor` ##### type `TSchemaType` ##### value? #### Returns [`Path`](/api/editor/type-aliases/path/) \| `undefined` *** ### ~~isAnnotationActive()~~ > `static` **isAnnotationActive**(`editor`, `annotationType`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:81 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isActive = useEditorSelector(editor, selectors.isActiveAnnotation(...)) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### annotationType `string` #### Returns `boolean` *** ### ~~isCollapsedSelection()~~ > `static` **isCollapsedSelection**(`editor`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:269 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isSelectionCollapsed = useEditorSelector(editor, selectors.isSelectionCollapsed) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns `boolean` *** ### ~~isExpandedSelection()~~ > `static` **isExpandedSelection**(`editor`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:282 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isSelectionExpanded = useEditorSelector(editor, selectors.isSelectionExpanded) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns `boolean` *** ### ~~isMarkActive()~~ > `static` **isMarkActive**(`editor`, `mark`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:295 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isActive = useEditorSelector(editor, selectors.isActiveDecorator(...)) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### mark `string` #### Returns `boolean` *** ### ~~isObjectPath()~~ > `static` **isObjectPath**(`_editor`, `path`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:373 #### Parameters ##### \_editor `PortableTextEditor` ##### path [`Path`](/api/editor/type-aliases/path/) #### Returns `boolean` *** ### ~~isSelectionsOverlapping()~~ > `static` **isSelectionsOverlapping**(`editor`, `selectionA`, `selectionB`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:531 :::caution[Deprecated] Use built-in selectors or write your own: https://www.portabletext.org/reference/selectors/ ``` import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isOverlapping = useEditorSelector(editor, selectors.isOverlappingSelection(selectionB)) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### selectionA [`EditorSelection`](/api/editor/type-aliases/editorselection/) ##### selectionB [`EditorSelection`](/api/editor/type-aliases/editorselection/) #### Returns `boolean` *** ### ~~isVoid()~~ > `static` **isVoid**(`editor`, `element`): `boolean` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:366 #### Parameters ##### editor `PortableTextEditor` ##### element [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) | [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/) | [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/)\<[`PortableTextObject`](/api/editor/interfaces/portabletextobject/) \| [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/)\> #### Returns `boolean` *** ### ~~marks()~~ > `static` **marks**(`editor`): `string`[] Defined in: packages/editor/src/editor/PortableTextEditor.tsx:382 #### Parameters ##### editor `PortableTextEditor` #### Returns `string`[] *** ### ~~redo()~~ > `static` **redo**(`editor`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:517 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'history.redo', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns `void` *** ### ~~removeAnnotation()~~ > `static` **removeAnnotation**\<`TSchemaType`\>(`editor`, `type`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:419 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'annotation.remove', annotation: { name: '...', }, }) ``` ::: #### Type Parameters ##### TSchemaType `TSchemaType` *extends* `object` #### Parameters ##### editor `PortableTextEditor` ##### type `TSchemaType` #### Returns `void` *** ### ~~select()~~ > `static` **select**(`editor`, `selection`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:398 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'select', selection: {...}, }) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### selection [`EditorSelection`](/api/editor/type-aliases/editorselection/) #### Returns `void` *** ### ~~toggleBlockStyle()~~ > `static` **toggleBlockStyle**(`editor`, `blockStyle`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:436 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'style.toggle', style: '...', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### blockStyle `string` #### Returns `void` *** ### ~~toggleList()~~ > `static` **toggleList**(`editor`, `listStyle`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:455 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'list item.toggle', listItem: '...', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### listStyle `string` #### Returns `void` *** ### ~~toggleMark()~~ > `static` **toggleMark**(`editor`, `mark`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:471 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'decorator.toggle', decorator: '...', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` ##### mark `string` #### Returns `void` *** ### ~~undo()~~ > `static` **undo**(`editor`): `void` Defined in: packages/editor/src/editor/PortableTextEditor.tsx:502 :::caution[Deprecated] Use `editor.send(...)` instead ``` const editor = useEditor() editor.send({ type: 'history.undo', }) ``` ::: #### Parameters ##### editor `PortableTextEditor` #### Returns `void` # defineBlockObject > **defineBlockObject**\<`TType`\>(`config`): [`BlockObject`](/api/editor/type-aliases/blockobject/) Defined in: packages/editor/src/renderers/renderer.types.ts:423 Define a non-editable block-level object renderer for a `_type` declared in the schema's `blockObjects` array. The render must always render `children` somewhere inside the outer element. `children` carries an engine-emitted void spacer the browser uses to anchor the caret next to the element. Dropping `children` makes the caret unable to land on the element. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TType `TType` *extends* `string` ## Parameters ### config #### render? [`BlockObjectRender`](/api/editor/type-aliases/blockobjectrender/) #### type `TType` *extends* `"block"` ? `"Error: defineBlockObject({type: 'block'}) is forbidden -- 'block' is always a text block, use defineTextBlock"` : `TType` *extends* `"span"` ? `"Error: defineBlockObject({type: 'span'}) is forbidden -- 'span' is always a span, use defineSpan"` : `TType` ## Returns [`BlockObject`](/api/editor/type-aliases/blockobject/) ## Example ```ts defineBlockObject({ type: 'image', render: ({attributes, children, node}) => (
{children}
), }) ``` # defineContainer > **defineContainer**\<`TType`\>(`config`): [`Container`](/api/editor/type-aliases/container/) Defined in: packages/editor/src/renderers/renderer.types.ts:344 Define a container renderer. The returned registration is mounted via the `` component at the top level, or nested inside another container's `of` array as a positional override. `type` cannot be `'span'` (use [defineSpan](/api/editor/functions/definespan/)) nor `'block'` (use [defineTextBlock](/api/editor/functions/definetextblock/)). The text block is not a container. The `node` argument of `render` narrows to a portable text object. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TType `TType` *extends* `string` ## Parameters ### config #### arrayField `string` #### of? readonly ([`Container`](/api/editor/type-aliases/container/) \| [`TextBlock`](/api/editor/type-aliases/textblock/) \| [`BlockObject`](/api/editor/type-aliases/blockobject/))[] #### render? (`props`) => `ReactElement` #### type `TType` *extends* `"span"` ? `"Error: defineContainer({type: 'span'}) is forbidden -- 'span' is always a span, use defineSpan"` : `TType` *extends* `"block"` ? `"Error: defineContainer({type: 'block'}) is forbidden -- 'block' is always a text block, use defineTextBlock"` : `TType` *extends* `"*"` ? `"Error: defineContainer({type: '*'}) is forbidden -- containers cannot be registered by wildcard"` : `TType` ## Returns [`Container`](/api/editor/type-aliases/container/) ## Example ```ts defineContainer({ type: 'table', arrayField: 'rows', render: ({children}) => ( {children}
), of: [ defineContainer({ type: 'row', arrayField: 'cells', render: ({children}) => ( {children} ), }), ], }) ``` # defineInlineObject > **defineInlineObject**\<`TType`\>(`config`): [`InlineObject`](/api/editor/type-aliases/inlineobject/) Defined in: packages/editor/src/renderers/renderer.types.ts:458 Define a non-editable inline object renderer for a `_type` declared in the schema's `inlineObjects` array. The render must always render `children` somewhere inside the outer element. `children` carries an engine-emitted void spacer the browser uses to anchor the caret next to the element. Dropping `children` makes the caret unable to land on the element. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TType `TType` *extends* `string` ## Parameters ### config #### render? [`InlineObjectRender`](/api/editor/type-aliases/inlineobjectrender/) #### type `TType` *extends* `"block"` ? `"Error: defineInlineObject({type: 'block'}) is forbidden -- 'block' is always a text block, use defineTextBlock"` : `TType` *extends* `"span"` ? `"Error: defineInlineObject({type: 'span'}) is forbidden -- 'span' is always a span, use defineSpan"` : `TType` ## Returns [`InlineObject`](/api/editor/type-aliases/inlineobject/) ## Example ```ts defineInlineObject({ type: 'mention', render: ({attributes, children, node}) => ( {children} @{(node as {username?: string}).username} ), }) ``` # defineSchema > **defineSchema**\<`TSchemaDefinition`\>(`definition`): `TSchemaDefinition` Defined in: packages/schema/dist/index.d.ts:189 A helper wrapper that adds editor support, such as autocomplete and type checking, for a schema definition. ## Type Parameters ### TSchemaDefinition `TSchemaDefinition` *extends* [`SchemaDefinition`](/api/editor/type-aliases/schemadefinition/) ## Parameters ### definition `TSchemaDefinition` ## Returns `TSchemaDefinition` ## Example ```ts import { defineSchema } from '@portabletext/editor' const schemaDefinition = defineSchema({ decorators: [{name: 'strong'}, {name: 'em'}, {name: 'underline'}], annotations: [{name: 'link'}], styles: [ {name: 'normal'}, {name: 'h1'}, {name: 'h2'}, {name: 'h3'}, {name: 'blockquote'}, ], lists: [], inlineObjects: [], blockObjects: [], } ``` # defineSpan > **defineSpan**\<`TType`\>(`config`): [`Span`](/api/editor/type-aliases/span/) Defined in: packages/editor/src/renderers/renderer.types.ts:390 Define a span renderer. The returned registration is mounted via the `` component at the top level, or nested inside a container's `of` array as a positional override. `type` is required even though there is only one top-level span type (`'span'`) today. Keeping `type` required leaves the door open for positional overrides of span-like inlines (e.g. a `code-span` inside a `code-block` container). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TType `TType` *extends* `string` ## Parameters ### config #### render? [`SpanRender`](/api/editor/type-aliases/spanrender/) #### type `TType` *extends* `"block"` ? `"Error: defineSpan({type: 'block'}) is forbidden -- 'block' is always a text block, use defineTextBlock"` : `TType` ## Returns [`Span`](/api/editor/type-aliases/span/) ## Example ```ts defineSpan({ type: 'span', render: ({attributes, children}) => ( {children} ), }) ``` # defineTextBlock > **defineTextBlock**\<`TType`\>(`config`): [`TextBlock`](/api/editor/type-aliases/textblock/) Defined in: packages/editor/src/renderers/renderer.types.ts:491 Define a text block renderer. The returned registration is mounted via the `` component, or nested inside a container's `of` array as a positional override. `type` is required even though the top-level text block type is always `'block'`. Keeping `type` required leaves the door open for positional overrides of text-block-like elements (e.g. a `code-line` inside a `code-block` container). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Type Parameters ### TType `TType` *extends* `string` ## Parameters ### config #### of? readonly ([`Span`](/api/editor/type-aliases/span/) \| [`InlineObject`](/api/editor/type-aliases/inlineobject/))[] #### render? [`TextBlockRender`](/api/editor/type-aliases/textblockrender/) #### type `TType` *extends* `"span"` ? `"Error: defineTextBlock({type: 'span'}) is forbidden -- 'span' is always a span, use defineSpan"` : `TType` ## Returns [`TextBlock`](/api/editor/type-aliases/textblock/) ## Example ```ts defineTextBlock({ type: 'block', render: ({attributes, children}) => (

{children}

), }) ``` # EditorProvider > **EditorProvider**(`props`): `Element` Defined in: packages/editor/src/editor/editor-provider.tsx:39 The EditorProvider component is used to set up the editor context and configure the Portable Text Editor. ## Parameters ### props [`EditorProviderProps`](/api/editor/type-aliases/editorproviderprops/) ## Returns `Element` ## Example ```tsx import {EditorProvider} from '@portabletext/editor' function App() { return ( ... ) } ``` # keyGenerator > **keyGenerator**(): `string` Defined in: packages/editor/src/utils/key-generator.ts:4 ## Returns `string` # resolveContainerAt > **resolveContainerAt**(`containers`, `value`, `path`): [`RegisteredContainer`](/api/editor/type-aliases/registeredcontainer/) \| [`RegisteredPositional`](/api/editor/type-aliases/registeredpositional/) \| `undefined` Defined in: packages/editor/src/schema/resolve-container-at.ts:37 Walk the editor value following `path` and return the [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/) or [RegisteredPositional](/api/editor/type-aliases/registeredpositional/) that applies at `path`'s target position. Resolution rules at each step: 1. **Positional override.** If the current parent declares the child's `_type` in its `of`, the positional entry wins. Used to resolve same-`_type` registered under different parents with different `field` values. 2. **Global fallback.** If the parent has no positional override, fall back to the top-level entry for `_type` in `containers`. 3. **Chain validity.** If any ancestor along the path has no resolved container entry (unregistered or not reachable as a container at its position), return `undefined`. Returns `undefined` when the target's `_type` is not registered at this position. Returns a [RegisteredPositional](/api/editor/type-aliases/registeredpositional/) when the target resolves to a leaf in a positional `of` (terminal node with no editable children). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### containers [`Containers`](/api/editor/type-aliases/containers/) ### value readonly `Node`[] ### path [`Path`](/api/editor/type-aliases/path/) ## Returns [`RegisteredContainer`](/api/editor/type-aliases/registeredcontainer/) \| [`RegisteredPositional`](/api/editor/type-aliases/registeredpositional/) \| `undefined` # useEditor > **useEditor**(): [`Editor`](/api/editor/type-aliases/editor/) Defined in: packages/editor/src/editor/use-editor.ts:19 Get the current editor context from the `EditorProvider`. Must be used inside the `EditorProvider` component. ## Returns [`Editor`](/api/editor/type-aliases/editor/) The current editor object. ## Example ```tsx import { useEditor } from '@portabletext/editor' function MyComponent() { const editor = useEditor() } ``` # useEditorSelector > **useEditorSelector**\<`TSelected`\>(`editor`, `selector`, `compare`): `TSelected` Defined in: packages/editor/src/editor/editor-selector.ts:39 Hook to select a value from the editor state. ## Type Parameters ### TSelected `TSelected` ## Parameters ### editor [`Editor`](/api/editor/type-aliases/editor/) ### selector [`EditorSelector`](/api/editor/type-aliases/editorselector/)\<`TSelected`\> ### compare (`a`, `b`) => `boolean` ## Returns `TSelected` ## Examples Pass a selector as the second argument ```tsx import { useEditorSelector } from '@portabletext/editor' function MyComponent(editor) { const value = useEditorSelector(editor, selector) } ``` Pass an inline selector as the second argument. In this case, use the editor context to obtain the schema. ```tsx import { useEditorSelector } from '@portabletext/editor' function MyComponent(editor) { const schema = useEditorSelector(editor, (snapshot) => snapshot.context.schema) } ``` # usePortableTextEditor > **usePortableTextEditor**(): [`PortableTextEditor`](/api/editor/classes/portabletexteditor/) Defined in: packages/editor/src/editor/usePortableTextEditor.ts:15 :::caution[Deprecated] Use `useEditor` to get the current editor instance. Get the current editor object from the React context. ::: ## Returns [`PortableTextEditor`](/api/editor/classes/portabletexteditor/) # usePortableTextEditorSelection > **usePortableTextEditorSelection**(): [`EditorSelection`](/api/editor/type-aliases/editorselection/) Defined in: packages/editor/src/editor/usePortableTextEditorSelection.tsx:11 :::caution[Deprecated] Use `useEditorSelector` to get the current editor selection. Get the current editor selection from the React context. ::: ## Returns [`EditorSelection`](/api/editor/type-aliases/editorselection/) # BlockAnnotationRenderProps Defined in: packages/editor/src/types/editor.ts:200 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### block > **block**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/) Defined in: packages/editor/src/types/editor.ts:201 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### children > **children**: `ReactElement`\<`any`\> Defined in: packages/editor/src/types/editor.ts:202 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### editorElementRef > **editorElementRef**: `RefObject`\<`HTMLElement` \| `null`\> Defined in: packages/editor/src/types/editor.ts:203 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/types/editor.ts:204 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:205 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### schemaType > **schemaType**: [`AnnotationSchemaType`](/api/editor/type-aliases/annotationschematype/) Defined in: packages/editor/src/types/editor.ts:206 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/types/editor.ts:207 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### value > **value**: [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) Defined in: packages/editor/src/types/editor.ts:208 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BlockChildRenderProps Defined in: packages/editor/src/types/editor.ts:188 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### annotations > **annotations**: [`PortableTextObject`](/api/editor/interfaces/portabletextobject/)[] Defined in: packages/editor/src/types/editor.ts:189 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### children > **children**: `ReactElement`\<`any`\> Defined in: packages/editor/src/types/editor.ts:190 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### editorElementRef > **editorElementRef**: `RefObject`\<`HTMLElement` \| `null`\> Defined in: packages/editor/src/types/editor.ts:191 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/types/editor.ts:192 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:193 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### schemaType > **schemaType**: [`InlineObjectSchemaType`](/api/editor/type-aliases/inlineobjectschematype/) Defined in: packages/editor/src/types/editor.ts:195 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/types/editor.ts:194 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### value > **value**: [`PortableTextChild`](/api/editor/type-aliases/portabletextchild/) Defined in: packages/editor/src/types/editor.ts:196 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BlockDecoratorRenderProps Defined in: packages/editor/src/types/editor.ts:211 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### children > **children**: `ReactElement`\<`any`\> Defined in: packages/editor/src/types/editor.ts:212 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### editorElementRef > **editorElementRef**: `RefObject`\<`HTMLElement` \| `null`\> Defined in: packages/editor/src/types/editor.ts:213 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/types/editor.ts:214 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:215 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### schemaType > **schemaType**: [`DecoratorSchemaType`](/api/editor/type-aliases/decoratorschematype/) Defined in: packages/editor/src/types/editor.ts:216 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/types/editor.ts:217 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### value > **value**: `string` Defined in: packages/editor/src/types/editor.ts:218 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BlockListItemRenderProps Defined in: packages/editor/src/types/editor.ts:221 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### block > **block**: [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/) Defined in: packages/editor/src/types/editor.ts:222 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### children > **children**: `ReactElement`\<`any`\> Defined in: packages/editor/src/types/editor.ts:223 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### editorElementRef > **editorElementRef**: `RefObject`\<`HTMLElement` \| `null`\> Defined in: packages/editor/src/types/editor.ts:224 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/types/editor.ts:225 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### level > **level**: `number` Defined in: packages/editor/src/types/editor.ts:226 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:227 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### schemaType > **schemaType**: [`ListSchemaType`](/api/editor/type-aliases/listschematype/) Defined in: packages/editor/src/types/editor.ts:228 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/types/editor.ts:229 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### value > **value**: `string` Defined in: packages/editor/src/types/editor.ts:230 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BlockRenderProps Defined in: packages/editor/src/types/editor.ts:174 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### children > **children**: `ReactElement`\<`any`\> Defined in: packages/editor/src/types/editor.ts:175 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### editorElementRef > **editorElementRef**: `RefObject`\<`HTMLElement` \| `null`\> Defined in: packages/editor/src/types/editor.ts:176 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/types/editor.ts:177 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### level? > `optional` **level**: `number` Defined in: packages/editor/src/types/editor.ts:178 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### listItem? > `optional` **listItem**: `string` Defined in: packages/editor/src/types/editor.ts:179 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:180 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### schemaType > **schemaType**: [`BlockObjectSchemaType`](/api/editor/type-aliases/blockobjectschematype/) Defined in: packages/editor/src/types/editor.ts:183 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/types/editor.ts:181 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### style? > `optional` **style**: `string` Defined in: packages/editor/src/types/editor.ts:182 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### value > **value**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/) Defined in: packages/editor/src/types/editor.ts:184 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BlockStyleRenderProps Defined in: packages/editor/src/types/editor.ts:256 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### block > **block**: [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/) Defined in: packages/editor/src/types/editor.ts:257 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### children > **children**: `ReactElement`\<`any`\> Defined in: packages/editor/src/types/editor.ts:258 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### editorElementRef > **editorElementRef**: `RefObject`\<`HTMLElement` \| `null`\> Defined in: packages/editor/src/types/editor.ts:259 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/types/editor.ts:260 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:261 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### schemaType > **schemaType**: [`StyleSchemaType`](/api/editor/type-aliases/styleschematype/) Defined in: packages/editor/src/types/editor.ts:263 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/types/editor.ts:262 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### value > **value**: `string` Defined in: packages/editor/src/types/editor.ts:264 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # EditableAPIDeleteOptions Defined in: packages/editor/src/types/editor.ts:28 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### mode? > `optional` **mode**: `"blocks"` \| `"children"` \| `"selected"` Defined in: packages/editor/src/types/editor.ts:29 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # PasteData Defined in: packages/editor/src/types/editor.ts:153 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### event > **event**: `ClipboardEvent` Defined in: packages/editor/src/types/editor.ts:154 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:155 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### schemaTypes > **schemaTypes**: `Schema` Defined in: packages/editor/src/types/editor.ts:156 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: *** ### value > **value**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` Defined in: packages/editor/src/types/editor.ts:157 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # PortableTextObject Defined in: packages/schema/dist/index.d.ts:294 ## Indexable \[`other`: `string`\]: `unknown` ## Properties ### \_key > **\_key**: `string` Defined in: packages/schema/dist/index.d.ts:296 *** ### \_type > **\_type**: `string` Defined in: packages/schema/dist/index.d.ts:295 # PortableTextSpan Defined in: packages/schema/dist/index.d.ts:279 ## Properties ### \_key > **\_key**: `string` Defined in: packages/schema/dist/index.d.ts:280 *** ### \_type > **\_type**: `"span"` Defined in: packages/schema/dist/index.d.ts:281 *** ### marks? > `optional` **marks**: `string`[] Defined in: packages/schema/dist/index.d.ts:283 *** ### text > **text**: `string` Defined in: packages/schema/dist/index.d.ts:282 # PortableTextTextBlock Defined in: packages/schema/dist/index.d.ts:261 ## Type Parameters ### TChild `TChild` = [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/) \| [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) ## Properties ### \_key > **\_key**: `string` Defined in: packages/schema/dist/index.d.ts:263 *** ### \_type > **\_type**: `string` Defined in: packages/schema/dist/index.d.ts:262 *** ### children > **children**: `TChild`[] Defined in: packages/schema/dist/index.d.ts:264 *** ### level? > `optional` **level**: `number` Defined in: packages/schema/dist/index.d.ts:268 *** ### listItem? > `optional` **listItem**: `string` Defined in: packages/schema/dist/index.d.ts:266 *** ### markDefs? > `optional` **markDefs**: [`PortableTextObject`](/api/editor/interfaces/portabletextobject/)[] Defined in: packages/schema/dist/index.d.ts:265 *** ### style? > `optional` **style**: `string` Defined in: packages/schema/dist/index.d.ts:267 # RangeDecoration Defined in: packages/editor/src/types/editor.ts:296 A range decoration is a UI affordance that wraps a given selection range in the editor with a custom component. This can be used to highlight search results, mark validation errors on specific words, draw user presence and similar. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### component() > **component**: (`props`) => `ReactElement`\<`any`\> Defined in: packages/editor/src/types/editor.ts:310 A component for rendering the range decoration. The component will receive the children (text) of the range decoration as its children. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Parameters ##### props ###### children? `ReactNode` #### Returns `ReactElement`\<`any`\> #### Example ```ts (rangeComponentProps: PropsWithChildren) => ( {rangeComponentProps.children} ) ``` *** ### onMoved()? > `optional` **onMoved**: (`details`) => `void` Defined in: packages/editor/src/types/editor.ts:318 A optional callback that will be called when the range decoration potentially moves according to user edits. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Parameters ##### details [`RangeDecorationOnMovedDetails`](/api/editor/interfaces/rangedecorationonmoveddetails/) #### Returns `void` *** ### payload? > `optional` **payload**: `Record`\<`string`, `unknown`\> Defined in: packages/editor/src/types/editor.ts:322 A custom payload that can be set on the range decoration :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: *** ### selection > **selection**: [`EditorSelection`](/api/editor/type-aliases/editorselection/) Defined in: packages/editor/src/types/editor.ts:314 The editor content selection range :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: # RangeDecorationOnMovedDetails Defined in: packages/editor/src/types/editor.ts:286 Parameters for the callback that will be called for a RangeDecoration's onMoved. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### newSelection > **newSelection**: [`EditorSelection`](/api/editor/type-aliases/editorselection/) Defined in: packages/editor/src/types/editor.ts:288 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: *** ### origin > **origin**: `"remote"` \| `"local"` Defined in: packages/editor/src/types/editor.ts:289 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: *** ### rangeDecoration > **rangeDecoration**: [`RangeDecoration`](/api/editor/interfaces/rangedecoration/) Defined in: packages/editor/src/types/editor.ts:287 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: # @portabletext/editor ## Classes - [~~PortableTextEditor~~](/api/editor/classes/portabletexteditor/) ## Interfaces - [BlockAnnotationRenderProps](/api/editor/interfaces/blockannotationrenderprops/) - [BlockChildRenderProps](/api/editor/interfaces/blockchildrenderprops/) - [BlockDecoratorRenderProps](/api/editor/interfaces/blockdecoratorrenderprops/) - [BlockListItemRenderProps](/api/editor/interfaces/blocklistitemrenderprops/) - [BlockRenderProps](/api/editor/interfaces/blockrenderprops/) - [BlockStyleRenderProps](/api/editor/interfaces/blockstylerenderprops/) - [EditableAPIDeleteOptions](/api/editor/interfaces/editableapideleteoptions/) - [PasteData](/api/editor/interfaces/pastedata/) - [PortableTextObject](/api/editor/interfaces/portabletextobject/) - [PortableTextSpan](/api/editor/interfaces/portabletextspan/) - [PortableTextTextBlock](/api/editor/interfaces/portabletexttextblock/) - [RangeDecoration](/api/editor/interfaces/rangedecoration/) - [RangeDecorationOnMovedDetails](/api/editor/interfaces/rangedecorationonmoveddetails/) ## Type Aliases - [AddedAnnotationPaths](/api/editor/type-aliases/addedannotationpaths/) - [AnnotationDefinition](/api/editor/type-aliases/annotationdefinition/) - [AnnotationPath](/api/editor/type-aliases/annotationpath/) - [AnnotationSchemaType](/api/editor/type-aliases/annotationschematype/) - [BaseDefinition](/api/editor/type-aliases/basedefinition/) - [BlockObject](/api/editor/type-aliases/blockobject/) - [BlockObjectDefinition](/api/editor/type-aliases/blockobjectdefinition/) - [BlockObjectRender](/api/editor/type-aliases/blockobjectrender/) - [BlockObjectRenderProps](/api/editor/type-aliases/blockobjectrenderprops/) - [BlockObjectSchemaType](/api/editor/type-aliases/blockobjectschematype/) - [BlockOffset](/api/editor/type-aliases/blockoffset/) - [BlockPath](/api/editor/type-aliases/blockpath/) - [ChildPath](/api/editor/type-aliases/childpath/) - [Container](/api/editor/type-aliases/container/) - [ContainerRender](/api/editor/type-aliases/containerrender/) - [ContainerRenderProps](/api/editor/type-aliases/containerrenderprops/) - [Containers](/api/editor/type-aliases/containers/) - [DecoratorDefinition](/api/editor/type-aliases/decoratordefinition/) - [DecoratorSchemaType](/api/editor/type-aliases/decoratorschematype/) - [Editor](/api/editor/type-aliases/editor/) - [EditorConfig](/api/editor/type-aliases/editorconfig/) - [EditorContext](/api/editor/type-aliases/editorcontext/) - [EditorEmittedEvent](/api/editor/type-aliases/editoremittedevent/) - [EditorEvent](/api/editor/type-aliases/editorevent/) - [EditorProviderProps](/api/editor/type-aliases/editorproviderprops/) - [EditorSchema](/api/editor/type-aliases/editorschema/) - [EditorSelection](/api/editor/type-aliases/editorselection/) - [EditorSelectionPoint](/api/editor/type-aliases/editorselectionpoint/) - [EditorSelector](/api/editor/type-aliases/editorselector/) - [EditorSnapshot](/api/editor/type-aliases/editorsnapshot/) - [FieldDefinition](/api/editor/type-aliases/fielddefinition/) - [HotkeyOptions](/api/editor/type-aliases/hotkeyoptions/) - [InlineObject](/api/editor/type-aliases/inlineobject/) - [InlineObjectDefinition](/api/editor/type-aliases/inlineobjectdefinition/) - [InlineObjectRender](/api/editor/type-aliases/inlineobjectrender/) - [InlineObjectRenderProps](/api/editor/type-aliases/inlineobjectrenderprops/) - [InlineObjectSchemaType](/api/editor/type-aliases/inlineobjectschematype/) - [InvalidValueResolution](/api/editor/type-aliases/invalidvalueresolution/) - [ListDefinition](/api/editor/type-aliases/listdefinition/) - [ListSchemaType](/api/editor/type-aliases/listschematype/) - [MutationEvent](/api/editor/type-aliases/mutationevent/) - [OnCopyFn](/api/editor/type-aliases/oncopyfn/) - [OnPasteFn](/api/editor/type-aliases/onpastefn/) - [OnPasteResult](/api/editor/type-aliases/onpasteresult/) - [OnPasteResultOrPromise](/api/editor/type-aliases/onpasteresultorpromise/) - [Operation](/api/editor/type-aliases/operation/) - [Patch](/api/editor/type-aliases/patch/) - [PatchesEvent](/api/editor/type-aliases/patchesevent/) - [Path](/api/editor/type-aliases/path/) - [PortableTextBlock](/api/editor/type-aliases/portabletextblock/) - [PortableTextChild](/api/editor/type-aliases/portabletextchild/) - [PortableTextEditableProps](/api/editor/type-aliases/portabletexteditableprops/) - [RegisteredBlockObject](/api/editor/type-aliases/registeredblockobject/) - [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/) - [RegisteredInlineObject](/api/editor/type-aliases/registeredinlineobject/) - [RegisteredPositional](/api/editor/type-aliases/registeredpositional/) - [RegisteredSpan](/api/editor/type-aliases/registeredspan/) - [RegistrableNode](/api/editor/type-aliases/registrablenode/) - [RenderAnnotationFunction](/api/editor/type-aliases/renderannotationfunction/) - [RenderBlockFunction](/api/editor/type-aliases/renderblockfunction/) - [RenderChildFunction](/api/editor/type-aliases/renderchildfunction/) - [RenderDecoratorFunction](/api/editor/type-aliases/renderdecoratorfunction/) - [RenderEditableFunction](/api/editor/type-aliases/rendereditablefunction/) - [RenderListItemFunction](/api/editor/type-aliases/renderlistitemfunction/) - [RenderPlaceholderFunction](/api/editor/type-aliases/renderplaceholderfunction/) - [RenderStyleFunction](/api/editor/type-aliases/renderstylefunction/) - [SchemaDefinition](/api/editor/type-aliases/schemadefinition/) - [ScrollSelectionIntoViewFunction](/api/editor/type-aliases/scrollselectionintoviewfunction/) - [Span](/api/editor/type-aliases/span/) - [SpanRender](/api/editor/type-aliases/spanrender/) - [SpanRenderProps](/api/editor/type-aliases/spanrenderprops/) - [StyleDefinition](/api/editor/type-aliases/styledefinition/) - [StyleSchemaType](/api/editor/type-aliases/styleschematype/) - [TextBlock](/api/editor/type-aliases/textblock/) - [TextBlockRender](/api/editor/type-aliases/textblockrender/) - [TextBlockRenderProps](/api/editor/type-aliases/textblockrenderprops/) ## Functions - [defineBlockObject](/api/editor/functions/defineblockobject/) - [defineContainer](/api/editor/functions/definecontainer/) - [defineInlineObject](/api/editor/functions/defineinlineobject/) - [defineSchema](/api/editor/functions/defineschema/) - [defineSpan](/api/editor/functions/definespan/) - [defineTextBlock](/api/editor/functions/definetextblock/) - [keyGenerator](/api/editor/functions/keygenerator/) - [resolveContainerAt](/api/editor/functions/resolvecontainerat/) - [~~usePortableTextEditor~~](/api/editor/functions/useportabletexteditor/) - [~~usePortableTextEditorSelection~~](/api/editor/functions/useportabletexteditorselection/) ## Components - [PortableTextEditable](/api/editor/variables/portabletexteditable/) - [EditorProvider](/api/editor/functions/editorprovider/) ## Hooks - [useEditor](/api/editor/functions/useeditor/) - [useEditorSelector](/api/editor/functions/useeditorselector/) # AddedAnnotationPaths > **AddedAnnotationPaths** = `object` Defined in: packages/editor/src/types/editor.ts:35 ## Properties ### ~~markDefPath~~ > **markDefPath**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:40 :::caution[Deprecated] An annotation may be applied to multiple blocks, resulting in multiple `markDef`'s being created. Use `markDefPaths` instead. ::: *** ### markDefPaths > **markDefPaths**: [`Path`](/api/editor/type-aliases/path/)[] Defined in: packages/editor/src/types/editor.ts:41 *** ### ~~spanPath~~ > **spanPath**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:47 :::caution[Deprecated] Does not return anything meaningful since an annotation can span multiple blocks and spans. If references the span closest to the focus point of the selection. ::: # AnnotationDefinition > **AnnotationDefinition**\<`TBaseDefinition`\> = `TBaseDefinition` & `object` Defined in: packages/schema/dist/index.d.ts:205 ## Type Declaration ### fields? > `optional` **fields**: `ReadonlyArray`\<[`FieldDefinition`](/api/editor/type-aliases/fielddefinition/)\> ## Type Parameters ### TBaseDefinition `TBaseDefinition` *extends* [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) # AnnotationPath > **AnnotationPath** = [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/paths.ts:43 A path to an annotation markDef on a block. # AnnotationSchemaType > **AnnotationSchemaType** = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` Defined in: packages/schema/dist/index.d.ts:52 ## Type Declaration ### fields > **fields**: `ReadonlyArray`\<[`FieldDefinition`](/api/editor/type-aliases/fielddefinition/)\> # BaseDefinition > **BaseDefinition** = `object` Defined in: packages/schema/dist/index.d.ts:147 ## Properties ### name > **name**: `string` Defined in: packages/schema/dist/index.d.ts:148 *** ### title? > `optional` **title**: `string` Defined in: packages/schema/dist/index.d.ts:149 # BlockObject > **BlockObject** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:269 A non-editable block-level object registration. Identifies a `_type` whose value renders as a block-level void node (image, embed, etc.). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### kind > **kind**: `"blockObject"` Defined in: packages/editor/src/renderers/renderer.types.ts:270 *** ### render? > `optional` **render**: [`BlockObjectRender`](/api/editor/type-aliases/blockobjectrender/) Defined in: packages/editor/src/renderers/renderer.types.ts:278 Outer render. Two modes: - omitted: fall through to global registered render (or engine default) - function: use this render. The function receives a `renderDefault` prop that returns the engine default when called. *** ### type > **type**: `string` Defined in: packages/editor/src/renderers/renderer.types.ts:271 # BlockObjectDefinition > **BlockObjectDefinition**\<`TBaseDefinition`\> = `TBaseDefinition` & `object` Defined in: packages/schema/dist/index.d.ts:211 ## Type Declaration ### fields? > `optional` **fields**: `ReadonlyArray`\<[`FieldDefinition`](/api/editor/type-aliases/fielddefinition/)\> ## Type Parameters ### TBaseDefinition `TBaseDefinition` *extends* [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) # BlockObjectRender > **BlockObjectRender** = (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:114 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`BlockObjectRenderProps`](/api/editor/type-aliases/blockobjectrenderprops/) ## Returns `ReactElement` # BlockObjectRenderProps > **BlockObjectRenderProps** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:97 A block object's render function. Receives a non-editable block-level portable text object. `children` carries an engine-emitted void spacer that the browser uses to anchor the caret next to the element. Dropping `children` makes the caret unable to land on the element. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### attributes > **attributes**: `Record`\<`string`, `unknown`\> Defined in: packages/editor/src/renderers/renderer.types.ts:98 *** ### children > **children**: `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:99 *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:100 *** ### node > **node**: [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) Defined in: packages/editor/src/renderers/renderer.types.ts:101 *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/renderers/renderer.types.ts:102 *** ### readOnly > **readOnly**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:103 *** ### renderDefault() > **renderDefault**: (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:109 Render this position with the engine's default wrapper. See [ContainerRenderProps.renderDefault](/api/editor/type-aliases/containerrenderprops/#renderdefault). #### Parameters ##### props `BlockObjectRenderProps` #### Returns `ReactElement` *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:104 # BlockObjectSchemaType > **BlockObjectSchemaType** = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` Defined in: packages/schema/dist/index.d.ts:58 ## Type Declaration ### fields > **fields**: `ReadonlyArray`\<[`FieldDefinition`](/api/editor/type-aliases/fielddefinition/)\> # BlockOffset > **BlockOffset** = `object` Defined in: packages/editor/src/types/block-offset.ts:6 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### offset > **offset**: `number` Defined in: packages/editor/src/types/block-offset.ts:8 *** ### path > **path**: [`BlockPath`](/api/editor/type-aliases/blockpath/) Defined in: packages/editor/src/types/block-offset.ts:7 # BlockPath > **BlockPath** = [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/paths.ts:36 A path to a block in the value. # ChildPath > **ChildPath** = [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/paths.ts:50 A path to a child of a text block. # Container > **Container** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:160 A container registration. Identifies a block object `_type` whose value holds editable children in `arrayField`. The optional `of` array carries nested registrations that override how immediate children of this container render at this lexical scope. `of` overrides apply ONE level down only. Children at deeper levels fall through to global registrations. The `kind` field is injected by `defineContainer` and discriminates containers from other registration kinds at runtime. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### arrayField > **arrayField**: `string` Defined in: packages/editor/src/renderers/renderer.types.ts:163 *** ### kind > **kind**: `"container"` Defined in: packages/editor/src/renderers/renderer.types.ts:161 *** ### of? > `optional` **of**: `ReadonlyArray`\<`Container` \| [`TextBlock`](/api/editor/type-aliases/textblock/) \| [`BlockObject`](/api/editor/type-aliases/blockobject/)\> Defined in: packages/editor/src/renderers/renderer.types.ts:175 Block-level positional overrides. Inline-content kinds (`Span`, `InlineObject`) belong in `TextBlock.of`, not here. *** ### render? > `optional` **render**: [`ContainerRender`](/api/editor/type-aliases/containerrender/) Defined in: packages/editor/src/renderers/renderer.types.ts:170 Outer render. Two modes: - omitted: fall through to global registered render (or engine default) - function: use this render. The function receives a `renderDefault` prop that returns the engine default when called. *** ### type > **type**: `string` Defined in: packages/editor/src/renderers/renderer.types.ts:162 # ContainerRender > **ContainerRender** = (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:60 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`ContainerRenderProps`](/api/editor/type-aliases/containerrenderprops/) ## Returns `ReactElement` # ContainerRenderProps > **ContainerRenderProps** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:34 A container's render function receives a node and renders an element that wraps its editable children. The render is positional: it fires for nodes of `type` whose parent permits this container at `arrayField`. `node` is `PortableTextObject` because containers cannot register the built-in `'span'` or `'block'` types (those are leaves and text blocks respectively). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### attributes > **attributes**: `Record`\<`string`, `unknown`\> Defined in: packages/editor/src/renderers/renderer.types.ts:35 *** ### children > **children**: `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:36 *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:37 *** ### node > **node**: [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) Defined in: packages/editor/src/renderers/renderer.types.ts:38 *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/renderers/renderer.types.ts:39 *** ### readOnly > **readOnly**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:40 *** ### renderDefault() > **renderDefault**: (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:55 Render this position with the engine's default wrapper. Call from inside a custom render to fall back to or wrap the default: ```ts render: ({renderDefault, ...rest}) => renderDefault(rest) ``` The default is the engine's minimal wrapper. It does not chain back to a globally-registered render: PTE has one user layer plus positional overrides, and the engine default is the canonical fallback at any position. #### Parameters ##### props `ContainerRenderProps` #### Returns `ReactElement` *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:41 # Containers > **Containers** = `ReadonlyMap`\<`string`, [`RegisteredContainer`](/api/editor/type-aliases/registeredcontainer/)\> Defined in: packages/editor/src/schema/container-types.ts:113 Map of registered editable containers carried on `EditorContext`. Keyed by bare block-object `_type` (e.g. `'callout'`, `'table'`). Each entry is a rich [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/) carrying its `field` plus any positional `of` registrations. The map preserves positional structure: a `_type` declared inside a parent's `of` array surfaces as a nested entry on that parent's `of`, NOT as a separate top-level entry. Path-driven resolution (see `resolveContainerAt`) reaches positional entries by walking the tree. Top-level entries are global fallbacks: when path-driven descent does not find a positional override, the resolver falls back to the top-level entry for the type if one is registered. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: # DecoratorDefinition > **DecoratorDefinition**\<`TBaseDefinition`\> = `TBaseDefinition` Defined in: packages/schema/dist/index.d.ts:201 ## Type Parameters ### TBaseDefinition `TBaseDefinition` *extends* [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) # DecoratorSchemaType > **DecoratorSchemaType** = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` Defined in: packages/schema/dist/index.d.ts:42 ## Type Declaration ### ~~value~~ > **value**: `string` :::caution[Deprecated] Use `name` instead ::: # Editor > **Editor** = `object` Defined in: packages/editor/src/editor.ts:38 ## Properties ### dom > **dom**: `EditorDom` Defined in: packages/editor/src/editor.ts:39 *** ### getSnapshot() > **getSnapshot**: () => [`EditorSnapshot`](/api/editor/type-aliases/editorsnapshot/) Defined in: packages/editor/src/editor.ts:40 #### Returns [`EditorSnapshot`](/api/editor/type-aliases/editorsnapshot/) *** ### on > **on**: `ActorRef`\<`Snapshot`\<`unknown`\>, `EventObject`, [`EditorEmittedEvent`](/api/editor/type-aliases/editoremittedevent/)\>\[`"on"`\] Defined in: packages/editor/src/editor.ts:55 *** ### registerBehavior() > **registerBehavior**: (`config`) => () => `void` Defined in: packages/editor/src/editor.ts:44 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: #### Parameters ##### config ###### behavior `Behavior` #### Returns > (): `void` ##### Returns `void` *** ### registerNode() > **registerNode**: (`config`) => () => `void` Defined in: packages/editor/src/editor.ts:53 Register a node renderer. The `node` argument is the result of one of the `defineX` factories (`defineContainer`, `defineTextBlock`, `defineSpan`, `defineBlockObject`, `defineInlineObject`). Returns a function that unregisters the node when called. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: #### Parameters ##### config ###### node [`RegistrableNode`](/api/editor/type-aliases/registrablenode/) #### Returns > (): `void` ##### Returns `void` *** ### send() > **send**: (`event`) => `void` Defined in: packages/editor/src/editor.ts:54 #### Parameters ##### event [`EditorEvent`](/api/editor/type-aliases/editorevent/) #### Returns `void` ## Methods ### subscribe() > **subscribe**(`observer`): `object` Defined in: packages/editor/src/editor.ts:66 Subscribe to editor state changes. The observer's `next` callback fires with the current `EditorSnapshot` on every relevant transition (selection updates, content mutations, behavior dispatch, configuration changes). The editor has no terminal state and no error path, so `error` and `complete` are part of the observable contract but never fire. They are kept for structural compatibility with `useSyncExternalStore`, `@xstate/react`'s `useSelector`, and other observer-shaped consumers. #### Parameters ##### observer ###### complete? () => `void` ###### error? (`err`) => `void` ###### next? (`snapshot`) => `void` #### Returns `object` ##### unsubscribe() > **unsubscribe**: () => `void` ###### Returns `void` # EditorConfig > **EditorConfig** = `object` Defined in: packages/editor/src/editor.ts:14 ## Properties ### initialValue? > `optional` **initialValue**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] Defined in: packages/editor/src/editor.ts:20 *** ### keyGenerator()? > `optional` **keyGenerator**: () => `string` Defined in: packages/editor/src/editor.ts:18 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: #### Returns `string` *** ### readOnly? > `optional` **readOnly**: `boolean` Defined in: packages/editor/src/editor.ts:19 *** ### schemaDefinition > **schemaDefinition**: [`SchemaDefinition`](/api/editor/type-aliases/schemadefinition/) Defined in: packages/editor/src/editor.ts:21 # EditorContext > **EditorContext** = `object` Defined in: packages/editor/src/editor/editor-snapshot.ts:11 ## Properties ### containers > **containers**: [`Containers`](/api/editor/type-aliases/containers/) Defined in: packages/editor/src/editor/editor-snapshot.ts:36 Map of registered editable containers keyed by their bare block-object `_type` (e.g. `'callout'`, `'table'`). Each entry is a [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/) carrying `type`, the array `field` that holds the container's editable children, and (when present) the nested positional `of` registrations consulted by [resolveContainerAt](/api/editor/functions/resolvecontainerat/). The render callback is engine-internal and not surfaced here. Only top-level registrations appear as flat entries. A `_type` registered only inside a parent's `of` is reachable through that parent's `of`, not as a top-level entry. Use `resolveContainerAt(containers, value, path)` for position-aware resolution that handles both top-level and positional entries. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: *** ### converters > **converters**: `Converter`[] Defined in: packages/editor/src/editor/editor-snapshot.ts:12 *** ### keyGenerator() > **keyGenerator**: () => `string` Defined in: packages/editor/src/editor/editor-snapshot.ts:13 #### Returns `string` *** ### readOnly > **readOnly**: `boolean` Defined in: packages/editor/src/editor/editor-snapshot.ts:14 *** ### schema > **schema**: [`EditorSchema`](/api/editor/type-aliases/editorschema/) Defined in: packages/editor/src/editor/editor-snapshot.ts:15 *** ### selection > **selection**: [`EditorSelection`](/api/editor/type-aliases/editorselection/) Defined in: packages/editor/src/editor/editor-snapshot.ts:16 *** ### value > **value**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] Defined in: packages/editor/src/editor/editor-snapshot.ts:17 # EditorEmittedEvent > **EditorEmittedEvent** = \{ `event`: `FocusEvent`\<`HTMLDivElement`, `Element`\>; `type`: `"blurred"`; \} \| \{ `type`: `"done loading"`; \} \| \{ `type`: `"editable"`; \} \| `ErrorEvent` \| \{ `event`: `FocusEvent`\<`HTMLDivElement`, `Element`\>; `type`: `"focused"`; \} \| \{ `resolution`: [`InvalidValueResolution`](/api/editor/type-aliases/invalidvalueresolution/) \| `null`; `type`: `"invalid value"`; `value`: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined`; \} \| \{ `type`: `"loading"`; \} \| [`MutationEvent`](/api/editor/type-aliases/mutationevent/) \| \{ `operation`: [`Operation`](/api/editor/type-aliases/operation/); `type`: `"operation"`; \} \| `PatchEvent` \| \{ `type`: `"read only"`; \} \| \{ `type`: `"ready"`; \} \| \{ `selection`: [`EditorSelection`](/api/editor/type-aliases/editorselection/); `type`: `"selection"`; \} \| \{ `type`: `"value changed"`; `value`: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined`; \} Defined in: packages/editor/src/editor/relay.ts:10 ## Type Declaration \{ `event`: `FocusEvent`\<`HTMLDivElement`, `Element`\>; `type`: `"blurred"`; \} ### event > **event**: `FocusEvent`\<`HTMLDivElement`, `Element`\> ### type > **type**: `"blurred"` \{ `type`: `"done loading"`; \} ### ~~type~~ > **type**: `"done loading"` :::caution[Deprecated] Will be removed in the next major version ::: \{ `type`: `"editable"`; \} ### type > **type**: `"editable"` `ErrorEvent` \{ `event`: `FocusEvent`\<`HTMLDivElement`, `Element`\>; `type`: `"focused"`; \} ### event > **event**: `FocusEvent`\<`HTMLDivElement`, `Element`\> ### type > **type**: `"focused"` \{ `resolution`: [`InvalidValueResolution`](/api/editor/type-aliases/invalidvalueresolution/) \| `null`; `type`: `"invalid value"`; `value`: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined`; \} ### resolution > **resolution**: [`InvalidValueResolution`](/api/editor/type-aliases/invalidvalueresolution/) \| `null` ### type > **type**: `"invalid value"` ### value > **value**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` \{ `type`: `"loading"`; \} ### ~~type~~ > **type**: `"loading"` :::caution[Deprecated] Will be removed in the next major version ::: [`MutationEvent`](/api/editor/type-aliases/mutationevent/) \{ `operation`: [`Operation`](/api/editor/type-aliases/operation/); `type`: `"operation"`; \} ### operation > **operation**: [`Operation`](/api/editor/type-aliases/operation/) ### type > **type**: `"operation"` Emitted synchronously for every document-changing operation the engine applies (`set.selection` is excluded; the `selection` event serves selection observers), including operations from initial value sync and normalization, unlike `patch` and `mutation` events, which are held back until the editor is dirty. Do not dispatch editor events from a listener; read current state via `editor.getSnapshot()`. The `operation` object is the engine's own, passed by reference: treat it as read-only and copy anything you retain. Normalization fix operations are delivered adjacent to the operation that triggered them, but not in a guaranteed order; see the [Operation](/api/editor/type-aliases/operation/) docs for the delivery-order contract. Subscribers attached after setup receive only subsequent operations: seed derived state from `editor.getSnapshot()` when subscribing, then apply deltas. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: `PatchEvent` \{ `type`: `"read only"`; \} ### type > **type**: `"read only"` \{ `type`: `"ready"`; \} ### type > **type**: `"ready"` \{ `selection`: [`EditorSelection`](/api/editor/type-aliases/editorselection/); `type`: `"selection"`; \} ### selection > **selection**: [`EditorSelection`](/api/editor/type-aliases/editorselection/) ### type > **type**: `"selection"` \{ `type`: `"value changed"`; `value`: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined`; \} ### type > **type**: `"value changed"` ### value > **value**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` # EditorEvent > **EditorEvent** = `ExternalEditorEvent` \| `ExternalBehaviorEvent` \| \{ `type`: `"update value"`; `value`: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined`; \} Defined in: packages/editor/src/editor.ts:27 # EditorProviderProps > **EditorProviderProps** = `object` Defined in: packages/editor/src/editor/editor-provider.tsx:16 ## Properties ### children? > `optional` **children**: `React.ReactNode` Defined in: packages/editor/src/editor/editor-provider.tsx:18 *** ### initialConfig > **initialConfig**: [`EditorConfig`](/api/editor/type-aliases/editorconfig/) Defined in: packages/editor/src/editor/editor-provider.tsx:17 # EditorSchema > **EditorSchema** = `Schema` Defined in: packages/editor/src/editor/editor-schema.ts:6 # EditorSelection > **EditorSelection** = \{ `anchor`: [`EditorSelectionPoint`](/api/editor/type-aliases/editorselectionpoint/); `backward?`: `boolean`; `focus`: [`EditorSelectionPoint`](/api/editor/type-aliases/editorselectionpoint/); \} \| `null` Defined in: packages/editor/src/types/editor.ts:109 # EditorSelectionPoint > **EditorSelectionPoint** = `object` Defined in: packages/editor/src/types/editor.ts:107 ## Properties ### offset > **offset**: `number` Defined in: packages/editor/src/types/editor.ts:107 *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/types/editor.ts:107 # EditorSelector > **EditorSelector**\<`TSelected`\> = (`snapshot`) => `TSelected` Defined in: packages/editor/src/editor/editor-selector.ts:13 ## Type Parameters ### TSelected `TSelected` ## Parameters ### snapshot [`EditorSnapshot`](/api/editor/type-aliases/editorsnapshot/) ## Returns `TSelected` # EditorSnapshot > **EditorSnapshot** = `object` Defined in: packages/editor/src/editor/editor-snapshot.ts:42 ## Properties ### blockIndexMap > **blockIndexMap**: `ReadonlyMap`\<`string`, `number`\> Defined in: packages/editor/src/editor/editor-snapshot.ts:44 *** ### context > **context**: [`EditorContext`](/api/editor/type-aliases/editorcontext/) Defined in: packages/editor/src/editor/editor-snapshot.ts:43 *** ### decoratorState > **decoratorState**: `Record`\<`string`, `boolean` \| `undefined`\> Defined in: packages/editor/src/editor/editor-snapshot.ts:49 Subject to change :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # FieldDefinition > **FieldDefinition** = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` \| [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` Defined in: packages/schema/dist/index.d.ts:138 # HotkeyOptions > **HotkeyOptions** = `object` Defined in: packages/editor/src/types/options.ts:7 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### custom? > `optional` **custom**: `Record`\<`string`, (`event`, `editor`) => `void`\> Defined in: packages/editor/src/types/options.ts:9 *** ### marks? > `optional` **marks**: `Record`\<`string`, `string`\> Defined in: packages/editor/src/types/options.ts:8 # InlineObject > **InlineObject** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:287 A non-editable inline object registration. Identifies a `_type` whose value renders as an inline void node (mention, inline image, etc.). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### kind > **kind**: `"inlineObject"` Defined in: packages/editor/src/renderers/renderer.types.ts:288 *** ### render? > `optional` **render**: [`InlineObjectRender`](/api/editor/type-aliases/inlineobjectrender/) Defined in: packages/editor/src/renderers/renderer.types.ts:296 Outer render. Two modes: - omitted: fall through to global registered render (or engine default) - function: use this render. The function receives a `renderDefault` prop that returns the engine default when called. *** ### type > **type**: `string` Defined in: packages/editor/src/renderers/renderer.types.ts:289 # InlineObjectDefinition > **InlineObjectDefinition**\<`TBaseDefinition`\> = `TBaseDefinition` & `object` Defined in: packages/schema/dist/index.d.ts:217 ## Type Declaration ### fields? > `optional` **fields**: `ReadonlyArray`\<[`FieldDefinition`](/api/editor/type-aliases/fielddefinition/)\> ## Type Parameters ### TBaseDefinition `TBaseDefinition` *extends* [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) # InlineObjectRender > **InlineObjectRender** = (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:142 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`InlineObjectRenderProps`](/api/editor/type-aliases/inlineobjectrenderprops/) ## Returns `ReactElement` # InlineObjectRenderProps > **InlineObjectRenderProps** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:125 An inline object's render function. Receives a non-editable inline portable text object. `children` carries an engine-emitted void spacer that the browser uses to anchor the caret next to the element. Dropping `children` makes the caret unable to land on the element. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### attributes > **attributes**: `Record`\<`string`, `unknown`\> Defined in: packages/editor/src/renderers/renderer.types.ts:126 *** ### children > **children**: `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:127 *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:128 *** ### node > **node**: [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) Defined in: packages/editor/src/renderers/renderer.types.ts:129 *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/renderers/renderer.types.ts:130 *** ### readOnly > **readOnly**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:131 *** ### renderDefault() > **renderDefault**: (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:137 Render this position with the engine's default wrapper. See [ContainerRenderProps.renderDefault](/api/editor/type-aliases/containerrenderprops/#renderdefault). #### Parameters ##### props `InlineObjectRenderProps` #### Returns `ReactElement` *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:132 # InlineObjectSchemaType > **InlineObjectSchemaType** = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` Defined in: packages/schema/dist/index.d.ts:64 ## Type Declaration ### fields > **fields**: `ReadonlyArray`\<[`FieldDefinition`](/api/editor/type-aliases/fielddefinition/)\> # InvalidValueResolution > **InvalidValueResolution** = `object` Defined in: packages/editor/src/types/editor.ts:118 The editor has invalid data in the value that can be resolved by the user :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### action > **action**: `string` Defined in: packages/editor/src/types/editor.ts:122 *** ### autoResolve? > `optional` **autoResolve**: `boolean` Defined in: packages/editor/src/types/editor.ts:119 *** ### description > **description**: `string` Defined in: packages/editor/src/types/editor.ts:121 *** ### i18n > **i18n**: `object` Defined in: packages/editor/src/types/editor.ts:132 i18n keys for the description and action These are in addition to the description and action properties, to decouple the editor from the i18n system, and allow usage without it. The i18n keys take precedence over the description and action properties, if i18n framework is available. #### action > **action**: `` `inputs.portable-text.invalid-value.${Lowercase}.action` `` #### description > **description**: `` `inputs.portable-text.invalid-value.${Lowercase}.description` `` #### values? > `optional` **values**: `Record`\<`string`, `string` \| `number` \| `string`[]\> *** ### item > **item**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/) \| [`PortableTextChild`](/api/editor/type-aliases/portabletextchild/) \| `undefined` Defined in: packages/editor/src/types/editor.ts:123 *** ### patches > **patches**: [`Patch`](/api/editor/type-aliases/patch/)[] Defined in: packages/editor/src/types/editor.ts:120 # ListDefinition > **ListDefinition**\<`TBaseDefinition`\> = `TBaseDefinition` Defined in: packages/schema/dist/index.d.ts:197 ## Type Parameters ### TBaseDefinition `TBaseDefinition` *extends* [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) # ListSchemaType > **ListSchemaType** = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` Defined in: packages/schema/dist/index.d.ts:32 ## Type Declaration ### ~~value~~ > **value**: `string` :::caution[Deprecated] Use `name` instead ::: # MutationEvent > **MutationEvent** = `object` Defined in: packages/editor/src/editor/relay.ts:93 ## Properties ### patches > **patches**: [`Patch`](/api/editor/type-aliases/patch/)[] Defined in: packages/editor/src/editor/relay.ts:95 *** ### type > **type**: `"mutation"` Defined in: packages/editor/src/editor/relay.ts:94 *** ### value > **value**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` Defined in: packages/editor/src/editor/relay.ts:96 # OnCopyFn > **OnCopyFn** = (`event`) => `undefined` \| `unknown` Defined in: packages/editor/src/types/editor.ts:169 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### event `ClipboardEvent`\<`HTMLDivElement` \| `HTMLSpanElement`\> ## Returns `undefined` \| `unknown` # OnPasteFn > **OnPasteFn** = (`data`) => [`OnPasteResultOrPromise`](/api/editor/type-aliases/onpasteresultorpromise/) Defined in: packages/editor/src/types/editor.ts:166 It is encouraged not to return `Promise` from the `OnPasteFn` as a mechanism to fall back to the native paste behaviour. This doesn't work in all cases. Always return plain `undefined` if possible. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### data [`PasteData`](/api/editor/interfaces/pastedata/) ## Returns [`OnPasteResultOrPromise`](/api/editor/type-aliases/onpasteresultorpromise/) # OnPasteResult > **OnPasteResult** = \{ `insert?`: `TypedObject`[]; `path?`: [`Path`](/api/editor/type-aliases/path/); \} \| `undefined` Defined in: packages/editor/src/types/editor.ts:140 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # OnPasteResultOrPromise > **OnPasteResultOrPromise** = [`OnPasteResult`](/api/editor/type-aliases/onpasteresult/) \| `Promise`\<[`OnPasteResult`](/api/editor/type-aliases/onpasteresult/)\> Defined in: packages/editor/src/types/editor.ts:150 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # Operation > **Operation** = `InsertOperation` \| `InsertTextOperation` \| `RemoveTextOperation` \| `SetOperation` \| `UnsetOperation` Defined in: packages/editor/src/types/operation.ts:41 The document-changing operations emitted through `editor.on('operation', ...)`. Every change to the editor (local edits, remote patches, value sync, normalization fixes, undo/redo) is expressed as a sequence of these five operations. The vocabulary is closed: there are exactly five, listed explicitly so that a new engine operation never becomes public surface by default, and the dot-named ones (`insert.text`, `remove.text`) are not namespaces that grow members. Selection movements are not emitted on this stream; subscribe to the `selection` event instead. Operation objects are the engine's own, passed by reference: treat them as read-only and copy anything you retain beyond the listener call. Delivery order: normalization fix operations are delivered adjacent to the operation that triggered them, but whether a fix arrives before or after its trigger depends on how the trigger was applied (a fix re-enters the engine's apply, so an unbatched trigger delivers the fix first; batched applies deliver fixes after the batch). Do not assume delivery order equals application order under normalization: seed derived state from `editor.getSnapshot()` and recompute on change rather than replaying deltas blindly. `inverse`, when present, reflects what the engine itself needs to make the operation reversible. Its presence follows the engine's history policy and is not a stable per-operation contract. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # Patch > **Patch** = `SetPatch` \| `SetIfMissingPatch` \| `UnsetPatch` \| `InsertPatch` \| `DiffMatchPatch` \| `IncPatch` \| `DecPatch` Defined in: packages/patches/dist/index.d.ts:69 # PatchesEvent > **PatchesEvent** = `object` Defined in: packages/editor/src/editor/editor-machine.ts:41 ## Properties ### patches > **patches**: [`Patch`](/api/editor/type-aliases/patch/)[] Defined in: packages/editor/src/editor/editor-machine.ts:43 *** ### snapshot > **snapshot**: [`PortableTextBlock`](/api/editor/type-aliases/portabletextblock/)[] \| `undefined` Defined in: packages/editor/src/editor/editor-machine.ts:44 *** ### type > **type**: `"patches"` Defined in: packages/editor/src/editor/editor-machine.ts:42 # Path > **Path** = `PathSegment`[] Defined in: packages/editor/src/types/paths.ts:29 A path is an array of path segments that describes a location in a document. # PortableTextBlock > **PortableTextBlock** = [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/) \| [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) Defined in: packages/schema/dist/index.d.ts:257 # PortableTextChild > **PortableTextChild** = [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/) \| [`PortableTextObject`](/api/editor/interfaces/portabletextobject/) Defined in: packages/schema/dist/index.d.ts:302 # PortableTextEditableProps > **PortableTextEditableProps** = `Omit`\<`TextareaHTMLAttributes`\<`HTMLDivElement`\>, `"onPaste"` \| `"onCopy"` \| `"onBeforeInput"`\> & `object` Defined in: packages/editor/src/editor/Editable.tsx:59 ## Type Declaration ### hotkeys? > `optional` **hotkeys**: [`HotkeyOptions`](/api/editor/type-aliases/hotkeyoptions/) ### onBeforeInput()? > `optional` **onBeforeInput**: (`event`) => `void` #### Parameters ##### event `InputEvent` #### Returns `void` ### onCopy? > `optional` **onCopy**: [`OnCopyFn`](/api/editor/type-aliases/oncopyfn/) ### onPaste? > `optional` **onPaste**: [`OnPasteFn`](/api/editor/type-aliases/onpastefn/) ### rangeDecorations? > `optional` **rangeDecorations**: [`RangeDecoration`](/api/editor/interfaces/rangedecoration/)[] ### ref? > `optional` **ref**: `React.Ref`\<`HTMLDivElement`\> ### renderAnnotation? > `optional` **renderAnnotation**: [`RenderAnnotationFunction`](/api/editor/type-aliases/renderannotationfunction/) ### renderBlock? > `optional` **renderBlock**: [`RenderBlockFunction`](/api/editor/type-aliases/renderblockfunction/) ### renderChild? > `optional` **renderChild**: [`RenderChildFunction`](/api/editor/type-aliases/renderchildfunction/) ### renderDecorator? > `optional` **renderDecorator**: [`RenderDecoratorFunction`](/api/editor/type-aliases/renderdecoratorfunction/) ### renderListItem? > `optional` **renderListItem**: [`RenderListItemFunction`](/api/editor/type-aliases/renderlistitemfunction/) ### renderPlaceholder? > `optional` **renderPlaceholder**: [`RenderPlaceholderFunction`](/api/editor/type-aliases/renderplaceholderfunction/) ### renderStyle? > `optional` **renderStyle**: [`RenderStyleFunction`](/api/editor/type-aliases/renderstylefunction/) ### scrollSelectionIntoView? > `optional` **scrollSelectionIntoView**: [`ScrollSelectionIntoViewFunction`](/api/editor/type-aliases/scrollselectionintoviewfunction/) ### selection? > `optional` **selection**: [`EditorSelection`](/api/editor/type-aliases/editorselection/) ### spellCheck? > `optional` **spellCheck**: `boolean` # RegisteredBlockObject > **RegisteredBlockObject** = `object` Defined in: packages/editor/src/schema/container-types.ts:64 Public view of a registered block object, surfaced inside a containing [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/)'s `of` array as a positional registration. The render function is engine-internal. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### kind > **kind**: `"blockObject"` Defined in: packages/editor/src/schema/container-types.ts:65 *** ### type > **type**: `string` Defined in: packages/editor/src/schema/container-types.ts:66 # RegisteredContainer > **RegisteredContainer** = `object` Defined in: packages/editor/src/schema/container-types.ts:38 Public view of a registered editable container, surfaced on [EditorContext.containers](/api/editor/type-aliases/editorcontext/#containers). Two array properties named `of` live on the same entry with different semantics: - `field.of` is the SCHEMA-DECLARED list of types this container's child field accepts (from `@portabletext/schema`'s `OfDefinition`). Tells you what the schema permits as children. - `of` (top-level on `RegisteredContainer`) is the list of POSITIONAL CHILD REGISTRATIONS - nested [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/) or [RegisteredPositional](/api/editor/type-aliases/registeredpositional/) entries - that override the global registration when the engine descends into this parent. Tells you which child renderings are scoped to this parent. The full container registration (including the render callback) lives on the editor's internal ResolvedContainers map and is not exposed on the public context. Two top-level entries with the same `_type` cannot coexist - the register handler warns on duplicates. But the SAME `_type` registered in two different parents' `of` arrays is supported as a feature; `resolveContainerAt` walks the positional tree using the path to return the entry that applies at a given position. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### field > **field**: `ChildArrayField` Defined in: packages/editor/src/schema/container-types.ts:41 *** ### kind > **kind**: `"container"` Defined in: packages/editor/src/schema/container-types.ts:39 *** ### of? > `optional` **of**: `ReadonlyArray`\<`RegisteredContainer` \| [`RegisteredPositional`](/api/editor/type-aliases/registeredpositional/)\> Defined in: packages/editor/src/schema/container-types.ts:42 *** ### type > **type**: `string` Defined in: packages/editor/src/schema/container-types.ts:40 # RegisteredInlineObject > **RegisteredInlineObject** = `object` Defined in: packages/editor/src/schema/container-types.ts:76 Public view of a registered inline object, surfaced inside a containing [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/)'s `of` array as a positional registration. The render function is engine-internal. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### kind > **kind**: `"inlineObject"` Defined in: packages/editor/src/schema/container-types.ts:77 *** ### type > **type**: `string` Defined in: packages/editor/src/schema/container-types.ts:78 # RegisteredPositional > **RegisteredPositional** = [`RegisteredSpan`](/api/editor/type-aliases/registeredspan/) \| [`RegisteredBlockObject`](/api/editor/type-aliases/registeredblockobject/) \| [`RegisteredInlineObject`](/api/editor/type-aliases/registeredinlineobject/) Defined in: packages/editor/src/schema/container-types.ts:89 Union of non-container positional registrations that may appear in a [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/)'s `of` array. Text-block registrations are NOT included here; they surface on `EditorContext.textBlocks`, not on the containers tree. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: # RegisteredSpan > **RegisteredSpan** = `object` Defined in: packages/editor/src/schema/container-types.ts:52 Public view of a registered span, surfaced inside a containing [RegisteredContainer](/api/editor/type-aliases/registeredcontainer/)'s `of` array as a positional registration. The render function is engine-internal. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### kind > **kind**: `"span"` Defined in: packages/editor/src/schema/container-types.ts:53 *** ### type > **type**: `string` Defined in: packages/editor/src/schema/container-types.ts:54 # RegistrableNode > **RegistrableNode** = [`Container`](/api/editor/type-aliases/container/) \| [`TextBlock`](/api/editor/type-aliases/textblock/) \| [`Span`](/api/editor/type-aliases/span/) \| [`BlockObject`](/api/editor/type-aliases/blockobject/) \| [`InlineObject`](/api/editor/type-aliases/inlineobject/) Defined in: packages/editor/src/renderers/renderer.types.ts:305 The discriminated union of every registration accepted by `editor.registerNode` and the `` component. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: # RenderAnnotationFunction > **RenderAnnotationFunction** = (`props`) => `JSX.Element` Defined in: packages/editor/src/types/editor.ts:245 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`BlockAnnotationRenderProps`](/api/editor/interfaces/blockannotationrenderprops/) ## Returns `JSX.Element` # RenderBlockFunction > **RenderBlockFunction** = (`props`) => `JSX.Element` Defined in: packages/editor/src/types/editor.ts:234 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`BlockRenderProps`](/api/editor/interfaces/blockrenderprops/) ## Returns `JSX.Element` # RenderChildFunction > **RenderChildFunction** = (`props`) => `JSX.Element` Defined in: packages/editor/src/types/editor.ts:237 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`BlockChildRenderProps`](/api/editor/interfaces/blockchildrenderprops/) ## Returns `JSX.Element` # RenderDecoratorFunction > **RenderDecoratorFunction** = (`props`) => `JSX.Element` Defined in: packages/editor/src/types/editor.ts:273 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`BlockDecoratorRenderProps`](/api/editor/interfaces/blockdecoratorrenderprops/) ## Returns `JSX.Element` # RenderEditableFunction > **RenderEditableFunction** = (`props`) => `JSX.Element` Defined in: packages/editor/src/types/editor.ts:240 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`PortableTextEditableProps`](/api/editor/type-aliases/portabletexteditableprops/) ## Returns `JSX.Element` # RenderListItemFunction > **RenderListItemFunction** = (`props`) => `JSX.Element` Defined in: packages/editor/src/types/editor.ts:268 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`BlockListItemRenderProps`](/api/editor/interfaces/blocklistitemrenderprops/) ## Returns `JSX.Element` # RenderPlaceholderFunction > **RenderPlaceholderFunction** = () => `React.ReactNode` Defined in: packages/editor/src/types/editor.ts:250 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Returns `React.ReactNode` # RenderStyleFunction > **RenderStyleFunction** = (`props`) => `JSX.Element` Defined in: packages/editor/src/types/editor.ts:253 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`BlockStyleRenderProps`](/api/editor/interfaces/blockstylerenderprops/) ## Returns `JSX.Element` # SchemaDefinition > **SchemaDefinition** = `object` Defined in: packages/schema/dist/index.d.ts:154 ## Properties ### annotations? > `optional` **annotations**: `ReadonlyArray`\<[`AnnotationDefinition`](/api/editor/type-aliases/annotationdefinition/)\> Defined in: packages/schema/dist/index.d.ts:162 *** ### block? > `optional` **block**: `object` Defined in: packages/schema/dist/index.d.ts:155 #### fields? > `optional` **fields**: `ReadonlyArray`\<[`FieldDefinition`](/api/editor/type-aliases/fielddefinition/)\> #### name? > `optional` **name**: `string` *** ### blockObjects? > `optional` **blockObjects**: `ReadonlyArray`\<[`BlockObjectDefinition`](/api/editor/type-aliases/blockobjectdefinition/)\> Defined in: packages/schema/dist/index.d.ts:163 *** ### decorators? > `optional` **decorators**: `ReadonlyArray`\<[`DecoratorDefinition`](/api/editor/type-aliases/decoratordefinition/)\> Defined in: packages/schema/dist/index.d.ts:161 *** ### inlineObjects? > `optional` **inlineObjects**: `ReadonlyArray`\<[`InlineObjectDefinition`](/api/editor/type-aliases/inlineobjectdefinition/)\> Defined in: packages/schema/dist/index.d.ts:164 *** ### lists? > `optional` **lists**: `ReadonlyArray`\<[`ListDefinition`](/api/editor/type-aliases/listdefinition/)\> Defined in: packages/schema/dist/index.d.ts:160 *** ### styles? > `optional` **styles**: `ReadonlyArray`\<[`StyleDefinition`](/api/editor/type-aliases/styledefinition/)\> Defined in: packages/schema/dist/index.d.ts:159 # ScrollSelectionIntoViewFunction > **ScrollSelectionIntoViewFunction** = (`editor`, `domRange`) => `void` Defined in: packages/editor/src/types/editor.ts:278 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### editor [`PortableTextEditor`](/api/editor/classes/portabletexteditor/) ### domRange `globalThis.Range` ## Returns `void` # Span > **Span** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:251 A span registration. The span `_type` is `'span'` at the top level. Positional overrides nested in a container's `of` array can register a different `_type` for a span-like inline at that lexical scope (e.g. a `code-span` inside a `code-block`). :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### kind > **kind**: `"span"` Defined in: packages/editor/src/renderers/renderer.types.ts:252 *** ### render? > `optional` **render**: [`SpanRender`](/api/editor/type-aliases/spanrender/) Defined in: packages/editor/src/renderers/renderer.types.ts:260 Outer render. Two modes: - omitted: fall through to global registered render (or engine default) - function: use this render. The function receives a `renderDefault` prop that returns the engine default when called. *** ### type > **type**: `string` Defined in: packages/editor/src/renderers/renderer.types.ts:253 # SpanRender > **SpanRender** = (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:86 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`SpanRenderProps`](/api/editor/type-aliases/spanrenderprops/) ## Returns `ReactElement` # SpanRenderProps > **SpanRenderProps** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:69 A span's render function. Receives a portable text span node and wraps it. `children` carries the styled text already decorated by `renderDecorator`/`renderAnnotation`/range decorations. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### attributes > **attributes**: `Record`\<`string`, `unknown`\> Defined in: packages/editor/src/renderers/renderer.types.ts:70 *** ### children > **children**: `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:71 *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:72 *** ### node > **node**: [`PortableTextSpan`](/api/editor/interfaces/portabletextspan/) Defined in: packages/editor/src/renderers/renderer.types.ts:73 *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/renderers/renderer.types.ts:74 *** ### readOnly > **readOnly**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:75 *** ### renderDefault() > **renderDefault**: (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:81 Render this position with the engine's default wrapper. See [ContainerRenderProps.renderDefault](/api/editor/type-aliases/containerrenderprops/#renderdefault). #### Parameters ##### props `SpanRenderProps` #### Returns `ReactElement` *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:76 # StyleDefinition > **StyleDefinition**\<`TBaseDefinition`\> = `TBaseDefinition` Defined in: packages/schema/dist/index.d.ts:193 ## Type Parameters ### TBaseDefinition `TBaseDefinition` *extends* [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) # StyleSchemaType > **StyleSchemaType** = [`BaseDefinition`](/api/editor/type-aliases/basedefinition/) & `object` Defined in: packages/schema/dist/index.d.ts:22 ## Type Declaration ### ~~value~~ > **value**: `string` :::caution[Deprecated] Use `name` instead ::: # TextBlock > **TextBlock** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:197 A text block registration. The text block `_type` is `'block'` at the top level. Positional overrides nested in a container's `of` array can register a different `_type` to render at that lexical scope. `defineTextBlock` opts the text block into the new render pipeline. The consumer's `render` callback owns the outer wrapper entirely: the engine emits `data-pt-*` attributes only - no `pt-*` CSS classes, no legacy `data-block-*` attributes - and the block-level `renderStyle`/`renderListItem`/`renderBlock` props on `` do not compose under this registration. Span-level render props - `renderDecorator`, `renderAnnotation`, `renderPlaceholder`, and range decorations - keep working. They fire on the spans inside `children` regardless of which text block outer wrapper renders them. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### kind > **kind**: `"textBlock"` Defined in: packages/editor/src/renderers/renderer.types.ts:198 *** ### of? > `optional` **of**: `ReadonlyArray`\<[`Span`](/api/editor/type-aliases/span/) \| [`InlineObject`](/api/editor/type-aliases/inlineobject/)\> Defined in: packages/editor/src/renderers/renderer.types.ts:212 Inline-content positional overrides. A `Span` or `InlineObject` placed here scopes the inline render to this text block (or any text block of this `type` if registered at the top level). *** ### render? > `optional` **render**: [`TextBlockRender`](/api/editor/type-aliases/textblockrender/) Defined in: packages/editor/src/renderers/renderer.types.ts:206 Outer render. Two modes: - omitted: fall through to global registered render (or engine default) - function: use this render. The function receives a `renderDefault` prop that returns the engine default when called. *** ### type > **type**: `string` Defined in: packages/editor/src/renderers/renderer.types.ts:199 # TextBlockRender > **TextBlockRender** = (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:241 :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props [`TextBlockRenderProps`](/api/editor/type-aliases/textblockrenderprops/) ## Returns `ReactElement` # TextBlockRenderProps > **TextBlockRenderProps** = `object` Defined in: packages/editor/src/renderers/renderer.types.ts:224 Text block render function. `children` carries the rendered spans - `renderDecorator`, `renderAnnotation`, `renderPlaceholder`, and range decorations have already fired at the leaf level. The render's job is the outer wrapper element and any block-level composition (style, list-item) the consumer wants. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### attributes > **attributes**: `Record`\<`string`, `unknown`\> Defined in: packages/editor/src/renderers/renderer.types.ts:225 *** ### children > **children**: `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:226 *** ### focused > **focused**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:227 *** ### node > **node**: [`PortableTextTextBlock`](/api/editor/interfaces/portabletexttextblock/) Defined in: packages/editor/src/renderers/renderer.types.ts:228 *** ### path > **path**: [`Path`](/api/editor/type-aliases/path/) Defined in: packages/editor/src/renderers/renderer.types.ts:229 *** ### readOnly > **readOnly**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:230 *** ### renderDefault() > **renderDefault**: (`props`) => `ReactElement` Defined in: packages/editor/src/renderers/renderer.types.ts:236 Render this position with the engine's default wrapper. See [ContainerRenderProps.renderDefault](/api/editor/type-aliases/containerrenderprops/#renderdefault). #### Parameters ##### props `TextBlockRenderProps` #### Returns `ReactElement` *** ### selected > **selected**: `boolean` Defined in: packages/editor/src/renderers/renderer.types.ts:231 # PortableTextEditable > `const` **PortableTextEditable**: `ForwardRefExoticComponent`\<`Omit`\<[`PortableTextEditableProps`](/api/editor/type-aliases/portabletexteditableprops/), `"ref"`\> & `RefAttributes`\<`Omit`\<`HTMLDivElement`, `"as"` \| `"onPaste"` \| `"onBeforeInput"`\>\>\> Defined in: packages/editor/src/editor/Editable.tsx:101 The core component that renders the editor. Must be placed within the [EditorProvider](/api/editor/functions/editorprovider/) component. ## Example ```tsx import { PortableTextEditable, EditorProvider } from '@portabletext/editor' function MyComponent() { return ( ) } ``` # createKeyboardShortcut > **createKeyboardShortcut**\<`TKeyboardEvent`\>(`definition`): [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`TKeyboardEvent`\> Defined in: keyboard-shortcuts.ts:86 Creates a `KeyboardShortcut` from a `KeyboardShortcutDefinition`. `default` keyboard event definitions are required while the `apple` keyboard event definitions are optional. ## Type Parameters ### TKeyboardEvent `TKeyboardEvent` *extends* `Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\> = `Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\> ## Parameters ### definition [`KeyboardShortcutDefinition`](/api/keyboard-shortcuts/type-aliases/keyboardshortcutdefinition/) ## Returns [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`TKeyboardEvent`\> ## Example ```typescript const shortcut = createKeyboardShortcut({ default: [{ key: 'B', alt: false, ctrl: true, meta: false, shift: false, }], apple: [{ key: 'B', alt: false, ctrl: false, meta: true, shift: false, }], }) ``` # @portabletext/keyboard-shortcuts ## Type Aliases - [KeyboardEventDefinition](/api/keyboard-shortcuts/type-aliases/keyboardeventdefinition/) - [KeyboardShortcut](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/) - [KeyboardShortcutDefinition](/api/keyboard-shortcuts/type-aliases/keyboardshortcutdefinition/) ## Variables - [blockquote](/api/keyboard-shortcuts/variables/blockquote/) - [bold](/api/keyboard-shortcuts/variables/bold/) - [code](/api/keyboard-shortcuts/variables/code/) - [h1](/api/keyboard-shortcuts/variables/h1/) - [h2](/api/keyboard-shortcuts/variables/h2/) - [h3](/api/keyboard-shortcuts/variables/h3/) - [h4](/api/keyboard-shortcuts/variables/h4/) - [h5](/api/keyboard-shortcuts/variables/h5/) - [h6](/api/keyboard-shortcuts/variables/h6/) - [italic](/api/keyboard-shortcuts/variables/italic/) - [link](/api/keyboard-shortcuts/variables/link/) - [normal](/api/keyboard-shortcuts/variables/normal/) - [redo](/api/keyboard-shortcuts/variables/redo/) - [strikeThrough](/api/keyboard-shortcuts/variables/strikethrough/) - [underline](/api/keyboard-shortcuts/variables/underline/) - [undo](/api/keyboard-shortcuts/variables/undo/) ## Functions - [createKeyboardShortcut](/api/keyboard-shortcuts/functions/createkeyboardshortcut/) # KeyboardEventDefinition > **KeyboardEventDefinition** = \{ `code`: `KeyboardEvent`\[`"code"`\]; `key`: `KeyboardEvent`\[`"key"`\]; \} \| \{ `code?`: `undefined`; `key`: `KeyboardEvent`\[`"key"`\]; \} \| \{ `code`: `KeyboardEvent`\[`"code"`\]; `key?`: `undefined`; \} & `object` Defined in: keyboard-event-definition.ts:25 A keyboard event definition that can be used to create a keyboard shortcut. At least one of `key` or `code` must be provided while the `alt`, `ctrl`, `meta`, and `shift` modifier configurations are optional. The `key` represents a https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key and is treated as case-insensitive. The `code` represents a https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code and is treated as case-insensitive. ## Type Declaration ### alt? > `optional` **alt**: `KeyboardEvent`\[`"altKey"`\] ### ctrl? > `optional` **ctrl**: `KeyboardEvent`\[`"ctrlKey"`\] ### meta? > `optional` **meta**: `KeyboardEvent`\[`"metaKey"`\] ### shift? > `optional` **shift**: `KeyboardEvent`\[`"shiftKey"`\] ## Example ```typescript const boldEvent: KeyboardEventDefinition = { key: 'B', alt: false, ctrl: true, meta: false, shift: false, } ``` # KeyboardShortcut > **KeyboardShortcut**\<`TKeyboardEvent`\> = `object` Defined in: keyboard-shortcuts.ts:46 A resolved keyboard shortcut for the current platform that has been processed by `createKeyboardShortcut(...)` to select the appropriate platform-specific key combination. The `guard` function determines if the shortcut applies to the current `KeyboardEvent`, while `keys` contains the display-friendly key combination for the current platform. ## Type Parameters ### TKeyboardEvent `TKeyboardEvent` *extends* `Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\> = `Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\> ## Properties ### guard() > **guard**: (`event`) => `boolean` Defined in: keyboard-shortcuts.ts:55 #### Parameters ##### event `TKeyboardEvent` #### Returns `boolean` *** ### keys > **keys**: `ReadonlyArray`\<`string`\> Defined in: keyboard-shortcuts.ts:56 # KeyboardShortcutDefinition > **KeyboardShortcutDefinition** = `object` Defined in: keyboard-shortcuts.ts:33 Definition of a keyboard shortcut with platform-specific keyboard event definitions. `default` keyboard event definitions are required while the `apple` keyboard event definitions are optional. ## Example ```typescript const boldShortcut: KeyboardShortcutDefinition = { default: [{ key: 'B', alt: false, ctrl: true, meta: false, shift: false, }], apple: [{ key: 'B', alt: false, ctrl: false, meta: true, shift: false, }], } ``` ## Properties ### apple? > `optional` **apple**: `ReadonlyArray`\<[`KeyboardEventDefinition`](/api/keyboard-shortcuts/type-aliases/keyboardeventdefinition/)\> Defined in: keyboard-shortcuts.ts:35 *** ### default > **default**: `ReadonlyArray`\<[`KeyboardEventDefinition`](/api/keyboard-shortcuts/type-aliases/keyboardeventdefinition/)\> Defined in: keyboard-shortcuts.ts:34 # blockquote > `const` **blockquote**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:444 # bold > `const` **bold**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:6 # code > `const` **code**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:54 # h1 > `const` **h1**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:192 # h2 > `const` **h2**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:234 # h3 > `const` **h3**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:276 # h4 > `const` **h4**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:318 # h5 > `const` **h5**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:360 # h6 > `const` **h6**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:402 # italic > `const` **italic**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:30 # link > `const` **link**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:126 # normal > `const` **normal**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:150 # redo > `const` **redo**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:483 # strikeThrough > `const` **strikeThrough**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:102 # underline > `const` **underline**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:78 # undo > `const` **undo**: [`KeyboardShortcut`](/api/keyboard-shortcuts/type-aliases/keyboardshortcut/)\<`Pick`\<`KeyboardEvent`, `"key"` \| `"code"` \| `"altKey"` \| `"ctrlKey"` \| `"metaKey"` \| `"shiftKey"`\>\> Defined in: common-shortcuts.ts:459 # BehaviorPlugin > **BehaviorPlugin**(`props`): `null` Defined in: plugin.behavior.tsx:15 Plugin component that registers a list of `Behavior`s with the editor. Stabilize the `behaviors` array (a module-level constant or `useMemo`) to avoid a full unregister/re-register cycle on every parent render: a new array reference per render triggers the registration effect to re-run. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### behaviors `Behavior`[] ## Returns `null` # EventListenerPlugin > **EventListenerPlugin**(`props`): `null` Defined in: plugin.event-listener.tsx:54 Listen for events emitted by the editor. Must be used inside `EditorProvider`. Events available include: - 'blurred' - 'done loading' - 'editable' - 'error' - 'focused' - 'invalid value' - 'loading' - 'mutation' - 'patch' - 'read only' - 'ready' - 'selection' - 'value changed' ## Parameters ### props #### on (`event`) => `void` ## Returns `null` ## Examples Listen and log events. ```tsx import {EditorProvider} from '@portabletext/editor' import {EventListenerPlugin} from '@portabletext/editor/plugins' function MyComponent() { return ( { console.log(event) } } /> { ... } ) } ``` Handle events when there is a mutation. ```tsx { if (event.type === 'mutation') { console.log('Value changed:', event.snapshot) } }} /> ``` # NodePlugin > **NodePlugin**(`props`): `null` Defined in: plugin.node.tsx:17 Plugin component that registers a list of nodes (containers, text blocks, spans, block objects, inline objects) with the editor. Each node is the result of a `defineX` factory. Stabilize the `nodes` array (a module-level constant or `useMemo`) to avoid a full unregister/re-register cycle on every parent render: a new array reference per render triggers the registration effect to re-run. :::caution[Alpha] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### nodes `RegistrableNode`[] ## Returns `null` # @portabletext/editor ## Variables - [EditorRefPlugin](/api/plugins/variables/editorrefplugin/) ## Functions - [BehaviorPlugin](/api/plugins/functions/behaviorplugin/) - [NodePlugin](/api/plugins/functions/nodeplugin/) ## Components - [EventListenerPlugin](/api/plugins/functions/eventlistenerplugin/) # EditorRefPlugin > `const` **EditorRefPlugin**: `ForwardRefExoticComponent`\<`RefAttributes`\<`Editor` \| `null`\>\> Defined in: plugin.editor-ref.tsx:8 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # compareApplicableSchema > **compareApplicableSchema**(`a`, `b`): `boolean` Defined in: selector.get-applicable-schema.ts:111 Structural comparator for [ApplicableSchema](/api/selectors/type-aliases/applicableschema/) values. Two results compare equal when every category contains the same names (set equality). Pass as the `compare` argument to `useEditorSelector` to keep React subscriptions stable. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### a [`ApplicableSchema`](/api/selectors/type-aliases/applicableschema/) ### b [`ApplicableSchema`](/api/selectors/type-aliases/applicableschema/) ## Returns `boolean` # isActiveAnnotation > **isActiveAnnotation**(`annotation`, `options?`): `EditorSelector`\<`boolean`\> Defined in: selector.is-active-annotation.ts:12 Check whether an annotation is active in the given `snapshot`. ## Parameters ### annotation `string` ### options? #### mode? `"partial"` \| `"full"` Choose whether the annotation has to take up the entire selection in the `snapshot` or if the annotation can be partially selected. Defaults to 'full' ## Returns `EditorSelector`\<`boolean`\> # isActiveDecorator > **isActiveDecorator**(`decorator`): `EditorSelector`\<`boolean`\> Defined in: selector.is-active-decorator.ts:10 ## Parameters ### decorator `string` ## Returns `EditorSelector`\<`boolean`\> # isActiveListItem > **isActiveListItem**(`listItem`): `EditorSelector`\<`boolean`\> Defined in: selector.is-active-list-item.ts:7 ## Parameters ### listItem `string` ## Returns `EditorSelector`\<`boolean`\> # isActiveStyle > **isActiveStyle**(`style`): `EditorSelector`\<`boolean`\> Defined in: selector.is-active-style.ts:7 ## Parameters ### style `string` ## Returns `EditorSelector`\<`boolean`\> # isAtTheEndOfBlock > **isAtTheEndOfBlock**(`block`): `EditorSelector`\<`boolean`\> Defined in: selector.is-at-the-end-of-block.ts:11 ## Parameters ### block #### node `PortableTextBlock` #### path `Path` ## Returns `EditorSelector`\<`boolean`\> # isAtTheStartOfBlock > **isAtTheStartOfBlock**(`block`): `EditorSelector`\<`boolean`\> Defined in: selector.is-at-the-start-of-block.ts:11 ## Parameters ### block #### node `PortableTextBlock` #### path `Path` ## Returns `EditorSelector`\<`boolean`\> # isOverlappingSelection > **isOverlappingSelection**(`selection`): `EditorSelector`\<`boolean`\> Defined in: selector.is-overlapping-selection.ts:15 Returns true if the supplied selection shares at least one point with the editor's current selection. Resolves at any container depth. Two selections that touch at a single endpoint share that point and are considered overlapping. ## Parameters ### selection `EditorSelection` ## Returns `EditorSelector`\<`boolean`\> # isPointAfterSelection > **isPointAfterSelection**(`point`): `EditorSelector`\<`boolean`\> Defined in: selector.is-point-after-selection.ts:9 ## Parameters ### point `EditorSelectionPoint` ## Returns `EditorSelector`\<`boolean`\> # isPointBeforeSelection > **isPointBeforeSelection**(`point`): `EditorSelector`\<`boolean`\> Defined in: selector.is-point-before-selection.ts:9 ## Parameters ### point `EditorSelectionPoint` ## Returns `EditorSelector`\<`boolean`\> # @portabletext/editor ## Type Aliases - [ApplicableSchema](/api/selectors/type-aliases/applicableschema/) - [MarkState](/api/selectors/type-aliases/markstate/) ## Variables - [getActiveAnnotations](/api/selectors/variables/getactiveannotations/) - [getActiveListItem](/api/selectors/variables/getactivelistitem/) - [getActiveStyle](/api/selectors/variables/getactivestyle/) - [getAnchorBlock](/api/selectors/variables/getanchorblock/) - [getAnchorChild](/api/selectors/variables/getanchorchild/) - [getAnchorSpan](/api/selectors/variables/getanchorspan/) - [getAnchorTextBlock](/api/selectors/variables/getanchortextblock/) - [getApplicableSchema](/api/selectors/variables/getapplicableschema/) - [getBlockOffsets](/api/selectors/variables/getblockoffsets/) - [getBlockTextAfter](/api/selectors/variables/getblocktextafter/) - [getBlockTextBefore](/api/selectors/variables/getblocktextbefore/) - [getCaretWordSelection](/api/selectors/variables/getcaretwordselection/) - [getFirstBlock](/api/selectors/variables/getfirstblock/) - [getFocusBlock](/api/selectors/variables/getfocusblock/) - [getFocusBlockObject](/api/selectors/variables/getfocusblockobject/) - [getFocusChild](/api/selectors/variables/getfocuschild/) - [getFocusInlineObject](/api/selectors/variables/getfocusinlineobject/) - [getFocusListBlock](/api/selectors/variables/getfocuslistblock/) - [getFocusSpan](/api/selectors/variables/getfocusspan/) - [getFocusTextBlock](/api/selectors/variables/getfocustextblock/) - [getFragment](/api/selectors/variables/getfragment/) - [getLastBlock](/api/selectors/variables/getlastblock/) - [getMarkState](/api/selectors/variables/getmarkstate/) - [getNextBlock](/api/selectors/variables/getnextblock/) - [getNextInlineObject](/api/selectors/variables/getnextinlineobject/) - [getNextInlineObjects](/api/selectors/variables/getnextinlineobjects/) - [getNextSpan](/api/selectors/variables/getnextspan/) - [getPreviousBlock](/api/selectors/variables/getpreviousblock/) - [getPreviousInlineObject](/api/selectors/variables/getpreviousinlineobject/) - [getPreviousInlineObjects](/api/selectors/variables/getpreviousinlineobjects/) - [getPreviousSpan](/api/selectors/variables/getpreviousspan/) - [getSelectedBlocks](/api/selectors/variables/getselectedblocks/) - [getSelectedSpans](/api/selectors/variables/getselectedspans/) - [getSelectedTextBlocks](/api/selectors/variables/getselectedtextblocks/) - [getSelectedValue](/api/selectors/variables/getselectedvalue/) - [getSelection](/api/selectors/variables/getselection/) - [getSelectionEndBlock](/api/selectors/variables/getselectionendblock/) - [getSelectionEndChild](/api/selectors/variables/getselectionendchild/) - [getSelectionEndPoint](/api/selectors/variables/getselectionendpoint/) - [getSelectionStartBlock](/api/selectors/variables/getselectionstartblock/) - [getSelectionStartChild](/api/selectors/variables/getselectionstartchild/) - [getSelectionStartPoint](/api/selectors/variables/getselectionstartpoint/) - [getSelectionText](/api/selectors/variables/getselectiontext/) - [getValue](/api/selectors/variables/getvalue/) - [isSelectingEntireBlocks](/api/selectors/variables/isselectingentireblocks/) - [isSelectionCollapsed](/api/selectors/variables/isselectioncollapsed/) - [isSelectionExpanded](/api/selectors/variables/isselectionexpanded/) ## Functions - [compareApplicableSchema](/api/selectors/functions/compareapplicableschema/) - [isActiveAnnotation](/api/selectors/functions/isactiveannotation/) - [isActiveDecorator](/api/selectors/functions/isactivedecorator/) - [isActiveListItem](/api/selectors/functions/isactivelistitem/) - [isActiveStyle](/api/selectors/functions/isactivestyle/) - [isAtTheEndOfBlock](/api/selectors/functions/isattheendofblock/) - [isAtTheStartOfBlock](/api/selectors/functions/isatthestartofblock/) - [isOverlappingSelection](/api/selectors/functions/isoverlappingselection/) - [isPointAfterSelection](/api/selectors/functions/ispointafterselection/) - [isPointBeforeSelection](/api/selectors/functions/ispointbeforeselection/) # ApplicableSchema > **ApplicableSchema** = `object` Defined in: selector.get-applicable-schema.ts:12 The set of schema member names applicable at the current selection, grouped by category. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### annotations > **annotations**: `ReadonlySet`\<`string`\> Defined in: selector.get-applicable-schema.ts:14 *** ### blockObjects > **blockObjects**: `ReadonlySet`\<`string`\> Defined in: selector.get-applicable-schema.ts:17 *** ### decorators > **decorators**: `ReadonlySet`\<`string`\> Defined in: selector.get-applicable-schema.ts:13 *** ### inlineObjects > **inlineObjects**: `ReadonlySet`\<`string`\> Defined in: selector.get-applicable-schema.ts:18 *** ### lists > **lists**: `ReadonlySet`\<`string`\> Defined in: selector.get-applicable-schema.ts:15 *** ### styles > **styles**: `ReadonlySet`\<`string`\> Defined in: selector.get-applicable-schema.ts:16 # MarkState > **MarkState** = \{ `marks`: `string`[]; `state`: `"unchanged"`; \} \| \{ `marks`: `string`[]; `previousMarks`: `string`[]; `state`: `"changed"`; \} Defined in: selector.get-mark-state.ts:16 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # getActiveAnnotations > `const` **getActiveAnnotations**: `EditorSelector`\<`PortableTextObject`[]\> Defined in: selector.get-active-annotations.ts:10 # getActiveListItem > `const` **getActiveListItem**: `EditorSelector`\<`PortableTextListBlock`\[`"listItem"`\] \| `undefined`\> Defined in: selector.get-active-list-item.ts:9 # getActiveStyle > `const` **getActiveStyle**: `EditorSelector`\<`PortableTextTextBlock`\[`"style"`\]\> Defined in: selector.get-active-style.ts:9 # getAnchorBlock > `const` **getAnchorBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-anchor-block.ts:11 Returns the block containing the anchor selection, resolved at any depth. # getAnchorChild > `const` **getAnchorChild**: `EditorSelector`\<\{ `node`: `PortableTextObject` \| `PortableTextSpan`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-anchor-child.ts:12 Returns the child (span or inline object) containing the anchor selection, resolved at any depth. # getAnchorSpan > `const` **getAnchorSpan**: `EditorSelector`\<\{ `node`: `PortableTextSpan`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-anchor-span.ts:11 Returns the span containing the anchor selection, resolved at any depth. # getAnchorTextBlock > `const` **getAnchorTextBlock**: `EditorSelector`\<\{ `node`: `PortableTextTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-anchor-text-block.ts:12 Returns the text block containing the anchor selection, resolved at any depth. # getApplicableSchema > `const` **getApplicableSchema**: `EditorSelector`\<[`ApplicableSchema`](/api/selectors/type-aliases/applicableschema/)\> Defined in: selector.get-applicable-schema.ts:56 Resolve which schema members are applicable at the current selection. For each named category (decorators, annotations, lists, styles, block objects, inline objects) returns the set of names that the editor allows at the current selection. Categories split by what they apply to: Text-only (decorators, annotations, lists, styles): require text-block content in the selection. A name is applicable when at least one text block the range covers declares it (union). The underlying operations apply per-block, validating each block's sub-schema and skipping blocks that don't declare the type, so the result reflects "will this produce any effect?" semantics. Selection on a void block, or no selection, returns empty sets. Insertion (blockObjects, inlineObjects): the things consumers might insert AT the current selection. The focus block's sub-schema applies even when the selection is on a void block (the question "what can I insert here?" still has an answer). No selection returns empty sets. Useful for gating toolbar buttons, slash-command items, command palettes, keyboard-shortcut hints and other selection-aware UIs. Pair with `getUnionSchema` (from `@portabletext/editor/traversal`) to render a static toolbar whose buttons stay stable across selection moves while gating their enabled state on whether the corresponding name is in the relevant set. Note for React consumers: the returned object is a fresh value on every call, so subscribing via `useEditorSelector` requires a structural compare to avoid re-rendering on every editor tick. Use [compareApplicableSchema](/api/selectors/functions/compareapplicableschema/) as the third argument. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # getBlockOffsets > `const` **getBlockOffsets**: `EditorSelector`\<\{ `end`: `BlockOffset`; `start`: `BlockOffset`; \} \| `undefined`\> Defined in: selector.get-block-offsets.ts:10 # getBlockTextAfter > `const` **getBlockTextAfter**: `EditorSelector`\<`string`\> Defined in: selector.get-text-after.ts:10 # getBlockTextBefore > `const` **getBlockTextBefore**: `EditorSelector`\<`string`\> Defined in: selector.get-text-before.ts:10 # getCaretWordSelection > `const` **getCaretWordSelection**: `EditorSelector`\<`EditorSelection`\> Defined in: selector.get-caret-word-selection.ts:23 Returns the selection of the of the word the caret is placed in. Note: Only returns a word selection if the current selection is collapsed # getFirstBlock > `const` **getFirstBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-first-block.ts:19 Returns the first block at the current container scope. When the focus is inside an editable container (e.g. a code block's line), this returns the first block within that container (the first line). When the focus is at root, or there is no selection, this returns the first block in the document. # getFocusBlock > `const` **getFocusBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-focus-block.ts:15 Returns the block containing the focus selection, resolved at any depth. When the focus is inside an editable container (e.g. a code block's line), this returns the innermost block ancestor (the line), not the outer container. When the focus is at root, behavior is unchanged. # getFocusBlockObject > `const` **getFocusBlockObject**: `EditorSelector`\<\{ `node`: `PortableTextObject`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-focus-block-object.ts:17 Returns the void block object containing the focus selection, resolved at any depth. Excludes text blocks and editable containers (which have their own children and are not "void"). When the focus is at root, behavior is unchanged. # getFocusChild > `const` **getFocusChild**: `EditorSelector`\<\{ `node`: `PortableTextObject` \| `PortableTextSpan`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-focus-child.ts:12 Returns the child (span or inline object) containing the focus selection, resolved at any depth. # getFocusInlineObject > `const` **getFocusInlineObject**: `EditorSelector`\<\{ `node`: `PortableTextObject`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-focus-inline-object.ts:13 Returns the inline object containing the focus selection, resolved at any depth. # getFocusListBlock > `const` **getFocusListBlock**: `EditorSelector`\<\{ `node`: `PortableTextListBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-focus-list-block.ts:13 Returns the list block containing the focus selection, resolved at any depth. # getFocusSpan > `const` **getFocusSpan**: `EditorSelector`\<\{ `node`: `PortableTextSpan`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-focus-span.ts:11 Returns the span containing the focus selection, resolved at any depth. # getFocusTextBlock > `const` **getFocusTextBlock**: `EditorSelector`\<\{ `node`: `PortableTextTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-focus-text-block.ts:15 Returns the text block containing the focus selection, resolved at any depth. When the focus is inside an editable container (e.g. a code block's line), this returns the innermost text block ancestor (the line), not the outer container. When the focus is at root, behavior is unchanged. # getFragment > `const` **getFragment**: `EditorSelector`\<`object`[]\> Defined in: selector.get-fragment.ts:30 Returns the smallest top-level-valid fragment of the editor's value that covers the current selection. Starts from [getSelectedValue](/api/selectors/variables/getselectedvalue/)'s envelope and unwraps it toward the selection's lowest common ancestor, stopping at the deepest level whose siblings are all root-accepted types. Intermediate single-child containers (a single row inside a table, a single cell inside a row) are walked through to look for a deeper unwrap target; an intermediate level with multiple siblings (the lowest common ancestor across two cells in one row) ends the walk and the last root-valid wrapping is returned. Backs every registered clipboard converter, `editor.getFragment()` (which projects to blocks only), and the drag preview pipeline (which uses the paths to find DOM nodes). Exposed for custom converters and any consumer that needs the clipboard-shaped view of the current selection without redundant ancestor envelopes. # getLastBlock > `const` **getLastBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-last-block.ts:19 Returns the last block at the current container scope. When the focus is inside an editable container (e.g. a code block's line), this returns the last block within that container (the last line). When the focus is at root, or there is no selection, this returns the last block in the document. # getMarkState > `const` **getMarkState**: `EditorSelector`\<[`MarkState`](/api/selectors/type-aliases/markstate/) \| `undefined`\> Defined in: selector.get-mark-state.ts:32 Given that text is inserted at the current position, what marks should be applied? :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # getNextBlock > `const` **getNextBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-next-block.ts:17 Returns the block after the selection's end block within the same container scope, if any. Siblings are resolved within the enclosing container (or the document root if the selection is at root level). Never crosses container boundaries. # getNextInlineObject > `const` **getNextInlineObject**: `EditorSelector`\<\{ `node`: `PortableTextObject`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-next-inline-object.ts:14 Returns the inline object after the selection end within the same text block, resolved at any depth. # getNextInlineObjects > `const` **getNextInlineObjects**: `EditorSelector`\<`object`[]\> Defined in: selector.get-next-inline-objects.ts:16 Returns all inline objects after the selection end within the same text block, resolved at any depth. # getNextSpan > `const` **getNextSpan**: `EditorSelector`\<\{ `node`: `PortableTextSpan`; `path`: `Path`; \} \| `undefined`\> Defined in: selector.get-next-span.ts:13 Returns the span after the selection end within the same text block, resolved at any depth. # getPreviousBlock > `const` **getPreviousBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-previous-block.ts:17 Returns the block before the selection's start block within the same container scope, if any. Siblings are resolved within the enclosing container (or the document root if the selection is at root level). Never crosses container boundaries. # getPreviousInlineObject > `const` **getPreviousInlineObject**: `EditorSelector`\<\{ `node`: `PortableTextObject`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-previous-inline-object.ts:14 Returns the inline object before the selection start within the same text block, resolved at any depth. # getPreviousInlineObjects > `const` **getPreviousInlineObjects**: `EditorSelector`\<`object`[]\> Defined in: selector.get-previous-inline-objects.ts:16 Returns all inline objects before the selection start within the same text block, resolved at any depth. # getPreviousSpan > `const` **getPreviousSpan**: `EditorSelector`\<\{ `node`: `PortableTextSpan`; `path`: `Path`; \} \| `undefined`\> Defined in: selector.get-previous-span.ts:13 Returns the span before the selection start within the same text block, resolved at any depth. # getSelectedBlocks > `const` **getSelectedBlocks**: `EditorSelector`\<`object`[]\> Defined in: selector.get-selected-blocks.ts:23 Returns the root-level blocks the selection covers. Only looks at direct children of the editor. If the selection is inside an editable container, the container itself is returned - not its inner blocks. Containers are preserved whole. Use for block-level operations like `move.block up/down` and drag-and-drop. For "selection as portable text" use `getSelectedValue`; for "text blocks at any depth" use `getSelectedTextBlocks`. # getSelectedSpans > `const` **getSelectedSpans**: `EditorSelector`\<`object`[]\> Defined in: selector.get-selected-spans.ts:11 Returns the spans touched by the selection, resolved at any depth. # getSelectedTextBlocks > `const` **getSelectedTextBlocks**: `EditorSelector`\<`object`[]\> Defined in: selector.get-selected-text-blocks.ts:18 Returns the text blocks touched by the selection, resolved at any depth. Walks the tree between the selection endpoints and returns every text block found, regardless of container nesting. For toolbar state and anywhere that needs "text blocks with text in the selection". # getSelectedValue > `const` **getSelectedValue**: `EditorSelector`\<`PortableTextBlock`[]\> Defined in: selector.get-selected-value.ts:39 Returns the portion of the document's value covered by the selection, resolved at any depth. Containers fully inside the selection are preserved verbatim. Containers on the selection boundary are recursed into so only the selected portion of their content is kept. Text blocks on the boundary are span-sliced. The result preserves the full ancestor envelope around the selection. For the clipboard-shaped view that unwraps the envelope toward the selection's lowest common ancestor, see [getFragment](/api/selectors/variables/getfragment/). # getSelection > `const` **getSelection**: `EditorSelector`\<`EditorSelection`\> Defined in: selector.get-selection.ts:7 # getSelectionEndBlock > `const` **getSelectionEndBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-selection-end-block.ts:13 Returns the block containing the selection's end point, resolved at any depth. # getSelectionEndChild > `const` **getSelectionEndChild**: `EditorSelector`\<\{ `node`: `PortableTextSpan` \| `PortableTextObject`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-selection-end-child.ts:13 Returns the child containing the selection's end point, resolved at any depth. # getSelectionEndPoint > `const` **getSelectionEndPoint**: `EditorSelector`\<`EditorSelectionPoint` \| `undefined`\> Defined in: selector.get-selection-end-point.ts:7 # getSelectionStartBlock > `const` **getSelectionStartBlock**: `EditorSelector`\<\{ `node`: `PortableTextBlock`; `path`: `BlockPath`; \} \| `undefined`\> Defined in: selector.get-selection-start-block.ts:13 Returns the block containing the selection's start point, resolved at any depth. # getSelectionStartChild > `const` **getSelectionStartChild**: `EditorSelector`\<\{ `node`: `PortableTextSpan` \| `PortableTextObject`; `path`: `ChildPath`; \} \| `undefined`\> Defined in: selector.get-selection-start-child.ts:13 Returns the child containing the selection's start point, resolved at any depth. # getSelectionStartPoint > `const` **getSelectionStartPoint**: `EditorSelector`\<`EditorSelectionPoint` \| `undefined`\> Defined in: selector.get-selection-start-point.ts:7 # getSelectionText > `const` **getSelectionText**: `EditorSelector`\<`string`\> Defined in: selector.get-selection-text.ts:12 # getValue > `const` **getValue**: `EditorSelector`\<`PortableTextBlock`[]\> Defined in: selector.get-value.ts:7 # isSelectingEntireBlocks > `const` **isSelectingEntireBlocks**: `EditorSelector`\<`boolean`\> Defined in: selector.is-selecting-entire-blocks.ts:11 # isSelectionCollapsed > `const` **isSelectionCollapsed**: `EditorSelector`\<`boolean`\> Defined in: selector.is-selection-collapsed.ts:7 # isSelectionExpanded > `const` **isSelectionExpanded**: `EditorSelector`\<`boolean`\> Defined in: selector.is-selection-expanded.ts:7 # useAnnotationButton > **useAnnotationButton**(`props`): [`AnnotationButton`](/api/toolbar/type-aliases/annotationbutton/) Defined in: toolbar/src/use-annotation-button.ts:297 Manages the state, keyboard shortcut and available events for an annotation button. Note: This hook assumes that the button triggers a dialog for inputting the annotation value. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaType [`ToolbarAnnotationSchemaType`](/api/toolbar/type-aliases/toolbarannotationschematype/) ## Returns [`AnnotationButton`](/api/toolbar/type-aliases/annotationbutton/) # useAnnotationPopover > **useAnnotationPopover**(`props`): [`AnnotationPopover`](/api/toolbar/type-aliases/annotationpopover/) Defined in: toolbar/src/use-annotation-popover.ts:276 Manages the state and available events for an annotation popover. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaTypes readonly [`ToolbarAnnotationSchemaType`](/api/toolbar/type-aliases/toolbarannotationschematype/)[] ## Returns [`AnnotationPopover`](/api/toolbar/type-aliases/annotationpopover/) # useApplicableSchema > **useApplicableSchema**(): [`ApplicableSchema`](/api/toolbar/type-aliases/applicableschema/) Defined in: toolbar/src/use-applicable-schema.ts:21 React hook that subscribes to getApplicableSchema for the active editor and returns a stable reference across editor ticks while the applicable set is unchanged. Pair with [useToolbarSchema](/api/toolbar/functions/usetoolbarschema/) to render a static toolbar whose buttons stay stable across selection moves and gate their enabled state on whether the corresponding name is in the relevant set. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Returns [`ApplicableSchema`](/api/toolbar/type-aliases/applicableschema/) # useBlockObjectButton > **useBlockObjectButton**(`props`): [`BlockObjectButton`](/api/toolbar/type-aliases/blockobjectbutton/) Defined in: toolbar/src/use-block-object-button.ts:172 Manages the state, keyboard shortcut and available events for a block object button. Note: This hook assumes that the button triggers a dialog for inputting the block object value. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaType [`ToolbarBlockObjectSchemaType`](/api/toolbar/type-aliases/toolbarblockobjectschematype/) ## Returns [`BlockObjectButton`](/api/toolbar/type-aliases/blockobjectbutton/) # useBlockObjectPopover > **useBlockObjectPopover**(`props`): [`BlockObjectPopover`](/api/toolbar/type-aliases/blockobjectpopover/) Defined in: toolbar/src/use-block-object-popover.ts:264 Manages the state and available events for a block object popover. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaTypes readonly [`ToolbarBlockObjectSchemaType`](/api/toolbar/type-aliases/toolbarblockobjectschematype/)[] ## Returns [`BlockObjectPopover`](/api/toolbar/type-aliases/blockobjectpopover/) # useDecoratorButton > **useDecoratorButton**(`props`): [`DecoratorButton`](/api/toolbar/type-aliases/decoratorbutton/) Defined in: toolbar/src/use-decorator-button.ts:176 Manages the state, keyboard shortcuts and available events for a decorator button and sets up mutually exclusive decorator behaviors. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaType [`ToolbarDecoratorSchemaType`](/api/toolbar/type-aliases/toolbardecoratorschematype/) ## Returns [`DecoratorButton`](/api/toolbar/type-aliases/decoratorbutton/) # useHistoryButtons > **useHistoryButtons**(): [`HistoryButtons`](/api/toolbar/type-aliases/historybuttons/) Defined in: toolbar/src/use-history-buttons.ts:83 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Returns [`HistoryButtons`](/api/toolbar/type-aliases/historybuttons/) # useInlineObjectButton > **useInlineObjectButton**(`props`): [`InlineObjectButton`](/api/toolbar/type-aliases/inlineobjectbutton/) Defined in: toolbar/src/use-inline-object-button.ts:166 Manages the state, keyboard shortcut and available events for an inline object button. Note: This hook assumes that the button triggers a dialog for inputting the inline object value. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaType [`ToolbarInlineObjectSchemaType`](/api/toolbar/type-aliases/toolbarinlineobjectschematype/) ## Returns [`InlineObjectButton`](/api/toolbar/type-aliases/inlineobjectbutton/) # useInlineObjectPopover > **useInlineObjectPopover**(`props`): [`InlineObjectPopover`](/api/toolbar/type-aliases/inlineobjectpopover/) Defined in: toolbar/src/use-inline-object-popover.ts:264 Manages the state and available events for an inline object popover. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaTypes readonly [`ToolbarInlineObjectSchemaType`](/api/toolbar/type-aliases/toolbarinlineobjectschematype/)[] ## Returns [`InlineObjectPopover`](/api/toolbar/type-aliases/inlineobjectpopover/) # useListButton > **useListButton**(`props`): [`ListButton`](/api/toolbar/type-aliases/listbutton/) Defined in: toolbar/src/use-list-button.ts:169 Manages the state, keyboard shortcuts and available events for a list button. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaType [`ToolbarListSchemaType`](/api/toolbar/type-aliases/toolbarlistschematype/) ## Returns [`ListButton`](/api/toolbar/type-aliases/listbutton/) # useStyleSelector > **useStyleSelector**(`props`): [`StyleSelector`](/api/toolbar/type-aliases/styleselector/) Defined in: toolbar/src/use-style-selector.ts:134 Manages the state, keyboard shortcuts and available events for a style selector. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### schemaTypes readonly [`ToolbarStyleSchemaType`](/api/toolbar/type-aliases/toolbarstyleschematype/)[] ## Returns [`StyleSelector`](/api/toolbar/type-aliases/styleselector/) # useToolbarSchema > **useToolbarSchema**(`props`): [`ToolbarSchema`](/api/toolbar/type-aliases/toolbarschema/) Defined in: toolbar/src/use-toolbar-schema.ts:74 Resolve the editor's full toolbar schema. Returns the union of every decorator, annotation, list, style, block object and inline object declared anywhere in the editor's schema graph that is reachable from a position where text is edited - the root schema merged with the sub-schema of every registered container whose field accepts text blocks. Useful for rendering a static toolbar whose buttons stay stable across selection moves. Re-renders only when the schema graph or the extension callbacks change. Pair with [useApplicableSchema](/api/toolbar/functions/useapplicableschema/) to determine which entries are applicable at the current selection (which buttons should be enabled vs. disabled). :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### props #### extendAnnotation? [`ExtendAnnotationSchemaType`](/api/toolbar/type-aliases/extendannotationschematype/) #### extendBlockObject? [`ExtendBlockObjectSchemaType`](/api/toolbar/type-aliases/extendblockobjectschematype/) #### extendDecorator? [`ExtendDecoratorSchemaType`](/api/toolbar/type-aliases/extenddecoratorschematype/) #### extendInlineObject? [`ExtendInlineObjectSchemaType`](/api/toolbar/type-aliases/extendinlineobjectschematype/) #### extendList? [`ExtendListSchemaType`](/api/toolbar/type-aliases/extendlistschematype/) #### extendStyle? [`ExtendStyleSchemaType`](/api/toolbar/type-aliases/extendstyleschematype/) ## Returns [`ToolbarSchema`](/api/toolbar/type-aliases/toolbarschema/) # @portabletext/toolbar ## Type Aliases - [AnnotationButton](/api/toolbar/type-aliases/annotationbutton/) - [AnnotationButtonEvent](/api/toolbar/type-aliases/annotationbuttonevent/) - [AnnotationPopover](/api/toolbar/type-aliases/annotationpopover/) - [AnnotationPopoverEvent](/api/toolbar/type-aliases/annotationpopoverevent/) - [ApplicableSchema](/api/toolbar/type-aliases/applicableschema/) - [BlockObjectButton](/api/toolbar/type-aliases/blockobjectbutton/) - [BlockObjectButtonEvent](/api/toolbar/type-aliases/blockobjectbuttonevent/) - [BlockObjectPopover](/api/toolbar/type-aliases/blockobjectpopover/) - [BlockObjectPopoverEvent](/api/toolbar/type-aliases/blockobjectpopoverevent/) - [DecoratorButton](/api/toolbar/type-aliases/decoratorbutton/) - [DecoratorButtonEvent](/api/toolbar/type-aliases/decoratorbuttonevent/) - [ExtendAnnotationSchemaType](/api/toolbar/type-aliases/extendannotationschematype/) - [ExtendBlockObjectSchemaType](/api/toolbar/type-aliases/extendblockobjectschematype/) - [ExtendDecoratorSchemaType](/api/toolbar/type-aliases/extenddecoratorschematype/) - [ExtendInlineObjectSchemaType](/api/toolbar/type-aliases/extendinlineobjectschematype/) - [ExtendListSchemaType](/api/toolbar/type-aliases/extendlistschematype/) - [ExtendStyleSchemaType](/api/toolbar/type-aliases/extendstyleschematype/) - [HistoryButtons](/api/toolbar/type-aliases/historybuttons/) - [HistoryButtonsEvent](/api/toolbar/type-aliases/historybuttonsevent/) - [InlineObjectButton](/api/toolbar/type-aliases/inlineobjectbutton/) - [InlineObjectButtonEvent](/api/toolbar/type-aliases/inlineobjectbuttonevent/) - [InlineObjectPopover](/api/toolbar/type-aliases/inlineobjectpopover/) - [InlineObjectPopoverEvent](/api/toolbar/type-aliases/inlineobjectpopoverevent/) - [ListButton](/api/toolbar/type-aliases/listbutton/) - [ListButtonEvent](/api/toolbar/type-aliases/listbuttonevent/) - [StyleSelector](/api/toolbar/type-aliases/styleselector/) - [StyleSelectorEvent](/api/toolbar/type-aliases/styleselectorevent/) - [ToolbarAnnotationSchemaType](/api/toolbar/type-aliases/toolbarannotationschematype/) - [ToolbarBlockObjectSchemaType](/api/toolbar/type-aliases/toolbarblockobjectschematype/) - [ToolbarDecoratorSchemaType](/api/toolbar/type-aliases/toolbardecoratorschematype/) - [ToolbarInlineObjectSchemaType](/api/toolbar/type-aliases/toolbarinlineobjectschematype/) - [ToolbarListSchemaType](/api/toolbar/type-aliases/toolbarlistschematype/) - [ToolbarSchema](/api/toolbar/type-aliases/toolbarschema/) - [ToolbarStyleSchemaType](/api/toolbar/type-aliases/toolbarstyleschematype/) ## Functions - [useAnnotationButton](/api/toolbar/functions/useannotationbutton/) - [useAnnotationPopover](/api/toolbar/functions/useannotationpopover/) - [useApplicableSchema](/api/toolbar/functions/useapplicableschema/) - [useBlockObjectButton](/api/toolbar/functions/useblockobjectbutton/) - [useBlockObjectPopover](/api/toolbar/functions/useblockobjectpopover/) - [useDecoratorButton](/api/toolbar/functions/usedecoratorbutton/) - [useHistoryButtons](/api/toolbar/functions/usehistorybuttons/) - [useInlineObjectButton](/api/toolbar/functions/useinlineobjectbutton/) - [useInlineObjectPopover](/api/toolbar/functions/useinlineobjectpopover/) - [useListButton](/api/toolbar/functions/uselistbutton/) - [useStyleSelector](/api/toolbar/functions/usestyleselector/) - [useToolbarSchema](/api/toolbar/functions/usetoolbarschema/) # AnnotationButton > **AnnotationButton** = `object` Defined in: toolbar/src/use-annotation-button.ts:272 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-annotation-button.ts:286 #### Parameters ##### event [`AnnotationButtonEvent`](/api/toolbar/type-aliases/annotationbuttonevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-annotation-button.ts:273 #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `disabled`: `"inactive"`; \} | \{ `disabled`: `"active"`; \} | \{ `enabled`: `"inactive"`; \} | \{ `enabled`: \{ `inactive`: `"idle"`; \}; \} | \{ `enabled`: \{ `inactive`: `"showing dialog"`; \}; \} | \{ `enabled`: `"active"`; \} ##### Returns `boolean` # AnnotationButtonEvent > **AnnotationButtonEvent** = \{ `type`: `"close dialog"`; \} \| \{ `type`: `"open dialog"`; \} \| \{ `annotation`: \{ `value`: `Record`\<`string`, `unknown`\>; \}; `type`: `"add"`; \} \| \{ `type`: `"remove"`; \} Defined in: toolbar/src/use-annotation-button.ts:258 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # AnnotationPopover > **AnnotationPopover** = `object` Defined in: toolbar/src/use-annotation-popover.ts:257 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-annotation-popover.ts:269 #### Parameters ##### event [`AnnotationPopoverEvent`](/api/toolbar/type-aliases/annotationpopoverevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-annotation-popover.ts:258 #### context > **context**: `ActiveContext` #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `enabled`: `"inactive"` \| `"active"`; \} ##### Returns `boolean` # AnnotationPopoverEvent > **AnnotationPopoverEvent** = \{ `schemaType`: `AnnotationSchemaType`; `type`: `"remove"`; \} \| \{ `at`: `AnnotationPath`; `props`: \{\[`key`: `string`\]: `unknown`; \}; `type`: `"edit"`; \} \| \{ `type`: `"close"`; \} Defined in: toolbar/src/use-annotation-popover.ts:240 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # ApplicableSchema > **ApplicableSchema** = `object` Defined in: editor/lib/selectors/index.d.ts:9 The set of schema member names applicable at the current selection, grouped by category. :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### annotations > **annotations**: `ReadonlySet`\<`string`\> Defined in: editor/lib/selectors/index.d.ts:11 *** ### blockObjects > **blockObjects**: `ReadonlySet`\<`string`\> Defined in: editor/lib/selectors/index.d.ts:14 *** ### decorators > **decorators**: `ReadonlySet`\<`string`\> Defined in: editor/lib/selectors/index.d.ts:10 *** ### inlineObjects > **inlineObjects**: `ReadonlySet`\<`string`\> Defined in: editor/lib/selectors/index.d.ts:15 *** ### lists > **lists**: `ReadonlySet`\<`string`\> Defined in: editor/lib/selectors/index.d.ts:12 *** ### styles > **styles**: `ReadonlySet`\<`string`\> Defined in: editor/lib/selectors/index.d.ts:13 # BlockObjectButton > **BlockObjectButton** = `object` Defined in: toolbar/src/use-block-object-button.ts:151 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-block-object-button.ts:161 #### Parameters ##### event [`BlockObjectButtonEvent`](/api/toolbar/type-aliases/blockobjectbuttonevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-block-object-button.ts:152 #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `enabled`: `"idle"`; \} | \{ `enabled`: `"showing dialog"`; \} ##### Returns `boolean` # BlockObjectButtonEvent > **BlockObjectButtonEvent** = \{ `type`: `"close dialog"`; \} \| \{ `type`: `"open dialog"`; \} \| \{ `placement`: `InsertPlacement` \| `undefined`; `type`: `"insert"`; `value`: \{\[`key`: `string`\]: `unknown`; \}; \} Defined in: toolbar/src/use-block-object-button.ts:135 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # BlockObjectPopover > **BlockObjectPopover** = `object` Defined in: toolbar/src/use-block-object-popover.ts:245 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-block-object-popover.ts:257 #### Parameters ##### event [`BlockObjectPopoverEvent`](/api/toolbar/type-aliases/blockobjectpopoverevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-block-object-popover.ts:246 #### context > **context**: `ActiveContext` #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `enabled`: `"inactive"` \| `"active"`; \} ##### Returns `boolean` # BlockObjectPopoverEvent > **BlockObjectPopoverEvent** = \{ `at`: `BlockPath`; `type`: `"remove"`; \} \| \{ `at`: `BlockPath`; `props`: \{\[`key`: `string`\]: `unknown`; \}; `type`: `"edit"`; \} \| \{ `type`: `"close"`; \} Defined in: toolbar/src/use-block-object-popover.ts:228 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # DecoratorButton > **DecoratorButton** = `object` Defined in: toolbar/src/use-decorator-button.ts:156 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-decorator-button.ts:168 #### Parameters ##### event [`DecoratorButtonEvent`](/api/toolbar/type-aliases/decoratorbuttonevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-decorator-button.ts:157 #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `disabled`: `"inactive"`; \} | \{ `disabled`: `"active"`; \} | \{ `enabled`: `"inactive"`; \} | \{ `enabled`: `"active"`; \} ##### Returns `boolean` # DecoratorButtonEvent > **DecoratorButtonEvent** = `object` Defined in: toolbar/src/use-decorator-button.ts:149 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### type > **type**: `"toggle"` Defined in: toolbar/src/use-decorator-button.ts:150 # ExtendAnnotationSchemaType > **ExtendAnnotationSchemaType** = (`annotation`) => [`ToolbarAnnotationSchemaType`](/api/toolbar/type-aliases/toolbarannotationschematype/) Defined in: toolbar/src/use-toolbar-schema.ts:27 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### annotation `AnnotationSchemaType` ## Returns [`ToolbarAnnotationSchemaType`](/api/toolbar/type-aliases/toolbarannotationschematype/) # ExtendBlockObjectSchemaType > **ExtendBlockObjectSchemaType** = (`blockObject`) => [`ToolbarBlockObjectSchemaType`](/api/toolbar/type-aliases/toolbarblockobjectschematype/) Defined in: toolbar/src/use-toolbar-schema.ts:41 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### blockObject `BlockObjectSchemaType` ## Returns [`ToolbarBlockObjectSchemaType`](/api/toolbar/type-aliases/toolbarblockobjectschematype/) # ExtendDecoratorSchemaType > **ExtendDecoratorSchemaType** = (`decorator`) => [`ToolbarDecoratorSchemaType`](/api/toolbar/type-aliases/toolbardecoratorschematype/) Defined in: toolbar/src/use-toolbar-schema.ts:20 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### decorator `DecoratorSchemaType` ## Returns [`ToolbarDecoratorSchemaType`](/api/toolbar/type-aliases/toolbardecoratorschematype/) # ExtendInlineObjectSchemaType > **ExtendInlineObjectSchemaType** = (`inlineObject`) => [`ToolbarInlineObjectSchemaType`](/api/toolbar/type-aliases/toolbarinlineobjectschematype/) Defined in: toolbar/src/use-toolbar-schema.ts:48 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### inlineObject `InlineObjectSchemaType` ## Returns [`ToolbarInlineObjectSchemaType`](/api/toolbar/type-aliases/toolbarinlineobjectschematype/) # ExtendListSchemaType > **ExtendListSchemaType** = (`list`) => [`ToolbarListSchemaType`](/api/toolbar/type-aliases/toolbarlistschematype/) Defined in: toolbar/src/use-toolbar-schema.ts:34 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### list `ListSchemaType` ## Returns [`ToolbarListSchemaType`](/api/toolbar/type-aliases/toolbarlistschematype/) # ExtendStyleSchemaType > **ExtendStyleSchemaType** = (`style`) => [`ToolbarStyleSchemaType`](/api/toolbar/type-aliases/toolbarstyleschematype/) Defined in: toolbar/src/use-toolbar-schema.ts:55 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Parameters ### style `StyleSchemaType` ## Returns [`ToolbarStyleSchemaType`](/api/toolbar/type-aliases/toolbarstyleschematype/) # HistoryButtons > **HistoryButtons** = `object` Defined in: toolbar/src/use-history-buttons.ts:73 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-history-buttons.ts:77 #### Parameters ##### event [`HistoryButtonsEvent`](/api/toolbar/type-aliases/historybuttonsevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-history-buttons.ts:74 #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` ##### Returns `boolean` # HistoryButtonsEvent > **HistoryButtonsEvent** = \{ `type`: `"history.undo"`; \} \| \{ `type`: `"history.redo"`; \} Defined in: toolbar/src/use-history-buttons.ts:62 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # InlineObjectButton > **InlineObjectButton** = `object` Defined in: toolbar/src/use-inline-object-button.ts:145 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-inline-object-button.ts:155 #### Parameters ##### event [`InlineObjectButtonEvent`](/api/toolbar/type-aliases/inlineobjectbuttonevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-inline-object-button.ts:146 #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `enabled`: `"idle"`; \} | \{ `enabled`: `"showing dialog"`; \} ##### Returns `boolean` # InlineObjectButtonEvent > **InlineObjectButtonEvent** = \{ `type`: `"close dialog"`; \} \| \{ `type`: `"open dialog"`; \} \| \{ `type`: `"insert"`; `value`: \{\[`key`: `string`\]: `unknown`; \}; \} Defined in: toolbar/src/use-inline-object-button.ts:130 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # InlineObjectPopover > **InlineObjectPopover** = `object` Defined in: toolbar/src/use-inline-object-popover.ts:245 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-inline-object-popover.ts:257 #### Parameters ##### event [`InlineObjectPopoverEvent`](/api/toolbar/type-aliases/inlineobjectpopoverevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-inline-object-popover.ts:246 #### context > **context**: `ActiveContext` #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `enabled`: `"inactive"` \| `"active"`; \} ##### Returns `boolean` # InlineObjectPopoverEvent > **InlineObjectPopoverEvent** = \{ `at`: `ChildPath`; `type`: `"remove"`; \} \| \{ `at`: `ChildPath`; `props`: \{\[`key`: `string`\]: `unknown`; \}; `type`: `"edit"`; \} \| \{ `type`: `"close"`; \} Defined in: toolbar/src/use-inline-object-popover.ts:228 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: # ListButton > **ListButton** = `object` Defined in: toolbar/src/use-list-button.ts:150 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-list-button.ts:162 #### Parameters ##### event [`ListButtonEvent`](/api/toolbar/type-aliases/listbuttonevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-list-button.ts:151 #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` | \{ `disabled`: `"inactive"`; \} | \{ `disabled`: `"active"`; \} | \{ `enabled`: `"inactive"`; \} | \{ `enabled`: `"active"`; \} ##### Returns `boolean` # ListButtonEvent > **ListButtonEvent** = `object` Defined in: toolbar/src/use-list-button.ts:143 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### type > **type**: `"toggle"` Defined in: toolbar/src/use-list-button.ts:144 # StyleSelector > **StyleSelector** = `object` Defined in: toolbar/src/use-style-selector.ts:119 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### send() > **send**: (`event`) => `void` Defined in: toolbar/src/use-style-selector.ts:126 #### Parameters ##### event [`StyleSelectorEvent`](/api/toolbar/type-aliases/styleselectorevent/) #### Returns `void` *** ### snapshot > **snapshot**: `object` Defined in: toolbar/src/use-style-selector.ts:120 #### context > **context**: `object` ##### context.activeStyle > **activeStyle**: `StyleSchemaType`\[`"name"`\] \| `undefined` #### matches() > **matches**: (`state`) => `boolean` ##### Parameters ###### state `"disabled"` | `"enabled"` ##### Returns `boolean` # StyleSelectorEvent > **StyleSelectorEvent** = `object` Defined in: toolbar/src/use-style-selector.ts:111 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### style > **style**: `StyleSchemaType`\[`"name"`\] Defined in: toolbar/src/use-style-selector.ts:113 *** ### type > **type**: `"toggle"` Defined in: toolbar/src/use-style-selector.ts:112 # ToolbarAnnotationSchemaType > **ToolbarAnnotationSchemaType** = `AnnotationSchemaType` & `object` Defined in: toolbar/src/use-toolbar-schema.ts:133 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration ### defaultValues? > `optional` **defaultValues**: `Record`\<`string`, `unknown`\> ### icon? > `optional` **icon**: `React.ComponentType` ### mutuallyExclusive? > `optional` **mutuallyExclusive**: `ReadonlyArray`\<`AnnotationDefinition`\[`"name"`\]\> ### shortcut? > `optional` **shortcut**: `KeyboardShortcut` # ToolbarBlockObjectSchemaType > **ToolbarBlockObjectSchemaType** = `BlockObjectSchemaType` & `object` Defined in: toolbar/src/use-toolbar-schema.ts:150 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration ### defaultValues? > `optional` **defaultValues**: `Record`\<`string`, `unknown`\> ### icon? > `optional` **icon**: `React.ComponentType` ### shortcut? > `optional` **shortcut**: `KeyboardShortcut` # ToolbarDecoratorSchemaType > **ToolbarDecoratorSchemaType** = `DecoratorSchemaType` & `object` Defined in: toolbar/src/use-toolbar-schema.ts:124 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration ### icon? > `optional` **icon**: `React.ComponentType` ### mutuallyExclusive? > `optional` **mutuallyExclusive**: `ReadonlyArray`\<`DecoratorDefinition`\[`"name"`\]\> ### shortcut? > `optional` **shortcut**: `KeyboardShortcut` # ToolbarInlineObjectSchemaType > **ToolbarInlineObjectSchemaType** = `InlineObjectSchemaType` & `object` Defined in: toolbar/src/use-toolbar-schema.ts:159 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration ### defaultValues? > `optional` **defaultValues**: `Record`\<`string`, `unknown`\> ### icon? > `optional` **icon**: `React.ComponentType` ### shortcut? > `optional` **shortcut**: `KeyboardShortcut` # ToolbarListSchemaType > **ToolbarListSchemaType** = `ListSchemaType` & `object` Defined in: toolbar/src/use-toolbar-schema.ts:143 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration ### icon? > `optional` **icon**: `React.ComponentType` # ToolbarSchema > **ToolbarSchema** = `object` Defined in: toolbar/src/use-toolbar-schema.ts:112 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Properties ### annotations > **annotations**: `ReadonlyArray`\<[`ToolbarAnnotationSchemaType`](/api/toolbar/type-aliases/toolbarannotationschematype/)\> Defined in: toolbar/src/use-toolbar-schema.ts:114 *** ### blockObjects > **blockObjects**: `ReadonlyArray`\<[`ToolbarBlockObjectSchemaType`](/api/toolbar/type-aliases/toolbarblockobjectschematype/)\> Defined in: toolbar/src/use-toolbar-schema.ts:116 *** ### decorators > **decorators**: `ReadonlyArray`\<[`ToolbarDecoratorSchemaType`](/api/toolbar/type-aliases/toolbardecoratorschematype/)\> Defined in: toolbar/src/use-toolbar-schema.ts:113 *** ### inlineObjects > **inlineObjects**: `ReadonlyArray`\<[`ToolbarInlineObjectSchemaType`](/api/toolbar/type-aliases/toolbarinlineobjectschematype/)\> Defined in: toolbar/src/use-toolbar-schema.ts:117 *** ### lists > **lists**: `ReadonlyArray`\<[`ToolbarListSchemaType`](/api/toolbar/type-aliases/toolbarlistschematype/)\> Defined in: toolbar/src/use-toolbar-schema.ts:115 *** ### styles > **styles**: `ReadonlyArray`\<[`ToolbarStyleSchemaType`](/api/toolbar/type-aliases/toolbarstyleschematype/)\> Defined in: toolbar/src/use-toolbar-schema.ts:118 # ToolbarStyleSchemaType > **ToolbarStyleSchemaType** = `StyleSchemaType` & `object` Defined in: toolbar/src/use-toolbar-schema.ts:168 :::caution[Beta] This API should not be used in production and may be trimmed from a public release. ::: ## Type Declaration ### icon? > `optional` **icon**: `React.ComponentType` ### shortcut? > `optional` **shortcut**: `KeyboardShortcut` # HTML to Portable Text > Convert HTML content to Portable Text using the official conversion packages. import {TabItem, Tabs} from '@astrojs/starlight/components' import {PackageManagers} from 'starlight-package-managers' Convert HTML strings to Portable Text blocks. This is useful for migrating content from a CMS that stores HTML, processing pasted content from web pages, or importing content from WordPress, Google Docs, or Notion. :::note[Prerequisites] This guide covers `@portabletext/html` **v1.x** and `@portabletext/block-tools` **v5.x** ([changelog](https://github.com/portabletext/editor/releases)). Both packages require Node.js 20.19+ or 22.12+. ::: ## Which package? **Using Sanity?** Use `@portabletext/block-tools`. It accepts your Sanity schema directly. **Everything else?** Use `@portabletext/html`. It works standalone with no Sanity dependency. Both packages use the same conversion engine (block-tools delegates to html internally). Custom rules are interchangeable between them, and they produce identical output for identical schemas. ## Install ## Basic usage ```ts import {htmlToPortableText} from '@portabletext/html' const blocks = htmlToPortableText('

Hello world

') ``` In the browser, the package uses the built-in `DOMParser`. In Node.js, you need to provide a `parseHtml` function: ```ts import {htmlToPortableText} from '@portabletext/html' import {JSDOM} from 'jsdom' const blocks = htmlToPortableText(html, { parseHtml: (html) => new JSDOM(html).window.document, }) ```
```ts import {htmlToBlocks} from '@portabletext/block-tools' import {Schema} from '@sanity/schema' import {JSDOM} from 'jsdom' // Get the block content type from your Sanity schema const defaultSchema = Schema.compile({ name: 'myBlog', types: [{ type: 'object', name: 'blogPost', fields: [{ name: 'body', type: 'array', of: [{type: 'block'}], }], }], }) const blockContentType = defaultSchema .get('blogPost') .fields.find((f) => f.name === 'body').type const blocks = htmlToBlocks(html, blockContentType, { parseHtml: (html) => new JSDOM(html).window.document, }) ```
## Node.js setup In the browser, HTML parsing works automatically via `DOMParser`. In Node.js, there is no built-in DOM, so you must provide a `parseHtml` function. The package throws a descriptive error if you forget. [JSDOM](https://github.com/jsdom/jsdom) is the most common choice: ```ts import {JSDOM} from 'jsdom' // Pass to either package const options = { parseHtml: (html) => new JSDOM(html).window.document, } ``` Lighter alternatives like [linkedom](https://github.com/WebReflection/linkedom) and [happy-dom](https://github.com/capricorn86/happy-dom) also work. Any library that returns a standard `Document` object is compatible. ## What converts by default The converter maps semantic HTML elements to Portable Text: | HTML | Portable Text | | --------------------------- | --------------------------------------------- | | `

` | Text block, style `"normal"` | | `

` through `

` | Text block, style `"h1"` through `"h6"` | | `
` | Text block, style `"blockquote"` | | ``, `` | `"strong"` decorator | | ``, `` | `"em"` decorator | | `` | `"code"` decorator | | ``, ``, `` | `"strike-through"` decorator | | `` | `"link"` annotation with `href` and `title` | | `
    ` / `
      ` with `
    1. ` | List items with `"bullet"` or `"number"` type | | `
      ` | Newline character within a span | | `
      ` | `"horizontal-rule"` block object | ## Schema as whitelist :::caution[Marks not in your schema are silently dropped] The schema controls what appears in the output. If a decorator, annotation, or block type isn't defined in the schema, the converter drops it without warning. This is the most common source of "my formatting disappeared" bugs. ::: The default schema includes `strong`, `em`, `code`, and `strike-through` as decorators. It does **not** include `underline`. If your HTML contains `` tags, the underline is silently removed unless you add it to your schema: ```ts import {htmlToPortableText} from '@portabletext/html' import {compileSchema, defineSchema} from '@portabletext/schema' const schema = compileSchema(defineSchema({ styles: [{name: 'normal'}, {name: 'h1'}, {name: 'h2'}, {name: 'h3'}], decorators: [ {name: 'strong'}, {name: 'em'}, {name: 'code'}, {name: 'strike-through'}, {name: 'underline'}, // now tags are preserved ], annotations: [{name: 'link', fields: [{name: 'href', type: 'string'}]}], lists: [{name: 'bullet'}, {name: 'number'}], })) const blocks = htmlToPortableText(html, {schema}) ``` In Sanity, your schema already defines which decorators are available. If your block type includes `underline` as a decorator, `htmlToBlocks` will preserve `` tags automatically. Other HTML elements that map to decorators but are **not** in the default schema: | HTML | Decorator name | Add to schema to preserve | | --------- | -------------- | ------------------------- | | `` | `underline` | `{name: 'underline'}` | | `` | `sup` | `{name: 'sup'}` | | `` | `sub` | `{name: 'sub'}` | | `` | `ins` | `{name: 'ins'}` | | `` | `mark` | `{name: 'mark'}` | | `` | `small` | `{name: 'small'}` | ## Handling images Images are skipped by default. This is intentional: the converter is synchronous, but image handling typically requires async work (downloading, uploading to a CDN, generating asset references). The converter can't do that inline. To capture images, provide a matcher: ```ts import {htmlToPortableText, type ObjectMatcher} from '@portabletext/html' import {compileSchema, defineSchema} from '@portabletext/schema' const schema = compileSchema(defineSchema({ blockObjects: [{name: 'image', fields: [{name: 'src', type: 'string'}]}], inlineObjects: [{name: 'image', fields: [{name: 'src', type: 'string'}]}], })) const imageMatcher: ObjectMatcher<{src?: string; alt?: string}> = ({ context, value, isInline, }) => { const collection = isInline ? context.schema.inlineObjects : context.schema.blockObjects if (!collection.some((obj) => obj.name === 'image')) return undefined return { _key: context.keyGenerator(), _type: 'image', ...(value.src ? {src: value.src} : {}), } } const blocks = htmlToPortableText(html, { schema, types: {image: imageMatcher}, }) ``` ```ts const blocks = htmlToBlocks(html, blockContentType, { parseHtml: (html) => new JSDOM(html).window.document, matchers: { image: ({context, props}) => ({ _key: context.keyGenerator(), _type: 'image', _sanityAsset: `image@${props.src}`, }), inlineImage: ({context, props}) => ({ _key: context.keyGenerator(), _type: 'image', _sanityAsset: `image@${props.src}`, }), }, }) ``` The `_sanityAsset` convention tells the Sanity client to download and upload the image during import. ### Two-phase image upload For migration scripts where you need to upload images to your own CDN or asset pipeline, use a two-phase approach: capture the URLs synchronously, then upload asynchronously: ```ts // Phase 1: capture image URLs as temporary block types const blocks = htmlToPortableText(html, { rules: [ { deserialize(el, next, createBlock) { if (el.tagName?.toLowerCase() !== 'img') return undefined return createBlock({ _type: 'externalImage', url: el.getAttribute('src') ?? '', alt: el.getAttribute('alt') ?? '', }).block }, }, ], }) // Phase 2: upload images and replace temporary blocks const finalBlocks = await Promise.all( blocks.map(async (block) => { if (block._type !== 'externalImage') return block const uploadedUrl = await uploadToYourCDN(block.url) return { _key: block._key, _type: 'image', src: uploadedUrl, alt: block.alt, } }), ) ``` ## Handling tables Tables are skipped by default. The `createFlattenTableRule` function (currently in beta) converts tables into a flat list of text blocks: ```ts import {htmlToPortableText} from '@portabletext/html' import {createFlattenTableRule} from '@portabletext/html/rules' const blocks = htmlToPortableText(html, { rules: [ createFlattenTableRule({ schema, separator: () => ({_type: 'span', text: ': '}), }), ], }) ``` This turns each table cell into a text block, with an optional separator between cells. For preserving full table structure, write a custom rule that maps `` to your own block type. :::note `createFlattenTableRule` is marked `@beta`. The API may change in future releases. ::: ## Custom rules Custom rules let you handle HTML elements that the built-in converter doesn't cover. A rule receives a DOM node and returns Portable Text blocks (or `undefined` to skip and let the next rule handle it). ### Example: code blocks with language The default converter treats `
      ` as a normal block with code marks. To convert fenced code blocks into a custom `code` block type:
      
      ```ts
      const codeBlockRule = {
        deserialize(el, next, createBlock) {
          if (el.tagName?.toLowerCase() !== 'pre') return undefined
          const code = el.querySelector('code')
          return createBlock({
            _type: 'code',
            text: (code ?? el).textContent ?? '',
            language: code?.className?.replace('language-', '') ?? undefined,
          }).block
        },
      }
      
      const blocks = htmlToPortableText(html, {
        rules: [codeBlockRule],
      })
      ```
      
      ### Example: callout blocks
      
      Convert `
      ` elements into a custom block type: ```ts const calloutRule = { deserialize(el, next, createBlock) { if ( el.tagName?.toLowerCase() !== 'div' || !el.classList?.contains('callout') ) { return undefined } const tone = el.classList.contains('warning') ? 'warning' : 'info' const children = next(el.childNodes) return createBlock({ _type: 'callout', tone, content: Array.isArray(children) ? children : children ? [children] : [], }).block }, } ``` Custom rules are checked before the built-in rules. Return `undefined` from your rule to fall through to the default handling. ## Paste source support The converter automatically detects and preprocesses content pasted from common applications. No configuration needed. | Source | How it's detected | What's handled | | -------------- | -------------------------------------- | ------------------------------------------------------------------- | | Google Docs | `id` containing `"docs-internal-guid"` | Inline styles converted to semantic marks, checklist images removed | | Microsoft Word | `class="Mso..."` or `mso-` styles | CSS classes remapped to semantic HTML, list numbering extracted | | Word Online | Specific paragraph markers | Paragraph styles converted to headings and blockquotes | | Notion | Inline style patterns | `font-weight:700` converted to strong, `font-style:italic` to em | :::tip For Google Docs content, use `whitespaceMode: 'normalize'` to collapse extra whitespace that Google Docs adds: ```ts const blocks = htmlToPortableText(html, { whitespaceMode: 'normalize', }) ``` ::: CSS-based formatting (like `style="font-weight: bold"`) is **not** converted in general HTML. Only the paste source preprocessors handle inline styles, and only for their specific source formats. ## Edge cases - **`deserialize` is synchronous.** You can't do async work (like image uploads) inside rules. Use the two-phase pattern described above. - **CSS formatting is ignored.** Only semantic HTML tags (``, ``, etc.) are converted. A `` in plain HTML produces no marks. - **Schema marks are filtered silently.** No warnings when decorators or annotations are dropped because they're not in the schema. Check your schema if formatting disappears. - **`createFlattenTableRule` is beta.** The API may change. For production table handling, consider a custom rule. - **Page builder HTML is difficult.** Content from WordPress page builders (Elementor, Divi) uses non-semantic markup that doesn't map cleanly to Portable Text. Manual cleanup or custom rules may be needed. ## Other conversion paths | Source format | Tool | | --------------- | ------------------------------------------------------------------------------------------------------------------------------ | | Markdown → PT | [`@portabletext/markdown`](/conversion/markdown-to-portable-text/) | | Gutenberg → PT | [`@emdash-cms/gutenberg-to-portable-text`](https://github.com/emdash-cms/emdash) (30+ block types) | | Contentful → PT | [`@portabletext/contentful-rich-text-to-portable-text`](https://github.com/portabletext/contentful-rich-text-to-portable-text) | | C# HTML → PT | [`portable-text-dotnet`](https://github.com/portabletext/dotnet-portable-text) | ## Further reading - [`@portabletext/html` source](https://github.com/portabletext/editor/tree/main/packages/html) - [`@portabletext/block-tools` source](https://github.com/portabletext/editor/tree/main/packages/block-tools) - [`@portabletext/schema` (defineSchema, compileSchema)](https://github.com/portabletext/editor/tree/main/packages/schema) # Markdown to Portable Text > Convert Markdown strings into Portable Text blocks using @portabletext/markdown. import {PackageManagers} from 'starlight-package-managers' :::note[Prerequisites] This guide covers `@portabletext/markdown` **v1.x** ([changelog](https://github.com/portabletext/editor/releases)). No framework dependencies. Works in Node.js, Deno, edge runtimes, and browsers. ::: Convert Markdown strings into Portable Text blocks. Use this for importing content from Markdown-based systems (static site generators, GitHub READMEs, AI-generated content), processing user input, or migrating from Markdown-first CMSes. Looking to render Portable Text as Markdown? See the [Markdown rendering guide](/rendering/markdown/). ## Install ## Basic usage `markdownToPortableText` takes a Markdown string and returns an array of Portable Text blocks. ```ts import {markdownToPortableText} from '@portabletext/markdown' const blocks = markdownToPortableText('# Hello **world**') ``` Standard Markdown elements (headings, paragraphs, bold, italic, links, lists, blockquotes, inline code) are handled automatically using the default schema. ## Schema configuration The conversion is schema-driven. The library only outputs types that exist in the schema, so the output always matches your content model. The default schema includes: | Type | Values | | --------------- | -------------------------------------------------------------- | | `styles` | `normal`, `h1`-`h6`, `blockquote` | | `lists` | `bullet`, `number` | | `decorators` | `strong`, `em`, `code`, `strike-through` | | `annotations` | `link` (fields: `href`, `title`) | | `blockObjects` | `code`, `image`, `horizontal-rule`, `html`, `table`, `callout` | | `inlineObjects` | `image` | To use a custom schema, import `compileSchema` and `defineSchema` from `@portabletext/schema`: ```ts import {compileSchema, defineSchema} from '@portabletext/schema' markdownToPortableText(markdown, { schema: compileSchema( defineSchema({ styles: [{name: 'normal'}, {name: 'heading 1'}], }), ), }) ``` If you are using a Sanity schema, use `@portabletext/sanity-bridge` to convert it first: ```ts import {sanitySchemaToPortableTextSchema} from '@portabletext/sanity-bridge' const schema = sanitySchemaToPortableTextSchema(sanityBlockArraySchema) markdownToPortableText(markdown, {schema}) ``` ## Matchers Matchers control how Markdown elements map to schema types. The library includes defaults for all standard elements. You can override individual matchers when your schema uses different type names. | Group | Matcher | Markdown | Maps to | | ---------- | ---------------- | --------------------- | ------------------- | | `block` | `normal` | Paragraphs | `'normal'` | | | `h1`-`h6` | `#`-`######` headings | `'h1'`-`'h6'` | | | `blockquote` | `>` blockquotes | `'blockquote'` | | `listItem` | `bullet` | `- ` or `* ` lists | `'bullet'` | | | `number` | `1. ` ordered lists | `'number'` | | `marks` | `strong` | `**bold**` | `'strong'` | | | `em` | `*italic*` | `'em'` | | | `code` | `` `inline code` `` | `'code'` | | | `strikeThrough` | `~~strikethrough~~` | `'strike-through'` | | | `link` | `[text](url "title")` | `'link'` | | `types` | `code` | Fenced code blocks | `'code'` | | | `horizontalRule` | `---` | `'horizontal-rule'` | | | `image` | `![alt](src)` | `'image'` | | | `html` | HTML blocks | `'html'` | | | `callout` | `> [!NOTE]`, etc. | `'callout'` | Override a matcher when your schema uses a different name for a type. For example, if your schema uses `'heading 1'` instead of `'h1'`: ```ts markdownToPortableText(markdown, { schema: compileSchema( defineSchema({ /* your schema */ }), ), block: { h1: ({context}) => { const style = context.schema.styles.find((s) => s.name === 'heading 1') return style?.name }, }, }) ``` Returning `undefined` from a matcher skips the element gracefully. This is useful when a type may or may not exist in the schema depending on the content model. ## Supported features | Feature | Markdown to PT | | ---------------- | :------------: | | Headings (h1-h6) | ✅ | | Paragraphs | ✅ | | Bold | ✅ | | Italic | ✅ | | Inline code | ✅ | | Strikethrough | ✅ | | Links | ✅ | | Blockquotes | ✅ | | Ordered lists | ✅ | | Unordered lists | ✅ | | Nested lists | ✅ | | Code blocks | ✅ | | Horizontal rules | ✅ | | Images | ✅ | | Tables | ✅\* | | HTML blocks | ✅ | | Callouts | ✅\* | \* Requires custom schema configuration (see above). :::note This package uses markdown-it as its Markdown parser. Remark and unified plugins are not compatible. ::: ## Other conversion paths | Source format | Tool | | --------------- | ------------------------------------------------------------------------------------------------------------------------------ | | HTML → PT | [`@portabletext/html`](/conversion/html-to-portable-text/) | | Gutenberg → PT | [`@emdash-cms/gutenberg-to-portable-text`](https://github.com/emdash-cms/emdash) (30+ block types) | | Contentful → PT | [`@portabletext/contentful-rich-text-to-portable-text`](https://github.com/portabletext/contentful-rich-text-to-portable-text) | ## Further reading - [Markdown rendering guide](/rendering/markdown/) for converting PT blocks to Markdown strings - [`@portabletext/markdown` on GitHub](https://github.com/portabletext/editor/tree/main/packages/markdown) for full API documentation and changelog # All packages > Every Portable Text package — serializers, converters, editor plugins, and utilities. import {Badge} from '@astrojs/starlight/components' The Portable Text ecosystem: official packages from the [`portabletext`](https://github.com/portabletext/) organization and community-maintained libraries. Packages marked with are maintained outside the official organization. ## Render Portable Text Serializers convert Portable Text JSON into your target output. ### JavaScript | Package | Target | Install | | ------------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------------------------------- | | [`@portabletext/react`](https://github.com/portabletext/react-portabletext/) | React | `npm i @portabletext/react` | | [`@portabletext/to-html`](https://github.com/portabletext/to-html/) | HTML string | `npm i @portabletext/to-html` | | [`@portabletext/vue`](https://github.com/portabletext/vue-portabletext/) | Vue | `npm i @portabletext/vue` | | [`@portabletext/svelte`](https://github.com/portabletext/svelte-portabletext/) | Svelte 5+ | `npm i @portabletext/svelte` | | [`@portabletext/solid`](https://github.com/portabletext/solid-portabletext/) | SolidJS | `npm i @portabletext/solid` | | [`@portabletext/react-native`](https://github.com/portabletext/react-native-portabletext/) | React Native | `npm i @portabletext/react-native` | | [`@portabletext/react-pdf`](https://github.com/portabletext/react-pdf-portabletext) | React PDF | `npm i @portabletext/react-pdf` | | [`astro-portabletext`](https://github.com/theisel/astro-portabletext) | Astro | `npm i astro-portabletext` | | [`portabletext-qwik`](https://github.com/tegonal/portabletext-qwik) | Qwik | `npm i portabletext-qwik` | | [`@limitless-angular/sanity`](https://github.com/limitless-angular/limitless-angular) | Angular | `npm i @limitless-angular/sanity` | ### Other languages | Package | Language | | --------------------------------------------------------------------------------------------------------------------------------- | -------------- | | [`dotnet-portable-text`](https://github.com/portabletext/dotnet-portable-text) | C# / .NET | | [`portable-text-dotnet`](https://github.com/nhi/portable-text-dotnet) | C# / .NET | | [`sanity-linq`](https://github.com/oslofjord/sanity-linq) | C# / .NET | | [`portabletext-html`](https://github.com/otovo/python-portabletext-html) | Python | | [`sanity-php`](https://github.com/sanity-io/sanity-php) | PHP | | [`flutter_sanity_portable_text`](https://pub.dev/packages/flutter_sanity_portable_text) | Dart / Flutter | | [`flutter_portabletext`](https://github.com/JobiJoba/flutter_portabletext) | Dart / Flutter | | [`ruby_portable_text`](https://github.com/beaucouplus/ruby_portable_text) | Ruby | | [`portabletext`](https://github.com/derickschaefer/portabletext) | Go | ### Platform integrations | Integration | Platform | | ------------------------------------------------------------------------------------ | ---------------- | | [`transform.PortableText`](https://gohugo.io/functions/transform/portabletext/) | Hugo (built-in) | | [`portable-text-to-liquid`](https://github.com/portabletext/portable-text-to-liquid) | Shopify / Liquid | ## Build an editor | Package | Purpose | Install | | ------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | ---------------------------------------- | | [`@portabletext/editor`](https://github.com/portabletext/editor/tree/main/packages/editor) | Headless, schema-driven block content editor for React | `npm i @portabletext/editor` | | [`@portabletext/toolbar`](https://github.com/portabletext/editor/tree/main/packages/toolbar) | Headless toolbar hooks for the editor | `npm i @portabletext/toolbar` | | [`@portabletext/schema`](https://github.com/portabletext/editor/tree/main/packages/schema) | Define and compile Portable Text schemas with full type safety | `npm i @portabletext/schema` | | [`@portabletext/keyboard-shortcuts`](https://github.com/portabletext/editor/tree/main/packages/keyboard-shortcuts) | Platform-aware keyboard shortcuts | `npm i @portabletext/keyboard-shortcuts` | ### Editor plugins | Plugin | What it does | Install | | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------- | | [`@portabletext/plugin-typography`](https://github.com/portabletext/editor/tree/main/packages/plugin-typography) | Smart quotes, em dashes, ellipses | `npm i @portabletext/plugin-typography` | | [`@portabletext/plugin-markdown-shortcuts`](https://github.com/portabletext/editor/tree/main/packages/plugin-markdown-shortcuts) | Markdown-style shortcuts in the editor | `npm i @portabletext/plugin-markdown-shortcuts` | | [`@portabletext/plugin-paste-link`](https://github.com/portabletext/editor/tree/main/packages/plugin-paste-link) | Paste URLs as links | `npm i @portabletext/plugin-paste-link` | | [`@portabletext/plugin-emoji-picker`](https://github.com/portabletext/editor/tree/main/packages/plugin-emoji-picker) | Emoji picker | `npm i @portabletext/plugin-emoji-picker` | | [`@portabletext/plugin-character-pair-decorator`](https://github.com/portabletext/editor/tree/main/packages/plugin-character-pair-decorator) | Match character pairs and decorate text between them | `npm i @portabletext/plugin-character-pair-decorator` | | [`@portabletext/plugin-input-rule`](https://github.com/portabletext/editor/tree/main/packages/plugin-input-rule) | Configure input rules | `npm i @portabletext/plugin-input-rule` | | [`@portabletext/plugin-one-line`](https://github.com/portabletext/editor/tree/main/packages/plugin-one-line) | Restrict editor to a single line | `npm i @portabletext/plugin-one-line` | | [`@portabletext/plugin-typeahead-picker`](https://github.com/portabletext/editor/tree/main/packages/plugin-typeahead-picker) | Typeahead/autocomplete picker infrastructure | `npm i @portabletext/plugin-typeahead-picker` | | [`@portabletext/plugin-sdk-value`](https://github.com/portabletext/editor/tree/main/packages/plugin-sdk-value) | Syncs editor value with the Sanity SDK | `npm i @portabletext/plugin-sdk-value` | ## Convert content | Package | Direction | Install | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------- | ----------------------------------------------------------- | | [`@portabletext/html`](https://github.com/portabletext/editor/tree/main/packages/html) | HTML → Portable Text | `npm i @portabletext/html` | | [`@portabletext/block-tools`](https://github.com/portabletext/editor/tree/main/packages/block-tools) | HTML → Portable Text (Sanity wrapper) | `npm i @portabletext/block-tools` | | [`@portabletext/markdown`](https://github.com/portabletext/editor/tree/main/packages/markdown) | Portable Text ↔ Markdown | `npm i @portabletext/markdown` | | [`@portabletext/contentful-rich-text-to-portable-text`](https://github.com/portabletext/contentful-rich-text-to-portable-text) | Contentful → Portable Text | `npm i @portabletext/contentful-rich-text-to-portable-text` | | [`@emdash-cms/gutenberg-to-portable-text`](https://github.com/emdash-cms/emdash/tree/main/packages/gutenberg-to-portable-text) | WordPress / Gutenberg → Portable Text | `npm i @emdash-cms/gutenberg-to-portable-text` | | [`editorjs-to-portabletext`](https://github.com/LiamMartens/editorjs-to-portabletext) | Editor.js → Portable Text | `npm i editorjs-to-portabletext` | | [`@aceccarello/portable-text-to-lexical`](https://github.com/ACeccarello/portable-text-to-lexical) | Portable Text → Payload Lexical JSON | `npm i @aceccarello/portable-text-to-lexical` | ## Work with Portable Text data | Package | Purpose | Install | | ----------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------- | | [`@portabletext/toolkit`](https://github.com/portabletext/toolkit) | `toPlainText()`, `buildMarksTree()`, `nestLists()`, type guards | `npm i @portabletext/toolkit` | | [`@portabletext/types`](https://github.com/portabletext/types) | TypeScript type definitions for Portable Text data | `npm i @portabletext/types` | | [`@portabletext/patches`](https://github.com/portabletext/editor/tree/main/packages/patches) | Apply patches to Portable Text values | `npm i @portabletext/patches` | | [`@portabletext-typed/types`](https://github.com/saiichihashimoto/sanity-typed/tree/main/packages/pt-types) | Typed generics for `@portabletext/types` | `npm i @portabletext-typed/types` | ## Testing | Package | Purpose | Install | | -------------------------------------------------------------------------------------- | ---------------------------------------------- | -------------------------- | | [`@portabletext/test`](https://github.com/portabletext/editor/tree/main/packages/test) | Testing utilities for the Portable Text Editor | `npm i @portabletext/test` | | [`racejar`](https://github.com/portabletext/editor/tree/main/packages/racejar) | Framework-agnostic Gherkin test driver | `npm i racejar` | ## Legacy packages These packages are deprecated. Use the current equivalents listed above. | Deprecated package | Replaced by | | -------------------------------------- | ----------------------------------------------------------------- | | `@sanity/block-content-to-react` | [`@portabletext/react`](/rendering/react/) | | `@sanity/block-content-to-html` | [`@portabletext/to-html`](/rendering/html/) | | `@sanity/block-content-to-hyperscript` | [`@portabletext/to-html`](/rendering/html/) | | `@sanity/block-content-to-markdown` | [`@portabletext/markdown`](/rendering/markdown/) | | `@sanity/block-tools` | [`@portabletext/block-tools`](/conversion/html-to-portable-text/) | | `@sanity/portable-text-editor` | [`@portabletext/editor`](/editor/getting-started/) | For more packages, see the [Portable Text organization on GitHub](https://github.com/portabletext/). # Portable Text Editor > A headless, schema-driven block content editor for React. Officially supported, fully customizable, and built around the Portable Text specification. The Portable Text Editor (`@portabletext/editor`) is the officially supported editor for working with [Portable Text](/specification/) content. It's a headless block content editor for React: you bring the UI, the editor handles the editing. You configure the editor by declaring a [schema](/editor/concepts/portabletext/) of styles, decorators, annotations, lists, block objects, and inline objects. Rendering is yours to control through render props on `PortableTextEditable`. Interactions are extensible through the [Behavior API](/editor/concepts/behavior/) and a growing set of [plugins](/editor/reference/plugins/). The companion [`@portabletext/toolbar`](/editor/reference/toolbar/) package gives you headless hooks for building toolbar UI. ### [Getting started](/editor/getting-started/) Install the editor, define a schema, and build your first toolbar. ### [Concepts](/editor/concepts/) How the schema, decorators, annotations, and behaviors fit together. ### [Guides](/editor/guides/) Practical walkthroughs: custom blocks and inline objects, custom rendering, building toolbars, writing behaviors, and testing. ### [Reference](/editor/reference/) API documentation for the editor, behaviors, plugins, selectors, toolbar, and keyboard shortcuts. # Concepts > Core concepts behind Portable Text and the Portable Text Editor. Learn about the building blocks of Portable Text and how the editor works. ### [Portable Text Editor](/editor/concepts/portabletext/) The parts that make up the Portable Text Editor: schema, styles, decorators, annotations, lists, block objects, inline objects, and how they connect. Includes a glossary of common terms. ### [Behaviors](/editor/concepts/behavior/) How behaviors work in the editor. Events, guards, and actions: the pattern that lets you customize how users interact with the editor. # Behaviors > How behaviors intercept and transform editor events to customize the Portable Text Editor's editing logic. import {CardGrid, LinkCard} from '@astrojs/starlight/components' Behaviors allow you to customize how users interact with the editor by hooking into events during the editing experience. All behaviors follow this process: 1. Listen for an **event**. 2. Use a **guard** to decide if they should run or not. 3. Trigger a set of **actions** to perform on the editor. This pattern is influenced by [Statecharts](https://statecharts.dev/). Behaviors are defined with the `defineBehavior` helper. Here's an example event from the [behavior guide](/editor/guides/create-behavior/): ```tsx defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [ ({snapshot, event}) => [ {type: 'execute', event: {type: 'insert.text', text: 'A'}}, ], ], }) ``` Revisiting the three step process above: - `on` listens for the event. - `guard` handles the conditional. - `actions` sends, or invokes, the desired actions. ## Events Whenever you enter text into the editor, activate a toolbar button, or anything else happens in the editor it sends an event. There are three categories of Behavior Events: - Native Events: Events that come from the browser or device directly. - Synthetic Events: Editor-specific events that directly modify the editor state. - Custom Events: Events that you create yourself. The Behavior API uses events to trigger actions by listening for a specific event. :::note All events except native events can be executed or raised from within behaviors, but only synthetic events are guaranteed to resolve into a state change. ::: ## Guards A guard is a condition that helps the behavior determine if it should perform the actions. The `guard` key expects a response or `false`. The example above shows a simple _truthy_ guard. Here it is again: ```js guard: ({snapshot, event}) => event.text === 'a' ``` Guards can also return parameters that you can access when firing an action. ```js guard: ({snapshot, event}) => { if (event.text === 'a') { return { secret: 'secret text' } } return false }, actions: [ ({snapshot, event}, {secret}) => [ /* ... */ ] ] ``` Passing parameters allows you to reuse conditional behavior, such as selecting part of a string, without rewriting the logic. Guards are optional. This means it's possible to create an unconditional behavior that always runs when an event occurs. The "soft return" behavior build into the PTE is one example: ```js const softReturn = defineBehavior({ on: 'insert.soft break', actions: [() => [execute({type: 'insert.text', text: '\n'})]], }) ``` This behavior listens for soft break events and uses the `insert.text` action to insert a `\n` instead to prevent splitting text blocks with `Shift+Enter`. These unconditional behaviors are rare and you'll mostly encounter conditional behaviors. ## Actions Actions make things happen in the editor. This is where you change the standard behavior by modifying actions before they occur or by circumventing them completely. You've seen actions in some of the guard examples above. The `actions` key expects an array of behavior action sets. As with guards, you have access to the `event` and `snapshot`. So far we've only seen examples that invoke a single action, but you can send multiple actions or sets of actions from a single event. ### Raise events within actions To send an event back into the editor, use the `raise` action type or the `raise` helper. This is useful for chaining behaviors and default events. ```tsx // Approach A: With the type set to 'raise' // Approach B: With the raise helper import {defineBehavior, raise} from '@portabletext/editor/behaviors' const raisedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [ ({snapshot, event}) => [ {type: 'raise', event: {type: 'insert.text', text: 'A'}}, ], ], }) const raisedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [({snapshot, event}) => [raise({type: 'insert.text', text: 'A'})]], }) ``` ## Selectors Selectors are pure functions that derive state from the editor snapshot (`snapshot` in the examples). A collection of selectors is included with the core library. ```tsx // import all selectors import * as selectors from '@portabletext/editor/selectors' // or individual ones import {getFocusSpan, getFocusTextBlock} from '@portabletext/editor/selectors' ``` The core selectors are useful helper functions for checking conditions of the editor, finding selected text, and more. You can manually do anything a selector can do by parsing the editor snapshot. ## Behavior examples Browse the existing behaviors, or check out the Behavior Recipes documentation for examples of real-world behaviors. # Schema and concepts > Learn more about the parts of Portable Text and the Editor, including common terms and concepts. import {CardGrid, LinkCard} from '@astrojs/starlight/components' The Portable Text Editor (PTE) is the officially supported editor for working with [Portable Text](https://github.com/portabletext/portabletext). Customize it to fit the needs of your authors and content team using a schema, ## Schema The PTE accepts a schema that describes the kinds of content it can implement. This is used throughout the editor to configure parts of the interface and aspects of the Portable Text output. {/* */} A schema consists of styles, decorators, annotations, block objects, inline objects, and lists. Here is an example schema from the [getting started guide](/editor/getting-started/): ```js { decorators: [{name: 'strong'}, {name: 'em'}, {name: 'underline'}], annotations: [{name: 'link', fields: [{name: 'href', type: 'string'}]}], styles: [ {name: 'normal'}, {name: 'h1'}, {name: 'h2'}, {name: 'h3'}, {name: 'blockquote'}, ], lists: [{name: 'bullet'}, {name: 'number'}], inlineObjects: [{name: 'stock-ticker'}], blockObjects: [{name: 'image'}], } ``` You'll likely see connections to the elements found in rich text editors, along with some extras. ### Styles Styles are a way to identify text blocks in Portable Text and signal to the editor how to classify the block. They don't add any other data to the text. Examples include: - Headings (H1, H2, H3, etc.) - Block quotes - Normal paragraphs Convention uses HTML naming for these styles, but it is not required and has no direct impact on the style of the content. ### Decorators Decorators are inline text with added meaning. Examples include: - Bold text - Emphasis or italicized text - Underlined text Decorators are a type of [Portable Text mark](https://github.com/portabletext/portabletext?tab=readme-ov-file#markdefs-array) that adds additional meaning to existing text. ### Annotations Like decorators, annotations are inline text with added meaning. Annotations are more complex than decorators, and often include additional functionality. Examples include: - Links - References Annotations are a type of [Portable Text mark](https://github.com/portabletext/portabletext?tab=readme-ov-file#markdefs-array) that adds additional meaning to existing text. ### Lists Lists are groups of text blocks. They take into account the position each block has in a group. Examples include: - Bullet lists - Numbered lists ### Block objects Block objects sit alongside text. For example, an image block may sit between a heading and a paragraph. They can hold any kind of data. Some examples include: - Images - Code blocks - Videos - Portable Text editors ### Inline objects Inline objects, much like block objects, can hold any kind of data. Instead of existing alongside text, inline objects exist inside text blocks. Examples include: - Footnote indicators - Non-text emoji substitutes Inline objects differ from annotations and decorators because they aren't wrapping existing text. Instead, they are self contained. ## Rendering content in and out of the editor The schema serves as a foundation for how the PTE renders the text. On it's own, passing just a schema to the editor only annotates or _describes_ text, but it won't change the way it renders. To affect the way text renders in the editor, pass custom renderers to the editor. To affect the way text renders once it's stored as Portable Text, use an existing serializer or write your own. ## The toolbar A schema describes the type of content an author can create in the editor, but without a toolbar there's no way to assign the schema to each piece of text. The toolbar allows you to define the graphical user interface for creating rich text in the PTE. The most common way to do this is by using the schema as a foundation. ## Behaviors Behaviors change how users interact with the editor. They handle everything from direct text input, to changing paste behavior, to allowing you to write custom shortcuts. The editor comes with many behaviors by default, but you can also create your own. ## Common terms The following terms come up when dealing with Portable Text and the editor. Some overlap with the schema above, while others are more general. - Block: Broken down into standard and custom blocks. Standard blocks are sections of text, like paragraphs and headings. Custom blocks are block objects like images, code blocks, embeds, etc. - Span: A standard way to express inline text within a block. - Children: An array of spans or other inline types contained within a block. - Marks: `markDefs` exist in the Portable Text format and connect spans with annotations and decorators. A span can have multiple marks associated with it. Such as text that is bold and italic. - List items: Blocks can be labeled as list items to resemble lists. - Selection: The range of text selected, or the cursor location in the editor at a given time. - Selection collapsed: A collapsed selection is one where nothing is selected and is instead a single point (cursor). - Anchor: Where the user began the selection. - Focus: Where the user ends the selection. - Offset: A distance, in characters, from the selection. - Path: A representation of the location of elements. Used in the behavior API to target or select specific blocks, spans, or text. - Node: A representation of an element. Contains details about it's type and content. # Getting started > Install the Portable Text Editor and build your first block content editing experience. import { CardGrid, LinkCard, Steps, TabItem, Tabs, } from '@astrojs/starlight/components' import {PackageManagers} from 'starlight-package-managers' This guide walks you through installing and configuring the Portable Text Editor. By the end, you'll have a working block content editor with custom styles, decorators, and a toolbar. :::tip[Just need to render Portable Text?] If you already have Portable Text content and want to display it, see [Render Portable Text](/rendering/) instead. ::: :::note[Prerequisites] This guide covers `@portabletext/editor` **v6.x**, `@portabletext/toolbar` **v7.x**, and `@portabletext/keyboard-shortcuts` **v2.x**. Requires React 18+. Check the [editor changelog](https://github.com/portabletext/editor/releases) for breaking changes. ::: You'll need to: - Create a schema that defines the rich text and block content elements. - Create a toolbar to toggle and insert these elements. - Write render functions to style and display each element type in the editor. - Render the editor. ## Parts of the editor Before starting, it helps to understand the components that make up the editor. - **Schema:** Describes the type of content the editor accepts. Think of this as the foundation for configuring the editor. - **`EditorProvider`:** Supplies the schema and initial state to the editor. - **`EventListenerPlugin`:** Listens to events emitted by the editor. Commonly used to update application state. - **Toolbars:** UI elements that interact with the editor. - **`PortableTextEditable`:** The core editor component. Handles text rendering and manages behavior. ## Add the library to your project Start by installing the editor: Next, import the components and types you'll need: ```tsx // App.tsx import { defineSchema, EditorProvider, PortableTextEditable, } from '@portabletext/editor' import type { PortableTextBlock, RenderDecoratorFunction, RenderStyleFunction, } from '@portabletext/editor' import {EventListenerPlugin} from '@portabletext/editor/plugins' ``` You won't need all of these right away, but you can add them now. ## Define your schema Before you can render the editor, you need a schema. The editor schema configures the types of content rendered by the editor. Start with a schema that includes some common rich text elements. :::note This guide includes a limited set of schema types to get you started. See the [rendering guide](/editor/guides/custom-rendering/) for additional examples. ::: ```tsx // App.tsx // ... const schemaDefinition = defineSchema({ // Decorators are simple marks that don't hold any data decorators: [{name: 'strong'}, {name: 'em'}, {name: 'underline'}], // Styles apply to entire text blocks // There's always a 'normal' style that can be considered the paragraph style styles: [ {name: 'normal'}, {name: 'h1'}, {name: 'h2'}, {name: 'h3'}, {name: 'blockquote'}, ], // The types below are left empty for this example. // See the rendering guide to learn more about each type. // Annotations are more complex marks that can hold data (for example, hyperlinks). annotations: [], // Lists apply to entire text blocks as well (for example, bullet, numbered). lists: [], // Inline objects hold arbitrary data that can be inserted into the text (for example, custom emoji). inlineObjects: [], // Block objects hold arbitrary data that live side-by-side with text blocks (for example, images, code blocks, and tables). blockObjects: [], }) ``` ## Render the editor With a schema defined, you have enough to render the editor. It won't do much yet, but you can confirm your progress. Add `react` and `useState`, then scaffold out a basic application component: ```tsx // app.tsx import { defineSchema, EditorProvider, PortableTextEditable, } from '@portabletext/editor' import type { PortableTextBlock, RenderDecoratorFunction, RenderStyleFunction, } from '@portabletext/editor' import {EventListenerPlugin} from '@portabletext/editor/plugins' import {useState} from 'react' const schemaDefinition = defineSchema({ /* your schema from the previous step */ }) function App() { // Set up the initial state getter and setter. Leave the starting value as undefined for now. const [value, setValue] = useState | undefined>( undefined, ) return ( <> { if (event.type === 'mutation') { setValue(event.value) } }} /> ) } export default App ``` Include the `App` component in your application and run it. You should see an outlined editor that accepts text, but doesn't do much else. ## Create render functions for schema elements At this point the PTE only has a schema, but it doesn't know how to render anything. Fix that by creating render functions for each property in the schema. Start by creating a render function for styles. ```tsx const renderStyle: RenderStyleFunction = (props) => { if (props.schemaType.value === 'h1') { return

      {props.children}

      } if (props.schemaType.value === 'h2') { return

      {props.children}

      } if (props.schemaType.value === 'h3') { return

      {props.children}

      } if (props.schemaType.value === 'blockquote') { return
      {props.children}
      } return <>{props.children} } ``` Render functions all follow the same format: - They take in props and return JSX elements. - They use the schema to make decisions. - They return JSX and pass `children` as a fallback. With this in mind, continue for the remaining schema types. Create a render function for decorators: ```tsx const renderDecorator: RenderDecoratorFunction = (props) => { if (props.value === 'strong') { return {props.children} } if (props.value === 'em') { return {props.children} } if (props.value === 'underline') { return {props.children} } return <>{props.children} } ``` :::note By default, text is rendered as an inline `span` element in the editor. While most render functions return a fragment (`<>`) as the fallback, make sure block-level elements return blocks, like `
      ` elements. ::: Update the `PortableTextEditable` with each corresponding function to attach them to the editor. You may notice that we skipped a few types from the schema. Declare these inline in the configuration: ```tsx
      {props.children}
      } renderListItem={(props) => <>{props.children}} /> ``` Before you can see if anything changed, you need a way to interact with the editor. ## Create a toolbar A toolbar is a collection of UI elements for interacting with the editor. The `@portabletext/toolbar` library exposes hooks and types that allow you to create a toolbar however you like. The `@portabletext/keyboard-shortcuts` library provides drop-in shortcut access to link toolbar buttons to key commands. Building a custom toolbar differs with each project, but in this example: 1. Add the `@portabletext/toolbar` and `@portabletext/keyboard-shortcuts` libraries to your project. 2. Create a `Toolbar` component, along with any sub-components in the same file. 3. Configure `useToolbarSchema` to access the editor schema, then loop over the schema types to create buttons for each style and decorator. 4. Enhance the schema with any icons, labels, or descriptions you want to display in the toolbar. 5. Create buttons for each schema group (styles, decorators, annotations, etc.). 6. Add the `Toolbar` to your render function inside the `EditorProvider`. This example shows a minimal toolbar: ```tsx // App.tsx // ... import {bold} from '@portabletext/keyboard-shortcuts' import { useDecoratorButton, useStyleSelector, useToolbarSchema, type ExtendDecoratorSchemaType, type ExtendStyleSchemaType, type ToolbarDecoratorSchemaType, type ToolbarStyleSchemaType, } from '@portabletext/toolbar' function Toolbar() { // useToolbarSchema provides access to the PTE schema // optionally, pass in updated schemas to override the default const toolbarSchema = useToolbarSchema({ extendDecorator, // see declarations below extendStyle, // see declarations below }) return (
      {toolbarSchema.decorators?.map((decorator) => ( ))} {toolbarSchema.styles?.map((style) => ( ))}
      ) } // Extend the schema with icons, titles, and keyboard shortcuts const extendStyle: ExtendStyleSchemaType = (style) => { // Apply updates to the schema, if needed if (style.name === 'h1') { return { ...style, title: 'Title', } } // ...repeat for each style type, or return the original style return style } const extendDecorator: ExtendDecoratorSchemaType = (decorator) => { if (decorator.name === 'strong') { return { ...decorator, // Optional: add a react component as an icon and unset the title icon: () => B, // Optional: connect to a keyboard shortcut from the keyboard-shortcuts library shortcut: bold, title: '', } } // ...repeat for each decorator type, or return the original decorator return decorator } // Create a button for each decorator type const DecoratorButton = (props: {schemaType: ToolbarDecoratorSchemaType}) => { const decoratorButton = useDecoratorButton(props) return ( ) } function StyleButton(props: {schemaType: ToolbarStyleSchemaType}) { const styleSelector = useStyleSelector({schemaTypes: [props.schemaType]}) return ( ) } // ... and so on for each schema type, or create a generic button ``` The `useStyleSelector` and `useDecoratorButton` hooks give you access to the active editor. `send` lets you send events to the editor, and `snapshot` lets you read the current state of the editor. In the next step, you'll add the toolbar to the editor. ## Bring it all together With render functions created and a toolbar in place, you can fully render the editor. Add the `Toolbar` inside the `EditorProvider`. ```tsx // App.tsx // ... function App() { const [value, setValue] = useState | undefined>( undefined, ) return ( <> { if (event.type === 'mutation') { setValue(event.value) } }} />
      {props.children}
      } renderListItem={(props) => <>{props.children}} />
      ) } // ... ``` You can now enter text and interact with the toolbar buttons to toggle the styles and decorators. These are only a small portion of the types of things you can do. Check out the [custom rendering guide](/editor/guides/custom-rendering/) and the [toolbar customization guide](/editor/guides/customize-toolbar/) for options. ## View the Portable Text data You can preview the Portable Text from the editor by reading the state. Add the following after the `EditorProvider`: ```tsx
        {JSON.stringify(value, null, 2)}
      
      ``` This displays the raw Portable Text. To customize how Portable Text renders in your apps, explore the serializers. ## Behavior API The Behavior API lets you customize how users interact with the editor by hooking into events: - Declaratively hook into editor **events** and define new behaviors. - Imperatively trigger **events**. - Derive editor **state** using **pure functions**. - Subscribe to **emitted** editor **events**. Learn more about [behaviors](/editor/concepts/behavior/) and how to [create your own](/editor/guides/create-behavior/). ## Next steps # Guides > Step-by-step guides for working with the Portable Text Editor. Practical guides for customizing and extending the Portable Text Editor. ### [Custom rendering](/editor/guides/custom-rendering/) Change how the editor renders and styles text. Covers render props for blocks, spans, decorators, annotations, list items, placeholders, and range decorations. ### [Customize the toolbar](/editor/guides/customize-toolbar/) Build custom toolbars using `@portabletext/toolbar` hooks. Covers decorator buttons, style selectors, keyboard shortcuts, undo/redo, and reflecting editor state. ### [Create a custom behavior](/editor/guides/create-behavior/) Add custom behaviors to the editor. Walk through defining a behavior with events, guards, and actions, then registering it with `BehaviorPlugin`. ### [Behavior recipes](/editor/guides/behavior-cheat-sheet/) Common solutions using the Behavior API: logging, auto-closing brackets, emoji pickers, and raising events. # Behavior recipes > Common solutions using the Behavior API. import {CardGrid, LinkCard} from '@astrojs/starlight/components' import {PackageManagers} from 'starlight-package-managers' Below are some common behavior examples. You can also find a list of core behaviors [on GitHub](https://github.com/portabletext/editor/tree/main/packages/editor/src/behaviors). :::note[Prerequisites] These recipes are written for `@portabletext/editor` **v6.x** ([changelog](https://github.com/portabletext/editor/releases)). ::: To add these to your editor, first import `defineBehavior` as well as the `BehaviorPlugin`. ```tsx import {defineBehavior} from '@portabletext/editor/behaviors' import {BehaviorPlugin} from '@portabletext/editor/plugins' ``` Then, register the behavior within the `EditorProvider` using the `BehaviorPlugin`. ```tsx {/* ... */} ``` Read more about using behaviors and building your own with these guides: ## Log inserted text Send and `effect` type action along with a `forward` action to perform side effects without altering the chain of events. ```js const logInsertText = defineBehavior({ on: 'insert.text', actions: [ ({event}) => [ { type: 'effect', effect: () => { console.log(event) }, }, { type: 'forward', event, }, ], ], }) ``` The `effect` and `forward` actions also have shorthand functions: ```tsx const logInsertText = defineBehavior({ on: 'insert.text', actions: [ ({event}) => [ effect(() => { console.log(event) }), forward(event), ], ], }) ``` ## Auto-close parenthesis You can write behaviors to auto-close common paired characters. This example closes parenthesis, and then moves the cursor in between the two characters. This logic can expand to cover more sets. ```js const autoCloseParens = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => { return event.text === '(' }, actions: [ ({snapshot, event}) => [ // Execute the original event that includes the '(' {type: 'execute', event}, // Execute a new insert.text event with a closing parenthesis { type: 'execute', event: { type: 'insert.text', text: ')', }, }, // Execute a select event to move the cursor in between the parens { type: 'execute', event: { type: 'select', selection: { anchor: { path: snapshot.context.selection.anchor.path, offset: snapshot.context.selection.anchor.offset + 1, }, focus: { path: snapshot.context.selection.focus.path, offset: snapshot.context.selection.focus.offset + 1, }, }, }, }, ], ], }) ``` The `execute` action also has a shorthand function: ```tsx const autoCloseParens = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => { return event.text === '(' }, actions: [ ({snapshot, event}) => [ execute(event), // ... ], ], }) ``` ## Emoji picker An emoji picker that triggers when you insert `:` is available as a separate plugin package. ![Emoji picker in Portable Text Editor](../../../../assets/emoji-picker.png) Test it out in the [Playground](https://playground.portabletext.org). Install the package: Use the `useEmojiPicker` hook to handle the state and logic: ```tsx import { createMatchEmojis, useEmojiPicker, } from '@portabletext/plugin-emoji-picker' const matchEmojis = createMatchEmojis({ emojis: { '😂': ['joy', 'laugh'], '😹': ['joy_cat'], }, }) function EmojiPickerComponent() { const {keyword, matches, selectedIndex, onDismiss, onNavigateTo, onSelect} = useEmojiPicker({matchEmojis}) // Render your emoji picker UI using these values } ``` - [View the plugin source](https://github.com/portabletext/editor/tree/main/packages/plugin-emoji-picker). - [View the playground editor source](https://github.com/portabletext/editor/blob/main/apps/playground/src/editor.tsx). ## Raise events Sometimes you want to trigger an event from within an action. This sends the event back to the editor, where the editor treats it like any other event. ```tsx const raisedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [ ({snapshot, event}) => [ {type: 'raise', event: {type: 'insert.text', text: 'A'}}, ], ], }) ``` The `raise` action also has a shorthand function: ```tsx const raisedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [({snapshot, event}) => [raise({type: 'insert.text', text: 'A'})]], }) ``` :::note Be careful when raising events, as this technique can lead to infinite loops if behaviors dispatch actions and events that trigger one another. ::: # Create a custom behavior > Add custom behaviors to the Portable Text Editor import {LinkCard} from '@astrojs/starlight/components' Behaviors add functionality to the Portable Text Editor (PTE) in a declarative way. :::note[Prerequisites] This guide covers `@portabletext/editor` **v6.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. ::: For a deep dive into behaviors and how they work, check out [Behaviors](/editor/concepts/behavior/). ## Import the behavior helper Begin by importing `defineBehavior`. ```tsx import {defineBehavior} from '@portabletext/editor/behaviors' ``` ## Define the behavior Behaviors need three things: - A triggering event. See the [full list of events](/editor/reference/behavior-api#behavior-event-types). - A guard, or condition that determines if this behavior should apply. - An action to invoke if the event and guard are met. Here's an example behavior: ```tsx const noLowerCaseA = defineBehavior({ on: 'insert.text', guard: ({event}) => event.text === 'a', actions: [() => [{type: 'execute', event: {type: 'insert.text', text: 'A'}}]], }) ``` Let's break it down: 1. It listens for the `insert.text` event. You can use any [native, synthetic or custom event](/editor/reference/behavior-api#behavior-event-types) here. 2. The guard checks if the text that triggered this event is equal to a lowercase `a`. The guard is true and the behavior will perform the actions. 3. It sends an `execute` action with an `insert.text` event to insert "A" instead of "a". ## Register the behavior In order to use the behavior, add it to the `EditorProvider` using the `BehaviorPlugin`. ```tsx import {defineBehavior} from '@portabletext/editor/behaviors' import {BehaviorPlugin} from '@portabletext/editor/plugins' const noLowerCaseA = defineBehavior({ on: 'insert.text', guard: ({event}) => event.text === 'a', actions: [() => [{type: 'execute', event: {type: 'insert.text', text: 'A'}}]], }) // ... {/* ... */} ``` # Custom blocks and inline objects > Add images, code blocks, and other structured content to the Portable Text Editor. import {LinkCard} from '@astrojs/starlight/components' The Portable Text Editor handles text blocks (paragraphs, headings, lists) by default. To add structured content like images, code blocks, or calls to action, you define custom block types in your schema and tell the editor how to render and insert them. :::note[Prerequisites] This guide covers `@portabletext/editor` **v6.x** and `@portabletext/toolbar` **v7.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. You should be familiar with the [getting started guide](/editor/getting-started/) and [custom rendering](/editor/guides/custom-rendering/). ::: ## How custom blocks work There are two kinds of custom content in Portable Text: - **Block objects** sit alongside text blocks in the document array. An image, a code block, or a call-to-action are block objects. - **Inline objects** sit inside text blocks, within the text flow. A stock ticker, a product reference, or a custom emoji are inline objects. Both follow the same three-step pattern: 1. **Define** the type in your schema (`blockObjects` or `inlineObjects`) 2. **Render** it in the editor (`renderBlock` or `renderChild` prop) 3. **Insert** it via the toolbar (`useBlockObjectButton` or `useInlineObjectButton` hook) ## Adding block objects ### Step 1: define in schema Add your block type to the `blockObjects` array in `defineSchema`. Each block object has a name and optional fields: ```tsx import {defineSchema} from '@portabletext/editor' const schemaDefinition = defineSchema({ // ... styles, decorators, annotations, lists blockObjects: [{name: 'image'}, {name: 'code'}], inlineObjects: [], }) ``` The schema tells the editor which block types are valid. The field data (image URL, code language, etc.) is stored on the block object itself. ### Step 2: render in the editor Use the `renderBlock` prop on `PortableTextEditable` to control how block objects appear in the editor. The function receives `props` with `schemaType.name` (the block type) and `value` (the block data): ```tsx import type {PortableTextBlock, RenderBlockFunction} from '@portabletext/editor' const renderBlock: RenderBlockFunction = (props) => { // Image block if (props.schemaType.name === 'image' && isImage(props.value)) { return (
      {props.value.alt {props.value.caption && (

      {props.value.caption}

      )}
      ) } // Code block if (props.schemaType.name === 'code' && isCodeBlock(props.value)) { return (
              {props.value.text}
            
      ) } // Default: render text blocks with children return
      {props.children}
      } // Type guards for block data function isImage( value: PortableTextBlock, ): value is PortableTextBlock & {src: string; alt?: string; caption?: string} { return 'src' in value } function isCodeBlock( value: PortableTextBlock, ): value is PortableTextBlock & {text: string; language?: string} { return 'text' in value } ``` Pass the function to `PortableTextEditable`: ```tsx ``` :::note The `renderBlock` function handles both text blocks and custom block objects. Text blocks have `children` (the text content). Custom block objects have `value` (the structured data). Always include a default case that renders `{props.children}` for text blocks. ::: ### Step 3: insert via toolbar Use the `useBlockObjectButton` hook from `@portabletext/toolbar` to create an insert button. The hook follows the same pattern as `useDecoratorButton` and `useStyleSelector`: ```tsx import { useBlockObjectButton, useToolbarSchema, type ExtendBlockObjectSchemaType, type ToolbarBlockObjectSchemaType, } from '@portabletext/toolbar' // Extend the schema to add icons and titles for the toolbar const extendBlockObject: ExtendBlockObjectSchemaType = (blockObject) => { if (blockObject.name === 'image') { return {...blockObject, title: 'Image', icon: () => 🖼} } if (blockObject.name === 'code') { return {...blockObject, title: 'Code', icon: () => {''}} } return blockObject } function Toolbar() { const toolbarSchema = useToolbarSchema({extendBlockObject}) return (
      {/* ... decorator and style buttons */} {toolbarSchema.blockObjects?.map((blockObject) => ( ))}
      ) } function BlockObjectButton(props: {schemaType: ToolbarBlockObjectSchemaType}) { const blockObjectButton = useBlockObjectButton(props) return ( ) } ``` When the user clicks the button, the editor opens a dialog for that block type. You control what happens in the dialog: collect the data, then insert the block. For blocks that need user input (like images), use the dialog flow: ```tsx // In the dialog's submit handler: blockObjectButton.send({ type: 'insert', value: {src: imageUrl, alt: altText}, placement: undefined, }) ``` For blocks that don't need initial data, insert directly: ```tsx onClick={() => blockObjectButton.send({ type: 'insert', value: {}, placement: undefined, })} ``` ## Adding inline objects Inline objects work the same way as block objects, but they appear inside text blocks rather than alongside them. ### Step 1: define in schema ```tsx const schemaDefinition = defineSchema({ // ... styles, decorators, annotations, lists, blockObjects inlineObjects: [{name: 'stock-ticker'}], }) ``` ### Step 2: render in the editor Use the `renderChild` prop. Inline objects receive `props.value` with the object data: ```tsx import type {PortableTextChild, RenderChildFunction} from '@portabletext/editor' const renderChild: RenderChildFunction = (props) => { if (props.schemaType.name === 'stock-ticker' && isStockTicker(props.value)) { return ( 📈 {props.value.symbol} {props.value.exchange && ( {props.value.exchange} )} ) } // Default: render inline children return <>{props.children} } function isStockTicker( value: PortableTextChild, ): value is PortableTextChild & {symbol: string; exchange?: string} { return 'symbol' in value } ``` ### Step 3: insert via toolbar Use `useInlineObjectButton`, which works identically to `useBlockObjectButton`: ```tsx import { useInlineObjectButton, type ExtendInlineObjectSchemaType, type ToolbarInlineObjectSchemaType, } from '@portabletext/toolbar' const extendInlineObject: ExtendInlineObjectSchemaType = (inlineObject) => { if (inlineObject.name === 'stock-ticker') { return {...inlineObject, title: 'Stock', icon: () => 📈} } return inlineObject } function InlineObjectButton(props: { schemaType: ToolbarInlineObjectSchemaType }) { const inlineObjectButton = useInlineObjectButton(props) return ( ) } ``` Add the inline object buttons to your toolbar alongside the block object buttons: ```tsx function Toolbar() { const toolbarSchema = useToolbarSchema({ extendBlockObject, extendInlineObject, }) return (
      {/* ... decorator and style buttons */} {toolbarSchema.blockObjects?.map((blockObject) => ( ))} {toolbarSchema.inlineObjects?.map((inlineObject) => ( ))}
      ) } ``` ## The Portable Text output When a user inserts a block object, the editor produces a block in the Portable Text array with the custom `_type`: ```json [ { "_type": "block", "_key": "abc123", "style": "normal", "children": [{"_type": "span", "text": "Here is a photo:"}], "markDefs": [] }, { "_type": "image", "_key": "def456", "src": "https://example.com/photo.jpg", "alt": "A mountain landscape" }, { "_type": "block", "_key": "ghi789", "style": "normal", "children": [ {"_type": "span", "text": "The current price of "}, { "_type": "stock-ticker", "_key": "jkl012", "symbol": "AAPL", "exchange": "NASDAQ" }, {"_type": "span", "text": " is rising."} ], "markDefs": [] } ] ``` The image block sits between text blocks. The stock ticker sits inside a text block's `children` array. Both carry structured data that your rendering serializers can use. ## Next steps # Customize editor rendering > Change the way the editor renders and styles text. The Portable Text Editor gives you control of how it renders each schema type element. You need to explicitly tell it what. These choices have no impact on the Portable Text output—they only affect how the editor itself renders content. :::note[Prerequisites] This guide covers `@portabletext/editor` **v6.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires React 18+. ::: The following props can be passed to the `PortableTextEditable` component: - `renderAnnotation`: For annotations (e.g., hyperlinks). - `renderBlock`: For block objects (e.g., images, embeds). - `renderChild`: For inline objects (e.g., custom emoji, stock symbols). - `renderDecorator`: For decorators (e.g., strong, italic, emphasis text). - `renderStyle`: For core text block types (e.g., normal, h1, h2, h3, blockquote). - `renderListItem`: For list item styling (e.g., bullet, numbered lists). - `renderPlaceholder`: For custom placeholder text when the editor is empty. - `rangeDecorations`: For highlighting specific ranges of text (e.g., search results, comments). All the different render functions passed to `PortableTextEditable` can be defined as stand-alone React components. Most follow the same pattern of reading `props` and conditionally rendering elements based on schema data. Lists are a bit unique. Portable Text has no concept of block nesting, so the solution is to use pure CSS to style them. We suggest [including this example CSS](https://github.com/portabletext/editor/blob/main/examples/basic/src/editor.css) or similar to manage list rendering. Here are basic implementations of some core types: ```tsx const renderDecorator: RenderDecoratorFunction = (props) => { if (props.value === 'strong') { return {props.children} } if (props.value === 'em') { return {props.children} } if (props.value === 'underline') { return {props.children} } return <>{props.children} } // Annotations const renderAnnotation: RenderAnnotationFunction = (props) => { if (props.schemaType.name === 'link') { return {props.children} } return <>{props.children} } // Block objects const renderBlock: RenderBlockFunction = (props) => { if (props.schemaType.name === 'image' && isImage(props.value)) { return (
      IMG: {props.value.src}
      ) } return
      {props.children}
      } // Check the shape of an image and confirm it has a src. function isImage( props: PortableTextBlock, ): props is PortableTextBlock & {src: string} { return 'src' in props } // Styles const renderStyle: RenderStyleFunction = (props) => { if (props.schemaType.value === 'h1') { return

      {props.children}

      } if (props.schemaType.value === 'h2') { return

      {props.children}

      } if (props.schemaType.value === 'h3') { return

      {props.children}

      } if (props.schemaType.value === 'blockquote') { return
      {props.children}
      } return <>{props.children} } // Inline objects const renderChild: RenderChildFunction = (props) => { if (props.schemaType.name === 'stock-ticker' && isStockTicker(props.value)) { return ( {props.value.symbol} ) } return <>{props.children} } // Check the shape of the object by confirming it has a symbol. function isStockTicker( props: PortableTextChild, ): props is PortableTextChild & {symbol: string} { return 'symbol' in props } // List items const renderListItem: RenderListItemFunction = (props) => { return <>{props.children} } ``` :::note List items in Portable Text don't nest like HTML lists. The `renderListItem` function wraps the content, but visual nesting is achieved through CSS. See the [example CSS](https://github.com/portabletext/editor/blob/main/examples/basic/src/editor.css) for list styling patterns. ::: ## Placeholder text Use `renderPlaceholder` to display custom placeholder text when the editor is empty: ```tsx Start typing...} // ... other props /> ``` ## Range decorations Use `rangeDecorations` to highlight specific ranges of text. This is useful for features like search highlighting, comments, or collaborative cursors: ```tsx import type {RangeDecoration} from '@portabletext/editor' const decorations: RangeDecoration[] = [ { selection: { anchor: {path: [{_key: 'block1'}, 'children', {_key: 'span1'}], offset: 0}, focus: {path: [{_key: 'block1'}, 'children', {_key: 'span1'}], offset: 5}, }, component: ({children}) => ( {children} ), }, ] ``` You can apply styles, libraries like Tailwind, or use custom react components within the rendering functions. # Customize the toolbar > Common patterns and techniques for creating custom toolbars for the editor. import {PackageManagers} from 'starlight-package-managers' The [getting started guide](/editor/getting-started/) introduces the basics of setting up toolbar components. This guide provides some extra context, best practices, and patterns to get you started. :::note[Prerequisites] This guide covers `@portabletext/toolbar` **v7.x** and `@portabletext/keyboard-shortcuts` **v2.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires `@portabletext/editor` v6.x and React 18+. ::: ## Render the toolbar inside the provider You must render any toolbars within `EditorProvider`, as any toolbar actions require access to the instance of the editor. There are two ways to do this: ### The `@portabletext/toolbar` hooks The `@portabletext/toolbar` library provides a variety of hooks that allow you to dispatch events and view a snapshot of the editor state. Each hook accepts an individual schema item and returns a `send` method and a `snapshot`. The most common pattern is to `send`, or dispatch, events, and `snapshot.matches` state, like enabled/disabled. For example, a button can use `useDecoratorButton` to create interactive buttons for decorators. The hook accepts details about the decorator, provided by the `useToolbarSchema` hook. ```tsx import { useDecoratorButton, useToolbarSchema, type ToolbarDecoratorSchemaType, } from '@portabletext/toolbar' const DecoratorButton = (props: {schemaType: ToolbarDecoratorSchemaType}) => { const decoratorButton = useDecoratorButton(props) return ( ) } function ToolbarPlugin() { const toolbarSchema = useToolbarSchema() return (
      {toolBarSchema.decorators?.map((decorator) => ( ))}
      ) } function App() { //... return ( <> // ... ) } ``` ### The `useEditor` hook The toolbar library is more closely linked to parts of the PTE—like decorators, styles, and annotations—but you can also access editor state without the context of individual schema items with `useEditor`. You can send [synthetic events](/editor/reference/behavior-api/) from within the toolbar using `editor.send`. ```tsx import {useEditor} from '@portabletext/editor' function Toolbar() { const editor = useEditor() // ... return ( <> ) } function App() { //... return ( <> // ... ) } ``` ## Modify or enhance the schema Sometimes you need to enhance your schema to change a label or add a shortcut behavior, as shown in the [getting started guide](/editor/getting-started/). A common approach is to extend the schema. This is done in two steps: ### Extend the schema ```tsx const extendDecorator: ExtendDecoratorSchemaType = (decorator) => { if (decorator.name === 'strong') { return { ...decorator, // Optional: add a react component as an icon and unset the title icon: () => B, // Optional: connect to a keyboard shortcut from the keyboard-shortcuts library shortcut: bold, // imported from @portabletext/keyboard-shortcuts title: '', } } // ...repeat for each decorator type, or return the original decorator return decorator } ``` Repeat this same approach for styles, annotations, blocks, etc. as needed using the types from `@portabletext/toolbar`. Types are available for each schema type: - [ExtendAnnotationSchemaType](/api/toolbar/type-aliases/extendannotationschematype/) - [ExtendBlockObjectSchemaType](/api/toolbar/type-aliases/extendblockobjectschematype/) - [ExtendDecoratorSchemaType](/api/toolbar/type-aliases/extenddecoratorschematype/) - [ExtendInlineObjectSchemaType](/api/toolbar/type-aliases/extendinlineobjectschematype/) - [ExtendListSchemaType](/api/toolbar/type-aliases/extendlistschematype/) - [ExtendStyleSchemaType](/api/toolbar/type-aliases/extendstyleschematype/) ### Configure `useToolbarSchema` with the extended schema The `useToolbarSchema` hook optionally accepts these extended schemas. ```tsx const toolbarSchema = useToolbarSchema({ extendDecorator, extendAnnotation, extendStyle, // etc }) ``` ## Add keyboard shortcuts The [Keyboard Shortcuts library](/editor/reference/keyboard-shortcuts/) offers drop-in keyboard shortcuts for the editor that can be paired with toolbar buttons, as well as a way to create your own custom keyboard shortcuts. This pairs best with the extend schema approach, as it allows you to add keyboard shortcuts to your toolbar buttons. Add the keyboard shortcut library to your project: Import the keyboard shortcut you want to use from the library, and extend your schema. ```tsx import {bold} from '@portabletext/keyboard-shortcuts' const extendDecorator: ExtendDecoratorSchemaType = (decorator) => { if (decorator.name === 'strong') { return { ...decorator, // Optional: add a react component as an icon and unset the title icon: () => B, // Optional: connect to a keyboard shortcut from the keyboard-shortcuts library shortcut: bold, // imported from @portabletext/keyboard-shortcuts title: '', } } // ...repeat for each decorator type, or return the original decorator return decorator } ``` ## Add history buttons for undo/redo The toolbar library ships with a `useHistoryButtons` hook. This is a convenience hook that limits which events the buttons can send. Import the hook and create buttons. ```tsx import {useHistoryButtons} from '@portabletext/toolbar' function HistoryButtons() { const historyButtons = useHistoryButtons() return ( <> ) } ``` Then, render the buttons in a toolbar component. ```tsx function ToolbarPlugin() { // ... return ( <> // ... ) } ``` ## Additional toolbar hooks The `@portabletext/toolbar` package provides several additional hooks for building toolbar components: **Button hooks:** - `useDecoratorButton` - Create buttons for toggling decorators (bold, italic, etc.) - `useStyleSelector` - Create style selectors/dropdowns - `useListButton` - Create buttons for list formatting - `useAnnotationButton` - Create buttons for annotations (links, etc.) - `useBlockObjectButton` - Create buttons for inserting block objects - `useInlineObjectButton` - Create buttons for inserting inline objects **Popover hooks:** - `useAnnotationPopover` - Manage popovers for annotation editing (e.g., link URL input) - `useBlockObjectPopover` - Manage popovers for block object editing - `useInlineObjectPopover` - Manage popovers for inline object editing Each hook returns a `send` method for dispatching events and a `snapshot` for reading state. ## Use selectors to reflect editor state The editor offers a variety of helpful selectors for checking the status of inline and block content. Selectors are pure functions that derive state from the editor snapshot. You can find the full list in the [selectors reference](/editor/reference/selectors/). :::note This approach is specific to accessing state with `useEditor`. While the hooks from `@portabletext/toolbar` do access and interact with the editor state, they do not expose the active editor itself for use in selectors. ::: A few useful selectors for using in the toolbar are: - `getActiveStyle`: Get's the active style of the selection. - `isActiveDecorator`: Returns `true` if the active selection matches the decorator. - `isActiveAnnotation`: Returns `true` if the active selection matches the annotation. - `isActiveStyle`: Returns `true` if the active selection matches the style. You can import each selector individually from `@portabletext/editor/selectors` or import them all as shown below. ```tsx import * as selectors from '@portabletext/editor/selectors' ``` You can then combine these with the `useEditorSelector` hook in your toolbar components. For example, this button will underline if the selected text matches the annotation. ```tsx function AnnotationButton(props: { annotation: SchemaDefinition['annotations'][number] }) { const editor = useEditor() // useEditorSelector takes the editor instance and the selector const active = useEditorSelector( editor, selectors.isActiveAnnotation(props.annotation.name), ) return ( ) } ``` # Testing behaviors > Test custom behaviors with Gherkin specs or direct Vitest tests. import {LinkCard, TabItem, Tabs} from '@astrojs/starlight/components' import {PackageManagers} from 'starlight-package-managers' The Portable Text Editor ships with testing infrastructure you can use for your own behaviors. There are two approaches: Gherkin specs with Racejar (the same approach every official plugin uses) and direct Vitest tests for simpler cases. Both approaches use Vitest Browser Mode with Playwright, running tests against a real browser. :::note[Prerequisites] This guide covers `@portabletext/editor` **v6.x** and `racejar` **v2.x** ([changelog](https://github.com/portabletext/editor/releases)). Requires Vitest with Browser Mode and Playwright. ::: ## Setup Install the testing dependencies: Configure Vitest for Browser Mode in your `vitest.config.ts`: ```ts import {defineConfig} from 'vitest/config' export default defineConfig({ test: { browser: { enabled: true, provider: 'playwright', instances: [{browser: 'chromium'}], }, }, }) ``` If you use TypeScript, declare `.feature` file imports: ```ts // global.d.ts declare module '*.feature?raw' { const content: string export default content } ``` ## Quick start: direct Vitest tests For simple behavior tests, use `createTestEditor` directly. This is the fastest way to verify a behavior works: ```tsx import {defineSchema} from '@portabletext/editor' import {defineBehavior, execute} from '@portabletext/editor/behaviors' import {BehaviorPlugin} from '@portabletext/editor/plugins' import {createTestEditor} from '@portabletext/editor/test/vitest' import {getTersePt} from '@portabletext/test' import {describe, expect, test, vi} from 'vitest' import {userEvent} from 'vitest/browser' describe('uppercase A behavior', () => { test('replaces lowercase a with uppercase A', async () => { const {editor, locator} = await createTestEditor({ children: ( event.text === 'a', actions: [() => [execute({type: 'insert.text', text: 'A'})]], }), ]} /> ), schemaDefinition: defineSchema({ decorators: [{name: 'strong'}], }), }) await userEvent.click(locator) await userEvent.type(locator, 'a') await vi.waitFor(() => { expect(getTersePt(editor.getSnapshot().context)).toEqual(['A']) }) }) }) ``` **Key parts:** - **`createTestEditor`** spins up a real editor in the browser. Returns `{editor, locator}` where `editor` is the editor instance and `locator` is a Playwright locator for user interactions. - **`getTersePt`** from `@portabletext/test` gives you a compact representation of the editor content for assertions. `['foo ,bar, baz']` means one block with three spans (commas separate spans). - **`editor.getSnapshot().context`** gives you the full editor state including `value` (the Portable Text array) and `selection`. - **`vi.waitFor`** is needed because editor updates are asynchronous. ## Gherkin approach with Racejar For comprehensive behavior specs, write Gherkin feature files and run them with Racejar. This is how every official plugin is tested. ### Step 1: write a feature file Create a `.feature` file that describes your behavior in plain English: ```gherkin # my-behavior.feature Feature: Auto-capitalize after period Scenario: Capitalizes first letter after period and space Given the text "hello." When the editor is focused And the caret is put after "hello." And " " is typed And "w" is typed Then the text is "hello. W" Scenario: Does not capitalize mid-sentence Given the text "hello" When the editor is focused And the caret is put after "hello" And " w" is typed Then the text is "hello w" Scenario: Undo restores original text Given the text "hello." When the editor is focused And the caret is put after "hello." And " " is typed And "w" is typed Then the text is "hello. W" When undo is performed Then the text is "hello. w" ``` Gherkin scenarios follow the Given/When/Then pattern: - **Given** sets up the editor state (text content, marks, selections) - **When** performs actions (typing, key presses, toolbar interactions) - **Then** asserts the result (text content, marks, selections) ### Step 2: wire up the test Create a test file that connects your feature file to the editor: ```tsx // my-behavior.test.tsx import {defineSchema} from '@portabletext/editor' import {parameterTypes} from '@portabletext/editor/test' import { createTestEditor, stepDefinitions, type Context, } from '@portabletext/editor/test/vitest' import {Before} from 'racejar' import {Feature} from 'racejar/vitest' import {MyBehaviorPlugin} from './my-behavior-plugin' import myFeature from './my-behavior.feature?raw' Feature({ hooks: [ Before(async (context: Context) => { const {editor, locator} = await createTestEditor({ children: , schemaDefinition: defineSchema({ decorators: [{name: 'strong'}, {name: 'em'}], annotations: [{name: 'link'}], }), }) context.locator = locator context.editor = editor }), ], featureText: myFeature, stepDefinitions, parameterTypes, }) ``` That's it. Racejar compiles the `.feature` file into test cases, and the pre-built step definitions handle the Given/When/Then steps. ### Step 3: run the tests ```bash npx vitest ``` Each Gherkin scenario becomes a separate test case. Failures show which step failed and what the editor state was at that point. ## Pre-built step definitions The editor ships with step definitions that cover the most common test scenarios. You get these for free when you import from `@portabletext/editor/test/vitest`: **Given steps (setup):** - `Given the text "..."` sets the editor content - `Given "strong" around "..."` applies a decorator to text - `Given a "link" "l1" around "..."` applies an annotation - `Given a global keymap` sets up keyboard shortcut handling **When steps (actions):** - `When the editor is focused` focuses the editor - `When "..." is typed` types text character by character - `When "..." is inserted` inserts text all at once (mimics Android input) - `When "{Enter}" is pressed` presses a key - `When "{Backspace}" is pressed` presses backspace - `When "..." is selected` selects text in the editor - `When the caret is put after "..."` positions the cursor - `When undo is performed` triggers undo - `When redo is performed` triggers redo - `When "link" is toggled` toggles an annotation - `When "strong" is toggled` toggles a decorator **Then steps (assertions):** - `Then the text is "..."` asserts the editor content - `Then "..." has marks "..."` asserts marks on text - `Then "..." has no marks` asserts no marks on text - `Then "..." is selected` asserts the current selection These steps handle text blocks, marks, selections, undo/redo, and keyboard interactions. For custom block types or inline objects, you can add your own step definitions alongside the pre-built ones. ## Real-world example: em dash input rule Here's how the official typography plugin tests its em dash behavior (converts `--` to `—`): **Feature file** (`input-rule.em-dash.feature`): ```gherkin Feature: Em Dash Input Rule Background: Given a global keymap Scenario: Inserting em dash in unformatted text Given the text "-" When "-" is inserted Then the text is "—" When undo is performed Then the text is "--" Scenario: Inserting em dash inside a decorator Given the text "foo-" And "strong" around "foo-" When the editor is focused And the caret is put after "foo-" And "-" is typed Then the text is "foo—" And "foo—" has marks "strong" ``` **Test file** (`input-rule.em-dash.test.tsx`): ```tsx import {defineSchema} from '@portabletext/editor' import {parameterTypes} from '@portabletext/editor/test' import { createTestEditor, stepDefinitions, type Context, } from '@portabletext/editor/test/vitest' import {Before} from 'racejar' import {Feature} from 'racejar/vitest' import emDashFeature from './input-rule.em-dash.feature?raw' import {TypographyPlugin} from './plugin.typography' Feature({ hooks: [ Before(async (context: Context) => { const {editor, locator} = await createTestEditor({ children: , schemaDefinition: defineSchema({ decorators: [{name: 'strong'}], annotations: [{name: 'link'}], }), }) context.locator = locator context.editor = editor }), ], featureText: emDashFeature, stepDefinitions, parameterTypes, }) ``` The test file is 20 lines. The feature file describes the behavior in plain English. The pre-built step definitions do the heavy lifting. ## Adding custom step definitions If your behavior involves custom block types or domain-specific assertions, add your own step definitions alongside the pre-built ones: ```tsx import {stepDefinitions as builtInSteps} from '@portabletext/editor/test/vitest' import {Given, Then, When} from 'racejar' const customSteps = [ Given('an image block with src {string}', async (context, src) => { // Set up editor with an image block }), Then('the image src is {string}', async (context, expectedSrc) => { // Assert image block data }), ] Feature({ // ... stepDefinitions: [...builtInSteps, ...customSteps], }) ``` Racejar will error if a step definition is missing or if you define duplicates. ## Next steps # Reference > API reference for the Portable Text Editor and its packages. Reference documentation for the editor's APIs, plugins, and tools. ### [Editor API](/editor/reference/editor/) The `useEditor` and `useEditorSelector` hooks for interacting with the editor programmatically. ### [Behavior API](/editor/reference/behavior-api/) Complete reference for `defineBehavior`: all native, synthetic, and custom event types, guard patterns, and action types (`execute`, `forward`, `raise`, `effect`). ### [Selectors](/editor/reference/selectors/) Pure functions for deriving state from the editor snapshot. Includes block selectors, span selectors, selection state, active marks, and text position utilities. ### [Keyboard shortcuts](/editor/reference/keyboard-shortcuts/) The `@portabletext/keyboard-shortcuts` package. Platform-aware shortcuts for common formatting operations. ### [Plugins](/editor/reference/plugins/) Available editor plugins: markdown shortcuts, emoji picker, input rules, character pair decorators, paste link, one-line mode, typeahead picker, and typography transforms. ### [Toolbar](/editor/reference/toolbar/) The `@portabletext/toolbar` package. React hooks for building toolbar UI components. # Behavior API overview > Reference documentation for the Behavior API. import EventTypesList from '@/components/EventTypesList.astro' Reference docs for the behavior API. ## `defineBehavior` Options Object - on (string): Internal editor event - guard (function or boolean): function accepts `snapshot`, `event`, and `dom`, returns boolean or guard response - actions (array): function accepts `snapshot`, `event`, `dom`, and guard response, returns array of actions The `dom` parameter provides access to DOM-related utilities like `dom.findDOMNode()` for accessing the underlying DOM elements. ### Example ```tsx const noLowerCaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [ ({snapshot, event}) => [ {type: 'execute', event: {type: 'insert.text', text: 'A'}}, ], ], }) ``` ## Behavior event types ### Native event types ### Synthetic event types ### Custom event types - custom.\* (e.g. custom.add link) ## Behavior actions - [execute](#execute) - [forward](#forward) - [raise](#raise) - effect ### `execute` The `execute` action type is used to execute events to resolution. Properties: - type: `execute` - event: Native event object ```tsx const executedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [ ({snapshot, event}) => [ {type: 'execute', event: {type: 'insert.text', text: 'A'}}, ], ], }) ``` When an event is executed, no other Behavior will be triggered. The `execute` action also has a handy shorthand function: ```tsx const executedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [({snapshot, event}) => [execute({type: 'insert.text', text: 'A'})]], }) ``` ### `forward` The `forward` action type is used to forward events to the next Behavior. Properties: - type: `forward` - event: Behavior event object ```tsx const forwardedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [({snapshot, event}) => [{type: 'forward', event: {type: 'insert.text', text: 'A'}})]], }) ``` When an event is forwarded, the next Behavior will be triggered. The `forward` action also has a handy shorthand function: ```tsx const forwardedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [({snapshot, event}) => [forward({type: 'insert.text', text: 'A'})]], }) ``` ### `raise` The `raise` action type is used to sends events back into the editor. Properties: - type: `raise` - event: Behavior event object ```tsx const raisedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [ ({snapshot, event}) => [ {type: 'raise', event: {type: 'insert.text', text: 'A'}}, ], ], }) ``` The `raise` action also has a handy shorthand function: ```tsx const raisedUppercaseA = defineBehavior({ on: 'insert.text', guard: ({snapshot, event}) => event.text === 'a', actions: [({snapshot, event}) => [raise({type: 'insert.text', text: 'A'})]], }) ``` # Editor API overview > Hooks for accessing the Portable Text Editor instance and deriving state with selectors. The editor API provides access to the editor instance and selectors for deriving state. Use the following hooks to access the editor from within your components. ## `useEditor` Access the editor instance. Commonly used when building toolbars or passing snapshot to selectors. ```tsx import {useEditor} from '@portabletext/editor' const editor = useEditor() ``` ## `useEditorSelector` Access selectors in areas where the snapshot is available by combining with `useEditor`. ```tsx import {useEditor, useEditorSelector} from '@portabletext/editor' import * as selectors from '@portabletext/editor/selectors' const editor = useEditor() const isActive = useEditorSelector(editor, selectors.isActiveDecorator('em')) ``` # Keyboard shortcuts API overview > Drop-in and custom keyboard shortcuts for the Portable Text Editor via @portabletext/keyboard-shortcuts. import {CardGrid, LinkCard} from '@astrojs/starlight/components' import {PackageManagers} from 'starlight-package-managers' The Keyboard Shortcuts API offers drop-in keyboard shortcuts for the editor that can be paired with toolbar buttons, as well as a way to create your own custom keyboard shortcuts. See the Generated API reference for available keyboard shortcuts (variables) and functions for creating your own. # Plugins > React components placed inside the EditorProvider to register behaviors and extend the Portable Text Editor. import {CardGrid, LinkCard} from '@astrojs/starlight/components' Plugins are React components placed inside the `EditorProvider`, and are primarily used to register [Behaviors](/editor/concepts/behavior/) ad-hoc in the Editor using the `editor.registerBehavior(...)` API: ```tsx function LogTextPlugin() { const editor = useEditor() useEffect(() => { const unregisterBehavior = editor.registerBehavior({ behavior: defineBehavior({ on: 'insert.text', actions: [ ({event}) => [ { type: 'effect', effect: () => { console.log(event) }, }, { type: 'forward', event, }, ], ], }), }) return () => { unregisterBehavior() } }, [editor]) return null } ``` ## Available plugins # Selectors API overview > Pure functions that derive state from the editor snapshot, used to build behaviors and toolbar components. import {CardGrid, LinkCard} from '@astrojs/starlight/components' Selectors are pure functions that derive state from the editor snapshot. They provide a clean way to access editor state without directly parsing the snapshot structure. ```tsx import * as selectors from '@portabletext/editor/selectors' ``` Selectors are commonly used for creating behaviors and toolbar components. :::note Unsure what a "span" is or what "focus" means in this context? Check the [common terms](/editor/concepts/portabletext/#common-terms) section. ::: ## Condition selectors Condition selectors return a boolean or truthy value based on the current editor state. ### Active state conditions - `isActiveAnnotation(name)` - Returns `true` if the specified annotation is active in the selection - `isActiveDecorator(name)` - Returns `true` if the specified decorator is active in the selection - `isActiveListItem(name)` - Returns `true` if the specified list type is active - `isActiveStyle(name)` - Returns `true` if the specified style is active ### Selection conditions - `isSelectionCollapsed` - Returns `true` if the selection is collapsed (cursor with no range) - `isSelectionExpanded` - Returns `true` if the selection spans multiple characters ### Position conditions - `isAtTheStartOfBlock` - Returns `true` if the cursor is at the start of a block - `isAtTheEndOfBlock` - Returns `true` if the cursor is at the end of a block - `isSelectingEntireBlocks` - Returns `true` if entire blocks are selected ### Range conditions - `isOverlappingSelection` - Returns `true` if selections overlap - `isPointBeforeSelection` - Returns `true` if a point is before the current selection - `isPointAfterSelection` - Returns `true` if a point is after the current selection ## Block selectors Retrieve block-level elements from the editor. ### Navigation - `getFirstBlock` - Get the first block in the editor - `getLastBlock` - Get the last block in the editor - `getNextBlock` - Get the block after the current focus block - `getPreviousBlock` - Get the block before the current focus block ### Focus & anchor - `getAnchorBlock` - Get the block where the selection anchor is located - `getFocusBlock` - Get the block where the selection focus is located ### Selection range - `getSelectedBlocks` - Get all blocks within the current selection - `getSelectionStartBlock` - Get the block at the start of the selection - `getSelectionEndBlock` - Get the block at the end of the selection ## Text block selectors Retrieve text blocks specifically (excludes block objects). - `getAnchorTextBlock` - Get the text block where the selection anchor is located - `getFocusTextBlock` - Get the text block where the selection focus is located - `getSelectedTextBlocks` - Get all text blocks within the current selection - `getFocusListBlock` - Get the list block where focus is located (if in a list) ## Span selectors Retrieve span elements (text segments within blocks). - `getAnchorSpan` - Get the span where the selection anchor is located - `getFocusSpan` - Get the span where the selection focus is located - `getNextSpan` - Get the span after the current focus span - `getPreviousSpan` - Get the span before the current focus span - `getSelectedSpans` - Get all spans within the current selection ## Child selectors Retrieve child elements within blocks. - `getAnchorChild` - Get the child element where the selection anchor is located - `getFocusChild` - Get the child element where the selection focus is located - `getSelectionStartChild` - Get the child at the start of the selection - `getSelectionEndChild` - Get the child at the end of the selection ## Inline object selectors Retrieve inline objects (custom elements embedded in text). - `getFocusInlineObject` - Get the inline object at the current focus position - `getNextInlineObject` - Get the next inline object after focus - `getNextInlineObjects` - Get all inline objects after focus - `getPreviousInlineObject` - Get the previous inline object before focus - `getPreviousInlineObjects` - Get all inline objects before focus ## Block object selectors Retrieve block objects (custom block-level elements). - `getFocusBlockObject` - Get the block object at the current focus position ## Selection selectors Get information about the current selection. - `getSelection` - Get the current selection (anchor and focus points) - `getSelectionText` - Get the text content of the current selection - `getSelectedValue` - Get the Portable Text value of the selected content - `getSelectionStartPoint` - Get the starting point of the selection - `getSelectionEndPoint` - Get the ending point of the selection - `getCaretWordSelection` - Get the word selection at the caret position ## Active state selectors Get information about active formatting and marks. - `getActiveAnnotations` - Get all active annotations in the selection - `getActiveListItem` - Get the active list item type - `getActiveStyle` - Get the active block style - `getMarkState` - Get comprehensive mark state information for the selection ## Text position selectors Get text content relative to the cursor position. - `getBlockTextBefore` - Get text in the current block before the cursor - `getBlockTextAfter` - Get text in the current block after the cursor - `getBlockOffsets` - Get character offsets within the current block ## Value selectors Access the editor value. - `getValue` - Get the complete Portable Text value from the editor # Toolbar API overview > Hooks and types from @portabletext/toolbar for building custom toolbars for the Portable Text Editor. import {CardGrid, LinkCard} from '@astrojs/starlight/components' import {PackageManagers} from 'starlight-package-managers' The Toolbar API offers assorted hooks and types for building your own toolbars. # Introduction > What Portable Text is, how it works, and where to start. import {CardGrid, LinkCard} from '@astrojs/starlight/components' ## What is Portable Text? Portable Text is a JSON-based specification for structured block content. Instead of storing content as an HTML string or Markdown, Portable Text represents it as an array of typed blocks: each block has a `_type` and carries its own data. ```text ┌─────────────────────────────────────────┐ │ Portable Text document │ │ (array of blocks) │ │ │ │ ┌─ _type: block ────────────────────┐ │ │ │ style: "h1" │ │ │ │ "Why Portable Text?" │ │ │ └───────────────────────────────────┘ │ │ ┌─ _type: block ────────────────────┐ │ │ │ style: "normal" │ │ │ │ "Read the **docs** for details." │ │ │ │ └─ annotation: link (href, ...) │ │ │ └───────────────────────────────────┘ │ │ ┌─ _type: image ────────────────────┐ │ │ │ url: "photo.jpg" │ │ │ │ alt: "A mountain landscape" │ │ │ │ caption: "View from the summit" │ │ │ └───────────────────────────────────┘ │ │ ┌─ _type: block ────────────────────┐ │ │ │ style: "normal" │ │ │ │ "Current price: [stockTicker] ." │ │ │ │ └─ inline: stockTicker │ │ │ │ symbol: "AAPL" │ │ │ │ exchange: "NASDAQ" │ │ │ └───────────────────────────────────┘ │ │ ┌─ _type: callToAction ─────────────┐ │ │ │ text: "Start building" │ │ │ │ url: "/getting-started" │ │ │ └───────────────────────────────────┘ │ └─────────────────────────────────────────┘ ``` Text blocks, image blocks, and custom blocks like a call-to-action all live in the same array. A serializer walks the array and renders each block based on its type. ### Three levels of extensibility Portable Text is a **block content format**, not just a rich text format. Rich text (formatted paragraphs with bold, italic, and links) is one type of block among many. The format supports three levels of custom content: ```text Block level Custom block types sit alongside text blocks (images, code blocks, CTAs, embeds, tables) ───────────────────────────────────────────── Inline level Inline objects sit inside text blocks (stock tickers, product refs, custom emoji) ───────────────────────────────────────────── Mark level Annotations carry data on text spans (links with tracking, refs with doc IDs, comments, footnotes) ``` **Custom blocks** are any `_type` you define. An image block carries url, alt, and caption. A code block carries language and source. A CTA carries text and a link. The serializer renders each one through a component you provide. **Inline objects** are structured data embedded in the text flow. A stock ticker inside a paragraph carries symbol and exchange data. A product reference carries a product ID. They're not text with formatting; they're data that happens to appear within text. **Annotations** are data-carrying marks on text spans. A link annotation isn't just an `` tag: it's a data object with href, target, tracking parameters, or whatever fields you define. You can query "find all documents that link to /pricing" because the link data is structured JSON, not buried in an HTML string. ### How rendering works A serializer converts the block array into your target format. The same Portable Text renders as React components, HTML strings, Markdown, PDFs, or plain text: ```text ┌──────────────┐ ┌──→│ React │──→
      ...
      │ └──────────────┘ ┌───────────┐ │ ┌──────────────┐ │ Portable │───┼──→│ HTML │──→
      ...
      │ Text JSON │ │ └──────────────┘ └───────────┘ │ ┌──────────────┐ ├──→│ Markdown │──→ # Hello **world** │ └──────────────┘ │ ┌──────────────┐ └──→│ PDF / email │──→ (any format) └──────────────┘ ``` Default block types (paragraphs, headings, lists, bold, italic, links) work out of the box. Custom blocks and annotations need a component for each type. If a serializer encounters a type it doesn't recognize, it skips it. Nothing breaks. ### Why structured data? Because content is JSON, you can: - **Render anywhere.** Pass the same data to any serializer. Each renders what it can, ignores what it can't. - **Query the content.** "Find all blocks that contain a link to /pricing" is a JSON query, not a regex over HTML. - **Extend without breaking.** Add new block types, inline objects, or annotation types. Renderers that don't recognize them skip them gracefully. - **Validate the structure.** The schema is explicit. You know exactly what types of content exist and what data they carry. [Learn why Portable Text over HTML, Markdown, or Gutenberg →](/why-portable-text/) ## Get started There are two ways to work with Portable Text: # Render Portable Text > How to render Portable Text as HTML, React components, or any other format. import {CardGrid, LinkCard, TabItem, Tabs} from '@astrojs/starlight/components' Portable Text is stored as JSON. To display it, you pass the data through a **serializer** that converts each block into your target output: HTML strings, React components, Vue templates, Markdown, or any other format. ## How it works A Portable Text document is an array of blocks. Each block has a `_type` that tells the serializer what it is and how to render it. Some blocks are **text blocks** (`_type: "block"`): paragraphs, headings, lists, and inline formatting. Serializers handle these out of the box. But Portable Text isn't just rich text. The same array can contain **any type of structured content**: images, code blocks, calls to action, videos, tables, or types you define yourself. These custom blocks sit alongside text blocks in the array, each with their own `_type` and data. You provide a component for each custom type, and the serializer renders them in sequence. Within text blocks, **marks** add meaning to inline text. **Decorators** (bold, italic, underline) work by default. **Annotations** (links, references, footnotes) carry structured data as mark definitions. A link annotation isn't just an `
      ` tag: it's a data object with href, target, tracking parameters, or whatever fields you define. You customize annotation rendering through the `marks` component map. Text blocks can also contain **inline objects**: structured data embedded in the text flow, like a stock ticker, a product reference, or a custom emoji. Inline objects are rendered through the same `types` component map as custom blocks. If a serializer encounters a type it doesn't recognize, it skips it. Nothing breaks. ## Get started Install the serializer for your framework: ```bash npm install @portabletext/react ``` ```tsx import {PortableText} from '@portabletext/react' function Article({content}) { return } ``` [React guide →](/rendering/react/) ```bash npm install @portabletext/to-html ``` ```ts import {toHTML} from '@portabletext/to-html' const html = toHTML(portableTextContent) ``` [HTML guide →](/rendering/html/) ```bash npm install @portabletext/markdown ``` ```ts import {portableTextToMarkdown} from '@portabletext/markdown' const markdown = portableTextToMarkdown(portableTextContent) ``` Also converts Markdown → Portable Text. [Conversion guide →](/conversion/markdown-to-portable-text/) ```bash npm install @portabletext/vue ``` ```vue ``` [Vue guide →](/rendering/vue/) ```bash npm install @portabletext/svelte -D ``` ```svelte ``` Requires Svelte 5+. [Svelte guide →](/rendering/svelte/) ```bash npm install astro-portabletext ``` ```astro --- import {PortableText} from 'astro-portabletext' const {content} = Astro.props --- ``` [Astro guide →](/rendering/astro/) That's enough to render paragraphs, headings, lists, bold, italic, and links. But Portable Text can contain much more than formatted text. The next sections show how to render custom block types and marks. ## Rendering custom block types This is where Portable Text goes beyond rich text. Custom blocks carry structured data (images, CTAs, embeds, code blocks) that the serializer doesn't know how to render by default. You tell it what to do by mapping the block's `_type` to a component. Here's an image block in Portable Text: ```json { "_type": "image", "_key": "abc123", "url": "https://example.com/photo.jpg", "alt": "A mountain landscape", "caption": "View from the summit" } ``` The `_type` is `"image"`. To render it, add an `image` entry to the `types` component map: ```tsx import {PortableText} from '@portabletext/react' const components = { types: { image: ({value}) => (
      {value.alt} {value.caption &&
      {value.caption}
      }
      ), }, } function Article({content}) { return } ```
      ```ts import {toHTML} from '@portabletext/to-html' const html = toHTML(portableTextContent, { components: { types: { image: ({value}) => { const caption = value.caption ? `
      ${value.caption}
      ` : '' return `
      ${value.alt}${caption}
      ` }, }, }, }) ```
      ```ts import {portableTextToMarkdown} from '@portabletext/markdown' const markdown = portableTextToMarkdown(portableTextContent, { types: { image: ({value}) => { const alt = value.alt || '' const caption = value.caption ? `\n\n*${value.caption}*` : '' return `![${alt}](${value.url})${caption}` }, }, }) ```
      The pattern is the same in every framework: map the `_type` to a function that receives the block's data as `value` and returns your output. This works for any custom block type. A call-to-action, a code block, an embedded video, a table: define the `_type` in your schema, add a component to the `types` map, and the serializer handles the rest. ## Rendering custom marks Marks add meaning to inline text. There are two kinds: **Decorators** are simple flags like `strong`, `em`, and `underline`. Serializers render these by default (bold, italic, underlined text). **Annotations** carry data. A link annotation looks like this in Portable Text: ```json { "_type": "block", "children": [ {"_type": "span", "text": "Read the "}, {"_type": "span", "text": "documentation", "marks": ["link1"]}, {"_type": "span", "text": "."} ], "markDefs": [ { "_key": "link1", "_type": "link", "href": "/docs", "openInNewTab": true } ] } ``` The span with `"marks": ["link1"]` references the mark definition with `"_key": "link1"`. The mark definition carries the data (`href`, `openInNewTab`). To customize how this renders, add a `link` entry to the `marks` component map: ```tsx const components = { marks: { link: ({children, value}) => { const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined return (
      {children} ) }, }, } ``` ```ts import {uriLooksSafe} from '@portabletext/to-html' const components = { marks: { link: ({children, value}) => { const href = value.href || '' if (!uriLooksSafe(href)) return children const rel = href.startsWith('/') ? '' : ' rel="noreferrer noopener"' const target = value.openInNewTab ? ' target="_blank"' : '' return `${children}` }, }, } ``` :::tip[Security] The `@portabletext/to-html` package exports `uriLooksSafe()` to reject dangerous URI schemes like `javascript:` and `data:`. Use it when rendering links from user-generated content. ::: ```ts import {portableTextToMarkdown} from '@portabletext/markdown' const markdown = portableTextToMarkdown(portableTextContent, { marks: { link: ({children, value}) => { const href = value.href || '' const title = value.title ? ` "${value.title}"` : '' return `[${children}](${href}${title})` }, }, }) ``` The default link renderer already handles standard links. Custom mark renderers are useful when your annotations carry extra data (tracking parameters, tooltips, etc.) that you want to preserve in the Markdown output. Mark components receive `children` (the annotated text, already rendered) and `value` (the mark definition data). The same pattern works for any annotation type: footnotes, internal references, highlights, or custom marks specific to your schema. ## Component map reference All serializers use the same component map structure (except Astro, which uses singular keys `type` and `mark`): | Key | What it renders | When you need it | | ----------- | ------------------------------- | --------------------------------------- | | `types` | Custom block and inline objects | Images, CTAs, code blocks, embeds | | `marks` | Annotations (marks with data) | Links, footnotes, references | | `block` | Block styles | Custom heading styles, pull quotes | | `list` | List containers | Custom bullet or numbered list wrappers | | `listItem` | List items | Custom list item rendering | | `hardBreak` | Line breaks within text | Custom line break handling | Unknown types, marks, and styles are skipped by default. You can handle them with `unknownType`, `unknownMark`, `unknownBlockStyle`, `unknownList`, and `unknownListItem` components. ## Framework guides Each framework has its own patterns for custom components, TypeScript support, and utilities: ## All packages For the complete list of serializers, converters, and community packages, see [All packages](/ecosystem/packages/). # Astro > Render Portable Text in Astro using the astro-portabletext community package. import {PackageManagers} from 'starlight-package-managers' `astro-portabletext` is a community package maintained by [theisel](https://github.com/theisel/astro-portabletext). It is recommended by Sanity for rendering Portable Text in Astro projects. It is not part of the official `@portabletext` organization. **Requires Astro v4.6 or later.** :::note[Prerequisites] This guide covers `astro-portabletext` **v0.13.x** ([changelog](https://github.com/theisel/astro-portabletext/releases)). Requires Astro 4.6+. ::: ## Install ## Basic usage Pass your Portable Text array to the `PortableText` component via the `value` prop. ```astro --- import {PortableText} from 'astro-portabletext' const {content} = Astro.props --- ``` ## Custom components You can override how any node type is rendered by passing a `components` object. The keys map to node types in your content. ```astro --- import {PortableText} from 'astro-portabletext' import BulletList from './BulletList.astro' import Hero from './Hero.astro' import Highlight from './Highlight.astro' import Link from './Link.astro' import PageHeading from './PageHeading.astro' const portableText = [ /* your Portable Text payload */ ] const components = { type: { hero: Hero, }, block: { h1: PageHeading, }, list: { bullet: BulletList, }, mark: { link: Link, highlight: Highlight, }, } --- ``` :::note[Singular prop names] Unlike React, Vue, and Svelte (which use plural `types` and `marks`), Astro uses **singular** `type` and `mark` in the components object. This matches the Astro package's API. ::: ### Custom link annotation Mark components receive a `MarkProps` type. Use `` to render the annotated text. ```astro --- import type {MarkProps} from 'astro-portabletext/types' export type Props = MarkProps<{href: string; target?: string}> const {node} = Astro.props const {href, target} = node.value --- ``` ### Custom block type Custom block types (non-text blocks like heroes, callouts, or embeds) receive a `TypeProps` type. ```astro --- import type {TypeProps} from 'astro-portabletext/types' export type Props = TypeProps<{heading: string; imageUrl: string}> const {node} = Astro.props ---

      {node.heading}

      {node.heading}
      ``` ## Slots Astro's named slots let you wrap rendered output without replacing the underlying component. This is useful for adding CSS classes or wrapper elements to a whole category of nodes. Available slot names: `type`, `block`, `list`, `listItem`, `mark`, `text`, `hardBreak`. ```astro --- import {PortableText} from 'astro-portabletext' const portableText = [ /* your Portable Text payload */ ] --- { ({Component, props, children}) => ( {children} ) } { ({Component, props, children}) => ( {children} ) } ``` Each slot receives a render function with `Component` (the resolved component), `props`, and `children`. You can add attributes, wrap with extra elements, or conditionally alter rendering without touching the component itself. ## Utility functions ```js import { mergeComponents, // deep-merge component maps spanToPlainText, // extract plain text from a single span (v0.11.0+) toPlainText, // extract plain text from a Portable Text array usePortableText, // access default/unknown components inside custom components } from 'astro-portabletext' ``` ### usePortableText `usePortableText` gives a custom component access to the default component for a given node. Use it when you want to handle some cases yourself and fall back to the default for everything else. ```astro --- import {usePortableText} from 'astro-portabletext' import type {MarkProps} from 'astro-portabletext/types' import CustomLink from './CustomLink.astro' export type Props = MarkProps const props = Astro.props const {getDefaultComponent} = usePortableText(props.node) const Cmp = props.node.markType === 'link' ? CustomLink : getDefaultComponent() --- ``` ### mergeComponents `mergeComponents` deep-merges two component maps. Useful when you have a base set of components and want to extend or override them in a specific context. ### toPlainText and spanToPlainText `toPlainText` extracts plain text from a Portable Text array. `spanToPlainText` (added in v0.11.0) does the same for a single span node. ## Further reading Full documentation, changelog, and advanced usage are on [GitHub](https://github.com/theisel/astro-portabletext). # HTML > Render Portable Text to an HTML string using @portabletext/to-html. Works in Node.js, Deno, edge runtimes, and browsers. import {PackageManagers} from 'starlight-package-managers' `@portabletext/to-html` renders Portable Text to a plain HTML string. It has no framework dependency and works anywhere JavaScript runs: Node.js, Deno, edge runtimes, and browsers. Use it for server-side rendering pipelines, static site generators, email templates, or any context where you need a string rather than a component tree. :::note[Prerequisites] This guide covers `@portabletext/to-html` **v5.x** ([changelog](https://github.com/portabletext/to-html/releases)). Works in Node.js, Deno, edge runtimes, and browsers. No framework dependencies. ::: ## Install ## Basic usage `toHTML` takes an array of Portable Text blocks and an options object. It returns an HTML string. ```js import {toHTML} from '@portabletext/to-html' const html = toHTML(portableTextBlocks, { components: { /* optional: override or extend default components */ }, }) ``` The `components` option is where you register renderers for custom types and marks. Without it, `toHTML` uses the built-in defaults for standard block styles, lists, and decorators. ## Custom components ### Custom link mark with URI safety checking When rendering links from user-supplied content, you should validate the `href` before emitting it. `@portabletext/to-html` exports `uriLooksSafe()` for exactly this purpose. :::caution[Security] `uriLooksSafe()` rejects URIs with dangerous schemes like `javascript:`, `data:`, and `vbscript:`. Always run user-supplied `href` values through it before rendering them into anchor tags. ::: The example below also uses [`htm`](https://github.com/developit/htm) and [`vhtml`](https://github.com/developit/vhtml) to construct HTML safely without string concatenation. See the note on [safe HTML construction](#safe-html-construction) below. ```js import {toHTML, uriLooksSafe} from '@portabletext/to-html' import htm from 'htm' import vhtml from 'vhtml' const html = htm.bind(vhtml) const components = { types: { image: ({value}) => html`${value.alt ?? ''}`, callToAction: ({value, isInline}) => isInline ? html`${value.text}` : html`
      ${value.text}
      `, }, marks: { link: ({children, value}) => { const href = value.href || '' if (uriLooksSafe(href)) { const rel = href.startsWith('/') ? undefined : 'noreferrer noopener' return html`${children}` } return children }, }, } const result = toHTML(portableTextBlocks, {components}) ``` The `link` mark renderer above: 1. Checks the `href` with `uriLooksSafe()` before rendering it. 2. Adds `rel="noreferrer noopener"` on external links (any `href` that does not start with `/`). 3. Returns `children` unwrapped if the URI fails the safety check, rather than rendering a broken or dangerous link. ### Safe HTML construction Component functions return strings. You can use template literals directly, but that requires careful manual escaping of every interpolated value. A safer approach is to use `htm` + `vhtml`: - [`htm`](https://github.com/developit/htm) provides JSX-like tagged template syntax. - [`vhtml`](https://github.com/developit/vhtml) renders virtual DOM nodes to an HTML string, escaping attribute values and text content automatically. Bind them once at the top of your file: ```js import htm from 'htm' import vhtml from 'vhtml' const html = htm.bind(vhtml) ``` Then use `` html`...` `` in your component functions instead of raw string interpolation. ## API reference ### `toHTML(blocks, options)` | Parameter | Type | Description | | ---------------------------- | ------------------------------------------ | ----------------------------------------------------------------------- | | `blocks` | `PortableTextBlock \| PortableTextBlock[]` | The Portable Text content to render. | | `options.components` | `PortableTextHtmlComponents` | Component map. Keys listed below. | | `options.onMissingComponent` | `function \| false` | Called when a component is not found. Pass `false` to silence warnings. | ### Component map keys | Key | Renders | Receives | | ------------------- | -------------------------------------------- | ------------------------------- | | `types` | Custom object types (block or inline) | `{ value, isInline }` | | `marks` | Decorators and annotations | `{ markType, value, children }` | | `block` | Block styles (normal, h1-h6, blockquote) | `{ value, children }` | | `list` | List containers (bullet, number) | `{ value, children }` | | `listItem` | List items | `{ value, children }` | | `hardBreak` | Line breaks within spans (default: `
      `) | (none) | | `unknownMark` | Fallback for unregistered marks | `{ markType, value, children }` | | `unknownType` | Fallback for unregistered types | `{ value, isInline }` | | `unknownBlockStyle` | Fallback for unregistered block styles | `{ value, children }` | | `unknownList` | Fallback for unregistered list styles | `{ value, children }` | | `unknownListItem` | Fallback for unregistered list item styles | `{ value, children }` | The `unknown*` keys let you define graceful fallbacks instead of relying on the default behavior (which logs a warning and renders nothing or plain text). ## Exported utilities ```js import { defaultComponents, // built-in component implementations escapeHTML, // HTML-escape a raw string toHTML, // main render function uriLooksSafe, // URI safety checker } from '@portabletext/to-html' ``` | Export | Description | | ------------------- | -------------------------------------------------------------------------------------------------------------- | | `toHTML` | Renders Portable Text blocks to an HTML string. | | `uriLooksSafe` | Returns `true` if a URI does not use a dangerous scheme. Use this before rendering any user-supplied `href`. | | `escapeHTML` | Escapes `&`, `<`, `>`, `"`, and `'` in a raw string. Useful when building component functions without `vhtml`. | | `defaultComponents` | The built-in component map. Spread and override individual keys to extend defaults rather than replace them. | ## Missing component warnings By default, `toHTML` logs a warning to the console when it encounters a type or mark with no registered component. You can route these warnings to your own logger or silence them entirely. ```js toHTML(blocks, { onMissingComponent: (message, {type, nodeType}) => { myLogger.warn(message, {type, nodeType}) }, }) // Or silence entirely: toHTML(blocks, {onMissingComponent: false}) ``` `nodeType` is one of `'block'`, `'mark'`, `'blockStyle'`, `'listStyle'`, or `'listItemStyle'`. Use it to filter which missing components you care about. ## Further reading Full API documentation, changelog, and source: [`@portabletext/to-html` on GitHub](https://github.com/portabletext/to-html). # Markdown > Render Portable Text as Markdown strings using @portabletext/markdown. import {PackageManagers} from 'starlight-package-managers' :::note[Prerequisites] This guide covers `@portabletext/markdown` **v1.x** ([changelog](https://github.com/portabletext/editor/releases)). No framework dependencies. Works in Node.js, Deno, edge runtimes, and browsers. ::: `@portabletext/markdown` renders Portable Text blocks as Markdown strings. Use it to generate Markdown for static site generators, README files, AI prompts, or any system that consumes Markdown. Looking to convert Markdown into Portable Text? See the [Markdown to Portable Text](/conversion/markdown-to-portable-text/) conversion guide. ## Install ## Basic usage `portableTextToMarkdown` takes an array of Portable Text blocks and returns a Markdown string. Standard block styles, decorators, and links are handled automatically. ```ts import {portableTextToMarkdown} from '@portabletext/markdown' const markdown = portableTextToMarkdown(blocks) ``` ## Custom type renderers Custom block types (objects in the blocks array) are not rendered by default. Register a renderer for each type you use. ```ts portableTextToMarkdown(blocks, { types: { chart: ({value}) => `![${value.title}](${value.imageUrl})`, }, }) ``` Type renderers receive `value` (the block object), `index` (position in the array), and `isInline` (whether the object appears inline or as a block). Return an empty string to skip an element entirely. ```ts portableTextToMarkdown(blocks, { types: { image: ({value, isInline}) => { if (isInline) return '' return `![${value.alt || ''}](${value.src})` }, }, }) ``` ## Custom block style renderers Override how block styles render by providing a `block` map. Each renderer receives `value` (the block) and `children` (the already-rendered content of the block). ```ts portableTextToMarkdown(blocks, { block: { h1: ({children}) => `# ${children} #`, blockquote: ({children}) => `
      ${children}
      `, }, }) ``` ## Built-in type renderers The package exports default renderers for common block types. Import and register the ones you need. ```ts import { DefaultCalloutRenderer, DefaultCodeBlockRenderer, DefaultHorizontalRuleRenderer, DefaultHtmlRenderer, DefaultImageRenderer, DefaultTableRenderer, portableTextToMarkdown, } from '@portabletext/markdown' portableTextToMarkdown(blocks, { types: { 'callout': DefaultCalloutRenderer, 'code': DefaultCodeBlockRenderer, 'horizontal-rule': DefaultHorizontalRuleRenderer, 'html': DefaultHtmlRenderer, 'image': DefaultImageRenderer, 'table': DefaultTableRenderer, }, }) ``` | Renderer | Expected fields | Output | | ------------------------------- | ----------------------- | ---------------------- | | `DefaultCalloutRenderer` | `tone`, `content` | `> [!TYPE]\n> content` | | `DefaultCodeBlockRenderer` | `code`, `language?` | ` ```lang\ncode\n``` ` | | `DefaultHorizontalRuleRenderer` | (none required) | `---` | | `DefaultHtmlRenderer` | `html` | Raw HTML string | | `DefaultImageRenderer` | `src`, `alt?`, `title?` | `![alt](src "title")` | | `DefaultTableRenderer` | `rows`, `headerRows?` | Markdown table | ## Renderer map keys | Key | What it renders | | ------------------- | ------------------------------------------ | | `types` | Custom block and inline objects | | `marks` | Annotations and decorators | | `block` | Block styles (headings, blockquotes, etc.) | | `listItem` | List items | | `hardBreak` | Line breaks within blocks | | `unknownType` | Fallback for unregistered types | | `unknownBlockStyle` | Fallback for unregistered block styles | | `unknownListItem` | Fallback for unregistered list items | | `unknownMark` | Fallback for unregistered marks | By default, unknown types render as JSON code blocks. Unknown marks, block styles, and list items pass through their children unchanged. ## Supported features | Feature | PT to Markdown | | ---------------- | :------------: | | Headings (h1-h6) | ✅ | | Paragraphs | ✅ | | Bold | ✅ | | Italic | ✅ | | Inline code | ✅ | | Strikethrough | ✅ | | Links | ✅ | | Blockquotes | ✅ | | Ordered lists | ✅ | | Unordered lists | ✅ | | Nested lists | ✅ | | Code blocks | ✅\* | | Horizontal rules | ✅\* | | Images | ✅\* | | Tables | ✅\* | | HTML blocks | ✅\* | | Callouts | ✅\* | \* Requires registering the built-in renderer (see above). :::note This package uses markdown-it as its Markdown parser. Remark and unified plugins are not compatible. ::: ## Further reading - [Markdown to Portable Text](/conversion/markdown-to-portable-text/) for converting Markdown into PT blocks - [`@portabletext/markdown` on GitHub](https://github.com/portabletext/editor/tree/main/packages/markdown) for full API documentation and changelog # React > How to render Portable Text in React using @portabletext/react. import {PackageManagers} from 'starlight-package-managers' ## Install ## Basic usage Pass your Portable Text value to the `PortableText` component. The library handles blocks, marks, lists, and inline content with sensible defaults. ```jsx import {PortableText} from '@portabletext/react' export default function MyComponent({value}) { return } ``` The `value` prop accepts a single block object or an array of block objects. :::note[Prerequisites] This guide covers `@portabletext/react` **v6.x** ([changelog](https://github.com/portabletext/react-portabletext/releases)). Requires React 18+. ::: ## Custom components Override the default rendering by passing a `components` prop. Each key in the object maps to a part of the Portable Text structure. ### Custom link annotation Marks are inline annotations: bold, italic, links, and any custom annotations you define. Override them under `marks`. This example adds safe `rel` attributes to external links: ```jsx const components = { marks: { link: ({children, value}) => { const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined return ( {children} ) }, }, } ``` Pass the components object to `PortableText`: ```jsx ``` ### Custom block type (image with @sanity/image-url) Custom block types (objects embedded in your content) are handled under `types`. Each component receives the block's `value` and an `isInline` boolean indicating whether it appears inline or as a standalone block. ```jsx import {getImageDimensions} from '@sanity/asset-utils' import {createImageUrlBuilder} from '@sanity/image-url' // Initialize with your Sanity project config const builder = createImageUrlBuilder({ projectId: 'yourProjectId', dataset: 'production', }) const urlFor = (source) => builder.image(source) const SampleImageComponent = ({value, isInline}) => { const {width, height} = getImageDimensions(value) return ( {value.alt ) } ``` Register it under the type name you used in your schema: ```jsx const components = { types: { image: SampleImageComponent, }, } ``` ### Combined example You can define `types`, `marks`, and `block` styles in a single components object: ```jsx const components = { types: { image: ({value}) => , callToAction: ({value, isInline}) => isInline ? ( {value.text} ) : (
      {value.text}
      ), }, marks: { link: ({children, value}) => { const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined return ( {children} ) }, }, block: { h1: ({children}) =>

      {children}

      , blockquote: ({children}) => (
      {children}
      ), }, } ``` ## TypeScript Import `PortableTextComponents` to type your components object. This gives you autocomplete on component keys and typed props for each renderer. ```tsx import {PortableText, PortableTextComponents} from '@portabletext/react' const components: PortableTextComponents = { marks: { link: ({value, children}) => { const target = (value?.href || '').startsWith('http') ? '_blank' : undefined return ( {children} ) }, }, block: { normal: ({children}) =>

      {children}

      , h1: ({children}) =>

      {children}

      , }, } export default function Article({value}) { return } ``` ## Available component keys The `components` prop accepts the following keys. Any key you omit falls back to the default renderer. | Key | What it renders | | ------------------- | ---------------------------------------------------------------------------------------- | | `types` | Custom block types and inline object types. Keyed by `_type` value. | | `marks` | Inline annotations (links, custom decorators). Keyed by mark type name. | | `block` | Standard text blocks. Keyed by `style` value (`normal`, `h1`, `h2`, `blockquote`, etc.). | | `list` | List wrappers. Keyed by list type (`bullet`, `number`). | | `listItem` | Individual list items. Keyed by list type. | | `hardBreak` | Line breaks within a block (rendered as `
      ` by default). | | `unknownMark` | Fallback for mark types with no matching component. | | `unknownType` | Fallback for block types with no matching component. | | `unknownBlockStyle` | Fallback for block styles with no matching component. | | `unknownList` | Fallback for list types with no matching component. | | `unknownListItem` | Fallback for list item types with no matching component. | The `unknown*` keys are useful for debugging: render a visible warning so you can spot unhandled types during development. ## Further reading Full API reference, changelog, and additional examples: [@portabletext/react on GitHub](https://github.com/portabletext/react-portabletext). # Svelte > Render Portable Text in Svelte 5 using @portabletext/svelte. import {PackageManagers} from 'starlight-package-managers' :::caution[Svelte 5 required] `@portabletext/svelte` uses Svelte 5 runes and Snippets. Svelte 4 is not supported. ::: :::note[Prerequisites] This guide covers `@portabletext/svelte` **v3.x** ([changelog](https://github.com/portabletext/svelte-portabletext/releases)). Requires Svelte 5. Svelte 4 is not supported. ::: ## Install Install as a dev dependency. Svelte packages are compiled at build time, so they belong in `devDependencies`. ## Basic usage Pass your Portable Text value to the `PortableText` component. Your component receives props via the `$props()` rune. ```svelte ``` ## Custom components Override the default rendering by passing a `components` object. Use `types` for custom block types and `marks` for annotations. ```svelte ``` ### Custom mark (annotation) Mark components receive a `portableText` prop typed with `MarkComponentProps`, and a `children` Snippet for the annotated text content. Use `$derived()` to destructure from `portableText`. This keeps the variable reactive when props change. See [Svelte 5 patterns](#svelte-5-patterns) below. ```svelte {#if value.href} {@render children()} {:else} {@render children()} {/if} ``` ### Custom block type Block type components receive a `portableText` prop typed with `CustomBlockComponentProps`. Use `$derived()` to destructure `value`. ```svelte
      {value.alt {#if value.caption}
      {value.caption}
      {/if}
      ``` ## Svelte 5 patterns `@portabletext/svelte` is built on Svelte 5 primitives. These patterns appear in every custom component. | Pattern | Usage | | ---------------------- | ---------------------------------------------------------------- | | `$props()` | All components receive props via the `$props()` rune | | `$derived()` | Use `$derived()` to keep variables reactive when props change | | `children: Snippet` | Mark and block components receive children as a Svelte 5 Snippet | | `{@render children()}` | Renders child content (replaces Svelte 4's ``) | ### The $derived() reactivity requirement Always use `$derived()` when destructuring from `portableText`. Destructuring directly breaks reactivity and leaves the variable stale after the first render. ```svelte let { value } = $derived(portableText) let { value } = portableText ``` This is the most common mistake when writing custom components. If your component renders correctly on first load but does not update when content changes, check your destructuring. ## Context prop Use the `context` prop to share data with all components in the tree. This is useful for things like footnote numbering, theme tokens, or any value that multiple components need to read. ```svelte ``` Inside `Footnote.svelte`, read context from `portableText.global.context`. Use `$derived()` here too. ```svelte let { footnotes } = $derived(portableText.global.context) let number = $derived( footnotes.findIndex(note => note._key === portableText.value._key) + 1 ) ``` ## Plain text Use `toPlainText()` to extract a plain text string from a Portable Text value. Useful for meta descriptions, search indexes, and other contexts where HTML is not appropriate. ```svelte ``` ## Full documentation For the complete API reference, all component prop types, and advanced usage, see the [`@portabletext/svelte` README on GitHub](https://github.com/portabletext/svelte-portabletext). # Vue > Render Portable Text in Vue 3 using @portabletext/vue. import {PackageManagers} from 'starlight-package-managers' ## Install ## Basic usage Pass your Portable Text value to the `PortableText` component using the `:value` prop. ```vue ``` The component renders standard block types (paragraphs, headings, lists, blockquotes) out of the box. :::note[Prerequisites] This guide covers `@portabletext/vue` **v1.x** ([changelog](https://github.com/portabletext/vue-portabletext/releases)). Requires Vue 3. ::: ## Custom components Pass a `components` object to the `:components` prop to override how specific node types are rendered. The prop names are plural: `types`, `marks`, `block`, `list`, `listItem`. ### Inline render functions with h() For simple overrides, you can define components inline using Vue's `h()` function. The render function receives props as the first argument and the render context (including `slots`) as the second. Use `slots.default?.()` to render child content inside block and mark components. ```vue ``` ### Vue SFC as a custom component For more complex components, write a standard Vue single-file component and register it in the `components` map. Use `PortableTextComponentProps` to type the props. The generic parameter `T` is the shape of your custom block's `value` field. ```vue ``` Then import and register it: ```vue ``` ## Vue-specific patterns | Pattern | Notes | | ------------------------------- | --------------------------------------------------------------------------------------------------------- | | `