Use Lexical playground editor in your application

When searching around for existing rich text editor solutions, Lexical caught our attention as a relatively new library with a straight forward plugin programming model and great customizability.

It was not layers of code wrapping around another dependency, like in the case of Tiptap. Rather, it was a lightweight React based framework, with which everything from toolbar, floating menu, drag-n-drop paragraph can be built as React components. Custom components interact with the core editor state via commands and events, and can be stacked up like any other React components.

Unlike Slate, which was in Beta and might ask for considerable effort to build commonly expected rich text features like link insertion, the Lexical team demonstrated its potentials with a feature rich playground. It has almost every feature for any rich-text editor use case. Draggable paragraphs are built into the playground, which we think are one of the few things that other editors don't demo very often.

One caveat is that, the Lexical playground editor is not meant to be used as a drop-in editor for your projects yet. In fact, one of the collaborator suggested copy and paste the source code to your project if you want to use it. Though it may work, it is likely to cost effort making the code compatible with a target project's lint rules. Besides, we may miss updates and patches to the original repository.

While there is on going effort to modularize the playground code, it may take a while. In the mean time, we take another approach: fork the lexical playground code and package the editor as a library with as little code change as possible. That way, we can still manage to pull the upstream source occasionally. The repository can be found here.

Update Vite config

Firstly, we need to create a custom Vite build config that does 3 things:

  • set the build output type to library. Pick a source file as entry. For us, a lightly customized editor source was it.
  • declare dependent Lexical packages as external since we don't want them to be bundled with the playground package.
  • declare the main entry js file in package.json
build: {
    ...viteProdConfig.build,
    outDir: 'dist',
    lib: {
      name: 'bitbriks-editor',
      entry: './src/BitbrikEditor.tsx',
      formats: ['es']
    },
vite build config for library target
{
	...
	"module": "./dist/lexical-playground.es.js",
	"main": "./dist/lexical-playground.es.js",
}
package.json

And for convenience, add a build command with the custom config:

"build-editor": "vite build --config vite.editor.config.js",

Add lexical playground to your target project

Next, adding the newly created library to your target project. For ease of development, we recommend adding lexical-playground as a local package. And don't forget to add other Lexical dependencies as well since they are declared externals in the previous step and are not bundled.

{
	"dependencies": {
    	"@lexical/clipboard": "file:../../../../lexical/packages/lexical-clipboard",
	    "@lexical/code": "file:../../../../lexical/packages/lexical-code",
    	...
	    "lexical": "file:../../../../lexical/packages/lexical",
    	"lexical-playground": "file:../../../../lexical/packages/lexical-playground",
    }
}

And in the source, import and use the editor as a normal React component.

import {  
  Page,
  Layout,
} from "@shopify/polaris";
import { BitbrikEditor } from 'lexical-playground';
//...
export default function HomePage() {
  return (
    <Page fullWidth>
      <Layout>
        <Layout.Section>
          <BitbrikEditor />
        </Layout.Section>	
      </Layout>
    </Page>
  );
}

Fix css bundling issue

At this point, the Lexical playground editor appears but without any styling. Adding the missing index.css into your custom editor source file will do the trick.

And voila, we have successfully embedded the Lexical playground editor into our project of choice.

Lexical Playground editor embedded in Shopify