Skip to main content

Migration to Remirror v3 beta

This guide lays out the required steps to migrate from Remirror v2 to Remirror v3 beta.

Consult the announcement post to learn more about the v3 beta release, and what we're trying to achieve.

Installing

npm add --save remirror@beta @remirror/react@beta @remirror/pm@beta

i18n prop removed from <Remirror />

In previous versions of Remirror, the i18n prop of the root <Remirror /> component allowed you to pass a customised Lingui instance.

With this version, we want to allow any i18n library to be used with Remirror, so the i18n prop has been removed, and replaced with an i18nFormat function.

This allows users to plug in any i18n library, by implementing a definition for this function.

This function is described by the TypeScript type I18nFormatter.

Example: Using react-i18next

import { useTranslation } from 'react-i18next';
import { Remirror, useRemirror } from '@remirror/react';

const Editor: React.FC = () => {
const { t } = useTranslation();

const i18nFormat: I18nFormatter = useCallback(
(message, values) => {
// Note only using the message ID here, more on this later
return t(message.id, values);
},
[t],
);

const { manager } = useRemirror({
extensions: () => [
// Some extensions here
],
});

return <Remirror manager={manager} i18nFormat={i18nFormat} />;
};

react-i18next, like many i18n solutions, requires you define your translatable strings up front, via key-value pairs.

To facilitate this, the @remirror/messages package now exposes the translatable strings as JSON files.

These messages are provided as key value pairs, so they can be loaded into your chosen i18n library.

Currently, only English locale (en) messages are provided.

import i18n from 'i18next';
import ICU from 'i18next-icu';
import { initReactI18next } from 'react-i18next';
import type { I18nFormatter } from 'remirror';
import allMessages from '@remirror/messages/en/all-messages.json';

// or messages for specific extension(s)
// import boldMessages from '@remirror/messages/en/extension-bold-messages.json';
// import italicMessages from '@remirror/messages/en/extension-italic-messages.json';

i18n
.use(ICU) // Required if using the provided messages from @remirror/messages
.use(initReactI18next)
.init({
resources: {
en: {
translation: allMessages,
},
},
lng: 'en',
fallbackLng: 'en',
interpolation: {
escapeValue: false,
},
});

You do not have to use the messages in these key value pairs, you could replace them with your own. They are provided for convenience, and to expose the message IDs Remirror uses.

Restoring the previous behaviour

If you wish to carry on using Remirror's default i18n solution (powered by Lingui), you will need to install the @remirror/i18n package, as this is now an optional package.

Example: Continue using @remirror/i18n

Install the i18n package, as it is now optional, and not installed by default.

npm add @remirror/i18n
import { i18nFormat } from '@remirror/i18n';
import { Remirror, useRemirror } from '@remirror/react';

const Editor: React.FC = () => {
const { manager } = useRemirror({
extensions: () => [
// Some extensions here
],
});

return <Remirror manager={manager} i18nFormat={i18nFormat} />;
};

The useI18n hook return value

As a consequence of the above, the useI18n no longer returns an object containing the Lingui i18n instance.

It now returns the i18nFormat function that was passed to the root <Remirror /> component.

Before

const { t, i18n } = useI18n();

After

const t = useI18n();

// Where "t" is the same function that was passed via `i18nFormat`

MUI based components in @remirror/react, moved to new package.

In previous versions of Remirror, menus/toolbar/buttons and "find and replace" components were exposed from @remirror/react.

As @remirror/react is a required module for Remirror, it meant @mui/material was bundled regardless of whether you used these components or not.

With this version, these components have been moved to a new optional package - @remirror/react-ui.

Other than the change in import path, there should be no changes to the MUI components themselves. The full list of affected components is included below.

Before: Remirror v2 example

import React from 'react';
import { BoldExtension } from 'remirror/extensions';
import { Remirror, ThemeProvider, ToggleBoldButton, Toolbar, useRemirror } from '@remirror/react';

const extensions = () => [new BoldExtension()];

const ToggleBold = (): JSX.Element => {
const { manager, state, onChange } = useRemirror({
extensions: extensions,
content: '<p>Text in <b>bold</b></p>',
stringHandler: 'html',
});

return (
<ThemeProvider>
<Remirror
manager={manager}
autoFocus
onChange={onChange}
initialContent={state}
autoRender='end'
>
<Toolbar>
<ToggleBoldButton />
</Toolbar>
</Remirror>
</ThemeProvider>
);
};

export default ToggleBold;

After: Diff for Remirror v3 example

