Compose's missing Rich Text editor
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Adam Brown 10864a3357
Nest inline markers instead of crossing them (#98)
A style applied across part of an existing emphasis run serialized as
crossing delimiters, `*hello ~~there*~~`, which no markdown parser reads
back. The markers landed in the reloaded document as literal characters
and the styling was lost.

toMarkdown now builds typed runs, splits any partially overlapping pair
at the crossing point, and emits through a stack so every close mirrors
its open. Links are never split, since that would emit the destination
twice.
2026-08-06 23:35:30 -07:00
.github/workflows Added docs website 2026-06-28 11:29:55 -07:00
androidApp Upgrade to AGP 9.0.0 2026-06-06 01:51:26 -07:00
ComposeTextEditor Nest inline markers instead of crossing them (#98) 2026-08-06 23:35:30 -07:00
ComposeTextEditorFind Fix indented text-left positions on Compose Android (#91) 2026-08-03 00:44:37 -07:00
ComposeTextEditorSpellCheck Remove the spell check guard (#90) 2026-08-03 00:03:25 -07:00
docs Carry block structure through the clipboard (#92) 2026-08-03 20:49:35 -07:00
gradle AGP bump 2026-08-03 21:06:48 -07:00
iosApp iOS input implemented 2026-01-03 00:48:13 -08:00
kotlin-js-store/wasm lock file 2026-01-04 02:12:04 -08:00
sampleApp Fix indented text-left positions on Compose Android (#91) 2026-08-03 00:44:37 -07:00
sampleAppiOS Fix iOS sample app 2026-01-03 00:48:13 -08:00
testUtils/countingMeasurer/utils Make relayout incremental: one shaping pass per edit, none for span overlays (#83) 2026-08-02 09:18:05 -07:00
.codacy.yaml Carry block structure through the clipboard (#92) 2026-08-03 20:49:35 -07:00
.gitignore Fix dead keys not working in the editor on desktop (#17) 2026-06-06 22:26:29 -07:00
.markdownlint.json Import/export as HTML (#50) 2026-07-31 08:41:27 -07:00
build.gradle.kts Rename repo 2026-06-29 01:22:02 -07:00
gradle.properties Fix dead keys not working in the editor on desktop (#17) 2026-06-06 22:26:29 -07:00
gradlew iOS input implemented 2026-01-03 00:48:13 -08:00
gradlew.bat Initial commit 2024-11-18 22:07:40 -08:00
LICENSE Create LICENSE 2025-02-27 20:22:38 -08:00
README.md Rename repo 2026-06-29 01:22:02 -07:00
sample_screenshot_00.png New example markdown content 2026-05-10 23:04:34 -07:00
settings.gradle.kts Upgrade to AGP 9.0.0 2026-06-06 01:51:26 -07:00

Compose Text Editor

Maven Central License CI Build API Docs

KMP badge-jvm badge-android badge-wasm badge-ios

Compose has been missing a Rich Text Editor since its inception. I've taken a crack at this previously, as have others.

However, they have all suffered from fundamental limitations in BasicTextField, the foundation of all text entry in Compose.

This project is an attempt to re-implement text entry from scratch to finally have a solution to the various problems.

Why?

I've been trying to implement a spell checking text field in Compose, and keep running up against the limitations of BasicTextField. Pretty much out of options, I decided to see what it might take to replace BasicTextField with something that solved all of my needs.

And now, it's working, and at this point, working pretty well.

  • 100% Compose Multiplatform
  • Efficient rendering and editing of long-form text
  • Rich text with custom spans
  • Expose scroll state
  • Spell checking
  • ☑️ CommonMark Spec (partial)
    • Inline styles (bold, italics, ect)
    • Block styles (code fence, lists, images)

You can Give it a try here, or browse the API reference & recipes.

sample_screenshot_00.png

Features:

  • Rich text rendering and editable
    • Semi-efficient rendering for long form text (only renders what is visible)
    • Semi-efficient data structure for text storage & editing. (but not nearly as efficient as the Gap Buffer BTF2 uses under the hood)
  • Cursor movement, clicking, keyboard short cuts, ect
  • Text selection (highlighting and edit ops)
  • copy/cut/paste
  • Exposed scroll state, so we can render scroll bars (BTF1 can't do this)
  • Doesn't copy and return full contents on each edit, so again better for longer form text. (BTF2 also works this way, but BTF2 doesn't support AnnotatedString for rich content)
  • Support custom Rich Span drawing (this allows us to render the traditional Spell Check red squiggle)
  • Emits edit events: so if a single character is inserted, you can collect a Flow, and know exactly what change was made. This makes managing Spell Check much more efficient as you can just respell-check the single word that was changed, rather than everything. (BTF2 now finally offers this!)
  • Find & Replace UI: Works exactly as you'd expect.

Platforms

Desktop (JVM), Android, iOS, WASM

Work left to do:

  • Copy/Paste of rich text always strips the formatting (this is a Compose MP bug)
  • Right-to-Left text is probably broken
  • Sentence level spell checking is not working as expected
  • Full CommonMark Spec compliance (nested lists)

Want to try it?

Text Editor:

implementation("com.darkrockstudios:composetexteditor:2.0.0")

Spell Checking addon:

implementation("com.darkrockstudios:composetexteditor-spellcheck:2.0.0")

Find & Replace addon:

implementation("com.darkrockstudios:composetexteditor-find:2.0.0")

Really?

I don't know. Maybe. It might not a permanent solution. BasicTextField2's new state based approach and gap-buffer has reach maturity and includes a lot of what I would need to replace this, but still cannot handle rich text rendering, still can't emit individual edit events. So until those are solved, this is the only solution that I am aware of that ticks every box.