Jetpack Compose: Building a Custom Markdown Renderer
·Your Name
Jetpack ComposeKotlinAndroid
Building a Markdown renderer in Compose without reaching for a WebView is more achievable than it sounds. This post walks through the annotated string approach, handling nested spans, and the performance tradeoffs involved.
Why avoid WebView?
WebView is heavy, slow to initialize, and makes theming difficult. For a notes app where users might have hundreds of items rendering in a LazyColumn, composable-native rendering is the only viable path.
The AnnotatedString approach
Kotlin's AnnotatedString with SpanStyle handles inline formatting (bold, italic, code, links) cleanly. For block-level elements like headings and code fences, a pre-pass parses the Markdown AST and emits layout composables.
Performance tips
- Cache parsed AST by content hash — re-parsing on every recomposition is expensive
- Use
remember(content)guards liberally - Measure before optimizing: most slowdowns come from image loading, not text parsing