Highlight Theme Provider
Provides HighlightTheme and a shared HighlightEngine to all SyntaxHighlightedCode composables in content.
Automatically selects between lightHighlightTheme and darkHighlightTheme based on the system dark mode setting. Place this at the top of your screen composable (or inside your setContent {} block) so that all code blocks on the screen share the same engine.
A single HighlightEngine (and thus a single hidden WebView) is created for the entire subtree and destroyed when this composable leaves the composition. This means all SyntaxHighlightedCode blocks inside share one WebView instead of creating one per block. Measured on a Pixel 8 Pro debug build (cold start, 17 blocks): with provider takes ~224 ms total and ~15 MB heap vs ~866 ms and ~34 MB heap without - roughly 4x faster and 55% less heap. Each extra standalone engine adds ~37 ms average latency and ~1-2 MB heap while on-screen.
1 provider = 1 WebView. Place it above all the code blocks you want to share.
Typical setup
Prefer rememberTomorrowTheme and rememberTomorrowNightTheme as the default pattern so theme instances stay stable across recompositions:
// At the top of your screen composable:
HighlightThemeProvider(
lightHighlightTheme = rememberTomorrowTheme(),
darkHighlightTheme = rememberTomorrowNightTheme(),
) {
// All SyntaxHighlightedCode composables inside here share 1 WebView
// and use the correct theme automatically.
MyScreenContent()
}Without a provider
Without HighlightThemeProvider, each SyntaxHighlightedCode (or rememberHighlightEngine call) creates its own HighlightEngine and hidden WebView. For a screen with 17 code blocks that measured ~866 ms total highlight time and ~34 MB heap vs ~224 ms and ~15 MB with a provider (Pixel 8 Pro, cold start, debug build). Wrapping the screen in a single provider reduces this to 1 WebView regardless of how many blocks are inside.
Multiple screens
For apps with multiple screens, place one provider at the top of each screen composable. Each screen's engine is automatically destroyed when the screen leaves the composition, so you never hold a WebView alive for screens not currently shown.
// Screen A
HighlightThemeProvider(...) { ScreenAContent() } // 1 WebView, destroyed when A is gone
// Screen B
HighlightThemeProvider(...) { ScreenBContent() } // 1 WebView, destroyed when B is goneManual dark/light override
Pass darkTheme = true/false to force a specific mode regardless of system setting:
HighlightThemeProvider(
darkTheme = userPrefersDark,
lightHighlightTheme = rememberTomorrowTheme(),
darkHighlightTheme = rememberTomorrowNightTheme(),
) { ... }Optional: WebView pre-warming
The hidden WebView initializes lazily on the first highlight call. If you want to reduce that first-call latency further, you can pre-warm the WebView renderer process by calling WebViewCompat.startUpWebView() (androidx.webkit 1.16+) as early as possible - ideally in Application.onCreate() before any Activity is created:
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// Pre-warm the WebView renderer process so it is ready when the first
// HighlightThemeProvider is composed. Best-effort - safe to ignore failures.
runCatching {
WebViewCompat.startUpWebView(
applicationContext,
WebViewStartUpConfig.Builder(mainExecutor).build(),
WebViewOutcomeReceiver { /* no-op */},
)
}
}
}Parameters
Whether to use the dark theme. Defaults to isSystemInDarkTheme.
The theme to use in light mode. Also provided via LocalLightHighlightTheme to the subtree.
The theme to use in dark mode. Also provided via LocalDarkHighlightTheme to the subtree.
The composable content to which the theme and shared engine are provided.