🤌 Rich text editor with real-time collaboration (Tiptap)

Rich Text Editor with Real-Time Collaboration (Tiptap.dev)

The most powerful rich text editor available for Bubble — built on Tiptap v3.

Bubble’s native rich text editor is limited. No tables. No collaboration. No markdown shortcuts. No fine-grained control over styling. If you’ve ever tried to build a real content editing experience — a CMS, a documentation tool, a notes app, a collaborative workspace — you’ve hit the wall.

This plugin removes that wall. Build editing experiences on par with Notion, Google Docs, and Linear — inside Bubble.


:clapper_board: See It in Action

→ Live demo: tiptap-demo.bubbleapps.io/version-test/collab

Open it in two browser tabs to test real-time collaboration.


What You Get

:writing_hand: Full Rich Text Editing

Bold, italic, underline, strikethrough, subscript, superscript, highlight, text color, font family, font size — all controllable via actions and keyboard shortcuts.

:triangular_ruler: Structure & Layout

  • Headings H1–H6 with individual size, color, weight, and CSS overrides
  • Blockquotes, horizontal rules, and code blocks (with tab indentation)
  • Text alignment and indentation controls
  • Details / Accordion blocks for collapsible content

:clipboard: Lists

Bullet lists, numbered lists, and task lists with checkboxes — all with markdown shortcut support.

:bar_chart: Tables

Full table support: insert, delete, resize columns, merge/split cells, toggle header rows and columns. Configure minimum cell widths, resize handle size, and column resizing behavior.

:framed_picture: Media

  • Images — insert, resize, inline or block-level
  • YouTube embeds — paste a URL, set default dimensions
  • Links — auto-detection, custom protocols (tel:, mailto:, etc.), visited/unvisited/hover styling

:speech_balloon: Mentions

@mention support with configurable trigger character. Use @ for users, # for tags — your call.

:magnet: Drag & Drop

Notion-style drag handles. A grip icon appears on hover, letting users click and drag blocks to reorder content. Enable nested drag for list items and quoted paragraphs.

:input_latin_uppercase: Invisible Characters

Power-user mode: show ¶ paragraph marks, · spaces, and hard breaks. Toggle on/off with one action.

:memo: Menus

  • Top menu — persistent toolbar
  • Bubble menu — appears on text selection
  • Floating menu — appears on empty lines

All three are optional. Style them with your own Bubble elements.

:keyboard: Keyboard & Markdown Shortcuts

Type # for H1, > for blockquote, ``` for code block, - [ ] for task list. Full shortcut support for bold, italic, strikethrough, and more — documented in every extension toggle.


:handshake: Real-Time Collaboration

Google Docs-style co-editing. Multiple users editing the same document simultaneously — with live cursors, colored name labels, and conflict-free merging.

Three provider options:

  1. Tiptap Cloud — managed, zero-infrastructure
  2. Custom Hocuspocus server — self-hosted, full control
  3. Liveblocks — third-party collaboration platform

Built-in features:

  • JWT authentication with configurable token expiration
  • Auto-retry with exponential backoff on auth failures
  • Collab status states: disconnected → connecting → connected → synced
  • Connected user count, sync status, and status change events
  • Automatic editor rebuild when switching documents — no page reload needed
  • Server-side action to convert Hocuspocus webhook payloads to HTML for database storage

:artist_palette: Total CSS Control

Every element has a CSS override field — headings, paragraphs, blockquotes, lists, links, images, tables, code blocks, highlights, mentions, collaboration cursors, and more. Inject raw CSS to override any default style.


:electric_plug: 29 Extensions, Each With Its Own Toggle

No more guessing which extensions are active. Every extension has a dedicated on/off switch in the property panel with clear documentation. Enable exactly what you need:

Text Formatting · Highlight · Headings · Blockquote · Code Block · Lists · Task Lists · Tables · Images · YouTube · Links · Mentions · Details/Accordion · Drag Handle · Invisible Characters · Trailing Node · Focus · Selection · Hard Break · Unique ID · Preserve Attributes · Undo/Redo · and more.


:outbox_tray: Output Formats

Three exposed states, always up to date:

  • contentHTML — clean HTML
  • contentText — plain text
  • contentJSON — ProseMirror JSON

Plus: selectedHTML, is_empty, can_undo, can_redo, selection states for every formatting type (is bold, is italic, is heading, is table, etc.).


:link: Resources

:star: If this plugin is useful to you, star the repo on GitHub — it helps others find it and keeps development going.


Built on Tiptap v3

27 Likes

Looks very nice Rico​:ok_hand: :ok_hand: :ok_hand:

1 Like

nice job @rico.trevisan, tiptap is a huge library so it probably too you some time to get it to work :fire:

5 Likes

There is my teacher, the Plugin Prince himself. Come here, gimme me a bearhug!

