# Integrating CKEditor 5 with React from CDN

CKEditor 5 has an official React integration that you can use to add a rich text editor to your application. It provides a `<CKEditor>` component that you configure with props for the editor build, its configuration, and event handlers. The component works with multiple editor types, including classic, inline, and decoupled (document). For the multi-root editor, use the dedicated [multi-root editor hook](react-multiroot-cdn.md). This guide will help you install and configure it to use the CDN distribution of CKEditor 5.

> **Create your own CKEditor 5**
>
> Check out our interactive Builder to quickly get a taste of CKEditor 5. It offers an easy-to-use user interface to help you configure, preview, and download the editor suited to your needs.
>
> * editor type,
> * the features you need,
> * the preferred framework (React, Angular, Vue or Vanilla JS),
> * the preferred distribution method.
>
> You get ready-to-use code tailored to your needs!
>
> [Check out our interactive Builder](https://builder.ckeditor.com/?redirect=docs)

<a id="quick-start">

## Quick start

This guide assumes that you already have a React project. If you do not have one, see the [React documentation](https://react.dev/learn/start-a-new-react-project) to learn how to create it.

> **Note**
>
> To use our Cloud CDN services, [create a free account](https://portal.ckeditor.com/checkout?plan=free). Learn more about [license key activation](../../../licensing/license-key-and-activation.md).

Start by installing the React integration for CKEditor 5 from npm:

```bash
npm install @ckeditor/ckeditor5-react
```

Once the integration is installed, create a new React component called `Editor.jsx`. It will use the `useCKEditorCloud` helper to load the editor code from the CDN and the `<CKEditor>` component to run it, both of which come from the above package. The following example shows a component with open source and premium CKEditor 5 plugins.

```js
import React from 'react';
import { CKEditor, useCKEditorCloud } from '@ckeditor/ckeditor5-react';

const CKEditorDemo = () => {
	const cloud = useCKEditorCloud( {
		version: '48.5.2',
		premium: true
	} );

	if ( cloud.status === 'error' ) {
		return <div>Error!</div>;
	}

	if ( cloud.status === 'loading' ) {
		return <div>Loading...</div>;
	}

	const {
		ClassicEditor,
		Essentials,
		Paragraph,
		Bold,
		Italic
	} = cloud.CKEditor;

	const { FormatPainter } = cloud.CKEditorPremiumFeatures;

	return (
		<CKEditor
			editor={ ClassicEditor }
			data={ '<p>Hello world!</p>' }
			config={ {
				licenseKey: '<YOUR_LICENSE_KEY>',
				plugins: [ Essentials, Paragraph, Bold, Italic, FormatPainter ],
				toolbar: [ 'undo', 'redo', '|', 'bold', 'italic', '|', 'formatPainter' ]
			} }
		/>
	);
};
```

In the above example, the `useCKEditorCloud` helper is used to load the editor code and plugins from CDN. The `premium` option is set to also load premium plugins. For more information about the `useCKEditorCloud` helper, see the [Loading CDN resources](../../../setup/loading-cdn-resources.md) guide.

<a id="component-properties">

## Component properties

The `<CKEditor>` component supports the following properties:

* `editor` (required) – The [`Editor`](../../../../api/module_core_editor_editor-Editor.md) constructor to use.
* `data` – The initial data for the created editor. See the [Getting and setting data](../../../setup/getting-and-setting-data.md) guide.
* `config` – The editor configuration. See the [Configuration](../../../setup/configuration.md) guide.
* `id` – The editor ID. When this property changes, the component restarts the editor with new data instead of setting it on an initialized editor.
* `disabled` – A Boolean value. The [`editor`](../../../../api/module_core_editor_editor-Editor.md) is being switched to read-only mode if the property is set to `true`.
* `disableWatchdog` – A Boolean value. If set to `true`, [the watchdog feature](../../../../features/watchdog.md) will be disabled. It is set to `false` by default.
* `watchdogConfig` – [Configuration object](../../../../api/module_watchdog_watchdog-WatchdogConfig.md) for the [watchdog feature](../../../../features/watchdog.md).
* `onReady` – A function called when the editor is ready with an [`editor`](../../../../api/module_core_editor_editor-Editor.md) instance. This callback is also called after the reinitialization of the component if an error occurred.
* `onAfterDestroy` – A function called after the successful destruction of an editor instance rendered by the component. This callback is also triggered after the editor has been reinitialized after an error. The component is not guaranteed to be mounted when this function is called.
* `onChange` – A function called when the editor data has changed. See the [`editor.model.document#change:data`](../../../../api/module_engine_model_document-ModelDocument.md#event-change:data) event.
* `onBlur` – A function called when the editor was blurred. See the [`editor.editing.view.document#blur`](../../../../api/module_engine_view_document-ViewDocument.md#event-blur) event.
* `onFocus` – A function called when the editor was focused. See the [`editor.editing.view.document#focus`](../../../../api/module_engine_view_document-ViewDocument.md#event-focus) event.
* `onError` – A function called when the editor has crashed during the initialization or during the runtime. It receives two arguments: the error instance and the error details. Error details is an object that contains two properties:
* `phase`: `'initialization'|'runtime'` – Informs when the error has occurred (during or after the editor/context initialization).
* `willEditorRestart` – When `true`, it means the editor component will restart itself.

The editor event callbacks (`onChange`, `onBlur`, `onFocus`) receive two arguments:

1. An [`EventInfo`](../../../../api/module_utils_eventinfo-EventInfo.md) object.
2. An [`Editor`](../../../../api/module_core_editor_editor-Editor.md) instance.

<a id="context-feature">

## Context feature

The [`@ckeditor/ckeditor5-react`](https://www.npmjs.com/package/@ckeditor/ckeditor5-react) package provides a ready-to-use component for the [context feature](../../../../features/collaboration/context-and-collaboration-features.md) that is useful to use with some [CKEditor 5 collaboration features](../../../../features/collaboration/collaboration.md).

```jsx
import React from 'react';
import { CKEditor, CKEditorContext, useCKEditorCloud } from '@ckeditor/ckeditor5-react';

export const CKEditorCloudContextDemo = () => {
	const cloud = useCKEditorCloud( {
		version: '48.5.2'
	} );

	if ( cloud.status === 'error' ) {
		return <div>Error!</div>;
	}

	if ( cloud.status === 'loading' ) {
		return <div>Loading...</div>;
	}

	const { ClassicEditor } = cloud.CKEditor;

	return (
		<CKEditorContext
			context={ ClassicEditor.Context }
			contextWatchdog={ ClassicEditor.ContextWatchdog }
			onChangeInitializedEditors={ editors => {
				console.log( 'Initialized editors:', editors );
			} }
		>
			<CKEditorNestedInstanceDemo
				name='editor1'
				content='<p>Editor 1</p>'
			/>

			<br />

			<CKEditorNestedInstanceDemo
				name='editor2'
				content='<p>Editor 2</p>'
			/>
		</CKEditorContext>
	);
};

function CKEditorNestedInstanceDemo( { name, content } ) {
	const cloud = useCKEditorCloud( {
		version: '48.5.2',
		premium: true
	} );

	if ( cloud.status === 'error' ) {
		console.error( cloud );
		return <div>Error!</div>;
	}

	if ( cloud.status === 'loading' ) {
		return <div>Loading...</div>;
	}

	const {
		ClassicEditor,
		Essentials,
		Paragraph,
		Bold,
		Italic,
		Mention
	} = cloud.CKEditor;

	return (
		<CKEditor
			contextItemMetadata={ {
				name
			} }
			editor={ ClassicEditor }
			data={ content }
			config={ {
				plugins: [
					Essentials,
					Paragraph,
					Bold,
					Italic,
					Mention
				],
				toolbar: {
					items: [ 'undo', 'redo', '|', 'bold', 'italic' ],
				}
			} }
		/>
	);
}
```

The `CKEditorContext` component supports the following properties:

* `context` (required) – [The CKEditor 5 context class](../../../../api/module_core_context-Context.md).
* `contextWatchdog` (required) – [The Watchdog context class](../../../../api/module_watchdog_contextwatchdog-ContextWatchdog.md).
* `config` – The CKEditor 5 context configuration.
* `isLayoutReady` – A property that delays the context creation when set to `false`. It creates the context and the editor children once it is `true` or unset. Useful when the CKEditor 5 annotations or a presence list are used.
* `id` – The context ID. When this property changes, the component restarts the context with its editor and reinitializes it based on the current configuration.
* `onChangeInitializedEditors` – A function called when any editor is initialized or destroyed in the tree. It receives a dictionary of fully initialized editors, where the key is the value of the `contextItemMetadata.name` property set on the `CKEditor` component. The editor’s ID is the key if the `contextItemMetadata` property is absent. Additional data can be added to the `contextItemMetadata` in the `CKEditor` component, which will be passed to the `onChangeInitializedEditors` function.
* `onReady` – A function called when the context is ready and all editors inside were initialized with the `context` instance. This callback is also called after the reinitialization of the component if an error has occurred.
* `onError` – A function called when the context has crashed during the initialization or during the runtime. It receives two arguments: the error instance and the error details. Error details is an object that contains two properties:
* `phase`: `'initialization'|'runtime'` – Informs when the error has occurred (during or after the editor/context initialization).
* `willContextRestart` – When `true`, it means that the context component will restart itself.

> **Note**
>
> An example build that exposes both context and classic editor can be found in the [CKEditor 5 collaboration sample](https://github.com/ckeditor/ckeditor5-collaboration-samples/tree/master/real-time-collaboration-comments-outside-of-editor).

<a id="how-to">

## How to?

<a id="using-the-document-editor-type">

### Using the document editor type

If you use the [document (decoupled) editor](../../../../framework/deep-dive/ui/document-editor.md), you need to [add the toolbar to the DOM manually](../../../../api/module_editor-decoupled_decouplededitor-DecoupledEditor.md#static-function-create):

```jsx
import { useEffect, useRef, useState } from 'react';
import { CKEditor } from '@ckeditor/ckeditor5-react';

function App() {
	const cloud = useCKEditorCloud( {
		version: '48.5.2'
	} );

	const editorToolbarRef = useRef( null );
	const [ isMounted, setMounted ] = useState( false );

	useEffect( () => {
		setMounted( true );

		return () => {
			setMounted( false );
		};
	}, [] );

	if ( cloud.status === 'error' ) {
		console.error( cloud );
		return <div>Error!</div>;
	}

	if ( cloud.status === 'loading' ) {
		return <div>Loading...</div>;
	}

	const { DecoupledEditor, Essentials, Paragraph, Bold, Italic } = cloud.CKEditor;

	return (
		<div>
			<div ref={ editorToolbarRef }></div>
			<div>
				{ isMounted && (
					<CKEditor
						editor={ DecoupledEditor }
						data='<p>Hello from CKEditor 5 decoupled editor!</p>'
						config={ {
							plugins: [ Essentials, Paragraph, Bold, Italic ],
							toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ]
						} }
						onReady={ ( editor ) => {
							if ( editorToolbarRef.current ) {
								editorToolbarRef.current.appendChild( editor.ui.view.toolbar.element );
							}
						}}
						onAfterDestroy={ ( editor ) => {
							if ( editorToolbarRef.current ) {
								Array.from( editorToolbarRef.current.children ).forEach( child => child.remove() );
							}
						} }
					/>
				) }
			</div>
		</div>
	);
}

export default App;
```

<a id="using-an-inline-editor">

### Using an inline editor

Single-root editors such as [`InlineEditor`](../../../../api/module_editor-inline_inlineeditor-InlineEditor.md), [`BalloonEditor`](../../../../api/module_editor-balloon_ballooneditor-BalloonEditor.md), and [`DecoupledEditor`](../../../../api/module_editor-decoupled_decouplededitor-DecoupledEditor.md) can be configured as inline editors that accept only inline content (text, bold, italic, links) instead of blocks. This is useful for short fields such as titles, captions, or single-line inputs.

Set [`root.modelElement`](../../../../api/module_core_editor_editorconfig-RootConfig.md#member-modelElement) to `'$inlineRoot'` to restrict the root to inline content. Optionally, provide a custom [`root.element`](../../../../api/module_core_editor_editorconfig-RootConfig.md#member-element) to render the editable host as a specific tag (for example, `<h1>` for a title) instead of the default `<div>`.

```jsx
import React from 'react';
import { CKEditor, useCKEditorCloud } from '@ckeditor/ckeditor5-react';

const InlineTitleDemo = () => {
	const cloud = useCKEditorCloud( {
		version: '48.5.2'
	} );

	if ( cloud.status === 'error' ) {
		return <div>Error!</div>;
	}

	if ( cloud.status === 'loading' ) {
		return <div>Loading...</div>;
	}

	const { BalloonEditor, Essentials, Bold, Italic } = cloud.CKEditor;

	return (
		<CKEditor
			editor={ BalloonEditor }
			config={ {
				licenseKey: '<YOUR_LICENSE_KEY>',
				plugins: [ Essentials, Bold, Italic ],
				toolbar: [ 'bold', 'italic' ],
				root: {
					element: 'h1',
					modelElement: '$inlineRoot',
					initialData: 'Document title',
					placeholder: 'Enter title...'
				}
			} }
		/>
	);
};
```

The `root.element` property accepts:

* A tag name string, for example `'h1'` or `'section'`.
* A descriptor object with `name`, `classes`, `styles`, and `attributes` fields.

Without `modelElement: '$inlineRoot'`, only the host tag changes – the schema still permits blocks inside the root.

> **Important**
>
> The `<CKEditor>` component always renders a `<div>` host for `ClassicEditor`, regardless of `root.element`. Classic editor wraps its toolbar and editable inside its own structure. Use `InlineEditor`, `BalloonEditor`, or `DecoupledEditor` to control the host element.

<a id="using-the-editor-with-collaboration-plugins">

### Using the editor with collaboration plugins

We provide **ready-to-use integration** featuring collaborative editing in a React application:

* [CKEditor 5 with real-time collaboration features](https://github.com/ckeditor/ckeditor5-collaboration-samples/tree/master/real-time-collaboration-for-react)

It is not mandatory to build an application on top of the above samples, however, it should help you get started.

<a id="localization">

### Localization

CKEditor 5 supports [multiple UI languages](../../../setup/ui-language.md), and so does the official React component. To translate the editor, pass the languages you need into the `translations` array inside the configuration of the `useCKEditorCloud` hook.

```jsx
import React from 'react';
import { CKEditor, useCKEditorCloud } from '@ckeditor/ckeditor5-react';

const CKEditorDemo = () => {
	const cloud = useCKEditorCloud( {
		version: '48.5.2',
		translations: [ 'es' ]
	} );

	if ( cloud.status === 'error' ) {
		return <div>Error!</div>;
	}

	if ( cloud.status === 'loading' ) {
		return <div>Loading...</div>;
	}

	const {
		ClassicEditor,
		Essentials,
		Bold,
		Italic,
		Paragraph
	} = cloud.CKEditor;

	return (
		<CKEditor
			editor={ ClassicEditor }
			data={ '<p>Hello world!</p>' }
			config={ {
				licenseKey: '<YOUR_LICENSE_KEY>',
				toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ],
				plugins: [ Bold, Essentials, Italic, Paragraph ],
			} }
		/>
	);
};
```

For more information, please refer to the [Setting the UI language](../../../setup/ui-language.md) guide.

<a id="typescript-support">

### TypeScript support

The official React integration for CKEditor 5 is written in TypeScript and fully supports it. If you use TypeScript in your project, you can use the `CKEditor` component without additional configuration. However, if you want to use some specific types from the CKEditor 5 packages, you can import them directly from a special package containing type definitions. Take a look at the following example:

```tsx
import React from 'react';
import { CKEditor, useCKEditorCloud } from '@ckeditor/ckeditor5-react';

import type { EventInfo } from 'https://cdn.ckeditor.com/typings/ckeditor5.d.ts';

const CKEditorDemo = () => {
	const cloud = useCKEditorCloud( {
		version: '48.5.2',
		translations: [ 'es' ]
	} );

	if ( cloud.status === 'error' ) {
		return <div>Error!</div>;
	}

	if ( cloud.status === 'loading' ) {
		return <div>Loading...</div>;
	}

	const {
		ClassicEditor,
		Essentials,
		Bold,
		Italic,
		Paragraph
	} = cloud.CKEditor;

	return (
		<CKEditor
			editor={ ClassicEditor }
			data={ '<p>Hello world!</p>' }
			config={ {
				licenseKey: '<YOUR_LICENSE_KEY>',
				toolbar: [ 'undo', 'redo', '|', 'bold', 'italic' ],
				plugins: [ Bold, Essentials, Italic, Paragraph ],
			} }
			onBlur={ ( event: EventInfo ) => {
				// your event handler
			} }
		/>
	);
};
```

In the example above, the `EventInfo` type is imported from the `https://cdn.ckeditor.com/typings/ckeditor5.d.ts` package, while the editor itself loads from the CDN. Note that `https://cdn.ckeditor.com/typings/ckeditor5.d.ts` is not an actual URL to the CKEditor 5 typings file but a synthetic TypeScript module providing typings for the editor. The `ckeditor5` package supplies the actual types, which depend on the `@ckeditor/ckeditor5-react` package.

Although this setup might seem complex, it prevents users from directly importing anything from the `ckeditor5` package, which could lead to duplicated code issues.

<a id="type-definitions-for-premium-features">

#### Type definitions for premium features

If you want to use types for premium features, you can import them similarly to the base editor types. Remember that you need to install the `ckeditor5-premium-features` package to use them. You can do it by running the following command:

```bash
npm install --save-dev ckeditor5-premium-features
```

After installing the package, you can import the types in the following way:

```html
<script setup>
// ...
import type { Mention } from 'https://cdn.ckeditor.com/typings/ckeditor5-premium-features.d.ts';
// ...
</script>
```

<a id="known-issues">

## Known issues

While type definitions for the base editor should be available out of the box, some bundlers do not install the `ckeditor5` package, which provides typing for the editor. If you encounter any issues with the type definitions, you can install the `ckeditor5` package manually:

```bash
npm install --save-dev ckeditor5
```

<a id="contributing-and-reporting-issues">

## Contributing and reporting issues

The source code of rich text editor component for React is available on GitHub in <https://github.com/ckeditor/ckeditor5-react>.

<a id="next-steps">

## Next steps

* See how to manipulate the editor’s data in the [Getting and setting data](../../../setup/getting-and-setting-data.md) guide.
* Refer to further guides in the [setup section](../../../setup/configuration.md) to see how to customize your editor further.
* Check the [features category](../../../../features/index.md) to learn more about individual features.

---

Full index of the CKEditor 5 documentation: [llms.txt](../../../../../llms.txt)
