Skip to main content
Elastic UI
Elastic UI
Getting startedComponentsPatternsContentData visualization
EUI ChangelogGitHubFigma
  • Overview
  • Layout
  • Containers
  • Navigation
  • Display
  • Forms
  • Tabular content
  • Templates
  • Editors and syntax
    • Markdown
      • Format
      • Editor
      • Plugins
  • EUI
  • Editors and syntax
  • Markdown
  • Plugins

Plugins

Both

EuiMarkdownEditor and EuiMarkdownFormat utilize the same underlying plugin architecture to transform string based syntax into React components. At a high level Unified JS is used in combination with Remark to provide EUI's markdown components, which are separated into a parsing and processing layer. These two concepts are kept distinct in EUI components to provide concrete locations for your plugins to be injected, be it editing or rendering. Finally you provide UI to the component to handle interactions with the editor.

In addition to running the full pipeline, EuiMarkdownEditor uses just the parsing configuration to determine the input's validity, provide messages back to the application, and allow the toolbar buttons to interact with existing markdown tags.

Default plugins

EUI provides additional plugins by default, but these can be omitted or otherwise customized by providing the

parsingPluginList, processingPluginList, and uiPlugins props to the editor and formatter components.

The parsing plugins, responsible for parsing markdown are:

  1. remark-parse
  2. additional pre-processing for code blocks
  3. remark-emoji
  4. remark-breaks
  5. link validation for security
  6. injection of EuiCheckbox for markdown check boxes
  7. tooltip plugin parser

The above set provides an abstract syntax tree used by the editor to provide feedback, and the renderer passes that output to the set of processing plugins to allow it to be rendered:

  1. remark-rehype
  2. rehype-react
  3. tooltip plugin renderer

The last set of plugin configuration - uiPlugins - allows toolbar buttons to be defined and how they alter or inject markdown and returns with only one plugin:

  1. tooltip plugin ui

These plugin definitions can be obtained by calling getDefaultEuiMarkdownParsingPlugins, getDefaultEuiMarkdownProcessingPlugins, and getDefaultEuiMarkdownUiPlugins respectively.

Configuring the default plugins

The above plugin utils, as well as getDefaultEuiMarkdownPlugins, accept an optional configuration object of:

  • exclude: an array of default plugins to unregister
  • parsingConfig: allows overriding the configuration of any default parsing plugin
  • processingConfig: currently only accepts a linkProps key, which accepts any prop that EuiLink accepts

The below example has the emoji plugin excluded, and custom configuration on the link validator parsing plugin and link processing plugin. See the Props table for all plugin config options.

Loading...

Unregistering plugins

EuiMarkdownEditor comes with several default plugins, demo'd below. If these defaults are unnecessary for your use-case or context, you can unregister these plugins with a single exclude parameter passed to getDefaultEuiMarkdownPlugins(). This will ensure the syntax won't be identified or rendered, and no additional UI (like toolbar buttons or help syntax) will be displayed by the unregistered plugins.

Loading...

Plugin development

An EuiMarkdown plugin is comprised of three major pieces, which are passed separately as props.

โœ„๐˜—
tsx code block:
โœ„๐˜—<EuiMarkdownEditor uiPlugin={myPluginUI} parsingPluginList={myPluginParsingList} processingPluginList={myPluginProcessingList} {..otherProps} /> <!-- Note that the format component does not need a UI prop. --> <EuiMarkdownFormat parsingPluginList={myPluginParsingList} processingPluginList={myPluginProcessingList} />

uiPlugin
Provides the UI for the button in the toolbar as well as any modals or extra UI that provides content to the editor.
parsingPluginList
Provides the logic to identify the new syntax and parse it into an AST node.
processingPluginList
Provides the logic to process the new AST node into a React node.

uiPlugin

โœ„๐˜—
tsx code block:
โœ„๐˜—const myPluginUI = { name: 'myPlugin', button: { label: 'Chart', iconType: 'visArea', }, helpText: (<div>A node that explains how the syntax works</div>), editor: function editor({ node, onSave, onCancel }) { return ('something'); }, };