import React from 'react';
import { BoldExtension } from 'remirror/extensions';
- import { Remirror, ThemeProvider, ToggleBoldButton, Toolbar, useRemirror } from '@remirror/react';
+ import { Remirror, ThemeProvider, useRemirror } from '@remirror/react';
+ import { ToggleBoldButton, Toolbar } from '@remirror/react-ui';

const extensions = () => [new BoldExtension()];

const ToggleBold = (): JSX.Element => {
const { manager, state, onChange } = useRemirror({
extensions: extensions,
content: '<p>Text in <b>bold</b></p>',
stringHandler: 'html',
});

return (
<ThemeProvider>
<Remirror
manager={manager}
autoFocus
onChange={onChange}
initialContent={state}
autoRender='end'
>
<Toolbar>
<ToggleBoldButton />
</Toolbar>
</Remirror>
</ThemeProvider>
);
};

export default ToggleBold;

Full list of affected components

These components have been moved from @remirror/react to the new optional packages @remirror/react-ui.

  • Toolbar
  • FloatingToolbar
  • MarkdownToolbar
  • WysiwygToolbar
  • VerticalDivider
  • CenterAlignButton
  • CommandButton
  • CopyButton
  • CreateTableButton
  • CutButton
  • DecreaseFontSizeButton
  • DecreaseIndentButton
  • DropdownButton
  • IncreaseFontSizeButton
  • IncreaseIndentButton
  • InsertHorizontalRuleButton
  • JustifyAlignButton
  • LeftAlignButton
  • PasteButton
  • RedoButton
  • RightAlignButton
  • ToggleBlockquoteButton
  • ToggleBoldButton
  • ToggleBulletListButton
  • ToggleCalloutButton
  • ToggleCodeBlockButton
  • ToggleCodeButton
  • ToggleColumnsButton
  • ToggleHeadingButton
  • ToggleItalicButton
  • ToggleOrderedListButton
  • ToggleStrikeButton
  • ToggleSubscriptButton
  • ToggleSuperscriptButton
  • ToggleTaskListButton
  • ToggleUnderlineButton
  • ToggleWhitespaceButton
  • UndoButton
  • BaselineButtonGroup
  • BasicFormattingButtonGroup
  • CalloutTypeButtonGroup
  • CommandButtonGroup
  • DataTransferButtonGroup
  • FormattingButtonGroup
  • HeadingLevelButtonGroup
  • HistoryButtonGroup
  • IndentationButtonGroup
  • ListButtonGroup
  • TextAlignmentButtonGroup
  • FindReplaceComponent
  • CommandMenuItem
  • ToggleCalloutMenuItem
  • ToggleHeadingMenuItem

Removed deprecated SearchExtension in favour of FindExtension

TLDR: SearchExtension has been removed from Remirror v3 completely, please use FindExtension instead.

SearchExtension has been deprecated since we released FindExtension, as FindExtension offers more features and is more performant.

Furthermore, as SearchExtension was previously exposed directly via remirror/extensions, and configurable via presetWysiwyg - we have updated both of these access points to expose FindExtension instead.

If using presetWysiwyg, the config options for SearchExtension will need updating to their FindExtension equivalents.

Before: Remirror v2 example

import React from 'react';
import { wysiwygPreset } from 'remirror/extensions';
import { Remirror, ThemeProvider, ToggleBoldButton, Toolbar, useRemirror } from '@remirror/react';

const extensions = () =>
wysiwygPreset({
alwaysSearch: true,
});

const UsingWysiwygPreset = (): JSX.Element => {
const { manager, state, onChange } = useRemirror({
extensions: extensions,
content: '<p>Text to search</p>',
stringHandler: 'html',
});

return (
<ThemeProvider>
<Remirror
manager={manager}
autoFocus
onChange={onChange}
initialContent={state}
autoRender='end'
/>
</ThemeProvider>
);
};

export default UsingWysiwygPreset;

After: Diff for Remirror v3 example

import React from 'react';
import { wysiwygPreset } from "remirror/extensions";
import { Remirror, ThemeProvider, ToggleBoldButton, Toolbar, useRemirror } from '@remirror/react';

const extensions = () => wysiwygPreset({
- alwaysSearch: true,
+ alwaysFind: true,
});

// Rest as above

Removed deprecated command dry run function isEnabled, use enabled instead.

When using commands or chained commands, you can "dry run" the command to see if it can be executed against the current editor state.

For instance, you can check if toggleBold is enabled by running toggleBold.enabled() which returns a boolean indicating whether it is possible, without actually changing the editor's state.

.isEnabled() was an alias of .enabled(), but this alias has now been removed. Please use .enabled() instead.

const { toggleBold } = useCommands();

