# Compose Rich Editor for Kotlin Multiplatform Apps

> Compose Rich Editor adds stateful rich-text editing to Jetpack Compose and Compose Multiplatform projects targeting Android, iOS, desktop, and web. It supports common formatting, links, lists, code spans, HTML, Markdown, and formatting-aware history.

**MohamedRejeb/compose-rich-editor** — A Rich text editor library for both Jetpack Compose and Compose Multiplatform, fully customizable, supports HTML and Markdown.

- Repository: https://github.com/MohamedRejeb/compose-rich-editor
- Website: https://mohamedrejeb.github.io/compose-rich-editor/
- Stars: 1,863 · Forks: 160
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/mohamedrejeb-compose-rich-editor

## RichTextState and the Compose Rich Editor editing model

Compose Rich Editor is intended for developers building editable content in Kotlin applications with Jetpack Compose or Compose Multiplatform. The README lists Android, iOS, desktop, and web as supported targets, so the library can serve applications whose presentation layer already shares Compose code. It is a WYSIWYG component rather than a source-only Markdown editor. The project also supports HTML and Markdown, although the README excerpts do not document conversion functions or serialization options.

The central object is `RichTextState`. The README requires this state object when creating an editor and directs callers to create it with `rememberRichTextState`. This keeps the editor UI separate from the object that manages content, selection, styles, and editing operations. Formatting commands operate on the current selection through methods such as `toggleSpanStyle`, `toggleParagraphStyle`, `addLink`, `toggleCodeSpan`, `toggleOrderedList`, and `toggleUnorderedList`.

That model is useful for a custom toolbar because the state exposes both mutation commands and current-selection properties. An application can toggle bold formatting, read `currentSpanStyle`, and update the corresponding button. It can also inspect `isLink`, `isCodeSpan`, `isOrderedList`, or `isUnorderedList`. This gives toolbar code a direct state model instead of requiring it to infer formatting solely from rendered text.

A basic first editor needs no custom toolbar. Add the state to a composable and pass it to `RichTextEditor`. The editor should then appear wherever this composable is placed.

```kotlin
val state = rememberRichTextState()

RichTextEditor(
    state = state,
)
```

## Formatting spans, paragraphs, lists, and code spans

Text and paragraph formatting use separate operations. `toggleSpanStyle` applies styles to selected text, while `toggleParagraphStyle` changes paragraph-level properties. A toolbar can derive its selected state from `currentSpanStyle` and `currentParagraphStyle`, avoiding a second set of formatting flags that could disagree with the editor.

```kotlin
richTextState.toggleSpanStyle(SpanStyle(fontWeight = FontWeight.Bold))

val currentSpanStyle = richTextState.currentSpanStyle
val isBold = currentSpanStyle.fontWeight == FontWeight.Bold
```

Paragraph alignment follows the same model. The state reports the current paragraph alignment after the command is toggled.

```kotlin
richTextState.toggleParagraphStyle(ParagraphStyle(textAlign = TextAlign.Center))

val currentParagraphStyle = richTextState.currentParagraphStyle
val isCentered = currentParagraphStyle.textAlign == TextAlign.Center
```

Code spans and lists are also controlled through the state. `toggleCodeSpan()` toggles inline code formatting, while `toggleOrderedList()` and `toggleUnorderedList()` control list formatting. The current states can be read through `isCodeSpan`, `isOrderedList`, and `isUnorderedList`. These operations provide enough primitives for a basic editor toolbar, although the README does not describe toolbar layout or explain how list nesting works beyond these methods.

The distinction between span and paragraph styles is worth preserving in application code. Font weight belongs to selected character spans, while text alignment belongs to the paragraph. Treating both as generic booleans would lose the structure exposed by `RichTextState` and make more advanced formatting harder to add.

## Maven installation for Kotlin and Compose compatibility

The README says Compose Rich Editor is available through `mavenCentral()`. Add the Compose artifact to the relevant Gradle dependency configuration. This command is the documented download example, so the artifact and version should be copied exactly when reproducing that setup.

```kotlin
implementation("com.mohamedrejeb.richeditor:richeditor-compose:1.1.0")
```

After dependency resolution, create a `RichTextState` with `rememberRichTextState()` and pass it to `RichTextEditor` as shown in the README quick start. The README does not document a repository bootstrap command or a complete application build, so it provides no separate installation sequence for cloning and running the sample.

Version compatibility is a real constraint. The compatibility table pairs Kotlin `2.4.10` and Compose `1.11.1` with Compose Rich Editor `1.1.0`. It also pairs Kotlin `2.4.0` with Compose `1.11.1` for version `1.0.0`, plus earlier combinations reaching Kotlin `1.9.24`, Compose `1.6.10`, and version `1.0.0-rc05`. These are declared pairings. The table does not say that every adjacent Kotlin or Compose release is supported.

The Android badge states API 23+, establishing an Android API floor. A project supporting an older Android API would need another editor or a different platform baseline. Check the compatibility row before changing the library version, Kotlin version, or Compose version; none of those upgrades is independent of the others.

## Link handling through LocalUriHandler

Links are added as text and a URL through `addLink`. The README example places the link after the current selection and associates the visible text with the supplied URL.

```kotlin
richTextState.addLink(
    text = "Compose Rich Editor",
    url = "https://github.com/MohamedRejeb/Compose-Rich-Editor"
)
```

By default, the editor opens clicked links through the platform's `UriHandler`. Applications that need an in-app confirmation screen, analytics callback, authentication flow, or custom navigation behavior can replace that handler. The README provides a `CompositionLocalProvider` example that supplies a mutable `UriHandler` whose `openUri` method contains application-specific handling.

