The official editor for editing Portable Text – the JSON based rich text specification for modern content editing platforms.
[!NOTE]
We are currently working hard on the general release of this component. Better docs and refined APIs are coming.
End-User Experience
In order to provide a robust and consistent end-user experience, the editor is backed by an elaborate E2E test suite generated from a human-readable Gherkin spec.
Build Your Own Portable Text Editor
[!WARNING]
The @portabletext/editor is currently on the path to deprecate legacy APIs and introduce new ones. The end goals are to make the editor easier to use outside of Sanity (and without @sanity/* libraries) as well as providing a brand new API to configure the behavior of the editor.
This means that the defineSchema and useEditor APIs showcased here are still experimental APIs tagged with @alpha and cannot be considered stable yet. At the same time, the examples below showcase usages of legacy static methods on the PortableTextEditor (for example, PortableTextEditor.isMarkActive(...) and PortableTextEditor.toggleMark(...)) that will soon be discouraged and deprecrated.
Check /examples/basic/src/App.tsx for a basic example of how to set up the edior. Most of the source code from this example app can also be found in the instructions below.
Define the Schema
The first thing to do is to define the editor schema definition. The schema definition is later passed into the editor where it's compiled and used in various callbacks and render functions.
// All options are optional// Only the `name` property is required, but you can define a `title` and an `icon` as well// You can use this schema definition later to build your toolbarconst schemaDefinition = defineSchema({
// Decorators are simple marks that don't hold any datadecorators: [{name: 'strong'}, {name: 'em'}, {name: 'underline'}],
// Annotations are more complex marks that can hold dataannotations: [{name: 'link'}],
// Styles apply to entire text blocks// There's always a 'normal' style that can be considered the paragraph stylestyles: [
{name: 'normal'},
{name: 'h1'},
{name: 'h2'},
{name: 'h3'},
{name: 'blockqoute'},
],
// Lists apply to entire text blocks as welllists: [{name: 'bullet'}, {name: 'number'}],
// Inline objects hold arbitrary data that can be inserted into the textinlineObjects: [{name: 'stock-ticker'}],
// Block objects hold arbitrary data that live side-by-side with text blocksblockObjects: [{name: 'image'}],
})
Render the Editor Component
Use useEditor to create an editor and pass that into PortableTextEditor. Use the editor.on method to listen for mutation changes inside the editor so you can use and store the value produced.
functionApp() {
// Create an editorconst editor = useEditor({
schemaDefinition,
})
const [value, setValue] = useState<Array<PortableTextBlock> | undefined>(
undefined,
)
// Subscribe to editor changesuseEffect(() => {
const subscription = editor.on('mutation', (mutation) => {
setValue(mutation.snapshot)
})
return() => {
subscription.unsubscribe()
}
}, [editor])
return (
<><PortableTextEditor
// Passinthe `editor` youcreatedearliereditor={editor}
// Andanoptionalvaluevalue={value}
>
{/* Toolbar needs to be rendered inside the `PortableTextEditor` component */}
<Toolbar />
{/* Component that controls the actual rendering of the editor */}
<PortableTextEditablestyle={{border: '1pxsolidblack', padding: '0.5em'}}
// ControlhowdecoratorsarerenderedrenderDecorator={renderDecorator}
// ControlhowannotationsarerenderedrenderAnnotation={renderAnnotation}
// Requiredtorenderblockobjectsbutalsotomake `renderStyle` takeeffectrenderBlock={renderBlock}
// ControlhowstylesarerenderedrenderStyle={renderStyle}
// ControlhowinlineobjectsarerenderedrenderChild={renderChild}
// RenderinglistsisharderandmostlikelyrequiresafairamountofCSS
// However, westillneedtoreturnandrenderthelistitem'schildrentoensureproperrenderingrenderListItem={(props) =><>{props.children}</>}
/>
</PortableTextEditor><prestyle={{border: '1pxdashedblack', padding: '0.5em'}}>
{JSON.stringify(value, null, 2)}
</pre></>
)
}
Render Marks, Blocks and Objects
All the different render functions passed to PortableTextEditable can be defined as stand-alone React components. Most of these are fairly straightforward to render because everything you need is provided via props. However, lists are a little special. Since Portable Text has no concept of block nesting, the easiest way get something looking like lists is with pure CSS. Head over to /examples/basic/src/editor.css for a full example.
Your toolbar needs to be rendered within PortableTextEditor because it requires a reference to the editorInstance that it produces. To toggle marks and styles and to insert objects, you'll have to use this editorInstance together with static methods on the PortableTextEditor class.
The npm package @portabletext/editor receives a total of 135,840 weekly downloads. As such, @portabletext/editor popularity was classified as popular.
We found that @portabletext/editor demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.It has 0 open source maintainers collaborating on the project.
Package last updated on 13 Nov 2024
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Newly introduced telemetry in devenv 1.4 sparked a backlash over privacy concerns, leading to the removal of its AI-powered feature after strong community pushback.
TC39 met in Seattle and advanced 9 JavaScript proposals, including three to Stage 4, introducing new features and enhancements for a future ECMAScript release.