How to Configure the RichTextProvider with Custom Tiptap Extensions
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, the RichTextProvider component functions as a thin context wrapper rather than an extension registry. It performs three critical functions:
-
Context Injection: Forwards the provided
editorinstance toEditorContext.Provider, making it available to all child components via hooks defined insrc/store/editor.ts(such asuseEditorInstance,useEditorState, anduseCanCommand). -
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. -
Reactive UI Mounting: Initializes auxiliary components including
TooltipProvider,SlashDialogTrigger,ThemeColorReactive, andEditorEditableReactive, 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:
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 for video embedding functionality.
2. Construct the Extensions Array
Assemble all extensions into a single array, configuring each as needed:
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:
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:
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:
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:
// 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:
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:
<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 |
Contains the RichTextProvider component implementation, handling context provision and helper component mounting. |
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 |
Reference implementation demonstrating real-world usage of the provider with multiple extensions. |
docs/guide/getting-started.md |
Official documentation showing extension array construction patterns. |
Summary
- The
RichTextProviderdoes not accept extensions directly; it receives a complete TiptapEditorinstance via theeditorprop. - Configure extensions during editor instantiation using the
extensionsarray inuseEditor()ornew Editor(). - Import built-in custom extensions from
src/extensions/*(such asVideofromsrc/extensions/Video/Video.tsx) and mix them with core Tiptap extensions. - Access extension data in UI components through hooks like
useEditorState()or directeditorproperty access, made available by the provider'sEditorContext.Providerinjection. - Enable dark mode by passing the
darkprop toRichTextProviderfor 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), any child component can call useEditor() from @tiptap/react or useEditorState() from 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →