🎤 Builder Talk: The Story Behind Lady Gaga’s Digital Experience – Register now

The final boss of front-end: block editors

Christian Grøngaard joins the Code && Content podcast to walk through shipping a block editor, behavior state charts, and gherkins.

Published

  • Simeon Griggs

    Simeon Griggs

    Principal Educator at Sanity

Code && Content Podcast

This is a recap of one episode of the Code && Content podcast, head to the show page to find out more and subscribe on YouTube or the podcast player of your choice.

Subscribe

Text editors are typically known as the final boss of front-end. There is so much built-in expectation from decades of experience of editors creating content. And content creation has only gotten more complex as we've moved from rich text editors in the past (TinyMCE, CKEditor etc) to the block content editors (Notion, Confluence, etc) of the present. Each of them work differently in small ways, and yet everybody brings the expectation that they must work.

The problem with simple rich text editors is that they store content as a string, like HTML or Markdown. Which is difficult to serialize and easy to break.

While many block editor applications typically lock down and store content in a proprietary format or cannot be embedded in your application.

If you've ever tried implementing a block editor into your applications, you're probably aware of the challenges of just trying to integrate something that is pre-built, let alone building your own block editor from scratch.

Christian Grøngaard, today's guest on the podcast has spent the last few months taking the Portable Text Editor—a customized version of the Slate.js block editor hard-coded into Sanity Studio—and extracting it into a reusable component. Now available as a standalone package to insert into any React application, distributed as the Portable Text Editor.

I've struggled in the past to implement existing block editors, but the Portable Text Editor the first block editing package which I have felt has a truly intuitive API.

It has a React-friendly declarative design to rendering text and components, setting and declaring schema types, as well as being able to set custom behaviours for events that happen. Events as simple as inserting text or complex as building your own "slash command," or opening a modal based on a character being typed.

I've built two demo apps already with different frontend libraries. One with the Tailwind CSS Headless UI library and another with shadcn/ui. In both cases, I'm able to have full control over how the editor looks while still relying on the Portable Text Editor package to handle all of the behaviors of content being generated.

It's only once you start implementing a block editor yourself you may begin to realize how many built-in expectations you have, which you've never thought twice about. Where should the toolbar be located? What should happen if I try to toggle bold using the spacebar? These are all concerns that need to be built into a block editor and need to have an accompanying test suite. That's what Christian has had to do to make sure that the Portable Text Editor is as reliable as possible.

There is also a whole world of block editor terminology and syntax that you may never have heard before. Maybe you know what your "caret" is, but have you ever thought about what a "collapsed selection" is? There's a whole world of front end behaviors and terminology you interact with every day you write content into a text input—you may just has never noticed.

Have you ever considered that if your caret is at the end of bold text and you keep typing, that it should be included in the bold text? But if your caret is at the end of more dynamic data, like a link, that link should probably not extend if you keep typing.

Along with open-sourcing the Portable Text Editor, the Portable Text specification is an open standard for describing rich text and block content in a way that separates presentation from its content. It is serializable to JSON and queryable with GROQ.

I really want to highlight how great the behvior API is ... to basically "open up" the editor. I can feel it myself when I work on it. I don't hard-code internal logic anymore. It's not only easier to do, it's easier to read, and it has fewer bugs!
— Christian Grøngaard

It was really great to speak with Christian about the challenges that he faced in extracting the editor from Sanity Studio and now releasing it as a standalone package. This was particularly interesting regarding the Behavior API and how it is not only a declarative API for users to interact with when using the package, but also the foundation for all of the core behaviors that ship with the Studio and a future where plugins will be able to interact with the package to extend the behavior in anybody's Portable Text Editor.

If there's anything you feel like we didn't cover in this episode or more you would like to know, reach out and let me know.