Inline styles in draft-js
Tags: react, wysiwyg, draftjs, contenidojs
Published: 2023-05-26 10:17:12

## What is this post about?

This tutorial is about inline styles in draft-js and for your. In this post, You will learn how to implement an editor with basic and custom inline styles. This tutorial is helpful if you're new to draft-js and web editors or looking for a way to implement an editor for your comment box or similar use cases.

## Table of contents

- [Requirements](#requirements)
- [Installation](#installation)
- [Create Editor Component](#create-editor-component)
- [Add Placeholder](#add-placeholder)
- [Add Toolbar Buttons](#add-toolbar-buttons)
- [Custom Inline Styles](#custom-inline-style)
- [Result and Demo](#result-demo)

## <a name='requirements'></a> Requirements

To be able to create an editor, the only requirement is to know how to set up a ReactJS (or NextJs) project. We're going to use [draft-js](https://draftjs.org/) and [contenido](https://contenidojs.com/) packages in this tutorial.

### <a name='darft-js'></a> Draft JS

Draft-js is a rich text editor framework for React. You can read [this post](https://www.contenidojs.com/blog/a-brief-introduction-of-draft-js) to get familiar with that.

### <a name='contenido'></a> Contenido

Contenido is a library with a set of tools to help you create your rich text editor on top of draft-js. We use contenido to boost the development process and avoid repetitive processes.

Read More: [A brief introduction to contenido](https://www.contenidojs.com/blog/a-brief-introduction-to-contenido)

## <a name='installation'></a> Installation

First, we will install React and Typescript (There will be a JS alternative for everything, so stick with your favorite one).

```bash
# typescript
npx create-react-app draft-inline-styles --template typescript

# javascript
npx create-react-app draft-inline-styles
```

After the installation is done, install `draft-js` and `contenido` with this command:

```bash
# typescript
npm i draft-js @types/draft-js contenido@latest

# javascript
npm i draft-js  contenido@latest
```

## <a name='create-editor-component'></a> Create Editor component

After setting up the project, create a `components` folder in the `src` directory and then create the `Editor` component:

Typescript:

```tsx
// src > components > Editor.tsx
import { useState } from 'react';
import { EditorState } from 'draft-js';
import { Editor } from 'contenido';

// Types
import type { FC } from 'react';

// Custom Types
export interface InlineStylesEditorProps {}

const InlineStylesEditor: FC<InlineStylesEditorProps> = (props) => {
  // States
  const [editorState, setEditorState] = useState(EditorState.createEmpty());

  return (
    <div>
      <div>// We'll add the toolbar here</div>
      <div>
        <Editor editorState={editorState} onChange={setEditorState} />
      </div>
    </div>
  );
};
```

Javascript:

```jsx
// src > components > Editor.jsx
import { useState } from 'react';
import { EditorState } from 'draft-js';
import { Editor } from 'contenido';

const InlineStylesEditor = (props) => {
  // States
  const [editorState, setEditorState] = useState(EditorState.createEmpty());

  return (
    <div>
      <div>// We'll add the toolbar here</div>
      <div>
        <Editor editorState={editorState} onChange={setEditorState} />
      </div>
    </div>
  );
};
```

to make the editor much more visible add the border styles to the div:

```tsx
...
      <div style={{
         border: '1px solid #252525',
         borderRadius: '0.5rem',
         padding: '0.5rem 1rem',
        }}
      >
        <Editor editorState={editorState} onChange={setEditorState} />
      </div>
...
```

## <a name='add-placeholder'></a> Add Placeholder

Also adding a placeholder can be useful, so:

```tsx
...
        <Editor
          editorState={editorState}
          onChange={setEditorState}
         placeholder='Write here...'
        />
...
```

## <a name='add-toolbar-buttons'></a> Add Toolbar Buttons

Now, it's time to add the buttons to the toolbar. First we will create an array of buttons to map them in the toolbar. After that, we use JS `map` to render the buttons.

Added lines are the same for both Javascript and Typescript:

```tsx
// src > components > Editor.tsx
import { useState } from 'react';
import { EditorState } from 'draft-js';
import { Editor } from 'contenido';

// Types
import type { FC } from 'react';

// Custom Types
export interface InlineStylesEditorProps {}

const buttons = [
  { title: 'Bold' },
  { title: 'Italic' },
  { title: 'Underline' },
  { title: 'Line Through' },
  { title: 'Overline' },
  { title: 'Sup' },
  { title: 'Sub' },
];

const InlineStylesEditor: FC<InlineStylesEditorProps> = (props) => {
  // States
  const [editorState, setEditorState] = useState(EditorState.createEmpty());

  return (
    <div>
      <div>
        {buttons.map((button) => (
          <button
            key={button.title}
            style={{
              minWidth: '2rem',
              padding: '0.5rem',
              borderRadius: '0.5rem',
              border: 'none',
              cursor: 'pointer',
            }}
          >
            {button.title}
          </button>
        ))}
      </div>
      <div>
        <Editor
          editorState={editorState}
          onChange={setEditorState}
          placeholder='Write here...'
        />
      </div>
    </div>
  );
};
```

For all of these buttons, there is a utility in `contenido` so we just need to import and add the handler for each button.

```tsx
// src > components > Editor.tsx
import { useState } from 'react';
import { EditorState } from 'draft-js';
import {
  Editor,
  initialStyleMap,
  toggleBold,
  toggleItalic,
  toggleLineThrough,
  toggleOverline,
  toggleSub,
  toggleSup,
  toggleUnderline,
} from 'contenido';

// Types
import type { FC } from 'react';

// Custom Types
export interface InlineStylesEditorProps {}

const buttons = [
  { title: 'Bold', handler: toggleBold },
  { title: 'Italic', handler: toggleItalic },
  { title: 'Underline', handler: toggleUnderline },
  { title: 'Line Through', handler: toggleLineThrough },
  { title: 'Overline', handler: toggleOverline },
  { title: 'Sup', handler: toggleSup },
  { title: 'Sub', handler: toggleSub },
];

const InlineStylesEditor: FC<InlineStylesEditorProps> = (props) => {
  // States
  const [editorState, setEditorState] = useState(EditorState.createEmpty());

  return (
    <div>
      <div>
        {buttons.map((button) => (
          <button
            key={button.title}
            style={{
              minWidth: '2rem',
              padding: '0.5rem',
              borderRadius: '0.5rem',
              border: 'none',
              cursor: 'pointer',
            }}
            onMouseDown={(e) => {
              e.preventDefault();
              button.handler(editorState, setEditorState);
            }}
          >
            {button.title}
          </button>
        ))}
      </div>
      <div>
        <Editor
          editorState={editorState}
          onChange={setEditorState}
          customStyleMap={initialStyleMap}
          placeholder='Write here...'
        />
      </div>
    </div>
  );
};
```

The editor basic inline style tools should work properly up to this point, but as an improvement we can change the style of selected button to illustrate the difference. There is also a function for each of this inline styles to find out if it is the selected style or not.

```tsx
// src > components > Editor.tsx
import { useState } from 'react';
import { EditorState } from 'draft-js';
import {
  Editor,
  initialStyleMap,
  isBold,
  isItalic,
  isLineThrough,
  isOverline,
  isSub,
  isSup,
  isUnderline,
  toggleBold,
  toggleItalic,
  toggleLineThrough,
  toggleOverline,
  toggleSub,
  toggleSup,
  toggleUnderline,
} from 'contenido';

// Types
import type { FC } from 'react';

// Custom Types
export interface InlineStylesEditorProps {}

const buttons = [
  { title: 'Bold', handler: toggleBold, checker: isBold },
  { title: 'Italic', handler: toggleItalic, checker: isItalic },
  { title: 'Underline', handler: toggleUnderline, checker: isUnderline },
  { title: 'Line Through', handler: toggleLineThrough, checker: isLineThrough },
  { title: 'Overline', handler: toggleOverline, checker: isOverline },
  { title: 'Sup', handler: toggleSup, checker: isSup },
  { title: 'Sub', handler: toggleSub, checker: isSub },
];

const InlineStylesEditor: FC<InlineStylesEditorProps> = (props) => {
  // States
  const [editorState, setEditorState] = useState(EditorState.createEmpty());

  return (
    <div>
      <div>
        {buttons.map((button) => (
          <button
            key={button.title}
            style={{
              minWidth: '2rem',
              padding: '0.5rem',
              backgroundColor: button.checker(editorState)
                ? '#4cb5f5'
                : 'rgba(125, 125, 125, 0.25)',
              color: button.checker(editorState) ? '#fff' : 'inherit',
              borderRadius: '0.5rem',
              border: 'none',
              cursor: 'pointer',
            }}
            onMouseDown={(e) => {
              e.preventDefault();
              button.handler(editorState, setEditorState);
            }}
          >
            {button.title}
          </button>
        ))}
      </div>
      <div>
        <Editor
          editorState={editorState}
          onChange={setEditorState}
          customStyleMap={initialStyleMap}
          placeholder='Write here...'
        />
      </div>
    </div>
  );
};
```

## <a name='custom-inline-style'></a> Custom Inline Styles

Although, contenido have a utility for the most common use cases but you can also have your custom inline styles. As an example, You can have buttons to change the text transformations or to change the text font or background color. We will add a support for the background color to our editor to have an example of inline style customization for `draft-js` as well. Approach will be the same for all valid CSS styles.

First we set a key for each of our colors:

```tsx
...
const HIGHLIGHT = 'HIGHLIGHT';
...
```

Then update the buttons array by importing `toggleInlineStyle` and `hasInlineStyleOf` from contenido (for Typescript we import `State` and `StateHandler` types as well):

Typescript:

```tsx
const buttons = [
  { title: 'Bold', handler: toggleBold, checker: isBold },
  { title: 'Italic', handler: toggleItalic, checker: isItalic },
  { title: 'Underline', handler: toggleUnderline, checker: isUnderline },
  { title: 'Line Through', handler: toggleLineThrough, checker: isLineThrough },
  { title: 'Overline', handler: toggleOverline, checker: isOverline },
  { title: 'Sup', handler: toggleSup, checker: isSup },
  { title: 'Sub', handler: toggleSub, checker: isSub },
  {
    title: 'Highlight',
    handler: (editorState: State, stateHandler: StateHandler) =>
      toggleInlineStyle(editorState, stateHandler, HIGHLIGHT),
    checker: (editorState: State) => hasInlineStyleOf(editorState, HIGHLIGHT),
  },
];
```

Javascript:

```tsx
const buttons = [
  { title: 'Bold', handler: toggleBold, checker: isBold },
  { title: 'Italic', handler: toggleItalic, checker: isItalic },
  { title: 'Underline', handler: toggleUnderline, checker: isUnderline },
  { title: 'Line Through', handler: toggleLineThrough, checker: isLineThrough },
  { title: 'Overline', handler: toggleOverline, checker: isOverline },
  { title: 'Sup', handler: toggleSup, checker: isSup },
  { title: 'Sub', handler: toggleSub, checker: isSub },
  {
    title: 'Highlight',
    handler: (editorState, stateHandler) =>
      toggleInlineStyle(editorState, stateHandler, HIGHLIGHT),
    checker: (editorState) => hasInlineStyleOf(editorState, HIGHLIGHT),
  },
];
```

The final step is to create a style map object and use it instead of the `initialStyleMap`. It's important to spread the initial one in order to keep the other styles functionality:

```tsx
...
  const styleMap = {
    ...initialStyleMap,
    HIGHLIGHT: { backgroundColor: '#fdffb4' },
  };
...
```

and then replace it like this:

```tsx
...
  <Editor
    editorState={editorState}
    onChange={setEditorState}
    customStyleMap={styleMap}
    placeholder='Write here...'
  />
...
```

## <a name='result-demo'></a>Result

You can see the demo of this post on [this link](https://www.contenidojs.com/blog/inline-styles-in-draft-js#result-demo).


---

[![A banner of become a backer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fnb2hbuk8abtl1amqxhi.jpg)](https://hpouyanmehr.com/backers#donate)

Hi! I'm **Hosein Pouyanmehr**. I enjoy sharing what I learn and what I find interesting. Let's connect on [LinkedIn](https://www.linkedin.com/in/hpouyanmehr/).

See my code interests on [GitHub](https://github.com/HPouyanmehr).