
Embeddable native code editor component matching CodeMirror 6 features: syntax highlighting, Vim modal editing, real keyboard input, autocompletion, linting, collaborative editing, diff/merge, themes.
A native Compose Multiplatform code editor — no WebView, no JS bridges. Built as a ground-up Kotlin port of CodeMirror 6, the industry-standard web editor.
Live Demo · Documentation · API Reference
Compose Multiplatform doesn't ship a code editor. Your options are embedding a WebView (heavy, hard to integrate) or building from scratch. KodeMirror gives you a real Compose component with the full feature set of CodeMirror 6:
All from shared Kotlin code across every Compose target.
Try the live demo to see it in action — syntax highlighting, vim mode, autocompletion, and more, all running in the browser.
| Platform | Status |
|---|---|
| wasmJs (Browser) | Tested — automated gap tests + manual browser testing |
| JVM Desktop | Unit tests pass, visual rendering lightly tested |
| Android | Unit tests pass via CI, untested on devices |
| iOS / macOS native | Compiles, experimental |
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation(platform("com.monkopedia.kodemirror:kodemirror-bom:0.3.6"))
implementation("com.monkopedia.kodemirror:view")
implementation("com.monkopedia.kodemirror:commands")
implementation("com.monkopedia.kodemirror:basic-setup")
// Pick your language(s)
implementation("com.monkopedia.kodemirror:lang-javascript")
}
}
}@Composable
fun Editor() {
val session = rememberEditorSession(
doc = "function hello() {\n return 'world';\n}",
extensions = basicSetup + javascript().extension
)
KodeMirror(session = session, modifier = Modifier.fillMaxSize())
}Also add com.monkopedia.kodemirror:vim, :theme-one-dark, and :lang-python:
@Composable
fun VimEditor() {
val session = rememberEditorSession(
doc = "print(\"Hello, KodeMirror!\")",
extensions = basicSetup + python().extension + vim() + oneDark
)
KodeMirror(session = session, modifier = Modifier.fillMaxSize())
}| Module | Description |
|---|---|
| state | Editor state, transactions, selections, facets |
| view | Compose UI component, decorations, key handling |
| language | Language support infrastructure, syntax highlighting |
| commands | Standard editor commands (cursor movement, delete, indent, undo/redo) |
| basic-setup | Convenience bundle combining common extensions |
| Module | Description |
|---|---|
| search | Find/replace panel, search commands |
| autocomplete | Completion popup and sources |
| lint | Diagnostic display and linting |
| collab | Collaborative editing support |
| merge | Side-by-side diff view |
| vim | Vim keybindings and modal editing |
JavaScript, TypeScript, HTML, CSS, Python, Java, Go, Rust, Markdown, JSON, YAML, XML, SQL, C++, PHP, and more — 20+ dedicated modules plus 100+ (including Kotlin) via legacy-modes.
One Dark, Dracula, Solarized Light, Tomorrow, Cobalt, Espresso, and 11 more, plus Material Design integration.
This is v0.3.6 — the API may evolve. Known issues to be aware of:
See all open issues for the full list.
Contributions are welcome! Please open an issue to discuss before submitting large changes.
Changelog entries go in changelog.d/, not in CHANGELOG.md. When your change is one a user
reads about — behaviour, public API, the build, or user-facing docs — add a new file named
<issue>.<section>.md — for example changelog.d/281.fixed.md — containing the markdown bullet(s)
your change should appear as. A change with no user-visible effect needs none. CHANGELOG.md is
assembled from those files at release time and has no [Unreleased] section to edit; editing it
directly is refused by CI. One file per change means two pull requests never touch the same file,
so they never conflict on the changelog. See
changelog.d/README.md for the sections, the format and when an entry is
expected, and run python3 .github/scripts/changelog.py check to validate.
Copyright 2026 Jason Monk
Licensed under the Apache License, Version 2.0
Originally based on CodeMirror 6 by Marijn Haverbeke, licensed under MIT. See NOTICE for details.
A native Compose Multiplatform code editor — no WebView, no JS bridges. Built as a ground-up Kotlin port of CodeMirror 6, the industry-standard web editor.
Live Demo · Documentation · API Reference
Compose Multiplatform doesn't ship a code editor. Your options are embedding a WebView (heavy, hard to integrate) or building from scratch. KodeMirror gives you a real Compose component with the full feature set of CodeMirror 6:
All from shared Kotlin code across every Compose target.
Try the live demo to see it in action — syntax highlighting, vim mode, autocompletion, and more, all running in the browser.
| Platform | Status |
|---|---|
| wasmJs (Browser) | Tested — automated gap tests + manual browser testing |
| JVM Desktop | Unit tests pass, visual rendering lightly tested |
| Android | Unit tests pass via CI, untested on devices |
| iOS / macOS native | Compiles, experimental |
// build.gradle.kts
kotlin {
sourceSets {
commonMain.dependencies {
implementation(platform("com.monkopedia.kodemirror:kodemirror-bom:0.3.6"))
implementation("com.monkopedia.kodemirror:view")
implementation("com.monkopedia.kodemirror:commands")
implementation("com.monkopedia.kodemirror:basic-setup")
// Pick your language(s)
implementation("com.monkopedia.kodemirror:lang-javascript")
}
}
}@Composable
fun Editor() {
val session = rememberEditorSession(
doc = "function hello() {\n return 'world';\n}",
extensions = basicSetup + javascript().extension
)
KodeMirror(session = session, modifier = Modifier.fillMaxSize())
}Also add com.monkopedia.kodemirror:vim, :theme-one-dark, and :lang-python:
@Composable
fun VimEditor() {
val session = rememberEditorSession(
doc = "print(\"Hello, KodeMirror!\")",
extensions = basicSetup + python().extension + vim() + oneDark
)
KodeMirror(session = session, modifier = Modifier.fillMaxSize())
}| Module | Description |
|---|---|
| state | Editor state, transactions, selections, facets |
| view | Compose UI component, decorations, key handling |
| language | Language support infrastructure, syntax highlighting |
| commands | Standard editor commands (cursor movement, delete, indent, undo/redo) |
| basic-setup | Convenience bundle combining common extensions |
| Module | Description |
|---|---|
| search | Find/replace panel, search commands |
| autocomplete | Completion popup and sources |
| lint | Diagnostic display and linting |
| collab | Collaborative editing support |
| merge | Side-by-side diff view |
| vim | Vim keybindings and modal editing |
JavaScript, TypeScript, HTML, CSS, Python, Java, Go, Rust, Markdown, JSON, YAML, XML, SQL, C++, PHP, and more — 20+ dedicated modules plus 100+ (including Kotlin) via legacy-modes.
One Dark, Dracula, Solarized Light, Tomorrow, Cobalt, Espresso, and 11 more, plus Material Design integration.
This is v0.3.6 — the API may evolve. Known issues to be aware of:
See all open issues for the full list.
Contributions are welcome! Please open an issue to discuss before submitting large changes.
Changelog entries go in changelog.d/, not in CHANGELOG.md. When your change is one a user
reads about — behaviour, public API, the build, or user-facing docs — add a new file named
<issue>.<section>.md — for example changelog.d/281.fixed.md — containing the markdown bullet(s)
your change should appear as. A change with no user-visible effect needs none. CHANGELOG.md is
assembled from those files at release time and has no [Unreleased] section to edit; editing it
directly is refused by CI. One file per change means two pull requests never touch the same file,
so they never conflict on the changelog. See
changelog.d/README.md for the sections, the format and when an entry is
expected, and run python3 .github/scripts/changelog.py check to validate.
Copyright 2026 Jason Monk
Licensed under the Apache License, Version 2.0
Originally based on CodeMirror 6 by Marijn Haverbeke, licensed under MIT. See NOTICE for details.