Welp, we did it. As of today, Flux now ships with a rich text editor 🎉.
Just slap this one-liner anywhere in your app and you're off and editing:
We really sweat the details on this one and hope it shows.
Configurable toolbar
To configure an editor's toolbar items, you can pass a custom arrangement into the
toolbar
prop as a string of item names mixed with separators (
|
) and spacers (
~
).
Because each of these items is a simple Blade component inside Flux, you can easily create your own Blade components and reference them by name inside the
toolbar
prop.
Custom toolbar items
If you want complete control over the toolbar, you can compose your own editor by assembling the Blade components yourself.
Here's an example of adding a custom dropdown menu to an editor's toolbar:
As you can see, this is just Blade/HTML like any other component. You can style and assemble any other Flux components you like into the editor's toolbar.
Mouse optional
The editor's toolbar uses a roving tabindex so that you don't have to tab through every toolbar item as you tab along the page. The toolbar acts as a single tab stop that you can navigate using the arrow keys.
You can navigate sub menus within the toolbar using only your keyboard as well.
In fact, you can operate everything about this editor, easily, without ever touching your mouse.
Markdown syntax
Although this is a rich text editor, it supports Markdown syntax as a convenient way to control styling in your document as you edit.
Shortcuts
All the standard keyboard shortcuts you're familiar with from other editors like Google Docs are available to use as well.
Here are a few examples:
-
Cmd+B
Bold
-
Cmd+I
Italic
-
Cmd+K
Insert link
As a form field
We wanted this component to feel no different than a textarea field when using it inside a form.
This means tackling things like disabled and invalid states at both the field AND fieldset level, associating labels/descriptions with the editor, as well as focusing the editor when the field label is clicked.
Screen readers
As you can see by the figure above, we worked hard to make sure the editor's accessibility tree was as descriptive and functional as possible.
Screen readers can easily discover and use all features inside the editor. This includes the tricky bits like toolbar sub-selects, tooltips, and insert-link popovers.
Dynamic JS bundle
Currently, every bit of Flux's JavaScript is contained inside a
21.5KB
file.
Because of the enormity of a rich text editor and its dependancies, this component would add a whopping
85.3KB
to the the Flux bundle size.
The problem is that many pages in an app don't use a rich-text editor at all and it doesn't make sense to penalize them with a bigger bundle.
Therefore, we included the editor's JavaScript as a separate bundle that gets loaded in parallel on an as-needed basis. The editor's JS will only be included at the exact moment it's needed to keep everything else in your app feeling light and snappy. This includes edge cases like lazy-loading an editor long after a page has already been loaded.
Localization
All of the english copy used in this component is translatable by copying a predefined set of keys into your
lang/?.json
files.
Built on Tiptap & ProseMirror
Rather than wrapping a more batteries-included editor like
Quill or
TinyMCE, we opted to use
Tiptap under the hood for the editor's core functionality.
Tiptap is a convenience wrapper around the extremely powerful
ProseMirror project.
It's completely headless, meaning we have full control over all of the markup, visuals, and accessibility, without having to be responsible for the input/formatting interactions.
This means we can make the toolbar as customizable as everything else in Flux because we own all the HTML/CSS/JS for it.
Also, to help support and thank the ProseMirror project, we donated $1000 via their
GitHub sponsors profile.
The future
For launch our goal was: a rock-solid editor that feels just as good to develop and use as any other component in Flux.
Now that we've accomplished that, we can start adding next-level features like image support and @mentions.
Stay tuned!