Using a React component in a block widget
In this tutorial, you will learn how to implement an editor plugin that uses the power of the React library inside the CKEditor 5 widget ecosystem. You will build a “Product preview” feature which renders an actual React component inside the editor to display some useful information about the product.
Later on, you will use the “Product preview” feature to build a simple React application that displays an editor next to the list of available products, allowing the user to insert the product into the editor content by clicking it on the list.
If you want to see the final product of this tutorial before you plunge in, check out the demo.
# Before you start
There are a couple of things you should know before you start:
- Since you are here, you probably have at least some basic understanding of what React is and how it works. But what you might not know is that CKEditor 5 has an official rich text editor component for React and it will be one of the key features used in this tutorial. Learning how to use it in your project is a good place to start.
- In this tutorial, you are going to implement a block editor widget and that itself could give you a headache. It is recommended to at least skim through the Implementing a block widget tutorial to get a grip on editor widgets, their API, and possible use cases.
- Also, while it is not strictly necessary to read the Quick start guide before going through this tutorial, it may help you to get more comfortable with CKEditor 5 Framework before you dive into this tutorial.
- Various parts of the CKEditor 5 architecture section will be referenced as you go. While reading them is not necessary to finish this tutorial, it is recommended to read those guides at some point to get a better understanding of the mechanisms used in this tutorial.
If you want to use your own event handler for events triggered by your React component, you must wrap it with a container that has a data-cke-ignore-events
attribute to exclude it from the editor’s default handlers. Refer to Exclude DOM events from default handlers for more details.
# Let’s start
This guide assumes that you are familiar with yarn and your project uses yarn already. If not, see the yarn documentation. If you are using npm, you do not have to worry — you can perform the same installation tasks just as easily using corresponding npm commands.
First, install the packages needed to build and set up a basic React application with a CKEditor 5 instance.
Create a minimal webpack configuration and save it as webpack.config.js
in the root of the application. To learn more about using webpack with CKEditor 5 and React, check out the Integrating CKEditor 5 built from source section of the CKEditor 5 React component guide.
Create your project’s entry point and save it as app.js
, also in the root of the application:
Add an index.html
page next to the app.js
file:
Finally, build your project and see if everything worked well by opening the index page in your browser:
You should see a “Hello world” application in your web browser, which might not be much but it is a good start:
# Application structure
Nothing warms the heart of a developer like a good “Hello world!”. But you probably agree that what you created is not the most useful application and it is time to change that. In the next sections, you will create some React components and CKEditor 5 classes to bring some real logic to the application.
To keep some order in the project, you will put CKEditor classes in the /ckeditor
directory and React components in the /react
directory. Images and CSS styles will land in the /assets
directory. By the time you are finished with this tutorial, the structure of the project should look as follows:
# CKEditor classes
Create the CKEditor–side logic that supports product preview widgets in the editor content:
- The
ProductPreviewEditing
plugin will extend the editor data layers to support the new kind of content. - The
InsertProductPreviewCommand
provides an easy way for the “outside world” to insert product previews into the editor content.
This guide assumes you are familiar with the Implementing a block widget guide which explains the basic concepts behind data structures and widgets. If in doubt, please refer to that guide for more information.
# Editing plugin
The ProductPreviewEditing
plugin defines the productPreview
element in the editor model and specifies the way it is converted to the editing and data views.
Read more about the editing engine architecture of CKEditor 5.
- In the data view, the
productPreview
is represented as an empty<section class="product" data-id="..."></section>
element with adata-id
attribute associating it with a particular product. A semantic representation of the product saved in the database can be then consumed in the front–end by retrieving a fresh preview using thedata-id
. Since it does not carry any formatting or styling, the data representation will never get outdated, even if the layout or styles of the application change in the future. - In the editing view, on the other hand, the product preview is a block widget, which acts as a self–contained piece of content the user can insert, copy, and paste as a whole but they cannot change its internal structure. Inside the widget, there is a
UIElement
with a.product__react-wrapper
class that hosts a React<ProductPreview>
component. Each time the model element is upcasted, the rendering function specified in the editor configuration (editor.config.products.productRenderer
) mounts a React component inside theUIElement
.
We recommend using the official CKEditor 5 inspector for development and debugging. It will give you tons of useful information about the state of the editor such as internal data structures, selection, commands, and many more.
The differences in data representations of the product preview are summarized in the following table:
Data structure | Representation |
---|---|
Model | |
Editing view | |
Data view (editor output) |
Here is the full source code of the ProductPreviewEditing
editor plugin:
# Command
The InsertProductPreviewCommand
inserts the productPreview
element into the model at the current selection position. It is executed by the <ProductPreview>
React component in the application sidebar to insert a widget into the editor content.
Learn more about widget commands in the Implementing a block widget guide. You can see this command in action in the section about the main application component.
# React components
It is time to define the React side of the application that renders the actual layout:
- The
<ProductList>
component displays a bunch of<ProductPreview>
children and allows the user to click them to insert them into the editor. - The
<ProductPreview>
component represents a single product with its name, price tag and a background image. - The
<App>
component glues all the things together.
# Product list
The <ProductList>
React component renders instances of <ProductPreview>
. When clicked, the preview executes a callback passed in “props” that inserts its own copy into the editor content by executing the 'insertProduct'
editor command. The list is displayed in the sidebar of the application.
# Product preview
The actual preview of the product, with its name, price and an image. Instances of the <ProductPreview>
component populate both the <ProductList>
and the editor widgets in the content.
Clicking a preview in the sidebar executes the 'insertProduct'
editor command and inserts the same preview into the editor content.
# Main application component
So far, you have CKEditor classes that bring the product preview into the content, a list of products, and a product component ready. It is time to glue things together in the App
class.
You are going to extend the main application file skeleton that you created earlier in this tutorial so it renders the official <CKEditor>
React component on the left side, and the list of available products on the right.
Have a look at the full source code of the App
class:
The JavaScript code is ready, but to run the application you need to specify a couple of product definitions. Do that when mounting the <App>
component:
Please note that each product comes with its own image (e.g. product1.jpg
), which should be stored in the assets/
directory to load correctly with the CSS background-image
. Learn more about styles in the next section.
# Styles and assets
The application needs some styling to look good. You are going to put them in the assets/styles.css
file imported in your main HTML file (index.html
):
The product preview (.product-preview
class) uses background-image: var(--product-image)
to set its background. It means that all images must be stored in the assets/
directory next to the styles.css
file in order to load properly.
# Demo
You can see the entire application working below. Click the products in the sidebar to add them to the editor. You can also check out the full source code of this tutorial if you want to extend it further or use it as base for your application.
Product offer editor
Editor data
Products
- Colors of summer in Polandfrom $1500
- Mediterranean sun on Maltafrom $1899
- Tastes of Asiafrom $2599
- Exotic Indiafrom $2200
Tip: Clicking the product will add it to the editor.
# Full source code
The following code snippets present the complete implementation of the application (and all its dependencies). Follow the application structure diagram to re–create the application.
# app.js
# productpreviewediting.js
# insertproductpreviewcommand.js
# productlist.js
# productpreview.js
# index.html
Every day, we work hard to keep our documentation complete. Have you spotted an outdated information? Is something missing? Please report it via our issue tracker.