StreamingSyntaxHighlightedCode¶
Experimental API
This composable is annotated with @ExperimentalHighlightApi.
The API surface may change without a deprecation cycle.
StreamingSyntaxHighlightedCode is a syntax-highlighted code block composable tailored specifically
for real-time and streaming code (such as LLM responses, terminal logs, and dynamic code generation).
As new tokens arrive, newly appended text renders immediately with 0 ms UI lag while existing lines
retain their syntax colors through span-transfer snapshotting.
Full API in Dokka:
StreamingSyntaxHighlightedCoderememberStreamingHighlightedCodeStreamingSyntaxHighlightedCodeDefaultsExperimentalHighlightApi
When to use it¶
- LLM & AI Chat Streaming: You are streaming code block responses from an AI model (e.g. Gemini, OpenAI, Claude) token-by-token.
- Live Logs & Telemetry: You are displaying incoming code or formatted logs in real time.
- Rapidly Updating Text: The input string updates multiple times per second (15-40+ Hz).
For static documentation, guide pages, or fixed code snippets, use SyntaxHighlightedCode instead.
How it works¶
Unlike SyntaxHighlightedCode (which uses fade-in animations and resets in-flight jobs on each string change), StreamingSyntaxHighlightedCode uses a span-transfer snapshot pipeline:
┌───────────────────────────────────────────────────────────────┐
│ Stream Update: New token arrives (15-40 Hz) │
└───────────────────────────────┬───────────────────────────────┘
│
┌────────────────┴────────────────┐
▼ ▼
┌───────────────────────────────┐ ┌─────────────────────────────┐
│ 1. Instant UI Render │ │ 2. Debounced Engine Run │
│ (0 ms latency, 60 fps) │ │ (Defaults: 200 ms) │
│ │ │ │
│ Render current text with │ │ Coalesce tokens and execute │
│ spans transferred from the │ │ WebView highlight when the │
│ previous snapshot. │ │ stream pauses or finishes. │
│ │ │ │
│ (Prior lines stay styled; │ │ When ready, update snapshot │
│ new tokens render instantly) │ │ with fresh full spans. │
└───────────────────────────────┘ └─────────────────────────────┘
- Zero UI Latency: The composable renders the current text immediately on every frame.
- Span Preservation: Syntax styling on unchanged prefixes (all previous lines) is carried forward seamlessly.
- Newline-Aware Progressive Backfilling: When
triggerOnNewlineis enabled (defaulttrue), completing a line (\n) triggers a background highlight run (throttled byminThrottleMs = 150L), progressively snapping finished lines to full syntax colors while subsequent tokens continue streaming. - Engine Debouncing: Idle pauses are debounced (
debounceMs = 200L), ensuring fast token streams do not overload the underlying JavaScript engine. - Streaming-Aware Scroll: Horizontal scroll position is preserved when text is appended, preventing jarring scroll resets while the user is reading streaming output.
Key parameters¶
code- The current source code string (growing dynamically or static).language- Highlight.js language identifier (e.g."kotlin","python","json").theme- Active theme, defaults toLocalHighlightTheme.current.style- Visual style configuration (CodeBlockStyle).showLineNumbers- Whether to show the line number gutter on the left.debounceMs- Delay in milliseconds to wait after the last token before triggering an idle highlight call. Defaults toStreamingSyntaxHighlightedCodeDefaults.DEBOUNCE_MS(200 ms).triggerOnNewline- Whether to trigger a background highlight run when a new newline (\n) is detected in the stream, progressively styling completed lines. Defaults totrue.minThrottleMs- Minimum interval in milliseconds between consecutive newline-triggered highlight runs. Defaults toStreamingSyntaxHighlightedCodeDefaults.MIN_THROTTLE_MS(150 ms).scrollState- Hoisted horizontalScrollState.languageLabel- Optional composable slot for the language badge in the header (nullto hide).copyButton- Optional composable slot for the copy button in the header (nullto hide).onCopyClick- Optional callback when the copy button is clicked.onHighlightComplete- Optional callback invoked withHighlightResulton successful highlight cycle.onError- Optional callback invoked withHighlightExceptionon failure.
Error handling¶
onError is observational - rendering never breaks when the highlight engine fails:
- Spans are preserved: a mid-stream failure (e.g. a transient WebView timeout) keeps the last successful snapshot, so already-colored lines do not flash back to plain text.
- Automatic retry: the next debounce or newline-triggered cycle retries highlighting with the latest text.
- Plain-text start: if highlighting has never succeeded, incoming text still renders immediately as unstyled monospace text.
Opting in¶
StreamingSyntaxHighlightedCode, rememberStreamingHighlightedCode, and StreamingSyntaxHighlightedCodeDefaults are annotated with @ExperimentalHighlightApi:
// Option 1 - opt in at the call site
@OptIn(ExperimentalHighlightApi::class)
@Composable
fun StreamingResponseScreen() {
StreamingSyntaxHighlightedCode(...)
}
// Option 2 - propagate to your own API
@ExperimentalHighlightApi
@Composable
fun MyChatBubble(...) {
StreamingSyntaxHighlightedCode(...)
}
Basic usage¶
@OptIn(ExperimentalHighlightApi::class)
@Composable
fun ChatCodeSnippet(
streamedCode: String,
language: String,
) {
HighlightThemeProvider(
lightHighlightTheme = rememberTomorrowLightTheme(),
darkHighlightTheme = rememberAtomOneDarkTheme(),
) {
StreamingSyntaxHighlightedCode(
code = streamedCode,
language = language,
showLineNumbers = true,
)
}
}
Streaming from a ViewModel¶
@OptIn(ExperimentalHighlightApi::class)
@Composable
fun StreamingScreen(viewModel: ChatViewModel = viewModel()) {
val streamedCode by viewModel.codeFlow.collectAsState(initial = "")
StreamingSyntaxHighlightedCode(
code = streamedCode,
language = "kotlin",
showLineNumbers = true,
debounceMs = StreamingSyntaxHighlightedCodeDefaults.DEBOUNCE_MS,
)
}
Lower-level helper for custom layouts¶
If you want the highlighted AnnotatedString without the standard code block container, use rememberStreamingHighlightedCode: