{"id":3601,"date":"2022-10-07T17:07:16","date_gmt":"2022-10-07T12:07:16","guid":{"rendered":"https:\/\/www.edopedia.com\/blog\/?p=3601"},"modified":"2022-10-07T17:07:19","modified_gmt":"2022-10-07T12:07:19","slug":"how-to-make-wysiwyg-rich-text-editor-using-react-draft-js","status":"publish","type":"post","link":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/","title":{"rendered":"How to Make WYSIWYG Rich Text Editor Using React &#038; Draft.js"},"content":{"rendered":"\n<p>In this article, you will learn how to make WYSIWYG rich text editor using React and Draft.js. The source code of this React WYSIWYG editor is given below.<\/p>\n\n\n\n<p>At the end of this article, I have provided links to view the Live Demo and Download the full source code from GitHub.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Project Screenshot<\/h2>\n\n\n\n<p>Here&#8217;s a screenshot of how our WYSIWYG Editor Using React and Draft.js will look.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"508\" src=\"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/10\/react_draftjs_wysiwyg_editor.gif\" alt=\"WYSIWYG Editor Using React and Draft.js\" class=\"wp-image-3603\"\/><figcaption>WYSIWYG Editor Using React and Draft.js<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Features<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Configurable toolbar with option to add\/remove controls.<\/li><li>Option to change the order of the controls in the toolbar.<\/li><li>Option to add custom controls to the toolbar.<\/li><li>Option to change styles and icons in the toolbar.<\/li><li>Option to show toolbar only when editor is focused.<\/li><li>Support for inline styles: Bold, Italic, Underline, StrikeThrough, Code, Subscript, Superscript.<\/li><li>Support for block types: Paragraph, H1 &#8211; H6, Blockquote, Code.<\/li><li>Support for setting font-size and font-family.<\/li><li>Support for ordered \/ unordered lists and indenting.<\/li><li>Support for text-alignment.<\/li><li>Support for coloring text or background.<\/li><li>Support for adding \/ editing links<\/li><li>Choice of more than 150 emojis.<\/li><li>Support for mentions.<\/li><li>Support for hashtags.<\/li><li>Support for adding \/ uploading images.<\/li><li>Support for aligning images, setting height, width.<\/li><li>Support for Embedded links, flexibility to set height and width.<\/li><li>Option provided to remove added styling.<\/li><li>Option of undo and redo.<\/li><li>Configurable behavior for RTL and Spellcheck.<\/li><li>Support for placeholder.<\/li><li>Support for WAI-ARIA Support attributes<\/li><li>Using editor as controlled or un-controlled React component.<\/li><li>Support to convert Editor Content to HTML, JSON, Markdown.<\/li><li>Support to convert the HTML generated by the editor back to editor content.<\/li><li>Support for internationalization.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Source Code of React WYSIWYG Editor Using Draft.js<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">package.json<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application\/json&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}\">{\n  &quot;name&quot;: &quot;react-draft-wysiwyg&quot;,\n  &quot;version&quot;: &quot;1.15.0&quot;,\n  &quot;description&quot;: &quot;A wysiwyg on top of DraftJS.&quot;,\n  &quot;main&quot;: &quot;dist\/react-draft-wysiwyg.js&quot;,\n  &quot;repository&quot;: {\n    &quot;type&quot;: &quot;git&quot;,\n    &quot;url&quot;: &quot;https:\/\/github.com\/jpuri\/react-draft-wysiwyg.git&quot;\n  },\n  &quot;author&quot;: &quot;Jyoti Puri&quot;,\n  &quot;devDependencies&quot;: {\n    &quot;@babel\/core&quot;: &quot;^7.7.4&quot;,\n    &quot;@babel\/preset-env&quot;: &quot;^7.7.4&quot;,\n    &quot;@babel\/preset-react&quot;: &quot;^7.7.4&quot;,\n    &quot;@babel\/register&quot;: &quot;^7.7.4&quot;,\n    &quot;@storybook\/react&quot;: &quot;^5.2.8&quot;,\n    &quot;autoprefixer&quot;: &quot;^9.7.3&quot;,\n    &quot;babel-eslint&quot;: &quot;^10.0.3&quot;,\n    &quot;babel-loader&quot;: &quot;^8.0.6&quot;,\n    &quot;babel-plugin-transform-flow-strip-types&quot;: &quot;^6.22.0&quot;,\n    &quot;chai&quot;: &quot;^4.2.0&quot;,\n    &quot;cross-env&quot;: &quot;^6.0.3&quot;,\n    &quot;css-loader&quot;: &quot;^3.2.1&quot;,\n    &quot;draft-js&quot;: &quot;^0.11.2&quot;,\n    &quot;draftjs-to-html&quot;: &quot;^0.9.0&quot;,\n    &quot;draftjs-to-markdown&quot;: &quot;^0.6.0&quot;,\n    &quot;embed-video&quot;: &quot;^2.0.4&quot;,\n    &quot;enzyme&quot;: &quot;^3.10.0&quot;,\n    &quot;enzyme-adapter-react-16&quot;: &quot;^1.15.1&quot;,\n    &quot;eslint&quot;: &quot;^6.7.2&quot;,\n    &quot;eslint-config-airbnb&quot;: &quot;^18.0.1&quot;,\n    &quot;eslint-plugin-import&quot;: &quot;^2.18.2&quot;,\n    &quot;eslint-plugin-jsx-a11y&quot;: &quot;^6.2.3&quot;,\n    &quot;eslint-plugin-mocha&quot;: &quot;^6.2.2&quot;,\n    &quot;eslint-plugin-react&quot;: &quot;^7.17.0&quot;,\n    &quot;file-loader&quot;: &quot;^5.0.2&quot;,\n    &quot;flow-bin&quot;: &quot;^0.113.0&quot;,\n    &quot;immutable&quot;: &quot;^4.0.0-rc.12&quot;,\n    &quot;jsdom&quot;: &quot;^15.2.1&quot;,\n    &quot;mini-css-extract-plugin&quot;: &quot;^0.8.0&quot;,\n    &quot;mocha&quot;: &quot;^6.2.2&quot;,\n    &quot;postcss-loader&quot;: &quot;^3.0.0&quot;,\n    &quot;precss&quot;: &quot;^4.0.0&quot;,\n    &quot;react&quot;: &quot;^16.12.0&quot;,\n    &quot;react-addons-test-utils&quot;: &quot;^15.6.2&quot;,\n    &quot;react-dom&quot;: &quot;^16.12.0&quot;,\n    &quot;react-test-renderer&quot;: &quot;^16.12.0&quot;,\n    &quot;rimraf&quot;: &quot;^3.0.0&quot;,\n    &quot;sinon&quot;: &quot;^7.5.0&quot;,\n    &quot;style-loader&quot;: &quot;^1.0.1&quot;,\n    &quot;uglifyjs-webpack-plugin&quot;: &quot;^2.2.0&quot;,\n    &quot;url-loader&quot;: &quot;^3.0.0&quot;,\n    &quot;webpack&quot;: &quot;^4.41.2&quot;,\n    &quot;webpack-bundle-analyzer&quot;: &quot;^3.6.0&quot;,\n    &quot;webpack-cli&quot;: &quot;^3.3.10&quot;\n  },\n  &quot;dependencies&quot;: {\n    &quot;classnames&quot;: &quot;^2.2.6&quot;,\n    &quot;draftjs-utils&quot;: &quot;^0.10.2&quot;,\n    &quot;html-to-draftjs&quot;: &quot;^1.5.0&quot;,\n    &quot;linkify-it&quot;: &quot;^2.2.0&quot;,\n    &quot;prop-types&quot;: &quot;^15.7.2&quot;\n  },\n  &quot;peerDependencies&quot;: {\n    &quot;draft-js&quot;: &quot;^0.10.x || ^0.11.x&quot;,\n    &quot;immutable&quot;: &quot;3.x.x || 4.x.x&quot;,\n    &quot;react&quot;: &quot;0.13.x || 0.14.x || ^15.0.0-0 || 15.x.x || ^16.0.0-0 || ^16.x.x || ^17.x.x || ^18.x.x&quot;,\n    &quot;react-dom&quot;: &quot;0.13.x || 0.14.x || ^15.0.0-0 || 15.x.x || ^16.0.0-0 || ^16.x.x || ^17.x.x || ^18.x.x&quot;\n  },\n  &quot;scripts&quot;: {\n    &quot;clean&quot;: &quot;rimraf dist&quot;,\n    &quot;build:webpack&quot;: &quot;cross-env NODE_ENV=production webpack  --mode production --config config\/webpack.config.js&quot;,\n    &quot;build&quot;: &quot;npm run clean &amp;&amp; npm run build:webpack&quot;,\n    &quot;test&quot;: &quot;cross-env BABEL_ENV=test mocha --require config\/test-compiler.js config\/test-setup.js src\/**\/*Test.js&quot;,\n    &quot;lint&quot;: &quot;eslint src&quot;,\n    &quot;lintdocs&quot;: &quot;eslint docs\/src&quot;,\n    &quot;flow&quot;: &quot;flow; test $? -eq 0 -o $? -eq 2&quot;,\n    &quot;check&quot;: &quot;npm run lint &amp;&amp; npm run flow&quot;,\n    &quot;storybook&quot;: &quot;start-storybook -p 6006&quot;,\n    &quot;build-storybook&quot;: &quot;build-storybook&quot;\n  },\n  &quot;license&quot;: &quot;MIT&quot;\n}<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">index.js<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;text\/javascript&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JavaScript&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;js&quot;}\">import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport {\n  Editor,\n  EditorState,\n  RichUtils,\n  convertToRaw,\n  convertFromRaw,\n  CompositeDecorator,\n  getDefaultKeyBinding,\n} from 'draft-js';\nimport {\n  changeDepth,\n  handleNewLine,\n  blockRenderMap,\n  getCustomStyleMap,\n  extractInlineStyle,\n  getSelectedBlocksType,\n} from 'draftjs-utils';\nimport classNames from 'classnames';\nimport ModalHandler from '..\/event-handler\/modals';\nimport FocusHandler from '..\/event-handler\/focus';\nimport KeyDownHandler from '..\/event-handler\/keyDown';\nimport SuggestionHandler from '..\/event-handler\/suggestions';\nimport blockStyleFn from '..\/utils\/BlockStyle';\nimport { mergeRecursive } from '..\/utils\/toolbar';\nimport { hasProperty, filter } from '..\/utils\/common';\nimport { handlePastedText } from '..\/utils\/handlePaste';\nimport Controls from '..\/controls';\nimport getLinkDecorator from '..\/decorators\/Link';\nimport getMentionDecorators from '..\/decorators\/Mention';\nimport getHashtagDecorator from '..\/decorators\/HashTag';\nimport getBlockRenderFunc from '..\/renderer';\nimport defaultToolbar from '..\/config\/defaultToolbar';\nimport localeTranslations from '..\/i18n';\nimport '.\/styles.css';\nimport '..\/..\/css\/Draft.css';\n\nclass WysiwygEditor extends Component {\n  constructor(props) {\n    super(props);\n    const toolbar = mergeRecursive(defaultToolbar, props.toolbar);\n    const wrapperId = props.wrapperId\n      ? props.wrapperId\n      : Math.floor(Math.random() * 10000);\n    this.wrapperId = `rdw-wrapper-${wrapperId}`;\n    this.modalHandler = new ModalHandler();\n    this.focusHandler = new FocusHandler();\n    this.blockRendererFn = getBlockRenderFunc(\n      {\n        isReadOnly: this.isReadOnly,\n        isImageAlignmentEnabled: this.isImageAlignmentEnabled,\n        getEditorState: this.getEditorState,\n        onChange: this.onChange,\n      },\n      props.customBlockRenderFunc\n    );\n    this.editorProps = this.filterEditorProps(props);\n    this.customStyleMap = this.getStyleMap(props);\n    this.compositeDecorator = this.getCompositeDecorator(toolbar);\n    const editorState = this.createEditorState(this.compositeDecorator);\n    extractInlineStyle(editorState);\n    this.state = {\n      editorState,\n      editorFocused: false,\n      toolbar,\n    };\n  }\n\n  componentDidMount() {\n    this.modalHandler.init(this.wrapperId);\n  }\n  \/\/ todo: change decorators depending on properties recceived in componentWillReceiveProps.\n\n  componentDidUpdate(prevProps) {\n    if (prevProps === this.props) return;\n    const newState = {};\n    const { editorState, contentState } = this.props;\n    if (!this.state.toolbar) {\n      const toolbar = mergeRecursive(defaultToolbar, toolbar);\n      newState.toolbar = toolbar;\n    }\n    if (\n      hasProperty(this.props, 'editorState') &amp;&amp;\n      editorState !== prevProps.editorState\n    ) {\n      if (editorState) {\n        newState.editorState = EditorState.set(editorState, {\n          decorator: this.compositeDecorator,\n        });\n      } else {\n        newState.editorState = EditorState.createEmpty(this.compositeDecorator);\n      }\n    } else if (\n      hasProperty(this.props, 'contentState') &amp;&amp;\n      contentState !== prevProps.contentState\n    ) {\n      if (contentState) {\n        const newEditorState = this.changeEditorState(contentState);\n        if (newEditorState) {\n          newState.editorState = newEditorState;\n        }\n      } else {\n        newState.editorState = EditorState.createEmpty(this.compositeDecorator);\n      }\n    }\n    if (\n      prevProps.editorState !== editorState ||\n      prevProps.contentState !== contentState\n    ) {\n      extractInlineStyle(newState.editorState);\n    }\n    if (Object.keys(newState).length) this.setState(newState);\n    this.editorProps = this.filterEditorProps(this.props);\n    this.customStyleMap = this.getStyleMap(this.props);\n  }\n\n  onEditorBlur = () =&gt; {\n    this.setState({\n      editorFocused: false,\n    });\n  };\n\n  onEditorFocus = event =&gt; {\n    const { onFocus } = this.props;\n    this.setState({\n      editorFocused: true,\n    });\n    const editFocused = this.focusHandler.isEditorFocused();\n    if (onFocus &amp;&amp; editFocused) {\n      onFocus(event);\n    }\n  };\n\n  onEditorMouseDown = () =&gt; {\n    this.focusHandler.onEditorMouseDown();\n  };\n\n  keyBindingFn = event =&gt; {\n    if (event.key === 'Tab') {\n      const { onTab } = this.props;\n      if (!onTab || !onTab(event)) {\n        const editorState = changeDepth(\n          this.state.editorState,\n          event.shiftKey ? -1 : 1,\n          4\n        );\n        if (editorState &amp;&amp; editorState !== this.state.editorState) {\n          this.onChange(editorState);\n          event.preventDefault();\n        }\n      }\n      return null;\n    }\n    if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n      if (SuggestionHandler.isOpen()) {\n        event.preventDefault();\n      }\n    }\n    return getDefaultKeyBinding(event);\n  };\n\n  onToolbarFocus = event =&gt; {\n    const { onFocus } = this.props;\n    if (onFocus &amp;&amp; this.focusHandler.isToolbarFocused()) {\n      onFocus(event);\n    }\n  };\n\n  onWrapperBlur = event =&gt; {\n    const { onBlur } = this.props;\n    if (onBlur &amp;&amp; this.focusHandler.isEditorBlur(event)) {\n      onBlur(event, this.getEditorState());\n    }\n  };\n\n  onChange = editorState =&gt; {\n    const { readOnly, onEditorStateChange } = this.props;\n    if (\n      !readOnly &amp;&amp;\n      !(\n        getSelectedBlocksType(editorState) === 'atomic' &amp;&amp;\n        editorState.getSelection().isCollapsed\n      )\n    ) {\n      if (onEditorStateChange) {\n        onEditorStateChange(editorState, this.props.wrapperId);\n      }\n      if (!hasProperty(this.props, 'editorState')) {\n        this.setState({ editorState }, this.afterChange(editorState));\n      } else {\n        this.afterChange(editorState);\n      }\n    }\n  };\n\n  setWrapperReference = ref =&gt; {\n    this.wrapper = ref;\n  };\n\n  setEditorReference = ref =&gt; {\n    if (this.props.editorRef) {\n      this.props.editorRef(ref);\n    }\n    this.editor = ref;\n  };\n\n  getCompositeDecorator = toolbar =&gt; {\n    const decorators = [\n      ...this.props.customDecorators,\n      getLinkDecorator({\n        showOpenOptionOnHover: toolbar.link.showOpenOptionOnHover,\n      }),\n    ];\n    if (this.props.mention) {\n      decorators.push(\n        ...getMentionDecorators({\n          ...this.props.mention,\n          onChange: this.onChange,\n          getEditorState: this.getEditorState,\n          getSuggestions: this.getSuggestions,\n          getWrapperRef: this.getWrapperRef,\n          modalHandler: this.modalHandler,\n        })\n      );\n    }\n    if (this.props.hashtag) {\n      decorators.push(getHashtagDecorator(this.props.hashtag));\n    }\n    return new CompositeDecorator(decorators);\n  };\n\n  getWrapperRef = () =&gt; this.wrapper;\n\n  getEditorState = () =&gt; this.state ? this.state.editorState : null;\n\n  getSuggestions = () =&gt; this.props.mention &amp;&amp; this.props.mention.suggestions;\n\n  afterChange = editorState =&gt; {\n    setTimeout(() =&gt; {\n      const { onChange, onContentStateChange } = this.props;\n      if (onChange) {\n        onChange(convertToRaw(editorState.getCurrentContent()));\n      }\n      if (onContentStateChange) {\n        onContentStateChange(convertToRaw(editorState.getCurrentContent()));\n      }\n    });\n  };\n\n  isReadOnly = () =&gt; this.props.readOnly;\n\n  isImageAlignmentEnabled = () =&gt; this.state.toolbar.image.alignmentEnabled;\n\n  createEditorState = compositeDecorator =&gt; {\n    let editorState;\n    if (hasProperty(this.props, 'editorState')) {\n      if (this.props.editorState) {\n        editorState = EditorState.set(this.props.editorState, {\n          decorator: compositeDecorator,\n        });\n      }\n    } else if (hasProperty(this.props, 'defaultEditorState')) {\n      if (this.props.defaultEditorState) {\n        editorState = EditorState.set(this.props.defaultEditorState, {\n          decorator: compositeDecorator,\n        });\n      }\n    } else if (hasProperty(this.props, 'contentState')) {\n      if (this.props.contentState) {\n        const contentState = convertFromRaw(this.props.contentState);\n        editorState = EditorState.createWithContent(\n          contentState,\n          compositeDecorator\n        );\n        editorState = EditorState.moveSelectionToEnd(editorState);\n      }\n    } else if (\n      hasProperty(this.props, 'defaultContentState') ||\n      hasProperty(this.props, 'initialContentState')\n    ) {\n      let contentState =\n        this.props.defaultContentState || this.props.initialContentState;\n      if (contentState) {\n        contentState = convertFromRaw(contentState);\n        editorState = EditorState.createWithContent(\n          contentState,\n          compositeDecorator\n        );\n        editorState = EditorState.moveSelectionToEnd(editorState);\n      }\n    }\n    if (!editorState) {\n      editorState = EditorState.createEmpty(compositeDecorator);\n    }\n    return editorState;\n  };\n\n  filterEditorProps = props =&gt;\n    filter(props, [\n      'onChange',\n      'onEditorStateChange',\n      'onContentStateChange',\n      'initialContentState',\n      'defaultContentState',\n      'contentState',\n      'editorState',\n      'defaultEditorState',\n      'locale',\n      'localization',\n      'toolbarOnFocus',\n      'toolbar',\n      'toolbarCustomButtons',\n      'toolbarClassName',\n      'editorClassName',\n      'toolbarHidden',\n      'wrapperClassName',\n      'toolbarStyle',\n      'editorStyle',\n      'wrapperStyle',\n      'uploadCallback',\n      'onFocus',\n      'onBlur',\n      'onTab',\n      'mention',\n      'hashtag',\n      'ariaLabel',\n      'customBlockRenderFunc',\n      'customDecorators',\n      'handlePastedText',\n      'customStyleMap',\n    ]);\n\n  getStyleMap = props =&gt; ({ ...getCustomStyleMap(), ...props.customStyleMap });\n\n  changeEditorState = contentState =&gt; {\n    const newContentState = convertFromRaw(contentState);\n    let { editorState } = this.state;\n    editorState = EditorState.push(\n      editorState,\n      newContentState,\n      'insert-characters'\n    );\n    editorState = EditorState.moveSelectionToEnd(editorState);\n    return editorState;\n  };\n\n  focusEditor = () =&gt; {\n    setTimeout(() =&gt; {\n      this.editor.focus();\n    });\n  };\n\n  handleKeyCommand = command =&gt; {\n    const {\n      editorState,\n      toolbar: { inline },\n    } = this.state;\n    if (inline &amp;&amp; inline.options.indexOf(command) &gt;= 0) {\n      const newState = RichUtils.handleKeyCommand(editorState, command);\n      if (newState) {\n        this.onChange(newState);\n        return true;\n      }\n    }\n    return false;\n  };\n\n  handleReturn = event =&gt; {\n    if (SuggestionHandler.isOpen()) {\n      return true;\n    }\n    const { editorState } = this.state;\n    const newEditorState = handleNewLine(editorState, event);\n    if (newEditorState) {\n      this.onChange(newEditorState);\n      return true;\n    }\n    return false;\n  };\n\n  handlePastedTextFn = (text, html) =&gt; {\n    const { editorState } = this.state;\n    const {\n      handlePastedText: handlePastedTextProp,\n      stripPastedStyles,\n    } = this.props;\n\n    if (handlePastedTextProp) {\n      return handlePastedTextProp(text, html, editorState, this.onChange);\n    }\n    if (!stripPastedStyles) {\n      return handlePastedText(text, html, editorState, this.onChange);\n    }\n    return false;\n  };\n\n  preventDefault = event =&gt; {\n    if (\n      event.target.tagName === 'INPUT' ||\n      event.target.tagName === 'LABEL' ||\n      event.target.tagName === 'TEXTAREA'\n    ) {\n      this.focusHandler.onInputMouseDown();\n    } else {\n      event.preventDefault();\n    }\n  };\n\n  render() {\n    const { editorState, editorFocused, toolbar } = this.state;\n    const {\n      locale,\n      localization: { locale: newLocale, translations },\n      toolbarCustomButtons,\n      toolbarOnFocus,\n      toolbarClassName,\n      toolbarHidden,\n      editorClassName,\n      wrapperClassName,\n      toolbarStyle,\n      editorStyle,\n      wrapperStyle,\n      uploadCallback,\n      ariaLabel,\n    } = this.props;\n\n    const controlProps = {\n      modalHandler: this.modalHandler,\n      editorState,\n      onChange: this.onChange,\n      translations: {\n        ...localeTranslations[locale || newLocale],\n        ...translations,\n      },\n    };\n    const toolbarShow =\n      editorFocused || this.focusHandler.isInputFocused() || !toolbarOnFocus;\n    return (\n      &lt;div\n        id={this.wrapperId}\n        className={classNames(wrapperClassName, 'rdw-editor-wrapper')}\n        style={wrapperStyle}\n        onClick={this.modalHandler.onEditorClick}\n        onBlur={this.onWrapperBlur}\n        aria-label=&quot;rdw-wrapper&quot;\n      &gt;\n        {!toolbarHidden &amp;&amp; (\n          &lt;div\n            className={classNames('rdw-editor-toolbar', toolbarClassName)}\n            style={{\n              visibility: toolbarShow ? 'visible' : 'hidden',\n              ...toolbarStyle,\n            }}\n            onMouseDown={this.preventDefault}\n            aria-label=&quot;rdw-toolbar&quot;\n            aria-hidden={(!editorFocused &amp;&amp; toolbarOnFocus).toString()}\n            onFocus={this.onToolbarFocus}\n          &gt;\n            {toolbar.options.map((opt, index) =&gt; {\n              const Control = Controls[opt];\n              const config = toolbar[opt];\n              if (opt === 'image' &amp;&amp; uploadCallback) {\n                config.uploadCallback = uploadCallback;\n              }\n              return &lt;Control key={index} {...controlProps} config={config} \/&gt;;\n            })}\n            {toolbarCustomButtons &amp;&amp;\n              toolbarCustomButtons.map((button, index) =&gt;\n                React.cloneElement(button, { key: index, ...controlProps })\n              )}\n          &lt;\/div&gt;\n        )}\n        &lt;div\n          ref={this.setWrapperReference}\n          className={classNames(editorClassName, 'rdw-editor-main')}\n          style={editorStyle}\n          onClick={this.focusEditor}\n          onFocus={this.onEditorFocus}\n          onBlur={this.onEditorBlur}\n          onKeyDown={KeyDownHandler.onKeyDown}\n          onMouseDown={this.onEditorMouseDown}\n        &gt;\n          &lt;Editor\n            ref={this.setEditorReference}\n            keyBindingFn={this.keyBindingFn}\n            editorState={editorState}\n            onChange={this.onChange}\n            blockStyleFn={blockStyleFn}\n            customStyleMap={this.getStyleMap(this.props)}\n            handleReturn={this.handleReturn}\n            handlePastedText={this.handlePastedTextFn}\n            blockRendererFn={this.blockRendererFn}\n            handleKeyCommand={this.handleKeyCommand}\n            ariaLabel={ariaLabel || 'rdw-editor'}\n            blockRenderMap={blockRenderMap}\n            {...this.editorProps}\n          \/&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    );\n  }\n}\n\nWysiwygEditor.propTypes = {\n  onChange: PropTypes.func,\n  onEditorStateChange: PropTypes.func,\n  onContentStateChange: PropTypes.func,\n  \/\/ initialContentState is deprecated\n  initialContentState: PropTypes.object,\n  defaultContentState: PropTypes.object,\n  contentState: PropTypes.object,\n  editorState: PropTypes.object,\n  defaultEditorState: PropTypes.object,\n  toolbarOnFocus: PropTypes.bool,\n  spellCheck: PropTypes.bool, \/\/ eslint-disable-line react\/no-unused-prop-types\n  stripPastedStyles: PropTypes.bool, \/\/ eslint-disable-line react\/no-unused-prop-types\n  toolbar: PropTypes.object,\n  toolbarCustomButtons: PropTypes.array,\n  toolbarClassName: PropTypes.string,\n  toolbarHidden: PropTypes.bool,\n  locale: PropTypes.string,\n  localization: PropTypes.object,\n  editorClassName: PropTypes.string,\n  wrapperClassName: PropTypes.string,\n  toolbarStyle: PropTypes.object,\n  editorStyle: PropTypes.object,\n  wrapperStyle: PropTypes.object,\n  uploadCallback: PropTypes.func,\n  onFocus: PropTypes.func,\n  onBlur: PropTypes.func,\n  onTab: PropTypes.func,\n  mention: PropTypes.object,\n  hashtag: PropTypes.object,\n  textAlignment: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  readOnly: PropTypes.bool,\n  tabIndex: PropTypes.number, \/\/ eslint-disable-line react\/no-unused-prop-types\n  placeholder: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  ariaLabel: PropTypes.string,\n  ariaOwneeID: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  ariaActiveDescendantID: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  ariaAutoComplete: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  ariaDescribedBy: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  ariaExpanded: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  ariaHasPopup: PropTypes.string, \/\/ eslint-disable-line react\/no-unused-prop-types\n  customBlockRenderFunc: PropTypes.func,\n  wrapperId: PropTypes.number,\n  customDecorators: PropTypes.array,\n  editorRef: PropTypes.func,\n  handlePastedText: PropTypes.func,\n};\n\nWysiwygEditor.defaultProps = {\n  toolbarOnFocus: false,\n  toolbarHidden: false,\n  stripPastedStyles: false,\n  localization: { locale: 'en', translations: {} },\n  customDecorators: [],\n};\n\nexport default WysiwygEditor;\n\n\/\/ todo: evaluate draftjs-utils to move some methods here\n\/\/ todo: move color near font-family<\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">styles.css<\/h3>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;css&quot;,&quot;mime&quot;:&quot;text\/css&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;CSS&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;css&quot;}\">.rdw-editor-main {\n  height: 100%;\n  overflow: auto;\n  box-sizing: border-box;\n}\n.rdw-editor-toolbar {\n  padding: 6px 5px 0;\n  border-radius: 2px;\n  border: 1px solid #F1F1F1;\n  display: flex;\n  justify-content: flex-start;\n  background: white;\n  flex-wrap: wrap;\n  font-size: 15px;\n  margin-bottom: 5px;\n  user-select: none;\n}\n.public-DraftStyleDefault-block {\n  margin: 1em 0;\n}\n.rdw-editor-wrapper:focus {\n  outline: none;\n}\n.rdw-editor-wrapper {\n  box-sizing: content-box;\n}\n.rdw-editor-main blockquote {\n  border-left: 5px solid #f1f1f1;\n  padding-left: 5px;\n}\n.rdw-editor-main pre {\n  background: #f1f1f1;\n  border-radius: 3px;\n  padding: 1px 10px;\n}<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Live Demo and Download<\/h2>\n\n\n<p><a class=\"ep_link_minor\" href=\"https:\/\/jpuri.github.io\/react-draft-wysiwyg\/#\/\" target=\"_blank\" rel=\"noopener\">Live Demo<\/a><a class=\"ep_link_major\" href=\"https:\/\/github.com\/jpuri\/react-draft-wysiwyg\" target=\"_blank\" rel=\"noopener\">Download<\/a><\/p>","protected":false},"excerpt":{"rendered":"<p>In this article, you will learn how to make WYSIWYG rich text editor using React and Draft.js. The source code of this React WYSIWYG editor is given below. At the end of this article, I have provided links to view the Live Demo and Download the full source code from GitHub. Project Screenshot Here&#8217;s a &#8230; <a title=\"How to Make WYSIWYG Rich Text Editor Using React &#038; Draft.js\" class=\"read-more\" href=\"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/\" aria-label=\"Read more about How to Make WYSIWYG Rich Text Editor Using React &#038; Draft.js\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":1762,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[112],"tags":[],"class_list":["post-3601","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>How to Make WYSIWYG Rich Text Editor Using React &amp; Draft.js<\/title>\n<meta name=\"description\" content=\"In this article, you will learn how to make WYSIWYG rich text editor using React and Draft.js. The source code of this React WYSIWYG editor is given\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Make WYSIWYG Rich Text Editor Using React &amp; Draft.js\" \/>\n<meta property=\"og:description\" content=\"In this article, you will learn how to make WYSIWYG rich text editor using React and Draft.js. The source code of this React WYSIWYG editor is given\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/\" \/>\n<meta property=\"og:site_name\" content=\"Edopedia\" \/>\n<meta property=\"article:author\" content=\"trulyfurqan\" \/>\n<meta property=\"article:published_time\" content=\"2022-10-07T12:07:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-10-07T12:07:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/10\/react_draftjs_wysiwyg_editor.gif\" \/>\n<meta name=\"author\" content=\"Furqan\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Furqan\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"How to Make WYSIWYG Rich Text Editor Using React & Draft.js","description":"In this article, you will learn how to make WYSIWYG rich text editor using React and Draft.js. The source code of this React WYSIWYG editor is given","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/","og_locale":"en_US","og_type":"article","og_title":"How to Make WYSIWYG Rich Text Editor Using React & Draft.js","og_description":"In this article, you will learn how to make WYSIWYG rich text editor using React and Draft.js. The source code of this React WYSIWYG editor is given","og_url":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/","og_site_name":"Edopedia","article_author":"trulyfurqan","article_published_time":"2022-10-07T12:07:16+00:00","article_modified_time":"2022-10-07T12:07:19+00:00","og_image":[{"url":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/10\/react_draftjs_wysiwyg_editor.gif","type":"","width":"","height":""}],"author":"Furqan","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Furqan","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#article","isPartOf":{"@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/"},"author":{"name":"Furqan","@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/person\/3951cb19e3aa56df09e408c98aa02339"},"headline":"How to Make WYSIWYG Rich Text Editor Using React &#038; Draft.js","datePublished":"2022-10-07T12:07:16+00:00","dateModified":"2022-10-07T12:07:19+00:00","mainEntityOfPage":{"@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/"},"wordCount":294,"commentCount":0,"publisher":{"@id":"https:\/\/www.edopedia.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#primaryimage"},"thumbnailUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/02\/default_featured_image.jpg","articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/","url":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/","name":"How to Make WYSIWYG Rich Text Editor Using React & Draft.js","isPartOf":{"@id":"https:\/\/www.edopedia.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#primaryimage"},"image":{"@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#primaryimage"},"thumbnailUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/02\/default_featured_image.jpg","datePublished":"2022-10-07T12:07:16+00:00","dateModified":"2022-10-07T12:07:19+00:00","description":"In this article, you will learn how to make WYSIWYG rich text editor using React and Draft.js. The source code of this React WYSIWYG editor is given","breadcrumb":{"@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#primaryimage","url":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/02\/default_featured_image.jpg","contentUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2022\/02\/default_featured_image.jpg","width":880,"height":495,"caption":"Default Featured Image"},{"@type":"BreadcrumbList","@id":"https:\/\/www.edopedia.com\/blog\/how-to-make-wysiwyg-rich-text-editor-using-react-draft-js\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.edopedia.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Make WYSIWYG Rich Text Editor Using React &#038; Draft.js"}]},{"@type":"WebSite","@id":"https:\/\/www.edopedia.com\/blog\/#website","url":"https:\/\/www.edopedia.com\/blog\/","name":"Edopedia","description":"Coding\/Programming Blog","publisher":{"@id":"https:\/\/www.edopedia.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.edopedia.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.edopedia.com\/blog\/#organization","name":"Edopedia","url":"https:\/\/www.edopedia.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2017\/10\/edopedia_icon_text_10.jpg","contentUrl":"https:\/\/www.edopedia.com\/blog\/wp-content\/uploads\/2017\/10\/edopedia_icon_text_10.jpg","width":400,"height":100,"caption":"Edopedia"},"image":{"@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.edopedia.com\/blog\/#\/schema\/person\/3951cb19e3aa56df09e408c98aa02339","name":"Furqan","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/e5e68aef3ad8f0b83d56f4953c512c8e57bd2e6dc64daec33b5d0495d9058f51?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/e5e68aef3ad8f0b83d56f4953c512c8e57bd2e6dc64daec33b5d0495d9058f51?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e5e68aef3ad8f0b83d56f4953c512c8e57bd2e6dc64daec33b5d0495d9058f51?s=96&d=mm&r=g","caption":"Furqan"},"description":"Well. I've been working for the past three years as a web designer and developer. I have successfully created websites for small to medium sized companies as part of my freelance career. During that time I've also completed my bachelor's in Information Technology.","sameAs":["http:\/\/www.edopedia.com\/blog\/","trulyfurqan"],"url":"https:\/\/www.edopedia.com\/blog\/author\/furqan\/"}]}},"_links":{"self":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts\/3601","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/comments?post=3601"}],"version-history":[{"count":2,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts\/3601\/revisions"}],"predecessor-version":[{"id":3604,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/posts\/3601\/revisions\/3604"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/media\/1762"}],"wp:attachment":[{"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/media?parent=3601"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/categories?post=3601"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.edopedia.com\/blog\/wp-json\/wp\/v2\/tags?post=3601"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}