---
title: "Rich Text — Official Lexical Features"
aliases: [lexical-features, richtext-features, lexical-official-features]
tags: [payloadcms, lexical, rich-text, editor, features]
sources: [raw/rich-text__official-features.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

Payload's Lexical editor is composed of **features** — individual plugins you opt into. All features are customizable and new ones can be [[wiki/payloadcms/rich-text-custom-features|created from scratch]]. Pass them via `lexicalEditor({ features: [...] })` in your [[wiki/payloadcms/fields-rich-text|rich text field config]].

---

## Features At a Glance

### Default-on Features (included without config)

| Feature | Markdown | Keyboard Shortcut |
|---------|----------|-------------------|
| `BoldFeature` | `**bold**` | Ctrl/Cmd+B |
| `ItalicFeature` | `*italic*` | Ctrl/Cmd+I |
| `UnderlineFeature` | — | Ctrl/Cmd+U |
| `StrikethroughFeature` | `~~text~~` | — |
| `SubscriptFeature` | — | — |
| `SuperscriptFeature` | — | — |
| `InlineCodeFeature` | `` `code` `` | — |
| `ParagraphFeature` | — | — |
| `HeadingFeature` | `#` … `######` | — |
| `AlignFeature` | — | Ctrl/Cmd+Shift+L/E/R/J |
| `IndentFeature` | — | Tab / Shift+Tab |
| `UnorderedListFeature` | `- ` / `* ` | — |
| `OrderedListFeature` | `1. ` | — |
| `ChecklistFeature` | `- [ ]` / `- [x]` | — |
| `LinkFeature` | `[text](url)` | — |
| `RelationshipFeature` | — | — |
| `BlockquoteFeature` | `> text` | — |
| `UploadFeature` | — | — |
| `HorizontalRuleFeature` | `---` | — |
| `InlineToolbarFeature` | — | floating on selection |

### Default-off Features (opt-in)

| Feature | Purpose |
|---------|---------|
| `FixedToolbarFeature` | Persistent pinned toolbar at editor top |
| `BlocksFeature` | Embed Payload [[wiki/payloadcms/fields-blocks|Blocks]] in rich text |
| `TreeViewFeature` | Debug panel — live state, DOM, time travel |
| `EXPERIMENTAL_TableFeature` | Tables (may have breaking changes in stable releases) |
| `TextStateFeature` | Per-text-node key-value attributes with inline CSS styles |

---

## Feature Config Reference

### HeadingFeature

```ts
HeadingFeature({
  enabledHeadingSizes: ['h1', 'h2', 'h3'], // default: h1–h6
})
```

### IndentFeature

```ts
IndentFeature({
  disableTabNode: true,     // Tab only indents blocks, never inserts tab character
  disabledNodes: ['quote'], // Prevent indentation for specific node types
})
```

### LinkFeature

```ts
LinkFeature({
  fields: ({ defaultFields }) => [
    ...defaultFields,
    { name: 'rel', type: 'select', options: ['noopener', 'noreferrer', 'nofollow'] },
  ],
  enabledCollections: ['pages', 'posts'], // Restrict internal-link pickers
  maxDepth: 2,                            // Population depth for internal links
  disableAutoLinks: false,                // Allow URL→link auto-conversion
})
```

### RelationshipFeature

```ts
RelationshipFeature({
  disabledCollections: ['users'],
  maxDepth: 2,
})
```

### UploadFeature

```ts
UploadFeature({
  collections: {
    uploads: {
      fields: [
        { name: 'caption', type: 'text' },
        { name: 'alt',     type: 'text' },
      ],
    },
  },
  maxDepth: 1,
  disabledCollections: ['specialUploads'],
})
```

### FixedToolbarFeature

```ts
FixedToolbarFeature({
  applyToFocusedEditor: false,      // Apply to focused editor in nested scenarios
  disableIfParentHasFixedToolbar: true,
  customGroups: { format: { /* … */ } },
})
```

### TextStateFeature

Stores key-value attributes in text nodes (e.g. text color, custom underlines). Uses a shared config object as the **single source of truth** for both the editor and the frontend renderer.

```ts
import { defaultColors, TextStateFeature } from '@payloadcms/richtext-lexical'

TextStateFeature({
  state: {
    color: {
      ...defaultColors,
      galaxy: { label: 'Galaxy', css: { background: 'linear-gradient(to right, #0000ff, #ff0000)', color: 'white' } },
    },
    underline: {
      solid: { label: 'Solid', css: { 'text-decoration': 'underline', 'text-underline-offset': '4px' } },
    },
  },
})
```

#### Rendering TextStateFeature on the frontend

State is serialized under the `"$"` key in text nodes. Extract config to a shared file, then add a custom `text` converter:

```ts
// src/fields/textStateConfig.ts — no package imports; safe for server + client
export const textStateConfig = {
  color: {
    'text-red': { label: 'Red', css: { color: 'light-dark(oklch(…), oklch(…))' } },
  },
} as const
```

```tsx
// Override the default "text" converter in your RichText component
const jsxConverters: JSXConvertersFunction = ({ defaultConverters }) => ({
  ...defaultConverters,
  text: (args) => {
    const { node } = args
    let text = defaultConverters.text(args) // renders bold/italic etc.
    const nodeState = (node as any)['$'] as Record<string, string> | undefined
    if (nodeState) {
      const styles: React.CSSProperties = {}
      for (const [stateKey, stateValue] of Object.entries(nodeState)) {
        const css = (textStateConfig as any)[stateKey]?.[stateValue]?.css
        if (css) {
          for (const [prop, value] of Object.entries(css)) {
            (styles as any)[hyphenToCamel(prop)] = value
          }
        }
      }
      if (Object.keys(styles).length > 0) text = <span style={styles}>{text}</span>
    }
    return text
  },
})
```

Key insight: `textStateConfig` is imported by **both** the `TextStateFeature` call and the frontend converter — no duplication, no drift.

---

## Key Takeaways

- **19 features are on by default** — no config needed for basic formatting, lists, links, uploads, and inline toolbar.
- **5 features require explicit opt-in** — `FixedToolbarFeature`, `BlocksFeature`, `TreeViewFeature`, `EXPERIMENTAL_TableFeature`, `TextStateFeature`.
- `InlineToolbarFeature` (floating) and `FixedToolbarFeature` (pinned) can be **active simultaneously**.
- `EXPERIMENTAL_TableFeature` may receive breaking changes outside major releases — use with caution in production.
- `TextStateFeature` requires a **shared config file** with no package imports so it can be safely imported in both server and client contexts.
- For block-level media/files in rich text use `UploadFeature`; for block-level doc references use `RelationshipFeature`.
- `LinkFeature` supports both internal (collection picker) and external links; `disableAutoLinks` controls URL→link auto-conversion.
- `TreeViewFeature` is a dev-only debugging tool — exclude it from production builds.

---

## Related Articles

- [[wiki/payloadcms/rich-text|Rich Text (Lexical) — Overview]]
- [[wiki/payloadcms/rich-text-blocks|Rich Text — Blocks (BlocksFeature)]]
- [[wiki/payloadcms/rich-text-custom-features|Rich Text — Custom Lexical Features]]
- [[wiki/payloadcms/rich-text-converting-jsx|Rich Text — Converting to JSX]]
- [[wiki/payloadcms/fields-rich-text|Rich Text Field Config]]
- [[wiki/payloadcms/fields-blocks|Blocks Field]]
- [[wiki/payloadcms/upload|Upload & Media]]