```kotlin
val myUriHandler by remember {
    mutableStateOf(object : UriHandler {
        override fun openUri(uri: String) {
            // Handle the clicked link however you want
        }
    })
}
CompositionLocalProvider(LocalUriHandler provides myUriHandler) {
    RichText(
        state = richTextState,
        modifier = Modifier.fillMaxWidth()
    )
}
```

This interception point has two consequences. Link security policy belongs partly to the application because the custom handler decides what happens after a click. Also, an application must supply appropriate behavior for every platform it supports; otherwise it may merely replace a valid platform default with an incomplete handler. The README does not document built-in URL validation, link editing UI, or automatic sanitization, so those behaviors should not be attributed to the library without further repository inspection.

## Configuration, history, and maintenance boundaries

The exposed configuration covers selected presentation details. The README gives direct assignments for `linkColor`, `linkTextDecoration`, `codeSpanColor`, and `codeSpanBackgroundColor`. These properties belong to `richTextState.config`, allowing local visual changes without replacing the editor composable.

```kotlin
richTextState.config.linkColor = Color.Blue
richTextState.config.linkTextDecoration = TextDecoration.Underline
richTextState.config.codeSpanColor = Color.Yellow
richTextState.config.codeSpanBackgroundColor = Color.Trans
```

Undo and redo are built in. The README describes rich-text-aware history through `state.history`, stating that it respects formatting and overrides the default `BasicTextField` history. It does not document history size, transaction grouping, persistence across process restarts, or configuration keys for these settings.

The repository is not archived, and its last push was on 2026-10-09. Release history lists `v1.2.0` on 2026-09-05, `v1.1.0` on 2026-08-29, and `v1.0.0-cmp-1.12.0-beta03` on 2026-08-10. The README download example still uses `1.1.0`, so consumers should not silently replace it with `v1.2.0` without checking that release's compatibility requirements.

The repository is licensed under Apache-2.0, with terms recorded in its `LICENSE` file. That does not remove the need to review the license and applicable notices when distributing an application. The README does not document a long-term support policy or migration procedure, making version-specific compatibility information especially important during upgrades.

## Compose Rich Editor versus BasicTextField

The practical alternative is to build editing around Compose's `BasicTextField` and manage formatting in the application. That approach can suit a small text-entry control, an existing document model, or a product that needs to retain exact `BasicTextField` behavior. Its cost is that the application must provide or integrate the formatting model, toolbar synchronization, link behavior, lists, and any format-aware history.

Compose Rich Editor offers a different balance. It supplies a rich-text state API across Jetpack Compose and Compose Multiplatform targets, including selection-aware styles, links, lists, code spans, and documented HTML and Markdown support at the project level. This is most useful when the host application already shares its UI across Android, iOS, desktop, and web.

The multiplatform design is not automatically a saving for an Android-only form. The dependency still introduces the version combinations listed in the compatibility table and an Android API 23 floor. A plain `BasicTextField` may be the smaller change if rich formatting is not part of the product. Compose Rich Editor becomes more relevant when formatting, HTML or Markdown interchange, and cross-platform presentation are explicit requirements.

The repository includes `richeditor-compose`, `richeditor-compose-json`, and `richeditor-compose-coil3` modules. Sample directories cover Android, desktop, iOS, web, and shared common code. The README does not say that all modules are required for basic editor use, so an application should start with `richeditor-compose` unless it has a documented reason to add another artifact.

## Adoption checks for Compose Rich Editor

Compose Rich Editor fits Kotlin developers that already use Compose and need a shared WYSIWYG editing surface across supported platforms. It is a poor fit for applications built around BasicTextField or projects unable to accommodate the documented Kotlin, Compose, and API constraints. Before adoption, verify the selected library version against the host application's Kotlin and Compose versions, then confirm that link handling and required formatting commands fit the editing workflow. The README's compatibility table, rather than its release list, is the first practical adoption boundary.

## Conclusion

Compose Rich Editor fits Kotlin developers that already use Compose and need a shared WYSIWYG editing surface across supported platforms. It is a poor fit for applications built around BasicTextField or projects unable to accommodate the documented Kotlin, Compose, and API constraints. Before adoption, verify the selected library version against the host application's Kotlin and Compose versions, then confirm that link handling and required formatting commands fit the editing workflow. The README's compatibility table, rather than its release list, is the first practical adoption boundary.

## FAQ

### Which dependency should I add for Compose Rich Editor?

The README documents `implementation("com.mohamedrejeb.richeditor:richeditor-compose:1.1.0")` through `mavenCentral()`. Match the Kotlin and Compose versions using the repository's compatibility table before selecting a release.

### How do I create a Compose Rich Editor?

Create state with `rememberRichTextState()` and pass it to `RichTextEditor`. The README's basic example contains no other required arguments.

### Does Compose Rich Editor support undo and redo?

Yes. The README describes built-in rich-text-aware undo and redo through `state.history`, stating that it respects formatting and overrides the default `BasicTextField` behavior.

## Sources

- [License: Apache-2.0](https://github.com/MohamedRejeb/compose-rich-editor/blob/main/LICENSE)
- [MohamedRejeb/compose-rich-editor on GitHub](https://github.com/MohamedRejeb/compose-rich-editor)
- [Project website](https://mohamedrejeb.github.io/compose-rich-editor/)
- [README](https://github.com/MohamedRejeb/compose-rich-editor/blob/main/README.md)
- [Releases](https://github.com/MohamedRejeb/compose-rich-editor/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mohamedrejeb-compose-rich-editor
