Drag-n-Drop Email Editor Component for React.js

Last update: May 13, 2022

React Email Editor

The excellent drag-n-drop email editor by Unlayer as a React.js wrapper component. This is the most powerful and developer friendly visual email builder for your app.

Video Overview
React Email Editor
Watch video overview: https://youtu.be/MIWhX-NF3j8

Live Demo

Check out the live demo here: http://react-email-editor-demo.netlify.com/ (Source Code)

Blog Post

Here's a blog post with a quickstart guide: https://medium.com/unlayer-blog/creating-a-drag-n-drop-email-editor-with-react-db1e9eb42386

Installation

The easiest way to use React Email Editor is to install it from NPM and include it in your own React build process.

npm install react-email-editor --save

Usage

Require the EmailEditor component and render it with JSX:

import React, { useRef } from 'react';
import { render } from 'react-dom'

import EmailEditor from 'react-email-editor';

const App = (props) => {
  const emailEditorRef = useRef(null);

  const exportHtml = () => {
    emailEditorRef.current.editor.exportHtml((data) => {
      const { design, html } = data;
      console.log('exportHtml', html);
    });
  };

  const onLoad = () => {
    // you can load your template here;
    // const templateJson = {};
    // emailEditorRef.current.editor.loadDesign(templateJson);
  };

  return (
    <div>
      <div>
        <button onClick={exportHtml}>Export HTML</button>
      </div>
      
      <EmailEditor
        ref={emailEditorRef}
        onLoad={onLoad}
      />
    </div>
  );
};

render(<App />, document.getElementById('app'))

Methods

method params description
loadDesign Object data Takes the design JSON and loads it in the editor
saveDesign Function callback Returns the design JSON in a callback function
exportHtml Function callback Returns the design HTML and JSON in a callback function

See the example source for a reference implementation.

Properties

  • style Object style object for the editor container (default {})
  • minHeight String minimum height to initialize the editor with (default 500px)
  • onLoad Function called when the editor has finished loading
  • options Object options passed to the Unlayer editor instance (default {})
  • tools Object configuration for the built-in and custom tools (default {})
  • appearance Object configuration for appearance and theme (default {})
  • projectId Integer Unlayer project ID (optional)

See the Unlayer Docs for all available options.

Custom Tools

Custom tools can help you add your own content blocks to the editor. Every application is different and needs different tools to reach it's full potential. Learn More

Custom Tools

Localization

You can submit new language translations by creating a PR on this GitHub repo: https://github.com/unlayer/translations. Translations managed by PhraseApp

License

Copyright (c) 2021 Unlayer. MIT Licensed.

GitHub