name
The name of your plugin. Use the button.label listed below if you need a more friendly display name. The button can be omitted if you wish the user to only utilize syntax to author the content.
button
Takes a label and an icon type. This forms the button that appear in the toolbar. Clicking the button will trigger either the editor or formatter.
editor
Provides UI controls (like an interactive modal) for how to build the initial content. Must exist if formatting does not.
formatting
If no editor is provided, this is an object defining how the plugins markdown tag is styled.
helpText
Contains a React node. Should contain some information and an example for how to utilize the syntax. Appears when the markdown icon is clicked on the bottom of the editor.

parsingPluginList

Remark-parse is used to parse the input text into markdown AST nodes. Its documentation for writing parsers is under the Extending the Parser section, but highlights are included below.

A parser is comprised of three pieces. There is a wrapping function which is provided to remark-parse and injects the parser, the parser method itself, and a locator function if the markdown tag is inline.

The parsing method is called at locations where its markdown down might be found at. The method is responsible for determining if the location is a valid tag, process the tag, and mark report the result.

Inline vs block

Inline tags are allowed at any point in text, and will be rendered somewhere within a <p> element. For better performance, inline parsers must provide a locate method which reports the location where their next tag might be found. They are not allowed to span multiple lines of the input.

Block tags are rendered inside <span> elements, and do not have a locate method. They can consume as much input text as desired, across multiple lines.

โœ„๐˜—
ts code block:
โœ„๐˜—// example plugin parser function EmojiMarkdownParser() { const Parser = this.Parser; const tokenizers = Parser.prototype.inlineTokenizers; const methods = Parser.prototype.inlineMethods; const emojiMap = { wave: '๐Ÿ‘‹', smile: '๐Ÿ˜€', plane: '๐Ÿ›ฉ', }; const emojiNames = Object.keys(emojiMap); // function to parse a matching string function tokenizeEmoji(eat, value, silent) { const tokenMatch = value.match(/^:(.*?):/); if (!tokenMatch) return false; // no match const [, emojiName] = tokenMatch; // ensure we know this one if (emojiNames.indexOf(emojiName) === -1) return false; if (silent) { return true; } // must consume the exact & entire match string return eat(`:${emojiName}:`)({ type: 'emojiPlugin', emoji: emojiMap[emojiName], // configuration is passed to the renderer }); } // function to detect where the next emoji match might be found tokenizeEmoji.locator = (value, fromIndex) => { return value.indexOf(':', fromIndex); }; // define the emoji plugin and inject it just before the existing text plugin tokenizers.emoji = tokenizeEmoji; methods.splice(methods.indexOf('text'), 0, 'emoji'); } // add the parser for `emojiPlugin` const parsingList = getDefaultEuiMarkdownParsingPlugins(); parsingList.push(EmojiMarkdownParser);

processingPluginList

After parsing the input into an AST, the nodes need to be transformed into React elements. This is performed by a list of processors, the default set converts remark AST into rehype and then into React. Plugins need to define themselves within this transformation process, identifying with the same type its parser uses in its eat call.

โœ„๐˜—
tsx code block:
โœ„๐˜—// example plugin processor // receives the configuration from the parser and renders const EmojiMarkdownRenderer = ({ emoji }) => { return <span>{emoji}</span>; }; // add the renderer for `emojiPlugin` const processingList = getDefaultEuiMarkdownProcessingPlugins(); processingList[1][1].components.emojiPlugin = EmojiMarkdownRenderer;

Putting it all together: a simple chart plugin

The below example takes the concepts from above to construct a simple chart embed that is initiated from a new button in the editor toolbar.

Note that the EuiMarkdownEditor and EuiMarkdownFormat examples utilize the same prop list. The editor manages additional controls through the uiPlugins prop.

Loading...
Edit this page

Previous
Editor
  • Default plugins
    • Configuring the default plugins
    • Unregistering plugins
  • Plugin development
    • uiPlugin
    • parsingPluginList
    • processingPluginList
    • Putting it all together: a simple chart plugin
EUI is dual-licensed under Elastic License 2.0 and Server Side Public License, v 1 | Crafted with โค by Elastic