> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frogbot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Rich Text Editor

> Configure and extend FrogBot's Lexical rich text editor.

FrogBot's Rich Text editor is based on Lexical. It provides formatting, links, files, relationships, lists, and extension points through composable features.

Lexical is FrogBot's supported rich text editor. The package provides healthy
defaults while letting you customize toolbars, slash-menu entries, nodes, and
nested fields.

## Configure the root editor

```bash theme={"theme":"tokyo-night"}
pnpm add @frogbotai/db-sqlite @frogbotai/richtext-lexical
```

```ts theme={"theme":"tokyo-night"}
import { sqliteAdapter } from '@frogbotai/db-sqlite'
import { lexicalEditor } from '@frogbotai/richtext-lexical'
import { buildConfig } from 'frogbot'

export default buildConfig({
  secret: process.env.FROGBOT_SECRET!,
  db: sqliteAdapter({
    client: {
      url: process.env.DATABASE_URL ?? 'file:./frogbot.db',
    },
  }),
  collections: [],
  editor: lexicalEditor({}),
})
```

The root editor supplies the default configuration for top-level [Rich Text fields](https://docs.frogbot.ai/fields/rich-text.md). Override `editor` on an individual field when it needs a different feature set.

### Editor options

`lexicalEditor` accepts one optional object. Call it with no argument or `{}` to use the recommended feature set and the built-in Lexical configuration.

| Option | Type | Default | Description |
| - | - | - | - |
| `admin` | `LexicalFieldAdminProps` | `{}` | Admin Panel display options for the editor. See [Admin customization](#admin-customization). |
| `features` | `FeatureProviderServer[] \| (args) => FeatureProviderServer[]` | Recommended defaults | Features enabled in this editor. An array replaces the defaults entirely. A function receives the values in [Feature function arguments](#feature-function-arguments) and returns the array to use. See [Official features](https://docs.frogbot.ai/rich-text/official-features.md) for the built-in set. |
| `lexical` | `EditorConfig` | `{ namespace: 'lexical', theme: <built-in theme> }` | Lexical editor configuration. When set, it replaces the built-in configuration, so `namespace` and `theme` are both required. The Admin Panel editor reads only `namespace` and `theme`; `disableEvents` and `dom` are accepted by the type but not used. |
| `views` | `string \| { path: string; exportName?: string }` | None | Experimental. [Component path](https://docs.frogbot.ai/custom-components/overview.md#component-paths) to an export of type `LexicalEditorViewMap`. If the path does not resolve, the editor renders without views. See [Views](https://docs.frogbot.ai/rich-text/views.md). |

```ts theme={"theme":"tokyo-night"}
import { lexicalEditor } from '@frogbotai/richtext-lexical'
import type { CollectionConfig } from 'frogbot'

export const Pages: CollectionConfig = {
  slug: 'pages',
  fields: [
    {
      name: 'content',
      type: 'richText',
      editor: lexicalEditor({}),
    },
  ],
}
```

## Extend the editor with features

Features add nodes, converters, toolbar controls, and other editor behavior. `features` accepts an array or a function. The function receives the recommended defaults and the features configured on the root editor.

```ts theme={"theme":"tokyo-night"}
import {
  BlocksFeature,
  LinkFeature,
  UploadFeature,
  lexicalEditor,
} from '@frogbotai/richtext-lexical'

const editor = lexicalEditor({
  features: ({ defaultFeatures }) => [
    ...defaultFeatures,
    LinkFeature({
      fields: ({ defaultFields }) => [
        ...defaultFields,
        {
          name: 'rel',
          type: 'select',
          hasMany: true,
          options: ['noopener', 'noreferrer', 'nofollow'],
        },
      ],
    }),
    UploadFeature({
      collections: {
        files: {
          fields: [{ name: 'caption', type: 'text' }],
        },
      },
    }),
    BlocksFeature({
      blocks: [
        {
          slug: 'callToAction',
          fields: [{ name: 'label', type: 'text', required: true }],
        },
      ],
    }),
  ],
})
```

### Feature function arguments

| Argument | Type | Default | Description |
| - | - | - | - |
| `defaultFeatures` | `FeatureProviderServer[]` | Always provided | Recommended feature set: every feature marked as a default in [Official features](https://docs.frogbot.ai/rich-text/official-features.md#features-overview). Omit it from the returned array to start with an empty editor. |
| `rootFeatures` | `FeatureProviderServer[]` | Always provided | Features enabled by the root `editor` in your FrogBot config. Empty while configuring the root editor itself. |

See [Official features](https://docs.frogbot.ai/rich-text/official-features.md) for the complete built-in set.

If you omit `defaultFeatures`, the editor starts with no features. This is useful when you want a deliberately small editor rather than the recommended feature set.

## TypeScript

Each serialized node type starts with `Serialized`, such as `SerializedUploadNode`. Use `TypedEditorState` when you want an exact union of allowed nodes:

```ts theme={"theme":"tokyo-night"}
import type {
  SerializedHeadingNode,
  SerializedParagraphNode,
  SerializedTextNode,
  TypedEditorState,
} from '@frogbotai/richtext-lexical'

type ArticleState = TypedEditorState<
  SerializedHeadingNode | SerializedParagraphNode | SerializedTextNode
>

const article: ArticleState = {
  root: {
    children: [
      {
        children: [
          {
            detail: 0,
            format: 0,
            mode: 'normal',
            style: '',
            text: 'Every property is fully typed.',
            type: 'text',
            version: 1,
          },
        ],
        direction: 'ltr',
        format: '',
        indent: 0,
        textFormat: 0,
        type: 'paragraph',
        version: 1,
      },
    ],
    direction: 'ltr',
    format: '',
    indent: 0,
    type: 'root',
    version: 1,
  },
}
```

`DefaultTypedEditorState` includes all nodes from the default feature set and accepts additional node types as its generic argument.

```ts theme={"theme":"tokyo-night"}
import type {
  DefaultTypedEditorState,
  SerializedBlockNode,
} from '@frogbotai/richtext-lexical'

type ContentState = DefaultTypedEditorState<SerializedBlockNode>
```

Import `SerializedEditorState` from `@frogbotai/richtext-lexical/lexical` when code accepts any serialized editor state.

Use types exported by `@frogbotai/richtext-lexical` rather than importing equivalent types from Lexical's internal packages. FrogBot controls and tests the public wrapper types.

## Admin customization

The editor's `admin` property supports these options:

| Option | Type | Default | Description |
| - | - | - | - |
| `placeholder` | `string \| Record<string, string> \| ({ i18n, t }) => string` | Built-in translated placeholder | Placeholder shown when the editor is empty. Use an object keyed by locale or a function for translated text. |
| `hideGutter` | `boolean` | `false` | Hides the gutter: the left padding and vertical line beside the content. |
| `hideInsertParagraphAtEnd` | `boolean` | `false` | Hides the add-paragraph control after the last node. |
| `hideDraggableBlockElement` | `boolean` | `false` | Hides the drag control shown beside nodes. |
| `hideAddBlockButton` | `boolean` | `false` | Hides the add-node control shown beside nodes. |

```ts theme={"theme":"tokyo-night"}
import { lexicalEditor } from '@frogbotai/richtext-lexical'

lexicalEditor({
  admin: {
    hideGutter: true,
    placeholder: 'Write your content...',
  },
})
```

## Detect an empty state

After an editor has been changed, deleting all visible content may leave an empty paragraph in its JSON value. Use `hasText` when you need to distinguish content from an empty editor state:

```ts theme={"theme":"tokyo-night"}
import type { SerializedEditorState } from '@frogbotai/richtext-lexical/lexical'
import { hasText } from '@frogbotai/richtext-lexical/shared'

export function containsText(content: SerializedEditorState) {
  return hasText(content)
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.