diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts
index 243af19..b4d2abe 100644
--- a/composeApp/build.gradle.kts
+++ b/composeApp/build.gradle.kts
@@ -108,6 +108,7 @@ kotlin {
             implementation(libs.kasechange)
 
             implementation(libs.oidc)
+            implementation(libs.kache)
 
             implementation("dev.datlag.sheets-compose-dialogs:rating:2.0.0-SNAPSHOT")
             implementation("dev.datlag.sheets-compose-dialogs:option:2.0.0-SNAPSHOT")
diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendComponent.kt
index fb1f445..ed8e498 100644
--- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendComponent.kt
+++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/common/ExtendComponent.kt
@@ -1,8 +1,10 @@
 package dev.datlag.aniflow.common
 
 import androidx.compose.runtime.*
+import androidx.compose.ui.graphics.painter.Painter
 import com.arkivanov.essenty.lifecycle.Lifecycle
 import com.arkivanov.essenty.lifecycle.LifecycleOwner
+import com.kmpalette.DominantColorState
 import dev.datlag.aniflow.LocalDI
 import dev.datlag.aniflow.ui.navigation.Component
 import dev.datlag.aniflow.ui.theme.SchemeTheme
@@ -33,14 +35,14 @@ fun Component.onRender(content: @Composable () -> Unit) {
 }
 
 @Composable
-fun Component.onRenderWithScheme(key: Any?, content: @Composable () -> Unit) {
+fun Component.onRenderWithScheme(key: Any?, content: @Composable (DominantColorState<Painter>) -> Unit) {
     onRender {
         SchemeTheme(key, content)
     }
 }
 
 @Composable
-fun Component.onRenderApplyCommonScheme(key: Any?, content: @Composable () -> Unit) {
+fun Component.onRenderApplyCommonScheme(key: Any?, content: @Composable (DominantColorState<Painter>) -> Unit) {
     onRenderWithScheme(key, content)
 
     SchemeTheme.setCommon(key)
diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt
index 08612ab..0f655ca 100644
--- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt
+++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/AiringCard.kt
@@ -21,9 +21,11 @@ import dev.datlag.aniflow.anilist.model.Medium
 import dev.datlag.aniflow.common.preferred
 import dev.datlag.aniflow.settings.Settings
 import dev.datlag.aniflow.settings.model.AppSettings
+import dev.datlag.aniflow.ui.theme.LocalDominantColorState
 import dev.datlag.aniflow.ui.theme.SchemeTheme
 import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
 import org.kodein.di.instance
 import org.kodein.di.instanceOrNull
 
@@ -34,6 +36,8 @@ fun AiringCard(
     modifier: Modifier = Modifier,
     onClick: (Medium) -> Unit
 ) {
+    val schemeState = LocalDominantColorState.current
+
     airing.media?.let(::Medium)?.let { media ->
         Card(
             modifier = modifier,
@@ -61,27 +65,27 @@ fun AiringCard(
                             model = media.coverImage.medium,
                             contentScale = ContentScale.Crop,
                             onSuccess = { state ->
-                                SchemeTheme.update(
-                                    key = media.id,
-                                    input = state.painter,
-                                    scope = scope
-                                )
+                                if (schemeState != null) {
+                                    scope.launch {
+                                        schemeState.updateFrom(state.painter)
+                                    }
+                                }
                             }
                         ),
                         onSuccess = { state ->
-                            SchemeTheme.update(
-                                key = media.id,
-                                input = state.painter,
-                                scope = scope
-                            )
+                            if (schemeState != null) {
+                                scope.launch {
+                                    schemeState.updateFrom(state.painter)
+                                }
+                            }
                         }
                     ),
                     onSuccess = { state ->
-                        SchemeTheme.update(
-                            key = media.id,
-                            input = state.painter,
-                            scope = scope
-                        )
+                        if (schemeState != null) {
+                            scope.launch {
+                                schemeState.updateFrom(state.painter)
+                            }
+                        }
                     }
                 )
                 Column(
diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt
index c359a81..bee8fd4 100644
--- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt
+++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/initial/home/component/MediumCard.kt
@@ -31,6 +31,7 @@ import dev.datlag.aniflow.ui.theme.rememberSchemeThemeDominantColorState
 import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalStdlibApi::class)
 @Composable
@@ -42,7 +43,7 @@ fun MediumCard(
 ) {
     SchemeTheme(
         key = medium.id
-    ) {
+    ) { schemeState ->
         Card(
             modifier = modifier,
             onClick = {
@@ -63,9 +64,7 @@ fun MediumCard(
                     defaultColor = color ?: MaterialTheme.colorScheme.primary,
                     defaultOnColor = contentColorFor(color ?: MaterialTheme.colorScheme.primary)
                 )
-                var successPainter by remember { mutableStateOf<Painter?>(null) }
-
-                SchemeTheme.update(medium.id, successPainter)
+                val scope = rememberCoroutineScope()
 
                 AsyncImage(
                     model = medium.coverImage.extraLarge,
@@ -79,18 +78,28 @@ fun MediumCard(
                             model = medium.coverImage.medium,
                             contentScale = ContentScale.Crop,
                             onSuccess = { state ->
-                                successPainter = state.painter
+                                scope.launch {
+                                    schemeState.updateFrom(state.painter)
+                                }
                             },
                             onError = {
-                                successPainter = color?.let(::ColorPainter)
+                                color?.let(::ColorPainter)?.let { painter ->
+                                    scope.launch {
+                                        schemeState.updateFrom(painter)
+                                    }
+                                }
                             }
                         ),
                         onSuccess = { state ->
-                            successPainter = state.painter
+                            scope.launch {
+                                schemeState.updateFrom(state.painter)
+                            }
                         }
                     ),
                     onSuccess = { state ->
-                        successPainter = state.painter
+                        scope.launch {
+                            schemeState.updateFrom(state.painter)
+                        }
                     }
                 )
 
diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/theme/SchemeTheme.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/theme/SchemeTheme.kt
index 4b8adb7..4659626 100644
--- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/theme/SchemeTheme.kt
+++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/theme/SchemeTheme.kt
@@ -15,7 +15,11 @@ import com.kmpalette.rememberPainterDominantColorState
 import com.materialkolor.AnimatedDynamicMaterialTheme
 import com.materialkolor.DynamicMaterialTheme
 import com.materialkolor.ktx.isDisliked
+import com.mayakapps.kache.InMemoryKache
+import com.mayakapps.kache.KacheStrategy
 import dev.datlag.aniflow.LocalDarkMode
+import dev.datlag.tooling.async.scopeCatching
+import dev.datlag.tooling.async.suspendCatching
 import dev.datlag.tooling.compose.ioDispatcher
 import dev.datlag.tooling.compose.launchIO
 import dev.datlag.tooling.compose.withIOContext
@@ -29,79 +33,40 @@ import kotlin.coroutines.CoroutineContext
 data object SchemeTheme {
 
     internal val commonSchemeKey = MutableStateFlow<Any?>(null)
-    internal val colorState = MutableStateFlow<Map<Any, DominantColorState<Painter>>>(emptyMap())
-    internal val itemScheme = MutableStateFlow<Map<Any, Color?>>(emptyMap())
+    internal val kache = InMemoryKache<Any, DominantColorState<Painter>>(
+        maxSize = 25L * 1024 * 1024
+    ) {
+        strategy = KacheStrategy.LRU
+    }
 
     fun setCommon(key: Any?) {
         commonSchemeKey.update { key }
     }
-
-    @Composable
-    fun update(key: Any?, input: Painter?) {
-        if (input == null) {
-            return
-        }
-
-        LaunchedEffect(key, input) {
-            suspendUpdate(key, input)
-        }
-    }
-
-    fun update(key: Any?, input: Painter?, scope: CoroutineScope) {
-        scope.launchIO {
-            suspendUpdate(key, input)
-        }
-    }
-
-    fun update(key: Any?, color: Color?, scope: CoroutineScope) {
-        scope.launchIO {
-            suspendUpdate(key, color)
-        }
-    }
-
-    suspend fun suspendUpdate(key: Any?, input: Painter?): Boolean {
-        if (key == null || input == null) {
-            return false
-        }
-
-        withIOContext {
-            val useState = (colorState.firstOrNull() ?: colorState.value)[key]
-            useState?.updateFrom(input)
-
-            itemScheme.getAndUpdate {
-                it.toMutableMap().apply {
-                    put(key, useState?.color)
-                }.toMap()
-            }
-        }
-        return true
-    }
-
-    suspend fun suspendUpdate(key: Any?, color: Color?) = suspendUpdate(key, color?.let { ColorPainter(it) })
 }
 
 @Composable
 fun rememberSchemeThemeDominantColor(
-    key: Any?
+    key: Any?,
+    state: DominantColorState<Painter>? = null,
 ): Color? {
     if (key == null) {
         return null
     }
 
-    val state = SchemeTheme.colorState.value[key] ?: rememberPainterDominantColorState(
+    val fallbackState = remember(state) {
+        state
+    } ?: remember(key) {
+        SchemeTheme.kache.getIfAvailable(key)
+    } ?: rememberPainterDominantColorState(
         coroutineContext = ioDispatcher()
     )
-    SchemeTheme.colorState.update {
-        it.toMutableMap().apply {
-            put(key, state)
-        }.toMap()
+    val useState by produceState(fallbackState, key) {
+        value = withIOContext {
+            SchemeTheme.kache.getOrPut(key) { fallbackState }
+        } ?: fallbackState
     }
 
-    val color by remember(key) {
-        SchemeTheme.itemScheme.map { it[key] }
-    }.collectAsStateWithLifecycle(SchemeTheme.itemScheme.value[key])
-
-    return color
+    return remember(useState) { useState.color }
 }
 
 @Composable
@@ -113,25 +78,25 @@ fun rememberSchemeThemeDominantColorState(
     isSwatchValid: (Palette.Swatch) -> Boolean = { true },
     builder: Palette.Builder.() -> Unit = {},
 ): DominantColorState<Painter> {
-    val state by remember(key) {
-        SchemeTheme.colorState.map { it[key] }
-    }.collectAsStateWithLifecycle(SchemeTheme.colorState.value[key])
 
-    return state ?: rememberPainterDominantColorState(
+    val fallbackState = remember(key) {
+        key?.let { SchemeTheme.kache.getIfAvailable(it) }
+    } ?: rememberPainterDominantColorState(
         defaultColor = defaultColor,
         defaultOnColor = defaultOnColor,
         coroutineContext = coroutineContext,
         builder = builder,
         isSwatchValid = isSwatchValid
-    ).also {
-        if (key != null) {
-            SchemeTheme.colorState.update { map ->
-                map.toMutableMap().apply {
-                    put(key, it)
-                }
-            }
+    )
+    val state by produceState(fallbackState, key) {
+        value = withIOContext {
+            key?.let {
+                SchemeTheme.kache.getOrPut(it) { fallbackState }
+            } ?: fallbackState
         }
     }
+
+    return state
 }
 
 @Composable
@@ -166,19 +131,27 @@ fun rememberSchemeThemeDominantColorState(
     )
 }
 
+val LocalDominantColorState = compositionLocalOf<DominantColorState<Painter>?>{ null }
+
 @Composable
-fun SchemeTheme(key: Any?, content: @Composable () -> Unit) {
+fun SchemeTheme(key: Any?, content: @Composable (DominantColorState<Painter>) -> Unit) {
+    val state = rememberSchemeThemeDominantColorState(key)
+
     DynamicMaterialTheme(
-        seedColor = rememberSchemeThemeDominantColor(key) ?: MaterialTheme.colorScheme.primary,
+        seedColor = rememberSchemeThemeDominantColor(key, state) ?: MaterialTheme.colorScheme.primary,
         useDarkTheme = LocalDarkMode.current,
         animate = true
     ) {
-        content()
+        CompositionLocalProvider(
+            LocalDominantColorState provides state,
+        ) {
+            content(state)
+        }
     }
 }
 
 @Composable
-fun CommonSchemeTheme(content: @Composable () -> Unit) {
+fun CommonSchemeTheme(content: @Composable (DominantColorState<Painter>) -> Unit) {
     val key by SchemeTheme.commonSchemeKey.collectAsStateWithLifecycle()
 
     SchemeTheme(key, content)