RHFRichTextEditor
RHFRichTextEditor wraps CKEditor
5 (ClassicEditor) and binds it
to React Hook Form. It allows users to format text with features such as
bold, italics, lists, links, images, and more,
making it suitable for blog posts, comments, or any content requiring rich
formatting. CKEditor 5 is highly customizable and supports advanced features
such as codeBlocks, findAndReplace, tables, and more.
Installation
The latest versions of CkEditor require a paid license key. Therefore, we will use the free version of this package.
To use the latest version, refer to the Use Latest Version section.
npm install ckeditor5@43.0.0 @ckeditor/ckeditor5-react@9.0.0yarn add ckeditor5@43.0.0 @ckeditor/ckeditor5-react@9.0.0pnpm add ckeditor5@43.0.0 @ckeditor/ckeditor5-react@9.0.0Usage
import RHFRichTextEditor, { RHFRichTextEditorProps, DefaultEditorConfig } from '@nish1896/rhf-mui-components/misc/rich-text-editor';The stored value is an HTML string.
import { useForm } from 'react-hook-form';
type FormValues = {
bio: string;
};
const { control, handleSubmit } = useForm<FormValues>({
defaultValues: { bio: '' }
});
<form onSubmit={handleSubmit(values => console.log(values.bio))}>
<RHFRichTextEditor
fieldName="bio"
control={control}
/>
</form>Validation
Rules go in registerOptions. Because the value is HTML, an empty editor can
still hold markup such as <p></p>, and runs of spaces are stored as
. Strip the tags and convert to spaces before checking the
length:
const stripHtml = (html: string) => html
.replace(/<[^>]*>/g, '')
.replace(/ /g, ' ')
.trim();
<RHFRichTextEditor
fieldName="bio"
control={control}
registerOptions={{
validate: value =>
stripHtml(value).length >= 20 || 'Write at least 20 characters'
}}
/>Handling value changes
onValueChange runs after the new HTML has been stored in the form. It receives
{ newValue, event, editor }: the new HTML string, the CKEditor event, and the
editor instance. onReady, onFocus, onBlur and onError are forwarded from
CKEditor.
<RHFRichTextEditor
fieldName="bio"
control={control}
onValueChange={({ newValue, event, editor }) => {
console.log(newValue.length, editor.getData());
}}
/>Use customOnChange when you need to change what gets stored. It replaces the
default handling, so you must call rhfOnChange yourself with the HTML to
store. It receives { rhfOnChange, newValue, event, editor }. This example
stores an empty string instead of an empty paragraph. onValueChange is not
called when customOnChange is used.
<RHFRichTextEditor
fieldName="bio"
control={control}
customOnChange={({ rhfOnChange, newValue, event, editor }) => {
rhfOnChange(newValue === '<p></p>' ? '' : newValue);
}}
/>Customizing the editor
To reorder formatting options or add advanced features, you can provide
a custom configuration via the editorConfig prop, or override the existing
configuration by modifying the DefaultEditorConfig object. You can check the
default configuration here.
To customize the editor theme, review the theme customization guide.
For advanced features like findAndReplace, inserting images, markdown support, and word count, refer to this example. For a full list of available CKEditor features, visit the CKEditor documentation.
If your website supports both light and dark themes, styling CKEditor5 for
theme switching can be challenging, as the editor doesn't dynamically adapt to
theme changes once rendered. To address this, you can override the editor's
CSS based on the active theme by referring to this gist.
You may also refer to the CkEditor5 documentation for a better understanding.
Labels, errors and disabling
showLabelAboveFormField, hideLabel, formLabelProps, helperText,
renderError, hideErrorMessage and disabled (or a form-level disabled in
useForm) work as described in RHFTextField.
With Latest Version
The latest version of CKEditor5 requires a paid license key. To continue accessing the newest features of the editor while preserving the underlying form logic, update your code as follows:
-
Obtain a License Key: Acquire a commercial license key from the CKEditor Pricing Page.
-
Update Your CKEditor 5 Configuration: Incorporate the licenseKey into your editor configuration:
import RHFRichTextEditor, { DefaultEditorConfig } from '@nish1896/rhf-mui-components/misc/rich-text-editor';
<RHFRichTextEditor
fieldName="bio"
control={control}
editorConfig={{
licenseKey: 'YOUR_LICENSE_KEY',
...DefaultEditorConfig,
}}
/>Replace 'YOUR_LICENSE_KEY' with the license key you obtained. You can also configure your own editor settings instead of using the default configuration.
Examples
Alternatives
I chose CKEditor for its advanced features, customization options, and support for multiple languages. Although the documentation can be extensive, this component provides both basic and advanced configuration options. If you're looking for a simpler solution, consider implementing mui-rte.
Quill and TipTap are excellent alternatives. They offer a wide range of rich text editor features along with well-documented examples and guides.
API
RHFRichTextEditorProps accepts the following props. Props marked with * are required.
| Name | Type | Description |
|---|---|---|
fieldName* | string | Name of the field registered with React Hook Form. This prop is required for all components. |
control* | UseFormControl | |
registerOptions | RegisterOptions | React Hook Form validation rules. Useful when not using a schema validation library such as Yup or Joi. |
required | boolean | Indicates that the field is mandatory by adding an asterisk symbol (*) to the formLabel. This visual cue helps users quickly identify required fields in the form. |
id | string | The context ID. When this property changes, the component restarts the context with its editor and reinitializes it based on the current configuration. Removed in v4.1.3 as the internally generated field id was already being assigned to the CKEditor instance. |
editorConfig | EditorConfig | A Configuration object for CkEditor to customize formatting controls and toolbar positioning, as per requirement. Refer to the toolbar positioning guide for more details. |
onReady | (editor: ClassicEditor) => void | A function called when the context is ready and all editors inside were (re)initialized with the context instance. |
onFocus | (event: EventInfo<string, unknown>, editor: ClassicEditor) => void | Callback function triggered when the editor gains focus. |
onBlur | (event: EventInfo<string, unknown>, editor: ClassicEditor) => void | Callback function triggered when the editor gains loses focus. |
customOnChange | ({ rhfOnChange, newValue, event, editor }) => void | Custom change handler that overrides the default editor value update behavior. Receives rhfOnChange, the updated HTML string, change event, and CKEditor instance. When provided, you must call rhfOnChange manually to update the form value. |
onValueChange | ({ newValue, event, editor }) => void | Optional callback fired after the editor content changes and the updated HTML string is stored in the form field. Receives the latest editor value, change event, and CKEditor instance. |
disabled | boolean | Disables the component and prevents user interaction. |
label | ReactNode | The text to render in the FormLabel component. By default, the value of fieldName is transformed (e.g., "firstName" to "First Name") using the fieldNameToLabel function. |
showLabelAboveFormField | boolean | Renders the form label above the field. |
formLabelProps | FormLabelProps | FormLabelProps to customise FormLabel component for a field. Multiple fields can be configured using the ConfigProvider component. |
hideLabel | boolean | Hides the FormLabel component if you don’t want to display the default form label component or prefer to render a fully custom label instead. |
onError | (error: Error, details: ErrorDetails) => void | A function called when the editor has crashed during the initialization or runtime. It receives two arguments: the error instance and the error details. |
renderError | (error: FieldError) => ReactNode | Custom renderer for the React Hook Form field error. Receives the current field error and returns the content to display, such as error.message or a custom React element in the HelperText component.Added in v4.1.0. |
hideErrorMessage | boolean | A flag to prevent replacement of helper text of a field by the errorMessage when the validation is triggered. |
helperText | ReactNode | The content to display within the FormHelperText component below the field. If the field validation fails, this content will be overridden by the corresponding error message. |
formHelperTextProps | FormHelperTextProps | FormHelperTextProps to customise FormHelperText component for a field. Multiple fields can be configured using the ConfigProvider component. |
customIds | { field, label, helperText, error } | Overrides the default field, label, helper text, and error IDs used for accessibility. |
Source Code
View the full implementation of this component on GitHub.

