Pick the storage format before you pick the editor. The editor is a widget you can swap. The format is every document your users have already written, and changing it means a migration script, a full backup and a very careful evening.
Consider a team that needs a rich text field in a FlutterFlow app. Someone searches the community forum, finds a Quill custom widget that looks good in a demo, wires its output to a Firestore string and ships. The toolbar works, bold is bold, and a field called body holds a blob of JSON. Nothing is wrong yet. Six weeks later someone asks for search across all notes, and the format presents its bill. That scenario is hypothetical, but the order of events is common enough to plan around.
Your first action: write down the three operations your app will perform most on stored documents. Be specific, such as "render in a list preview" rather than "display text".
FlutterFlow's palette does not settle the question for you. The built-in Markdown widget documents two properties, Data and Selectable, and it displays markdown; its documentation page describes no editing. Editing comes from custom code, so every rich text editor you add is also a storage decision. My own FlutterFlow Marketplace portfolio includes a Content Creator Toolkit bundle, submitted in the April 2026 batch, with five widgets: a Quill rich text editor producing Delta JSON, a drawing canvas, a markdown renderer, an HTML renderer, and a Fleather editor with PDF export. They are separate widgets because they solve separate problems that share a name.
Why does the storage format matter more than the editor?
The format decides what you can render, search, diff, sanitise and migrate for years, while the editor only decides what typing feels like this week.
Think about what happens after a user hits save. The document is rendered on a detail screen, then again, smaller and plainer, in a list preview. Someone searches for a phrase inside it. A teammate wants to see what changed between versions. A client wants a PDF. Much later, someone asks why a document written on one platform renders differently on another.
Every one of those is a format operation, and none is an editor operation. The editor finished its job when the user stopped typing. So the useful question is not which editor is best for FlutterFlow, but which operations you will perform a thousand times a day and which you will perform twice. If Flutter itself is new to you, the short version of what Flutter is will help with the rest.
What is Delta JSON, and when should you store it?
Delta is an operation log: an ordered list of insert, retain and delete instructions that rebuild a document when you replay them from the start.
The Quill Delta specification calls it "a strict subset of JSON" that is human readable and easily parsable by machines. An insert adds text or an embed with optional formatting attributes. A retain keeps existing characters and can apply formatting to them. A delete removes characters, and the spec notes that "since delete operations do not include what was deleted, a Delta is not reversible."
The shape is deliberate. The same page says Delta "is suitable for Operational Transform and can be used in realtime, Google Docs like applications." If two people type into one paragraph at the same moment, you need a format that expresses what changed, not only what the document looks like now.
flutter_quill is direct about where the result should live. As of 30 September 2026 its page reads: "We recommend storing the Document as Delta JSON instead of other formats (e.g., HTML, Markdown, PDF, Microsoft Word, Google Docs, Apple Pages, XML)." The stated reason is that HTML and Delta are structurally different, so converting back and forth can lose formatting.
Delta is awkward for search. A sentence with one bold word in the middle is stored as several inserts, not one string, so a naive query over the JSON can miss a phrase that is split by formatting. That is a reading of the format, not a measured failure, and it is fixable: extract plain text when you save, keep it in a second field, and index that. You would do the same on a FlutterFlow, Firebase and Stripe stack whichever format you chose.
Which is better for rich text: Delta, HTML or markdown?
There is no universally better format. Choose the one that makes your most frequent operation cheap: Delta for collaborative editing, HTML for content served elsewhere, markdown for readable diffs.
The table below is my assessment of each format by operation, because the operation is what you are choosing.
| Operation | Delta JSON | HTML | Markdown |
|---|---|---|---|
| Rendering | Needs the matching editor package to display faithfully | One widget from an HTML package | One widget with a small, predictable feature set |
| Full-text search | Needs a separate extracted plain-text field | Needs tag stripping before indexing | Indexable as-is, with some syntax noise |
| Diffing | Structurally correct, hard to show a human | Noisy, tag churn hides the text change | Line diff readable by anyone |
| Sanitisation | Constrained by the attributes your editor emits | Full sanitiser required on write | Only needed if raw HTML blocks are allowed |
| Portability | Quill and Fleather family, mainly | Read by every platform | Read by every platform |
| Migration cost | Cheap out to HTML, lossy coming back | Costly into Delta, structure mismatch | Cheap either way, ceiling limits what carries |
A collaborative notes app lives in the diffing and realtime rows, so Delta wins on merit. A CMS whose content also feeds a website lives in the rendering and portability rows, so HTML earns its sanitisation cost. A tool where users write release notes lives in the diffing and search rows, and markdown costs almost nothing.
HTML: the easy render and the hard sanitise
HTML renders with little widget code and charges you a sanitisation pipeline in return. Anything a user can store, a user can aim at the next reader.
The render side is easy. flutter_widget_from_html builds a Flutter widget tree from HTML, supports more than 70 tags, and states that "SCRIPT, STYLE" tags and their contents will be ignored. For an app where users write and read their own content, that may be enough.
It stops being enough when a second reader exists. Ignoring script tags at render time is not sanitising at write time. The stored string still contains whatever arrived. Consider a web dashboard that reads the same Firestore field: it would receive the same string, and your Flutter renderer's habits do not travel with it.
If you store HTML, sanitise on the way in. The sanitize_html package for Dart parses with an HTML5 parser, builds a DOM tree and filters elements and attributes against an allow-list, stripping inline JavaScript, CSS and form elements. Run the sanitiser on a path you control, such as your backend, and use an equivalent allow-list library if that backend is not Dart. A client-only check can be bypassed.
Markdown: the easy diff and the low ceiling
Markdown is plain text, so the tools you already own work on it: diff, grep, git, a text index, a human reading a log.
A markdown document diffs line by line, which gives you version history your users can read. A Delta diff is structurally correct but hard to show as "here is what your colleague changed" without a translation layer.
The ceiling is real too. The CommonMark 0.31.2 specification, dated 28 January 2024, does not define tables. Extensions such as GitHub Flavored Markdown add them, and once your app depends on an extension you store a dialect that one renderer understands. There is also a trapdoor: CommonMark defines an HTML block as "a group of lines that is treated as raw HTML (and will not be escaped in HTML output)." Allow raw HTML in markdown and you inherit every sanitisation problem above, hidden inside a format people consider safe.
Can you store two formats?
Yes: Delta for fidelity, plus a plain-text or rendered copy for everything that is not the editor. The catch is a consistency problem, because you now have two fields, one source of truth and a write path that must update both or neither. That is worth it for search and rarely worth it for rendering alone.
What does PDF export demand from the format?
PDF export needs an unambiguous mapping from stored formatting to layout, which favours formats where you know every construct that can appear.
This is my reasoning, not a spec claim. With loose HTML the PDF generator must decide what to do with nested tables, floats and inline styles. With Delta, the converter only has to handle the attributes your editor can emit, such as bold, italic, lists, headings and links. Fleather, at version 1.28.0 as of 30 September 2026, uses the Parchment document model with Quill.js Delta as its underlying format, and says that makes it ready for collaborative editing. PDF export in my toolkit bundle is part of that bundle's own code, not something the Fleather page documents.
Can a renderer be discontinued while the format survives?
Yes, and markdown offers a recent example. The Flutter team's flutter_markdown discontinuation issue was opened on 10 February 2025. Its continuation, flutter_markdown_plus, is maintained by Foresight Mobile and was at version 1.0.12 on 30 September 2026.
In principle, a team that stored markdown only had to change a dependency, and nothing in its database had to move. That is the argument for boring formats. Formats with a published specification and several independent implementations outlive any one package. A custom widget also has to build inside FlutterFlow's cloud build, and some packages that work in plain Flutter break there, which makes renderer churn more expensive. I cover that boundary in when to drop to pure Flutter custom code, and the Flutter developer tools list covers the neighbouring choices.
How do you pick a rich text format in order?
Work in this order rather than starting from the editor demo.
- Write down the three operations your app will perform most on stored documents, in specific terms.
- If realtime collaborative editing is on the list, store Delta JSON.
- If the content is also served to a website, email or public API, store HTML and budget a server-side sanitiser before launch.
- If your users are technical, or readable version history matters more than formatting range, store markdown and accept the ceiling.
- Whichever you chose, add a plain-text field at write time. It makes search possible and is cheap insurance against the next migration.
- Only now pick the editor widget, one that emits the format you already decided on.
Open the file where your rich text is stored and look at the first document. Can you grep it, diff it and sanitise it? If the answer to all three is no, which operation are you going to fix first?



