Building a Rich Text Editor with React and Draft.js, Part 2.4: Embedding Images

This post is part of the second installment of a multi-part series about building a Rich Text Editor with React and Draft.js. For an introduction to the Draft.js framework with setup instructions, checkout Building a Rich Text Editor with Draft.js, Part 1: Basic Set Up. Links to additional posts in this series can be found below. The full codebase for my ongoing Draft.js project is available on GitHub. Feel free to checkout the deployed demo here.

In this post, I will review how to build the functionality to allow image embedding in your React text editor using tools provided by the Draft.js API.

Draft.js can be installed with:

For reference, I’ll be integrating these features into the React basic text editor I built for the demo discussed in the first post in this series, Building a Rich Text Editor with Draft.js, Part 1: Basic Set Up, the code for which is available on CodeSandbox.

Overview of Relevant Draft.js API Building Blocks

Custom Block Components

As touched upon in my post covering how to build a key command and button for embedding links, a is an immutable record that represents the full state of a single block of editor content, and together, are structured in an OrderedMap, which makes up the editor’s. The rendering of may be customized to render rich media content instead of text.

Demo: Building the Image Embedding Functionality

The following demonstration is based on the Media Editor example provided in the official Draft.js documentation.

Defining a Custom Renderer Object

Custom Block rendering relies on the prop, which, when passed to the Editor component as a prop, enables a higher-level component to define a rendering protocol based on the specified block type, text, or other criteria.

To begin, let’s build a function to define the prop in our Editor component.

Create an directory within , and within it create a file entitled . Start off by importing the following:

Then write create :

  • Check if the block passed in as an argument has type
  • Indicate the respective to render (in this example, we’ll be rendering , which we will build in a sec)
  • Pass the optional object includes props that will be passed through to the rendered custom component via the sub property object
  • Define the property as since the component is will not include text content

We’ll then import into our component and pass it to our component as a prop. **Note: in doing so, you’ll also need to pass as a prop as well¹:

If there isn’t a custom renderer object for the function to return, the will render the default text block component.

The benefit of defining the function within the context of a higher-level component is that it enables props to be bound to the custom component, allowing instance methods for custom component props.

Building the Media Custom Block Component and Image Entity

Next, we’ll create the entity and custom block component referenced in the object returned by the function:

Here, we first define entity type. We then create the component, we access the object and the record, which are made available within the custom component, along with the props ().

By grabbing the entity information from the and the map, we can access the entity metadata needed to render our custom component.

Building addImage()

Now that we have a custom block component to render an image, let’s build the functionality to insert the image into our editor.

To do so, we’ll update our component with the below steps:

  1. First, add to our list of imports in the component:

2. Define a function:

We’ll use as a callback in the event listener responsible for adding the image (described in greater detail in the below step).

3. Finally, we’ll build our function:

Here’s what’s happening:

  • The user is prompted to input the url source of an image
  • An entity is created with type (), mutability (), and data ()
  • Using the key of the newly created entity, we can then update our the
  • We’ll call once our has finished updating. This way, users will be able to immediately resume entering (or deleting) text upon the addition of the image¹.

Building ‘Add Image’ UI Button

Last but not least, we will create a UI button to which will be passed as a callback function, allowing users to access the new feature we just built:

Note: The button in my example is labeled with an icon from Google’s Material Icon catalogue.

And, there you go!

Demo Code is available here: https://codesandbox.io/embed/n0ozyqr9z4

¹ Note on Managing Focus:

But Siobhan, aren’t you breaking the declarative paradigm by using a ref to call directly on the component?

To the curious and incredulous readers who might be wondering why we are managing focus state here and whether this is advisable, this is indeed the recommended approach.

As highlighted in the DraftJS docs:

Managing text input focus can be a tricky task within React components. The browser focus/blur API is imperative, so setting or removing focus via declarative means purely through tends to feel awkward and incorrect, and it requires challenging attempts at controlling focus state.

With that in mind, at Facebook we often choose to expose methods on components that wrap text inputs. This breaks the declarative paradigm, but it also simplifies the work needed for engineers to successfully manage focus behavior within their apps.

The component follows this pattern, so there is a public method available on the component. This allows you to use a ref within your higher-level component to call directly on the component when needed.

The event listeners within the component will observe focus changes and propagate them through as expected, so state and DOM will remain correctly in sync.