3 Likes

Looking very nice Rico!
Congrats!
Does it work with the new resposiveness?

Thanks. Yes, it works with the new responsive engine. The plugin itself is a simple <div> and TipTap does all the filling in.

I’m currently testing out which options are needed. (I currently have all the options turned on which is overkill…)

1 Like

Would be cool if this had some sort of Iframe implemented where you could embed social media post like Twitter post or Youtube Videos.

1 Like

collaboration is not working yet.
will work on that in version 2

:eyes:

4 Likes

v1 is released → Rich text editor -- 🩰 Tiptap.dev Plugin | Bubble

Unfortunately, no collaboration in this version. I’ve gotta harass some more people in the #plugin-builders forum. And @Kayami.

4 Likes

@rico.trevisan I’m having a lot of fun playing around with tiptap (I’m a huge tiptap fan :heart:)

First impressions

  • The freedom to create custom menus is a really nice touch!
  • Ability to output html and json: awesome!

Issue - auto-binding

(some context of usage)

  • I’m using your plugin in a SPA
  • The tiptap editor is inserted into a floating group
  • This floating group is used to create an edit notes.

Problem:

  • When auto-binding is enable, if I view note A, it’s content is loaded (expected behavior);
  • When I view note B, content A is displayed and is saved to note B (not expected behavior);

Work-around:

Requests

My experimentation is still in early stages, but I feel confident that this plugin could very well replace the current RTE plugin used in my project.

Thanks for this plugin :pray:

2 Likes

Excellent feedback, thank you!

Yep. Yep. This autobinding business is giving me a proper headache. I posted a question hoping the gods of plugins will help me out.

I’ll certainly add the highlight text feature to the list.

3 Likes

@rico.trevisan back with some more feedback (using version v.1.0.1 ) :smiley:

Issues

:warning: input is enabled option:

  • When check it works as expected;
  • When uncheck it doesn’t work as expected (input is still possible);

:warning: Responsive/layout behavior:
(context of use)

  • I’m using your plugin in a SPA
  • The tiptap editor is inserted into a floating group;
  • This floating group is used to create an edit notes;

I’m under the impression that in order to get inputted content in tiptap element, to scroll in a floating group, you need to enable the option “fit height to content” even though I have inf max height for the element.

Would you consider this to be expected behavior? Try it out yourself by replicating these steps:

In editor:

  1. Create a floating group with scrollable content (e.g: text). Configure the floating group as you would for a SPA (fullcreen);
  2. Create a second floating group and include the tiptap element. Configure the second floating group as you would for a SPA (fullcreen);
  3. Configure the tiptap element to have no max height. Keep the “fit height to content” option unchecked;

In preview:

  1. Insert a few paragraphs of text until text passes page fold;
  2. Try to scroll (it won’t work)

Now, go back to the editor an enable the option “fit height to content”. Try to scroll again (it will scroll).

This behavior just doesn’t seem super intuitive (at least for me). Curious to know what you and perhaps others think.

Thanks again! :+1:

1 Like

Excellent feedback. That’s my homework for the weekend.

Can’t believe i just saw this thread… Amazing work man! Can’t wait to see where it goes… +1 for json as input.

1 Like

this is excellent @rico.trevisan. Nice work!

I think I just solved the auto-binding issue.

I want to test it on a real app. To do that I need to publish a new version which will roll out to all of you. I’ll make sure to tag it as BETA. Try it out and let me know.

1 Like

Could you show me your app? What do you mean when input is still possible? Can you type? Or are you referring to the actions (bold, italic, etc.)?

On my demo it seems to work properly.
Properly’ish. Typing input is disabled but actions still work. What’s the right approach / philosophy: who should be responsible to restrict it: the plugin or the Bubble dev?

1 Like

A small quibble from a UI perspective. The cursor doesn’t change to a finger icon when you hover over a url. Not sure if this is under your control.

For the custom menu, can I highlight text and then click a custom menu button to act upon the highlighted text? Is there any docs on how to do this yet?

If I’m using the current Bubble RTE, is there any migration issues?

Lastly, based on previous discussions we’ve had, if I paste in an image, is it stored securely?

Looks great and can’t wait to try it out!

1 Like

Friday night updates :sunglasses:

Just updated to beta version. From my brief testing, it does seem like auto-binding is working as expected! I’ll continue testing though and will report back :+1:

I’m referring to this option:

I’ve just realized that your demo actually has two pages… (index and doc). I completely missed the doc page (default goes to index :sweat_smile:).

I see that you check a value and then based on that, the tiptap element is editable or not. I haven’t tried this approach. I’m just using the option shown in the screenshot above…

In any case, it’s not really a deal breaker for me at the moment, as I can just use an html element to display content vs enable/disabling input within tiptap.