Skip to content
Remirror
DocsAPIGitHub

Advanced Guide

One of the core goals of remirror is allowing developers to customise the editors they build fully.

Layered packages provide this flexibility, with each layer building on the abstractions found in the lower-level layers.

If you've followed the quickstart guide you'll have seen how to use the editors that remirror provides.

This guide aims to provide insights into how all the lower-level layers can be composed together to achieve the same world-class outcome.

Installation

First we need to install the core libraries

yarn add @emotion/core @remirror/core @remirror/react @remirror/core-extensions

The code

So we want to build a simple editor with a floating menu and the ability to make text bold, italic, add links, and insert code blocks.

To get started, we need to import the right extensions and set up the editor as follows.

import {
BoldExtension,
CodeBlockExtension,
HardBreakExtension,
HeadingExtension,
ItalicExtension,
LinkExtension,
LinkExtensionOptions,
UnderlineExtension,
} from '@remirror/core-extensions';
import {
asDefaultProps,
ManagedRemirrorProvider,
RemirrorExtension,
RemirrorManager,
RemirrorProps,
} from '@remirror/react';
import React, { FC, PureComponent } from 'react';
import { wysiwygEditorTheme } from '../theme';
interface State {
linkActivated: boolean;
}
export class MyAwesomeEditor extends PureComponent<{}, State> {
public state: State = {
linkActivated: false,
};
private activateLinkExtension = () => {
this.setState({ linkActivated: true });
};
private deactivateLinkExtension = () => {
this.setState({ linkActivated: false });
};
public render() {
return (
<RemirrorManager>
<RemirrorExtension Constructor={BoldExtension} />
<RemirrorExtension Constructor={UnderlineExtension} />
<RemirrorExtension Constructor={ItalicExtension} />
<RemirrorExtension<LinkExtensionOptions>
Constructor={LinkExtension}
activationHandler={this.activateLinkExtension}
/>
<RemirrorExtension Constructor={CodeBlockExtension} />
<ManagedRemirrorProvider>
// Later we will create an inner component
<div />
</ManagedRemirrorProvider>
</RemirrorManager>
);
}
}

Quite a lot has been introduced here.

  • <RemirrorManager /> - Injects an ExtensionManager instance into the react context.
  • <RemirrorExtension /> - Injects the extension (passed via the Constructor prop into the created extensionManager). These only activate if they are direct descendants of the `
  • <ManagedRemirrorProvider /> - This component automatically pulls the created extension manager instance from the context and passes it on to the underlying Remirror component. It also injects it's own context to all nested components, as we shall see soon.

Remirror automatically injects extensions into the context which is being picked up by the ManagedRemirrorProvider.

Now we need to create our internal editor. But before we do that, there's just one small change to make the initial code.

Prosemirror controls the underlying text editor, and unlike React, it needs a DOM node which it can render within. To that end, the Editor context provides a method called getRootProps which passes back a ref function that will be used to store the editor dom node.

Now let's create the inner editor.

/** @jsx jsx */
import { jsx } from '@emotion/core';
import { styled } from '@emotion/styled';
import { EDITOR_CLASS_SELECTOR } from '@remirror/core';
// Add styles to the editor instance
export const InnerEditorWrapper = styled.div`
height: 100%;
& * {
box-sizing: border-box;
}
${EDITOR_CLASS_SELECTOR}:focus {
outline: none;
}
${EDITOR_CLASS_SELECTOR} p {
margin: 0;
letter-spacing: 0.6px;
color: black;
}
p em {
letter-spacing: 1.2px;
}
${EDITOR_CLASS_SELECTOR} {
box-sizing: border-box;
position: relative;
/* border: 0.1px solid ${({ theme }) => theme.colors.border}; */
/* box-shadow: 0 0 0 1px ${({ theme }) => theme.colors.border}; */
line-height: 1.6em;
width: 100%;
font-size: ${({ theme }) => theme.font.size};
/* max-height: calc(90vh - 124px); */
min-height: 142px;
padding: 10px;
padding-right: 0;
font-weight: ${({ theme }) => theme.font.weight};
}
${EDITOR_CLASS_SELECTOR} a {
text-decoration: none !important;
color: ${props => props.theme.colors.primary};
}
${EDITOR_CLASS_SELECTOR} a.mention {
pointer-events: none;
cursor: default;
}
${EDITOR_CLASS_SELECTOR} .ProseMirror-selectednode {
background-color: rgb(245, 248, 250);
}
`;
const InnerEditor: FC<BubbleMenuProps> = ({
linkActivated,
deactivateLinkExtension,
activateLinkExtension,
}) => {
const { getRootProps } = useRemirrorContext();
return (
<EditorWrapper>
<BubbleMenu
linkActivated={linkActivated}
deactivateLinkExtension={deactivateLinkExtension}
activateLinkExtension={activateLinkExtension}
/>
<InnerEditorWrapper {...getRootProps()} />
</EditorWrapper>
);
};

Initializing the element Prose Mirror will Control

By setting childAsRoot to an object, Remirror will inject these props into the first child element.

You have two choices for instantiating Prosemirror:

Option 1: Simply set childAsRoot on <ManagedRemirrorProvider /> to be true and pass a single child HTML element (usually a div) to serve as the content editable Prosemirror will use.

Example:

<ManagedRemirrorProvider childAsRoot={true}>
<div />
</ManagedRemirrorProvider>

Option 2: Pass all of the props from getRootProps() (get this from the useRemirror context consumer hook).

Note the example above, or:

/** @jsx jsx */
import { jsx } from '@emotion/core'; // Needed to support `getRootProps`
import { useRemirrorContext, ManagedRemirrorProvider } from '@remirror/react';
const MyEditor = () => (
<ManagedRemirrorProvider>
<h1>My Editor</h1>
<MyInnerEditor />
</ManagedRemirrorProvider>
);
/**
* Inject the editor here using `getRootProps`
*/
const MyInnerEditor = () => {
const { getRootProps } = useRemirrorContext();
return (
<div>
<div {...getRootProps()} />
</div>
);
};
Edit the page on GitHub
Previous:
Controlled
Next:
Showcase