How to Format Editable Text in Compose 1.12
Apply SpanStyle in BasicTextField with Compose 1.12 addStyle APIs and query live text styles.
Jetpack Compose 1.12 adds first-party rich formatting for editable text. You can apply bold, color, and paragraph styles inside BasicTextField without a custom editor kit.
The APIs shipped in the August 2026 release on BOM 2026.08.00. This guide shows the upgrade, addStyle() on TextFieldBuffer, how to read styles back, and how selection and credentials fit the same text stack.
Facts come from the official Android Developers Blog post for the Compose August 2026 release.
What Compose 1.12 changes for text
TextFieldBuffer now exposes addStyle() so you can attach SpanStyle and ParagraphStyle while you edit. Call it inside textFieldState.edit { } or from an InputTransformation.
You can also inspect styles. getSpanStyles() and getParagraphStyles() return TrackedRange objects you can update or remove. TextFieldState adds a read-only textStyles property. TextFieldBuffer.originalTextStyles shows formatting as it existed before the current edit.
Google states that text formatting and custom annotations persist across configuration changes. That removes a common source of lost highlights after rotation.
Upgrade the project first
Pin the August BOM and let Compose modules resolve without hard-coded versions:
dependencies {
implementation(platform("androidx.compose:compose-bom:2026.08.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.foundation:foundation")
implementation("androidx.compose.material3:material3")
}
Set compileSdk = 37. Compose 1.12 requires Android Gradle Plugin 9.1.1 or newer. Replace Modifier.onFirstVisible() with Modifier.onVisibilityChanged() before you ship.
If you already tuned startup with Android Baseline Profiles, regenerate profiles after the BOM bump. The same release notes say Time to Initial Display in Google’s hero benchmarks is now comparable to Views.
Apply styles while the user types
Create a TextFieldState, then edit the buffer. The official sample bolds and colors the first nine characters:
val state = rememberTextFieldState("Formatted text in Compose 1.12")
state.edit {
addStyle(
SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue),
start = 0,
end = 9
)
}
val currentStyles = state.textStyles
Keep the edit block short. Apply styles after you know the range, not on every keystroke unless product requires live highlighting.
For a notes or comments field, wrap the same call in an InputTransformation that scans for markers such as **bold** and maps them to SpanStyle. Read originalTextStyles if you need to restore a range the user just overtyped.
ParagraphStyle uses the same addStyle() entry point. Use it for alignment or line height on a selected block, then query getParagraphStyles() to keep a format toolbar in sync.
Control selection next to formatting
Formatting is only half of an editor. Compose 1.12 also adds SelectionState for SelectionContainer.
Hoist state with rememberSelectionState(). You get a reactive selectedTexts list of AnnotatedString values, plus selectAll(), clear(), select(TextRange), and extendSelectionByWord().
getSelectableTexts() returns every selectable item in layout order. That lets you select across multiple composables with a global range.
@Composable
fun ProgrammaticSelectionExample() {
val selectionState = rememberSelectionState()
Column {
Button(
onClick = { selectionState.selectAll() },
modifier = Modifier.disableSelectionClearOnTap()
) {
Text("Select All")
}
SelectionContainer(state = selectionState) {
Text("Text content to be selected programmatically.")
}
}
}
Use disableSelectionClearOnTap() on toolbar buttons so a format action does not wipe the selection. SelectionContainer now auto-scrolls when a drag leaves the viewport.
Pair fields with Credential Manager
Login screens sit next to rich-text editors in many apps. On API 34 and higher, Compose text fields integrate with Android Credential Manager through Autofill. Below API 34, androidx.credentialslibrary covers the gap.
Attach credentialRequest semantics and a CredentialRequestData object so the field can request a passkey or saved credential during input. A dedicated walkthrough lives in Compose Credential Manager Google Sign-In.
Other text notes from the same release:
- Downloadable fonts support font variation settings.
KeyboardTypeadds Date, Time, DateTime, and SignedDecimal.BasicSecureTextFielddefaults toTextObfuscationMode.System.RevealLastTypedremains an explicit override.- Compose components play click and focus sounds. Wrap with
SoundEffectOnInteractionto opt out. Semantics click listeners must run on the main thread.
Practical tips
Store style ranges in your document model, not only in the field. Re-apply them in edit { } when you restore state so rotation and process death stay consistent with what Compose already persists.
Do not mix addStyle() with manual AnnotatedString rebuilds on the same buffer in one frame. Pick one writer.
Keep color tokens in a theme. Display P3 and HDR now travel through the Compose graphics pipeline in 1.12. If you paint mesh gradient backgrounds behind an editor, test contrast on both sRGB and P3 panels.
For dashboards that sit beside the editor, named areas on the experimental Grid keep chrome stable. See named Grid areas in Compose 1.12 if the layout is more than a single column.
Write tests with hasPendingWork() and runWithoutImplicitWait when you assert style after an animation frame. Those APIs exist to cut flakiness while you step the clock.
Conclusion
Compose 1.12 turns BasicTextField into a usable rich-text surface. Upgrade to BOM 2026.08.00, call addStyle() on the buffer, and read textStyles to drive a toolbar.
Add SelectionState when users need Select All or word-level extend. Attach Credential Manager semantics on sign-in fields in the same release so the text stack stays consistent.
Start with one formatted title range. Expand to paragraph styles only after that path survives rotation and screenshot tests.
Sources
Share this story