# How to Configure the RichTextProvider with Custom Tiptap Extensions

> Learn to configure RichTextProvider with custom Tiptap extensions by passing a pre-configured Editor instance via the editor prop. Access extensions seamlessly in your React Tiptap editor.

- Repository: [Hung Hoang/reactjs-tiptap-editor](https://github.com/hunghg255/reactjs-tiptap-editor)
- Tags: how-to-guide
- Published: 2026-03-03

---

**The `RichTextProvider` accepts a pre-configured Tiptap `Editor` instance via the `editor` prop, automatically injecting it into the internal `EditorContext.Provider` and enabling all downstream components to access your custom extensions.**

The `RichTextProvider` component in the `hunghg255/reactjs-tiptap-editor` repository serves as the central context wrapper for React-based Tiptap editors. To configure the RichTextProvider with custom Tiptap extensions, you must first construct an `extensions` array containing both core TipTap extensions and your custom implementations, then instantiate the editor using the `useEditor` hook before passing it to the provider. This architecture decouples extension configuration from the UI layer while ensuring reactive components like slash-command dialogs and theme trackers remain synchronized with your editor state.

## Understanding the RichTextProvider Architecture

Located in [`src/components/RichTextProvider.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/components/RichTextProvider.tsx), the `RichTextProvider` component functions as a thin context wrapper rather than an extension registry. It performs three critical functions:

1. **Context Injection**: Forwards the provided `editor` instance to `EditorContext.Provider`, making it available to all child components via hooks defined in [`src/store/editor.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/editor.ts) (such as `useEditorInstance`, `useEditorState`, and `useCanCommand`).

2. **CSS Reset Injection**: Automatically applies base styling classes to the wrapper `<div className="reactjs-tiptap-editor">`, ensuring consistent typography and layout regardless of which extensions are active.

3. **Reactive UI Mounting**: Initializes auxiliary components including `TooltipProvider`, `SlashDialogTrigger`, `ThemeColorReactive`, and `EditorEditableReactive`, which listen to editor events and maintain UI synchronization without manual wiring.

Because the provider expects a ready-made **Editor instance**, all extension configuration happens during editor instantiation using the standard Tiptap `extensions` array parameter.

## Step-by-Step Configuration Guide

### 1. Import Core and Custom Extensions

Begin by importing the essential Tiptap core extensions alongside any custom extensions from the library's built-in collection or your own codebase:

```typescript
import { Document } from '@tiptap/extension-document';
import { Paragraph } from '@tiptap/extension-paragraph';
import { TextStyle } from '@tiptap/extension-text-style';
import { Dropcursor, Gapcursor, Placeholder } from '@tiptap/extensions';

// Built-in custom extension from the library
import { Video } from '@/extensions/Video/Video';

// Your own custom extension
import { WordCount } from '@/extensions/WordCount/WordCount';

```

Built-in custom extensions reside in `src/extensions/*`, such as [`src/extensions/Video/Video.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Video/Video.tsx) for video embedding functionality.

### 2. Construct the Extensions Array

Assemble all extensions into a single array, configuring each as needed:

```typescript
const extensions = [
  Document,
  Paragraph,
  TextStyle,
  Dropcursor,
  Gapcursor,
  Placeholder.configure({ placeholder: "Press '/' for commands" }),
  Video,        // Built-in custom extension
  WordCount,    // Your custom extension
];

```

The order matters only when extensions depend on each other's schema definitions, though Tiptap generally merges them intelligently.

### 3. Instantiate the Editor

Create the editor instance using the `useEditor` hook from `@tiptap/react`, passing your `extensions` array:

```typescript
import { useEditor } from '@tiptap/react';

const editor = useEditor({
  extensions,
  content: '<p>Initial content</p>',
  textDirection: 'auto', // Optional global configuration
});

```

The `useEditor` hook initializes the Tiptap `Editor` class with your specified extensions, making them available throughout the editor's lifecycle.

### 4. Provide the Editor to RichTextProvider

Pass the instantiated editor to the `RichTextProvider` component, wrapping your `EditorContent`:

```tsx
import { RichTextProvider } from 'reactjs-tiptap-editor';
import { EditorContent } from '@tiptap/react';

export const App = () => {
  return (
    <RichTextProvider editor={editor}>
      <EditorContent editor={editor} />
    </RichTextProvider>
  );
};

```

Once mounted, the provider injects the editor into the context, enabling toolbar buttons, bubble menus, and slash commands to access your custom extension functionality via `editor.extensionOptions` or `editor.extensionStorage`.

## Practical Implementation Examples

### Basic Setup with Multiple Extension Types

This complete example demonstrates combining core extensions, library-specific extensions, and third-party configurations:

```tsx
import { RichTextProvider } from 'reactjs-tiptap-editor';
import { EditorContent, useEditor } from '@tiptap/react';
import { Document } from '@tiptap/extension-document';
import { Paragraph } from '@tiptap/extension-paragraph';
import { Dropcursor, Gapcursor, Placeholder, TrailingNode } from '@tiptap/extensions';
import { TextStyle } from '@tiptap/extension-text-style';
import { ListItem } from '@tiptap/extension-list-item';

// Library extensions
import { Video } from '@/extensions/Video/Video';
import { Twitter } from '@/extensions/Twitter/Twitter';

// Custom user extension
import MyHighlight from '@/my-extensions/MyHighlight';

const extensions = [
  Document,
  Paragraph,
  Dropcursor,
  Gapcursor,
  TextStyle,
  ListItem,
  TrailingNode,
  Placeholder.configure({ placeholder: "Press '/' for commands" }),
  Video,
  Twitter,
  MyHighlight,
];

export const App = () => {
  const editor = useEditor({
    extensions,
    content: '<p>Edit me!</p>',
  });

  if (!editor) return null;

  return (
    <RichTextProvider editor={editor}>
      <EditorContent editor={editor} />
    </RichTextProvider>
  );
};

```

### Creating a Custom Word Counter Extension

To add functionality not provided by the base library, define a custom extension using Tiptap's `Extension` API:

```typescript
// src/extensions/WordCount/WordCount.ts
import { Extension } from '@tiptap/core';

export const WordCount = Extension.create({
  name: 'wordCount',
  
  addProseMirrorPlugins() {
    // Plugin implementation tracking word count
    return [];
  },
  
  addStorage() {
    return {
      words: 0,
    };
  },
});

```

Register it in your extensions array:

```typescript
import { WordCount } from '@/extensions/WordCount/WordCount';

const extensions = [
  // ... other extensions
  WordCount,
];

```

Toolbar components can then access the word count via `editor.storage.wordCount.words` because the `RichTextProvider` makes the editor instance available through React context.

### Enabling Dark Mode Support

The `RichTextProvider` accepts an optional `dark` boolean prop that toggles CSS classes on the container:

```tsx
<RichTextProvider editor={editor} dark={true}>
  <EditorContent editor={editor} />
</RichTextProvider>

```

When `dark` is true, the wrapper div applies dark-mode specific styling, allowing your custom extensions to inherit theme variables defined in the library's CSS.

## Key Source Files and API References

Understanding these implementation details helps debug configuration issues:

| File Path | Purpose |
|-----------|---------|
| **[`src/components/RichTextProvider.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/components/RichTextProvider.tsx)** | Contains the `RichTextProvider` component implementation, handling context provision and helper component mounting. |
| **[`src/store/editor.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/editor.ts)** | Defines `useEditorInstance()`, `useEditorState()`, and `useCanCommand()` hooks that consume the editor from context. |
| **`src/extensions/*`** | Directory containing built-in custom extensions (e.g., Video, Color, Twitter) that you can import into your configuration. |
| **[`playground/src/App.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx)** | Reference implementation demonstrating real-world usage of the provider with multiple extensions. |
| **[`docs/guide/getting-started.md`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/docs/guide/getting-started.md)** | Official documentation showing extension array construction patterns. |

## Summary

- **The `RichTextProvider` does not accept extensions directly**; it receives a complete Tiptap `Editor` instance via the `editor` prop.
- **Configure extensions during editor instantiation** using the `extensions` array in `useEditor()` or `new Editor()`.
- **Import built-in custom extensions** from `src/extensions/*` (such as `Video` from [`src/extensions/Video/Video.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/extensions/Video/Video.tsx)) and mix them with core Tiptap extensions.
- **Access extension data in UI components** through hooks like `useEditorState()` or direct `editor` property access, made available by the provider's `EditorContext.Provider` injection.
- **Enable dark mode** by passing the `dark` prop to `RichTextProvider` for automatic CSS class toggling.

## Frequently Asked Questions

### Can I add or remove extensions dynamically after the editor is initialized?

**No.** Tiptap's architecture requires the `extensions` array to be defined during editor instantiation. To modify available extensions, you must create a new editor instance with the updated configuration and pass it to `RichTextProvider`. The provider will automatically re-inject the new editor into the context, triggering updates in all consuming components.

### How do toolbar components access custom extension state?

**Toolbar components use context hooks.** Because `RichTextProvider` wraps children in `EditorContext.Provider` (as implemented in [`src/components/RichTextProvider.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/components/RichTextProvider.tsx)), any child component can call `useEditor()` from `@tiptap/react` or `useEditorState()` from [`src/store/editor.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/editor.ts) to access the editor instance. From there, components can read `editor.storage.extensionName` or `editor.extensionOptions.extensionName` to retrieve custom extension data.

### What is the difference between `RichTextProvider` and `EditorContext.Provider`?

**`RichTextProvider` is a higher-level abstraction.** While `EditorContext.Provider` (imported from the internal context module) simply provides the raw editor instance, `RichTextProvider` handles additional setup: it mounts reactive components (`ThemeColorReactive`, `EditorEditableReactive`, `SlashDialogTrigger`), applies CSS reset styles, and manages the `TooltipProvider`. You should use `RichTextProvider` at the application level and avoid using `EditorContext.Provider` directly unless building custom provider logic.

### Does the provider support server-side rendering (SSR)?

**Yes, with caveats.** The `RichTextProvider` itself is safe for SSR, but the Tiptap `Editor` instance must be created on the client side because it depends on browser APIs. Instantiate the editor inside a `useEffect` hook or use the `useEditor` hook (which handles client-side initialization internally) before passing it to `RichTextProvider` to avoid hydration mismatches.