11.5.0 • Published 2 years ago

react-mde v11.5.0

Weekly downloads
17,418
License
MIT
Repository
github
Last release
2 years ago

react-mde

A simple yet powerful and extensible Markdown Editor editor for React, inspired by GitHub. This is the editor used by http://aboutdevs.com.

image

Demos

Demos are provided through https://codesandbox.io. Feel free to fork and play with different options :smile:.

React-mde 3.* - JavaScript - Basic Demo

React-mde 3.* - TypeScript - Basic Demo

Installing

npm install --save react-mde

Dependencies

React-mde currently depends on:

  • Font Awesome - For the icons
  • Showdown - For rendering the markdown preview

React-mde used Font Awesome 4.7. as an NPM dependency before 3.. Now Font Awesome needs to be installed separately using your preferred method. The easiest is just add this to <head/>:

<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>

You also need NPM packages.

For React-mde 3.*:

npm install --save showdown

For React-mde 2.: (this will install Font Awesome 4.7.)

npm install --save showdown font-awesome

Using

React-mde is a completely controlled component. You can experiment with the props below forking the Codesandbox demos

Props:

  • value: The current value. An object with this structure {text: "", selection?: {start:0, end:2}, scrollTop?: number } where text is the text and selection is an object containing start and end representing what should be selected. Passing null to selection is perfectly fine.
  • onChange: Function that should handle the value. The value passed as a parameter to the onChange function is of the same type as the value prop above.
  • commands: An array of array of command objects ([[cmd1, cmd2],[cmd3, cmd4]]). The first array represents groups, the second represents commands inside that group. For an example, refer to how the getDefaultCommands() is implemented. How to create custom commands is explained below.
  • textAreaProps: Whatever you want to pass to the textarea component.
  • showdownFlavor: The Markdown flavor to use. This defaults to original, the original specs by John Gruber. Please refer to the Showdown documentation for the complete list of supported flavors.
  • showdownOptions: An object with options to be passed to Showdown. Please refer to the Showdown documentation for the complete list of options. Note that, unlike what happens by default with Showdown, the options passed here will override the defined flavor. Notice that tables, for example, are disabled by default in Showdown. So, in order to enable tables, you'd pass something like showdownOptions={{tables: true}} to ReactMde.
  • visibility: Determines which sub-components are visible. visibility is an object optionally containing these booleans: toolbar, textarea, preview and previewHelp. For example, in order to hide the preview, you can pass visibility:{{preview:false}}.
  • className: Custom className.
  • processHtml: A function that receives the HTML generated by Showdown and returned a processed/sanitized version before displaying it in the preview. This is useful to avoid XSS.

Styling

