Atomic Payload
Plugins

@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/richtext
npm install @pro-laico/richtext
yarn add @pro-laico/richtext

@payloadcms/richtext-lexical (the Lexical editor package), payload, and react are required peers, so you install the Lexical package alongside this one. Keeping the Lexical dependency here means other Atomic Payload packages don't pay that bundle cost unless they actually use rich text.

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 and a fixed + inline toolbar) tuned for Atomic Payload. Set it as the editor in your Payload config so every rich-text field in the admin uses it:

import { buildConfig } from 'payload'
import { defaultLexical } from '@pro-laico/richtext/default-lexical'

export default buildConfig({
  editor: defaultLexical,
  // ...
})

The template sets it exactly this way (editor: defaultLexical in its payload.config.ts). Want internal links to point at collections other than pages? Build the preset with the createDefaultLexical factory and pass enabledCollections:

import { createDefaultLexical } from '@pro-laico/richtext/default-lexical'

const editor = createDefaultLexical({ enabledCollections: ['pages', 'posts'] })

Add the rich-text block

RichText is a ready-made Payload block (slug RichTextChild) you can drop into any blocks field. It carries a Content tab with a required rich-text area:

import { RichText } from '@pro-laico/richtext'

const PageBlocks = {
  name: 'children',
  type: 'blocks',
  blocks: [RichText],
}

To add your own fields to the Content tab (for example a CSS class field) use the createRichTextBlock factory instead of the pre-built RichText value. Pass prependFields / appendFields (the RichTextBlockOptions type) and they're spread at the start / end of the tab:

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' })],
})

Using the @pro-laico/atomic child-blocks plugin? It already includes RichTextChild as one of its default blocks and lets you weave fields in through its blockFields option, so you don't register the block separately. That's how the template adds the prose class field above.

Render it on the frontend

The package ships RichTextChild, a React component that renders the block's serialized Lexical content as JSX (it uses @payloadcms/richtext-lexical/react under the hood and resolves internal links to their href). It's a child-block renderer: the render pipeline hands it the block (whose .richText holds the serialized state) plus a pt pass-through, so you don't pass content into it by hand.

If you're using @pro-laico/atomic, this is already wired up. RichTextChild is registered as the renderer for the RichTextChild block, so rendering a page's children through atomic's 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, so reach for Tailwind's typography (prose) classes, via a class field on the block or your own wrapper, to style the output.

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'],
})

Exports

Export

Type

RichTextblock
The pre-built Payload rich-text block (slug RichTextChild). Drop it into a blocks field to give editors a Lexical content area. It is createRichTextBlock() with no extra fields.

Location

@pro-laico/richtext
createRichTextBlockfunction
Factory that builds the 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/richtext
RichTextBlockOptionstype
The options type for createRichTextBlock (prependFields / appendFields).

Location

@pro-laico/richtext
RichTextChildcomponent
The frontend renderer for the block. Turns the saved Lexical content into React (via @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/richtext
defaultLexicalconstant
The opinionated Lexical editor preset. Set it as your Payload config's editor. Also available from the /default-lexical subpath.

Location

@pro-laico/richtext
createDefaultLexicalfunction
Factory for the Lexical preset (bold/italic/headings/lists/links plus a fixed and inline toolbar). Pass 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-lexical
DefaultLexicalOptionstype
The options type for createDefaultLexical (enabledCollections).

Location

@pro-laico/richtext/default-lexical

On this page