Bangle.dev: higher level Prosemirror components

Hey folks,

I am super excited :partying_face: to share with you all something I have been working as a side project for the past 1.5 years.

bangle.dev is a Javascript library that provides high level components built on top of amazing Prosemirror API.

Here are some of the highlights of this library:

  • React support: While the core is written in vanillaJS, I built a React wrapper to create some pretty UI using NodeViews without much friction.
  • Doesn’t add more abstractions: Looking at other libraries which try to blanket Prosemirror’s API with their own, I realized that it is not a great approach, as the moment you try to write a moderately complex app, you end up hitting the walls of these libraries. BangleJS focuses on providing code as components with minimal abstraction over Prosemirror.
  • There are tons of components within @banglejs please check them out here

I will keep this brief and show you some screenshots:

Floating menu

Suggestions

Todo Lists

And more examples here

Next steps

BangleJS is currently in its early stage, so I would love to hear your feedback and thoughts on the library.

Also, would like to thank this awesome community and the maintainer marijnh, I have learnt a lot and I hope this contribution will help some of you out there.

If you like it, I would kindly request you to :star: the project at https://github.com/bangle-io/bangle.dev and dont forget to try it out yourself.

4 Likes

This is very interesting. Do I understand correctly that the core is mostly a set of tools for composing configurations more easily than with raw ProseMirror, or is there more going on? How does a BangleEditorState differ from a raw EditorState?

The goal of BangleJS is to provide pluggable opinionated components to build a reasonably good editor quickly. As soon as you hit the wall with these components, BangleJS doesn’t stand in your way to build complex components that leverages Prosemirror’s API.

Here is a skeleton of the bold component to show how grouping of various related things are done in Bangle component:

export const bold = {
    spec(opts) {
        type: 'mark',
        name: 'bold',
        schema: {
           // Prosemirror MarkSpec
        },
        markdown: {
            // markdown parsing and serializing things
        }
        // plans to add more fields in future
    },
    plugins(opts) {
        // can be a deeply nested array of plugins 
        return [ 
            ...
        ]
    },
    commands: {
        toggleBold: (opts) => (state, dispatch) => { .... },
        ...
    }
}

To answer your question, BangleEditorState is a thin wrapper which does the following:

Accepts:

  • Nested array of PM Plugins
  • Array of objects where one of the field is schema this is where you put the Prosemirror Node/Mark spec of the component.
  • Initial content of the doc
  • pipes anything else directly to the PM EditorState.create static method

Processes:

  • Flattens the deeply nested array of plugins.
  • does same basic sanity checks and adds some reasonable default like doc, paragraph, text, history etc

Outputs:

  • An instance where the field pmState points to the PM EditorState instance built using the inputs.

Here’s the source code.

I have intentionally kept the EditorState initialization separate from EditorView, just like Prosemirror so that if one fancies a Bangle component to be used in an existing Prosemirror app, they can do:

import {pluginLoader} from '@bangljs/core';
import tooltip from '@banglejs/tooltip';

const bangleRegistry = new SpecRegistry([tooltip.spec()], {defaultSpecs: false});
const banglePlugins = pluginLoader(bangleRegistry, [tooltip.plugins()]);

const newSchema = mergeSchema(myExistingSchema, bangleRegistry.schema)
const plugins = [myExistingPlugins, ...banglePlugins]

// use the above to create a PM EditorView
const view = new EditorView(...)

This has only been possible due to amazing level of modularity in Prosemirror, so hats off to you for that.

Note to myself: Write a guide on interoperability with PM.