The following styles from React-mde should be added: (Both .scss and .css files are available. No need to use sass-loader if you don't want)

Easiest way: import react-mde-all.css:

import 'react-mde/lib/styles/css/react-mde-all.css';

If you want to have a more granular control over the styles, you can import each individual file:

import 'react-mde/lib/styles/css/react-mde.css';
import 'react-mde/lib/styles/css/react-mde-toolbar.css';
import 'react-mde/lib/styles/css/react-mde-textarea.css';
import 'react-mde/lib/styles/css/react-mde-preview.css';

If you're using SASS, you can override these variables: https://github.com/andrerpena/react-mde/blob/master/src/styles/variables.scss

You also need Font Awesome for the toolbar icons. React-mde 3. uses Font Awesome 5.*. React-mde 2.* uses Font Awesome 4..

Font Awesome 5 can be installed in different ways, but the easiest is just adding this to the <head/>:

<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>

If you're using React-mde 2.*, Font Awesome 4.* is required. After installing the NPM, import it in your JavaScript or TypeScript like this:

import '../node_modules/font-awesome/css/font-awesome.css';

Normalize is optional but it's used in the Demo.

import '../node_modules/normalize.css/normalize.css';

XSS concerns

Taken from the Markdown repository documentation:

Cross-side scripting is a well known technique to gain access to private information of the users of a website. The attacker injects spurious HTML content (a script) on the web page which will read the user’s cookies and do something bad with it (like steal credentials). As a countermeasure, you should filter any suspicious content coming from user input. Showdown doesn’t include an XSS filter, so you must provide your own. But be careful in how you do it…

React-mde does not automatically sanitize the Showdown generated HTML for you. However, you can use your preferred anti-XSS library and use the processHtml prop to sanitize the HTML before it is presented in the preview.

Commands

React-mde allows you to use the build-in commands, implement your own commands, or both.

There are two types of commands, the button commands (default if you don't say anything), and the dropdown commands. button commands appear as button and execute a single action when clicked. dropdown commands have subCommands and will display a dropdown when you click them.

Anatomy of a button command

You don't have to create your own commands at all, but if you want, this is how a command looks like:

const makeLinkCommand = {
    icon: 'link',
    tooltip:
        'Insert a link',
    execute:
        (text: string, selection: TextSelection) => {
            const {newText, insertionLength} = insertText(text, '[', selection.start);
            const finalText = insertText(newText, '](url)', selection.end + insertionLength).newText;
            return {
                text: finalText,
                selection: {
                    start: selection.start + insertionLength,
                    end: selection.end + insertionLength,
                },
            };
        },
};

props:

  • type: The type of the command.
  • icon: If this is a text, it will print a font-awesome <i/> element with the classes fa fa-${icon}. Passing bold will print <i className="fa fa-bold"></i>. If the passing value is a React element, it will print the react element.
  • tooltip: If any, it will print a tooltip with the passed text.
  • execute: The function that will actually execute the command. This function accepts 2 parameters: text, which is the textarea text before the command, and selection, an object containing start and end. Your function should return the new text and the new selection (after your command).

Anatomy of a dropdown command

{
	type: 'dropdown',
	icon: 'header',
	subCommands: [
		{
			content: <p className="header-1">Header</p>,
			execute: function (text, selection) {
				return makeHeader(text, selection, '# ');
			}
		},
		{
			content: <p className="header-2">Header</p>,
			execute: function (text, selection) {
				return makeHeader(text, selection, '## ');
			}
		},
		{
			content: <p className="header-3">Header</p>,
			execute: function (text, selection) {
				return makeHeader(text, selection, '### ');
			}
		}
	]
}

props:

  • type: The type of the command.
  • icon: If this is a text, it will print a font-awesome <i/> element with the classes fa fa-${icon}. Passing bold will print <i className="fa fa-bold"></i>. If the passing value is a React element, it will print the react element.
  • subCommands: A list of commands that will dropdown when you click the button.

subCommands is an array of objects with the following props:

  • content: A React component that will be displayed within the li.
  • execute: The function that will actually execute the command. This function accepts 2 parameters: text, which is the whole textarea text before your command, and selection, a 2 items array containing the beggining and end of the current selection. Your function should return the current text (after your command) and the current selection (after your command).

Composition and custom layouts

ReactMde is designed to make it easy for users to customize the layout, or to make any of it sub-components invisible, like the Preview, for instance. The ReactMde component, itself, just a thin layout wrapper for its 3 internal components with visibility options.

If you want to create your own layout, please take a look at the source code of the ReactMde component: https://github.com/andrerpena/react-mde/blob/master/src/ReactMde.tsx. It's easy to simply create your own, only leveraging the internal components you'd like and laying them out in the way you prefer

import * as ReactMde from 'react-mde';
// Now you have (among other utility modules):
// The ReactMde.ReactMdeToolbar component
// The ReactMde.ReactMdeTextArea component
// The ReactMde.ReactMdePreview component

Migrating

From 2. to 3.

Font Awesome 5 is now used, and it's not a NPM peer dependency anymore. It's up to you how to install it, it can be installed in different ways, but the easiest is just adding this to the <head/>:

<script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>

From 1. to 2.

2.* is a major refactoring of the code to improve composability

Major differences:

  • Now the main react-mde component is composed of 3 sub-components, the ReactMdeToolbar, the ReactMdeTextArea and the ReactMdePreview. You can import react-mde directly or import each of the sub-components and have more flexibility building your own layout.
  • Each sub-component now has its own CSS/SCSS file and we're now including a react-mde-all.css, or the SCSS alternative, for simplicity.
  • We realized that on version 1.*, it was difficult to select which components you wanted to use. So now...
import * as ReactMde from 'react-mde';
ReactMde.ReactMdeComponents // contains all components and you can select your own components
ReactMde.getDefaultComponents() // will return an array with all components

How to upgrade an existing 1.* JavaScript project: https://github.com/andrerpena/react-mde-js-demo/commit/e62af170fa258f7f17f29d41f395f24e9eaf3b72

How to upgrade an existing 1.* TypeScript project: https://github.com/andrerpena/react-mde-ts-demo/commit/d6718305c0132649cabca432e1e9415ea06cd643

From 0. to 1.

Major differences:

  • Instead of using the getDefaultCommands function, now the default commands are exported directly.
  • The textAreaId and textareaName props were replaced by textAreaProps that allows you to pass whatever you want to the textarea component.
  • The paths of the CSS and SCSS have changed.

Roadmap

Check the project here: https://github.com/andrerpena/react-mde/projects/1

Licence

React-mde is MIT licensed

About the author

Made with :heart: by André Pena. Check out my website: https://aboutdevs.com/andrerpena

12.0.7

2 years ago

12.0.8

2 years ago

12.0.3

2 years ago

12.0.4

2 years ago

12.0.5

2 years ago

12.0.6

2 years ago

11.2.0

3 years ago

11.5.0

3 years ago

11.4.0

3 years ago

11.3.0

3 years ago

11.1.0

3 years ago

11.0.7

3 years ago

11.0.6

3 years ago

11.0.5

3 years ago

11.0.4

3 years ago

11.0.3

3 years ago

11.0.2

3 years ago

11.0.0

4 years ago

10.3.0-alpha1

4 years ago

10.2.1

4 years ago

10.2.0

4 years ago

10.1.0

4 years ago

10.0.3

4 years ago

10.0.2

4 years ago

10.0.1

4 years ago

10.0.0

4 years ago

9.1.2

4 years ago

9.1.1

4 years ago

9.1.0

4 years ago

9.0.0

4 years ago

8.3.0

4 years ago

8.2.0

4 years ago

8.1.0

4 years ago

8.0.2

4 years ago

8.0.1

4 years ago

8.0.0

4 years ago

7.9.0

4 years ago

7.8.0

4 years ago

7.7.0

4 years ago

7.6.2

5 years ago

7.6.1

5 years ago

7.6.0

5 years ago

7.5.0

5 years ago

7.4.1

5 years ago

7.4.0

5 years ago

7.3.5

5 years ago

7.3.4

5 years ago

7.3.3

5 years ago

7.3.2

5 years ago

7.3.1

5 years ago

7.3.0

5 years ago

7.2.0

5 years ago

7.1.0

5 years ago

7.0.4

5 years ago

7.0.3

5 years ago

7.0.2

5 years ago

7.0.0

5 years ago

7.0.0-alpha.3

5 years ago

7.0.0-alpha.2

5 years ago

7.0.0-alpha.1

5 years ago

6.0.0

5 years ago

6.0.0-alpha.1

6 years ago

5.8.0

6 years ago

5.7.0

6 years ago

5.6.0

6 years ago

5.5.3

6 years ago

5.5.2

6 years ago

5.5.1

6 years ago

5.5.0-alpha.4

6 years ago

5.5.0-alpha.3

6 years ago

5.5.0-alpha.2

6 years ago

5.5.0-alpha.1

6 years ago

5.5.0

6 years ago

5.4.0

6 years ago

5.3.0

6 years ago

5.2.0

6 years ago

5.1.1

6 years ago

5.1.0

6 years ago

5.0.0

6 years ago

4.0.3

6 years ago

4.0.2

6 years ago

4.0.1

6 years ago

4.0.0

6 years ago

3.1.1

6 years ago

3.1.0

6 years ago

3.0.0

6 years ago

2.3.4

6 years ago

2.3.3

6 years ago

2.3.2

6 years ago

2.3.1

6 years ago

2.3.0

6 years ago

2.2.1

6 years ago

2.2.0

6 years ago

2.1.2

6 years ago

2.1.1

6 years ago

2.1.0

6 years ago

2.0.3

6 years ago

2.0.2

6 years ago

2.0.1

6 years ago

2.0.0

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago

1.0.0-rc1

6 years ago

0.9.5

7 years ago

0.9.4

7 years ago

0.9.3

7 years ago

0.9.2

7 years ago

0.9.1

7 years ago

0.9.0

7 years ago

0.0.3

7 years ago

0.0.2

7 years ago

0.0.1

7 years ago