@pro-laico/richtext
A ready-to-use rich-text block for Atomic Payload: a Lexical editor for writing formatted content in the admin and a renderer that turns it into clean React on your site.
@pro-laico/richtext gives you a rich-text block for your pages. Editors get a ready-to-use Lexical editor for writing formatted content in the Payload admin, and the package ships a renderer that turns that content into clean React on the frontend. It bundles three pieces: the RichTextChild block, the defaultLexical editor preset, and a JSX renderer.
@pro-laico/richtext is a Tool package, a building block of the Atomic Payload stack. It's meant to be used alongside @pro-laico/core and the other @pro-laico/* packages (most easily via the atomic-payload template), not on its own.
Installation
pnpm add @pro-laico/richtextnpm install @pro-laico/richtextyarn add @pro-laico/richtextInstalled for you: @pro-laico/core.
Provide yourself (peers): payload, react, and @payloadcms/richtext-lexical come with your app; also install the @pro-laico/atomic plugin.
Setup
This package isn't a buildConfig plugin you add to the plugins array. Instead it hands you the pieces to wire rich text into your project yourself: an editor preset for the whole admin, a block your editors can drop into a page, and a renderer for the frontend. The atomic-payload template wires all three for you, so the steps below are the standalone path.
@pro-laico/core comes bundled with this plugin — it's a dependency, not a separate install — and provides the shared fields, hooks, and utilities this plugin builds on. See @pro-laico/core → Setup.
Set the default editor
defaultLexical is an opinionated Lexical preset (bold/italic/headings/lists/links, fixed and inline toolbars). Set it as the editor in your Payload config, exactly as the template does:
import { buildConfig } from 'payload'
import { defaultLexical } from '@pro-laico/richtext/default-lexical'
export default buildConfig({
editor: defaultLexical,
// ...
})To let internal links target collections other than pages, build the preset with the createDefaultLexical factory instead:
import { createDefaultLexical } from '@pro-laico/richtext/default-lexical'
const editor = createDefaultLexical({ enabledCollections: ['pages', 'posts'] })Add the rich-text block
RichText is a ready-made block (slug RichTextChild) carrying a Content tab with a required rich-text area. Drop it into any blocks field:
import { RichText } from '@pro-laico/richtext'
const PageBlocks = {
name: 'children',
type: 'blocks',
blocks: [RichText],
}To add your own fields to the Content tab, build the block with the createRichTextBlock factory and pass prependFields / appendFields instead:
import type { Block } from 'payload'
import { createRichTextBlock } from '@pro-laico/richtext'
import { ClassNameField } from '@pro-laico/styles'
// a className field at the top of the Content tab feeds the generated stylesheet
const RichTextWithClassName: Block = createRichTextBlock({
prependFields: [ClassNameField({ label: 'Rich Text Atomic Classes', defaultValue: 'prose dark:prose-invert' })],
})The @pro-laico/atomic child-blocks plugin already includes RichTextChild and weaves fields in through its blockFields option (how the template adds the prose class field above), so you don't register the block separately.
Render it on the frontend
RichTextChild is the block's React renderer: the render pipeline hands it the block and a pt pass-through, so you never pass content in by hand. With @pro-laico/atomic it's already registered for the block, so rendering a page's children through RenderChildren draws your rich text automatically:
import { RenderChildren } from '@pro-laico/atomic/children/render'
export default function Page({ page }: { page: { children: any } }) {
return (
<main>
<RenderChildren blocks={page.children} />
</main>
)
}The component sets no styling of its own; style the output with Tailwind's typography (prose) classes via a class field on the block or your own wrapper.
Options
createRichTextBlock(options?) accepts:
Prop
Type
createDefaultLexical(options?) accepts:
Prop
Type
Both factories with every option at its default value, as a working starting point:
import { createDefaultLexical, createRichTextBlock } from '@pro-laico/richtext'
// the same shape as the pre-built `RichText` block and `defaultLexical` preset
createRichTextBlock({
prependFields: [],
appendFields: [],
})
createDefaultLexical({
enabledCollections: ['pages'],
})Environment variables
@pro-laico/richtext reads no environment variables.
Exports
Export
Type
RichTextblock
RichTextChild). Drop it into a blocks field to give editors a Lexical content area. It is createRichTextBlock() with no extra fields.Location
@pro-laico/richtextcreateRichTextBlockfunction
RichTextChild block, taking prependFields / appendFields to add your own fields to its Content tab (a CSS class field, project fields, or nothing), so the block carries no CSS dependency of its own.Parameters
options?:RichTextBlockOptionsprependFields (spread at the start of the Content tab) and appendFields (spread at the end). Both default to [].Returns
BlockA Payload blocks field entry (slug RichTextChild) you add to a blocks field.Example
import type { Block } from 'payload'import { createRichTextBlock } from '@pro-laico/richtext'import { ClassNameField } from '@pro-laico/styles'// a className field at the top of the Content tab feeds the generated stylesheetconst RichTextWithClassName: Block = createRichTextBlock({prependFields: [ClassNameField({ label: 'Rich Text Atomic Classes', defaultValue: 'prose dark:prose-invert' })],})const PageBlocks = { name: 'children', type: 'blocks', blocks: [RichTextWithClassName] }Location
@pro-laico/richtextRichTextBlockOptionstype
createRichTextBlock (prependFields / appendFields).Location
@pro-laico/richtextRichTextChildcomponent
@payloadcms/richtext-lexical/react, resolving internal links to their href). Wired into the @pro-laico/atomic render pipeline automatically, so you rarely render it by hand.Parameters
block:RichTextChildThe block document. Its .richText holds the serialized Lexical state.pt:PassThroughsThe render pipeline pass-through props (spreadable props, data attributes). Supplied by RenderChildren.Returns
JSX.ElementA <div> wrapping the rendered rich-text content.Example
import { RenderChildren } from '@pro-laico/atomic/children/render'// RichTextChild is already registered for the RichTextChild block, so a page's// children render their rich text automatically:export default function Page({ page }: { page: { children: any } }) {return ( <main> <RenderChildren blocks={page.children} /> </main>)}Location
@pro-laico/richtextdefaultLexicalconstant
editor. Also available from the /default-lexical subpath.Location
@pro-laico/richtextcreateDefaultLexicalfunction
enabledCollections to point internal links at a different set of collections without copy-pasting the whole preset.Parameters
options?:DefaultLexicalOptionsenabledCollections (the collections the internal-link feature can target). Defaults to ['pages'].Returns
ReturnType<typeof lexicalEditor>A Lexical editor config you set as your Payload config's editor.Example
import { buildConfig } from 'payload'import { createDefaultLexical } from '@pro-laico/richtext/default-lexical'export default buildConfig({// internal links can target both pages and postseditor: createDefaultLexical({ enabledCollections: ['pages', 'posts'] }),// ...})Location
@pro-laico/richtext/default-lexicalDefaultLexicalOptionstype
createDefaultLexical (enabledCollections).Location
@pro-laico/richtext/default-lexicalRelated
@pro-laico/mux-video
Lets editors upload videos in the Payload admin and drop them onto pages. Mux handles the encoding and adaptive streaming, and a ready-made video block renders the player.
@pro-laico/seed
Fill a fresh project with sample content in one click: a SEED DATABASE button in the admin and a guarded POST /api/seed endpoint behind it.