Views let you define shared rendering logic for Lexical nodes that works in both the admin panel and JSX converters, creating different rendering modes for the same content. In the admin panel, editors can switch between views to preview how content will look on your site. The same view node maps can be passed to JSX converters for consistent rendering outside the editor. Common use cases include site previews within the admin panel, A/B testing different designs, or applying different visual themes.
JSX converters are the standard way to render Lexical content in your app. Views are an alternative worth considering when:
If you only need to render content outside the editor and don't need admin panel integration, JSX converters alone are simpler and sufficient.
When you pass both converters and a view's nodeMap to convertLexicalToJSX or the RichText component, the node map takes precedence per-node-type. For blocks and inline blocks, converters from both sources are deep-merged, with the node map winning when both define a converter for the same block type.
Views work in two contexts: in the admin panel, where editors can switch between them using the built-in view selector, and in JSX converters, where you pass a view's node map for consistent rendering. You can use views for either context independently — to customize how nodes render in the admin panel, to share rendering logic between both, or any combination.
The view system works by overriding three aspects of the editor per view:
You can define multiple named views and, in the admin panel, switch between them using the built-in view selector, without changing the underlying data structure.
default, preview, debug)nodes, admin, and lexical editor configViews are defined using the views property on your rich text field configuration. The value is an import path pointing to your views file.
Create a client component file that exports your view maps:
In your collection config, reference the views using an import path:
Each view can customize three aspects of the editor:
Override admin UI settings for a specific view:
These are the same admin options available on the lexicalEditor() config.
Use filterFeatures to control which client features are active for a specific view. It receives the full features map and returns a modified version, allowing you to remove features or even add new ones per-view:
The lexical property accepts any standard Lexical Editor Config options such as theme classes. Pass a function to extend the default config, or an object to replace it entirely:
Each node type can be customized using three different approaches:
Use a React component for full control over rendering. Works in both admin panel and JSX converters.
Use native DOM manipulation for ElementNodes. Only works in the admin panel editor.
Provide raw HTML as a string or function. Works in both admin panel and JSX converters.
Note: If both createDOM and html are provided for a DecoratorNode, html will only be used in JSX converters, not in the admin panel editor where createDOM takes precedence.
The nodes structure mirrors how JSX converters are organized: top-level keys for built-in node types, with block and inline block types nested under blocks and inlineBlocks keys respectively.
Block types are nested under the blocks key, keyed by block slug:
For blocks and inline blocks, you can also use Block and Label as alternatives to Component.
Block is equivalent to passing admin.Block on the block definition itself — the block is still wrapped in the default Form with collapsible, edit/remove buttons, and field state. This makes it useful when you want to customize how a block looks in the editor while still rendering its fields.
useBlockComponentContext is passed as a prop rather than imported directly from @payloadcms/richtext-lexical/client. This is intentional: if this view's node map is also passed to a JSX converter, importing from /client would pull unnecessary Lexical editor dependencies into your bundle. By passing it as a prop, it is only available (and only callable) when isEditor is true.
Always narrow by props.isEditor before calling props.useBlockComponentContext — without that check you will get a TypeScript error, since the prop does not exist on the JSX converter variant of the props type.
Label replaces just the block label in the collapsible header. It is only used in the editor — it has no effect in JSX converter / frontend rendering.
Override specific inline block types:
When views are defined, a view selector automatically appears next to the field label in the admin panel. Users can switch between views to see how content renders in different modes.
Use the useRichTextView hook to access the current view:
You can pass a view's node map to JSX converters (e.g. the RichText component or convertLexicalToJSX) to reuse the same rendering logic outside the editor. This is useful when you want the admin panel preview and your site to render content identically.
When both converters and nodeMap are provided, the node map takes precedence per-node-type. For blocks and inline blocks, converters from both sources are deep-merged, with the node map winning when both define a converter for the same block type.