https://github.com/unlayer/react-email-editor
Comments
  • 1. Custom Tools Implementation Not Working

    Hey so I'm trying to figure out how to implement a custom tool into the react component, and the link you provide takes me to the Unlayer documentation, which provides zero helpful documentation for this component. It assumes the user is using the CDN for Unlayer and has access to the unlayer global object, which makes all their documentation useless to me. I have the react-email-editor component set up in my project and working quite well, and I need to add several custom tools. I've been emailing back and forth with Unlayer Support trying to figure out how to add my own tool but the rep I talk to keeps going back to using the unlayer global object, and when I tried to patchwork a tool into the tools prop, nothing worked.

    An example of how to properly use the tools prop would be amazing.

    Reviewed by Tenurian at 2018-07-09 19:25
  • 2. Call onLoad callback only when editor it's loaded

    If external editor script it's already loaded, the onLoad function is called too fast, before React could register any ref to rendered component. And this causes further updates on the email editor impossible.

    Reviewed by hpaul at 2021-03-30 09:52
  • 3. Export HTML not updated

    Environment

    • FF 67.0b2
    • MacOS 10.14.3

    Test case

    • Open console
    • Add a button to canvas
    • Rename button "click"
    • Click on "Export HTML" button directly w/o clicking on editor canvas

    Expected Behavior

    • Output HTML for button is "click"

    Actual behvior

    • Output HTML for button is "Button Text"

    Reproduction link

    • https://codesandbox.io/s/ojr04wo6wy
    Reviewed by fr4nc0is at 2019-03-19 18:02
  • 4. Component calls home

    Hey there,

    I really like the editor, so first off thanks for publishing it.

    What I'm wondering about however: When loading the editor sends a GET request to one of your servers, passing the current domain as parameter, like the following: https://adminapi.unlayer.com/v1/editor/0?domain=yourdomain.com.

    Could you tell which other data you send to your servers? In addition to that, as your editor is being loaded as iframe, I think people might feel a little bit more secure if you used the sandbox property of iframes.

    Thanks in advance.

    Kind Regards, Florian

    Edit: Just saw that several events also send requests with the full refferer url: URL: https://adminapi.unlayer.com/v1/editor/0/events Payload: {"type":"editorReady","referrer":"http://domain.com/path/emaileditor.html?pagemode=iframe"} Examples for these events are the editorReady event on load and the exportHtml function for getting the editor's current state.

    I'm not really happy about this, is this solely for telemetry?

    Reviewed by DigitalFlow at 2019-02-04 12:53
  • 5. Text-editing capabilities are broken for Brave browser

    It looks like the following are broken for the Brave browser (it's Chromium based like Chrome so I'm surprised there are issues that don't pop up in Chrome):

    1. Editing a text field placed on a template
    2. Editing a header placed on a template

    Clicking the text field or header to alter its text gives the following error:

    editor.js:101 TypeError: Cannot read property 'getItem' of null at contentEditor.chunk.js:1 at contentEditor.chunk.js:1 at Object.<anonymous> (contentEditor.chunk.js:1) at Object.1406 (contentEditor.chunk.js:1) at i (editor.js:1) at Module.1232 (contentEditor.chunk.js:1) at i (editor.js:1)

    To reproduce:

    1. Install and open Brave via https://brave.com/download/
    2. Navigate to https://react-email-editor-demo.netlify.app/
    3. Add a text field to the template
    4. Click on the text field

    Expected result: The text field is editable like it is in Chrome

    Actual result: The entire editor turns white and the above error is reported in the console

    Reviewed by finaldave at 2021-02-02 19:41
  • 6. Custom tool missing on load

    When I trying to load my saved template vie load this.editor.loadDesign(design) all my custom tools show like "Missing", but when I clicked on this block it loading my tool.

    Example - https://prnt.sc/os5o9i

    Reviewed by niksib at 2019-08-13 15:02
  • 7. demo does not work on Chrome and Safari

    I tried the demo http://react-email-editor-demo.netlify.com/

    It works in Firefox, but does not work in Chrome and Safari. It only shows a top bar. Chrome 65.0.3325.31 Safari 11.0.3

    image

    Reviewed by Hongbo-Miao at 2018-02-05 21:41
  • 8. Set the default values of the editor on load

    I want to set the default background color to white and the content width to 100% but haven't found a good way to do this yet. I am using React v17.0.2

    The following code doesn't seem to work:

    unlayer.setBodyValues({
      backgroundColor: "#e7e7e7",
      contentWidth: "500px", // or percent "50%"
      fontFamily: {
        label: "Helvetica",
        value: "'Helvetica Neue', Helvetica, Arial, sans-serif"
      },
      preheaderText: "Hello World"
    });
    

    This code seems to work for 1 second, then the styles are reset to the email editor's defaults:

    emailEditorRef.current.editor.loadBlank({
              backgroundColor: "#fff",
            });
    

    Has anyone found a solution to this?

    Reviewed by anargiris at 2022-03-16 16:20
  • 9. "Upload image" Button missing when trying to use custom Media Library

    Setup

    1. Using free version without project id
    2. calling emailEditorRef.current?.editor?.registerCallback('selectImage', onSelectImageHandler); to make a custom function handle media selection

    Now there is, as expected, no drag and drop area, but there is also no button to open the library. Notice how in the following screenshot, only the URL field is visible: image

    Contrast that to the following screenshot form the examples: image

    Is this feature only available for paid plans? If yes, it would be great if it said so in the docs.

    Reviewed by pr0gr8mm3r at 2021-03-31 20:20
  • 10. Could not find a valid element for given id or className

    Hello, I'm using a react version of 16.13.1, I got the above issue while rendering EmailEditor of 1.1.1 version. Can someone help me.

    Thanks in advance.

    Reviewed by jayanthbondi at 2020-10-05 15:54
  • 11. Server side html render

    Not sure if I just missed it but, is there a way to render HTML via JSON without using the editor? I want to automate the emails that are created and currently store the JSON markdown in my database. When a user triggers an email, it should go to the database, grab the JSON markdown, convert to HTML, and then send off using an email package. Is this possible? Do I have to store the generated HTML in my database as well?

    Reviewed by munroe7 at 2019-07-02 15:36
  • 12. Renders twice with latest nextjs

    The default implementation renders twice using nextjs.

    I used this example: https://github.com/unlayer/react-email-editor

    and got this outcome image

    package.json

    {
      "name": "polyma",
      "version": "2.0.0",
      "private": true,
      "license": "The Unlicense",
      "scripts": {
        "dev": "next dev",
        "build": "next build",
        "start": "next start",
        "lint": "next lint",
        "fauna": "fgu"
      },
      "dependencies": {
        "@apollo/client": "^3.5.10",
        "@nivo/bar": "^0.79.1",
        "@nivo/core": "^0.79.0",
        "@nivo/line": "^0.79.1",
        "@tailwindcss/line-clamp": "^0.4.0",
        "framer-motion": "^6.3.0",
        "graphql": "^16.3.0",
        "next": "12.1.6",
        "react": "^18.0.0",
        "react-dnd": "^16.0.0",
        "react-dnd-html5-backend": "^16.0.0",
        "react-dom": "^18.0.0",
        "react-email-editor": "^1.6.0",
        "react-hook-form": "^7.30.0",
        "react-ranger": "^2.1.0",
        "react-use-copy-to-clipboard": "^1.0.1",
        "uuid": "^8.3.2"
      },
      "devDependencies": {
        "@fortawesome/fontawesome-svg-core": "^6.1.1",
        "@fortawesome/free-brands-svg-icons": "^6.1.1",
        "@fortawesome/free-regular-svg-icons": "^6.1.1",
        "@fortawesome/free-solid-svg-icons": "^6.1.1",
        "@fortawesome/react-fontawesome": "^0.1.18",
        "autoprefixer": "^10.4.4",
        "eslint": "^8.12.0",
        "eslint-config-next": "12.1.6",
        "fauna-gql-upload": "^2.4.3",
        "faunadb": "^4.5.4",
        "postcss": "^8.4.12",
        "sharp": "^0.30.3",
        "tailwind-scrollbar": "^1.3.1",
        "tailwindcss": "^3.0.24",
        "vercel": "^24.1.0"
      }
    }
    

    Full code Email.js

    import { useRef } from "react";
    import dynamic from "next/dynamic";
    
    const EmailEditor = dynamic(() => import("react-email-editor"), {
      ssr: false,
    });
    
    export default function Email() {
      const emailEditorRef = useRef(null);
    
      function saveDesign() {
        emailEditorRef.current.editor.saveDesign((design) => {
          console.log("saveDesign", design);
        });
      }
    
      function exportHtml() {
        emailEditorRef.current.editor.exportHtml((data) => {
          const { design, html } = data;
          console.log("exportHtml", html);
        });
      }
    
      function onLoad() {
        // editor instance is created
        // you can load your template here;
        // const templateJson = {};
        // emailEditorRef.current.editor.loadDesign(templateJson);
      }
    
      function onReady() {
        // editor is ready
        console.log("onReady");
      }
    
      return (
        <div className="">
          <div>
            <button onClick={saveDesign}>Save Design</button>
            <button onClick={exportHtml}>Export HTML</button>
          </div>
    
          <div className="h-screen overflow-hidden">
            <EmailEditor
              ref={emailEditorRef}
              onLoad={onLoad}
              onReady={onReady}
              // works for now, the second email builder gets hidden.
              // minHeight="100vh"
            />
          </div>
        </div>
      );
    }
    
    
    Reviewed by ari-motors-team at 2022-05-10 14:41
  • 13. In Legacy Templates mode, design:updated doesn't work when editor content changes

    unlayer.init({
      id: 'editor-container',
    })
    
    unlayer.loadDesign({
      classic: true,
      html: '<html><body><div>This is a legacy HTML template.</div></body></html>',
    })
    
    unlayer.addEventListener('design:updated', function(data) {
      console.log(data);
    })
    

    design:updated doesn't work when editor content changes

    Reviewed by Lmangoxx at 2022-04-28 07:56
  • 14. Bump url-parse from 1.4.0 to 1.4.7

    Bumps url-parse from 1.4.0 to 1.4.7.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-02-17 13:45
  • 15. Bump handlebars from 4.0.11 to 4.7.7

    Bumps handlebars from 4.0.11 to 4.7.7.

    Changelog

    Sourced from handlebars's changelog.

    v4.7.7 - February 15th, 2021

    • fix weird error in integration tests - eb860c0
    • fix: check prototype property access in strict-mode (#1736) - b6d3de7
    • fix: escape property names in compat mode (#1736) - f058970
    • refactor: In spec tests, use expectTemplate over equals and shouldThrow (#1683) - 77825f8
    • chore: start testing on Node.js 12 and 13 - 3789a30

    (POSSIBLY) BREAKING CHANGES:

    • the changes from version 4.6.0 now also apply in when using the compile-option "strict: true". Access to prototype properties is forbidden completely by default, specific properties or methods can be allowed via runtime-options. See #1633 for details. If you are using Handlebars as documented, you should not be accessing prototype properties from your template anyway, so the changes should not be a problem for you. Only the use of undocumented features can break your build.

    That is why we only bump the patch version despite mentioning breaking changes.

    Commits

    v4.7.6 - April 3rd, 2020

    Chore/Housekeeping:

    Compatibility notes:

    • Restored Node.js compatibility

    Commits

    v4.7.5 - April 2nd, 2020

    Chore/Housekeeping:

    • Node.js version support has been changed to v6+ Reverted in 4.7.6

    Compatibility notes:

    • Node.js < v6 is no longer supported Reverted in 4.7.6

    Commits

    v4.7.4 - April 1st, 2020

    Chore/Housekeeping:

    Compatibility notes:

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-02-11 00:36
  • 16. Bump ua-parser-js from 0.7.14 to 0.7.31

    Bumps ua-parser-js from 0.7.14 to 0.7.31.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-02-09 22:56
A generic drag-and-drop dataflow editor for React
A generic drag-and-drop dataflow editor for React

react-dataflow-editor A generic drag-and-drop dataflow editor for React. ✨ You can read about the design of this component in this blog post! Table of

May 13, 2022
A fully featured editor with drag and drop interface to create your README's with speed.

A fully featured editor with drag and drop interface to create your README's with speed.

Apr 11, 2022
Embeddable React widget for email template editor.
Embeddable React widget for email template editor.

Embeddable React widget for email template editor.

Jan 29, 2022
Monaco Editor for React - use the monaco-editor in any React application

Monaco Editor for React - use the monaco-editor in any React application without needing to use webpack (or rollup/parcel/etc) configuration files / plugins

May 11, 2022
React Trix rich text editor is react wrapper built for the Trix editor
React Trix rich text editor is react wrapper built for the Trix editor

React wrapper for Trix rich text editor created by Basecamp

Apr 4, 2022
for-editor - A markdown editor based on React

for-editor for-editor 是一个基于 react 的 markdown 语法编辑器 English Documents demo github 安装 npm install for-editor -S 使用 import React, { Component } from 'rea

May 13, 2022
Markdown-editor - React markdown editor with preview

React Markdown Editor with preview How cool would it be to have a markdown edito

Jan 11, 2022
Monaco Editor component for React.
Monaco Editor component for React.

Monaco Editor component for React.

May 10, 2022
⚛️📝The unofficial editor-js component for React
⚛️📝The unofficial editor-js component for React

?? DEMO CodeSandbox ?? Supported Official Plugin Paragraph (default) Embed Table List Warning Code Link Image Raw Header Quote Marker CheckList Delimi

May 13, 2022
A ubb editor component based on react

react-ubb-editor A ubb editor based on react See more at https://asukasong.github.io/react-ubb-editor/ Install with yarn yarn add @cc98/react-ubb-edit

Sep 14, 2020
React component for Froala WYSIWYG HTML Rich Text Editor.

React JS Froala WYSIWYG Editor react-froala-wyswiyg provides React bindings to the Froala WYSIWYG editor VERSION 3. Installation npm install react-fro

May 12, 2022
Teselagen's Open Source Vector/Plasmid Editor Component
 Teselagen's Open Source Vector/Plasmid Editor Component

Congrats, you've made it to the repo for Teselagen's Open Source Vector Editor Component Built With React & Redux Built for easy extensibility + embed

Apr 30, 2022
A Wysiwyg editor build on top of ReactJS and DraftJS. https://jpuri.github.io/react-draft-wysiwyg
A Wysiwyg editor build on top of ReactJS and DraftJS. https://jpuri.github.io/react-draft-wysiwyg

React Draft Wysiwyg A Wysiwyg editor built using ReactJS and DraftJS libraries. Demo Page. Features Configurable toolbar with option to add/remove con

May 12, 2022
Pure Rich-text Editor with TS & React-hooks

react-editor is FINALLY RETURNED !! It's been over 4 years... Now react-editor is BACK with fully TS & REACT-HOOKS integrated Rich-text Editor with TS

Apr 17, 2022
React wrapper for medium-editor

react-medium-editor React wrapper for medium-editor Demo http://wangzuo.github.io/react-medium-editor Installation npm install react-medium-editor --s

May 13, 2022
React wrapper for lightweight WYSIWYG editor Trumbowyg
React wrapper for lightweight WYSIWYG editor Trumbowyg

React-Trumbowyg React wrapper for trumbowyg. If you ❤️ library, please star it and upvote it on awesome-react-components Table of contents How do I ad

Jan 17, 2022
intuitive block based wysiwyg editor built with React and ProseMirror
intuitive block based wysiwyg editor built with React and ProseMirror

intuitive block based wysiwyg editor built with React and ProseMirror

May 11, 2022
⚡️ Full-featured visual editor and code generator for React using Chakra UI
⚡️ Full-featured visual editor and code generator for React using Chakra UI

OpenChakra is a visual editor for the best component library in town: Chakra UI ?? . Quickly draft components with the simple drag and drop UI.

May 14, 2022
Wysiwyg / Text editor components built using React and Prosemirror
Wysiwyg / Text editor components built using React and Prosemirror

Wysiwyg / Text editor components built using React and Prosemirror

May 15, 2022