const handleClick = useCallback(() => {
if (toggleBold.isEnabled()) {
toggleBold();
}
}, [toggleBold]);
const { toggleBold } = useCommands();

const handleClick = useCallback(() => {
- if (toggleBold.isEnabled()) {
+ if (toggleBold.enabled()) {
toggleBold();
}
}, [toggleBold]);

CodeBlockLanguageSelector component moved to @remirror/react-ui package

The CodeBlockLanguageSelector component has been moved from @remirror/extension-react-language-select to @remirror/react-ui.

While it was originally named as an "extension", upon closer examination, we've realised that its role aligns more with that of a component, rather than a true extension.

To maintain the integrity of our definition of an extension, we believe this move is necessary. This is to help provide a more accurate representation of functionality, and enhance overall understanding and usage of the library.

Additionally CodeBlockLanguageSelector now renders a MUI Select component, rather than a native select element. This enables us to utilise the "auto width" behaviour, rather than implementing this behaviour ourselves.

Furthermore, it will now render in the top right corner of the code block by default, rather than the top left. Passing position='left' will revert to rendering in the top left corner as before.

Before: Remirror v2 example

import 'remirror/styles/all.css';

import React from 'react';
import css from 'refractor/lang/css.js';
import javascript from 'refractor/lang/javascript.js';
import typescript from 'refractor/lang/typescript.js';
import { CodeBlockExtension } from 'remirror/extensions';
import { CodeBlockLanguageSelect } from '@remirror/extension-react-language-select';
import { Remirror, ThemeProvider, useRemirror } from '@remirror/react';

const extensions = () => [
new CodeBlockExtension({
supportedLanguages: [css, javascript, json, markdown, typescript],
}),
];

const content = `
<pre><code data-code-block-language="typescript">function sayHello {
console.log('Hello world, TypeScript!')
}</code></pre>
`;

const EditorWithCodeBlocks = (): JSX.Element => {
const { manager, state } = useRemirror({ extensions, content, stringHandler: 'html' });

return (
<ThemeProvider>
<Remirror manager={manager} initialContent={state} autoRender>
<CodeBlockLanguageSelect />
</Remirror>
</ThemeProvider>
);
};

export default EditorWithCodeBlocks;

After: Diff for Remirror v3 example

import 'remirror/styles/all.css';

import React from 'react';
import css from 'refractor/lang/css.js';
import javascript from 'refractor/lang/javascript.js';
import typescript from 'refractor/lang/typescript.js';
import { CodeBlockExtension } from 'remirror/extensions';
- import { CodeBlockLanguageSelect } from '@remirror/extension-react-language-select';
import { Remirror, ThemeProvider, useRemirror } from '@remirror/react';
+ import { CodeBlockLanguageSelect } from '@remirror/react-ui';

const extensions = () => [
new CodeBlockExtension({
supportedLanguages: [css, javascript, json, markdown, typescript],
}),
];

const content = `
<pre><code data-code-block-language="typescript">function sayHello {
console.log('Hello world, TypeScript!')
}</code></pre>
`;

const EditorWithCodeBlocks = (): JSX.Element => {
const { manager, state } = useRemirror({ extensions, content, stringHandler: 'html' });

return (
<ThemeProvider>
<Remirror manager={manager} initialContent={state} autoRender>
<CodeBlockLanguageSelect />
</Remirror>
</ThemeProvider>
);
};

export default EditorWithCodeBlocks;

Update any usages of extensionDecorator to extension

The extensionDecorator decorator was deprecated alias and has now been removed. Please use extension instead

Update any usages of useSuggester to useSuggest

The useSuggester React hook was deprecated alias and has now been removed. Please use the useSuggest hook instead.

Similarly, the type of this function has been renamed, please update any usages of UseSuggesterProps to UseSuggestProps.

Update any usages of getRemirrorJSON to useSuggest

The getRemirrorJSON helper function was deprecated alias and has now been removed. Please use the getJSON helper instead.

jest-prosemirror's jumpTo has been removed

Please use selectText instead.

jest-remirror updates

We have removed the deprecated properties start and end, use from and to respectively instead.

Additionally, we have removed the deprecated function jsdomExtras, please use jsdomPolyfills instead.

@remirror/theme updates

We have removed the deprecated functions getTheme and getThemeProps, please use getThemeVar and getThemeVarName respectively.

Removed type definitions

We have removed the following types, please use the alternative suggested below.

Old nameNew name
ClickHandlerClickEventHandler
ClickMarkHandlerClickMarkEventHandler
PromiseValueAwaited (built in)
MutableWritable
UseSuggesterPropsUseSuggestProps

Feedback

If you spot anything we have missed in this guide, or if you run into any issues, please reach out to us on Discord.