diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
index acfc83f33..75ef269ee 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceItem.kt
@@ -82,7 +82,7 @@ internal fun PreferenceItem(
                 ListPreferenceWidget(
                     value = value,
                     title = item.title,
-                    subtitle = item.subtitle,
+                    subtitle = item.internalSubtitleProvider(value, item.entries),
                     icon = item.icon,
                     entries = item.entries,
                     onValueChange = { newValue ->
@@ -98,7 +98,7 @@ internal fun PreferenceItem(
                 ListPreferenceWidget(
                     value = item.value,
                     title = item.title,
-                    subtitle = item.subtitle,
+                    subtitle = item.subtitleProvider(item.value, item.entries),
                     icon = item.icon,
                     entries = item.entries,
                     onValueChange = { scope.launch { item.onValueChanged(it) } },
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt
index 89c88c7f7..ce0238407 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceModel.kt
@@ -1,7 +1,11 @@
 package eu.kanade.presentation.more.settings
 
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
 import eu.kanade.domain.ui.model.AppTheme
+import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.track.TrackService
 import eu.kanade.tachiyomi.core.preference.Preference as PreferenceData
 
@@ -47,6 +51,8 @@ sealed class Preference {
             val pref: PreferenceData<T>,
             override val title: String,
             override val subtitle: String? = "%s",
+            val subtitleProvider: @Composable (value: T, entries: Map<T, String>) -> String? =
+                { v, e -> subtitle?.format(e[v]) },
             override val icon: ImageVector? = null,
             override val enabled: Boolean = true,
             override val onValueChanged: suspend (newValue: T) -> Boolean = { true },
@@ -55,6 +61,10 @@ sealed class Preference {
         ) : PreferenceItem<T>() {
             internal fun internalSet(newValue: Any) = pref.set(newValue as T)
             internal suspend fun internalOnValueChanged(newValue: Any) = onValueChanged(newValue as T)
+
+            @Composable
+            internal fun internalSubtitleProvider(value: Any?, entries: Map<out Any?, String>) =
+                subtitleProvider(value as T, entries as Map<T, String>)
         }
 
         /**
@@ -64,6 +74,8 @@ sealed class Preference {
             val value: String,
             override val title: String,
             override val subtitle: String? = "%s",
+            val subtitleProvider: @Composable (value: String, entries: Map<String, String>) -> String? =
+                { v, e -> subtitle?.format(e[v]) },
             override val icon: ImageVector? = null,
             override val enabled: Boolean = true,
             override val onValueChanged: suspend (newValue: String) -> Boolean = { true },
@@ -78,7 +90,15 @@ sealed class Preference {
         data class MultiSelectListPreference(
             val pref: PreferenceData<Set<String>>,
             override val title: String,
-            override val subtitle: String? = null,
+            override val subtitle: String? = "%s",
+            val subtitleProvider: @Composable (value: Set<String>, entries: Map<String, String>) -> String? = { v, e ->
+                val combined = remember(v) {
+                    v.map { e[it] }
+                        .takeIf { it.isNotEmpty() }
+                        ?.joinToString()
+                } ?: stringResource(id = R.string.none)
+                subtitle?.format(combined)
+            },
             override val icon: ImageVector? = null,
             override val enabled: Boolean = true,
             override val onValueChanged: suspend (newValue: Set<String>) -> Boolean = { true },
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt
index e423712ce..fa384feeb 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt
@@ -72,7 +72,6 @@ class SettingsAppearanceScreen : SearchableSettings {
                 Preference.PreferenceItem.ListPreference(
                     pref = themeModePref,
                     title = stringResource(R.string.pref_theme_mode),
-                    subtitle = "%s",
                     entries = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                         mapOf(
                             ThemeMode.SYSTEM to stringResource(R.string.theme_system),
@@ -129,7 +128,6 @@ class SettingsAppearanceScreen : SearchableSettings {
                 Preference.PreferenceItem.ListPreference(
                     pref = uiPreferences.relativeTime(),
                     title = stringResource(R.string.pref_relative_format),
-                    subtitle = "%s",
                     entries = mapOf(
                         0 to stringResource(R.string.off),
                         2 to stringResource(R.string.pref_relative_time_short),
@@ -139,7 +137,6 @@ class SettingsAppearanceScreen : SearchableSettings {
                 Preference.PreferenceItem.ListPreference(
                     pref = uiPreferences.dateFormat(),
                     title = stringResource(R.string.pref_date_format),
-                    subtitle = "%s",
                     entries = DateFormats
                         .associateWith {
                             val formattedDate = UiPreferences.dateFormat(it).format(now)
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
index e8a1e313f..f606674be 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
@@ -10,7 +10,6 @@ import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.saveable.rememberSaveable
 import androidx.compose.runtime.setValue
@@ -27,7 +26,6 @@ import eu.kanade.presentation.more.settings.Preference
 import eu.kanade.presentation.more.settings.widget.TriStateListDialog
 import eu.kanade.presentation.util.collectAsState
 import eu.kanade.tachiyomi.R
-import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.runBlocking
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
@@ -101,9 +99,11 @@ class SettingsDownloadScreen : SearchableSettings {
         return Preference.PreferenceItem.ListPreference(
             pref = currentDirPref,
             title = stringResource(R.string.pref_download_directory),
-            subtitle = remember(currentDir) {
-                UniFile.fromUri(context, currentDir.toUri())?.filePath
-            } ?: stringResource(R.string.invalid_location, currentDir),
+            subtitleProvider = { value, _ ->
+                remember(value) {
+                    UniFile.fromUri(context, value.toUri())?.filePath
+                } ?: stringResource(R.string.invalid_location, value)
+            },
             entries = mapOf(
                 defaultDirPair,
                 customDirEntryKey to stringResource(R.string.custom_dir),
@@ -173,25 +173,10 @@ class SettingsDownloadScreen : SearchableSettings {
         downloadPreferences: DownloadPreferences,
         categories: () -> List<Category>,
     ): Preference.PreferenceItem.MultiSelectListPreference {
-        val none = stringResource(R.string.none)
-        val pref = downloadPreferences.removeExcludeCategories()
-        val entries = categories().associate { it.id.toString() to it.visualName }
-        val subtitle by produceState(initialValue = "") {
-            pref.changes()
-                .stateIn(this)
-                .collect { mutable ->
-                    value = mutable
-                        .mapNotNull { id -> entries[id] }
-                        .sortedBy { entries.values.indexOf(it) }
-                        .joinToString()
-                        .ifEmpty { none }
-                }
-        }
         return Preference.PreferenceItem.MultiSelectListPreference(
-            pref = pref,
+            pref = downloadPreferences.removeExcludeCategories(),
             title = stringResource(R.string.pref_remove_exclude_categories),
-            subtitle = subtitle,
-            entries = entries,
+            entries = categories().associate { it.id.toString() to it.visualName },
         )
     }
 
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt
index 906b8f4e1..62fc12a88 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsGeneralScreen.kt
@@ -72,7 +72,6 @@ class SettingsGeneralScreen : SearchableSettings {
                 Preference.PreferenceItem.BasicListPreference(
                     value = currentLanguage,
                     title = stringResource(R.string.pref_app_language),
-                    subtitle = "%s",
                     entries = langs,
                     onValueChanged = { newValue ->
                         currentLanguage = newValue
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
index 5230d141f..5ff503ae7 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
@@ -177,28 +177,6 @@ class SettingsLibraryScreen : SearchableSettings {
 
         val libraryUpdateInterval by libraryUpdateIntervalPref.collectAsState()
 
-        val deviceRestrictionEntries = mapOf(
-            DEVICE_ONLY_ON_WIFI to stringResource(R.string.connected_to_wifi),
-            DEVICE_NETWORK_NOT_METERED to stringResource(R.string.network_not_metered),
-            DEVICE_CHARGING to stringResource(R.string.charging),
-            DEVICE_BATTERY_NOT_LOW to stringResource(R.string.battery_not_low),
-        )
-        val deviceRestrictions = libraryUpdateDeviceRestrictionPref.collectAsState()
-            .value
-            .sorted()
-            .map { deviceRestrictionEntries.getOrElse(it) { it } }
-            .let { if (it.isEmpty()) stringResource(R.string.none) else it.joinToString() }
-
-        val mangaRestrictionEntries = mapOf(
-            MANGA_HAS_UNREAD to stringResource(R.string.pref_update_only_completely_read),
-            MANGA_NON_READ to stringResource(R.string.pref_update_only_started),
-            MANGA_NON_COMPLETED to stringResource(R.string.pref_update_only_non_completed),
-        )
-        val mangaRestrictions = libraryUpdateMangaRestrictionPref.collectAsState()
-            .value
-            .map { mangaRestrictionEntries.getOrElse(it) { it } }
-            .let { if (it.isEmpty()) stringResource(R.string.none) else it.joinToString() }
-
         val included by libraryUpdateCategoriesPref.collectAsState()
         val excluded by libraryUpdateCategoriesExcludePref.collectAsState()
         var showDialog by rememberSaveable { mutableStateOf(false) }
@@ -224,7 +202,6 @@ class SettingsLibraryScreen : SearchableSettings {
                 Preference.PreferenceItem.ListPreference(
                     pref = libraryUpdateIntervalPref,
                     title = stringResource(R.string.pref_library_update_interval),
-                    subtitle = "%s",
                     entries = mapOf(
                         0 to stringResource(R.string.update_never),
                         12 to stringResource(R.string.update_12hour),
@@ -242,8 +219,13 @@ class SettingsLibraryScreen : SearchableSettings {
                     pref = libraryUpdateDeviceRestrictionPref,
                     enabled = libraryUpdateInterval > 0,
                     title = stringResource(R.string.pref_library_update_restriction),
-                    subtitle = stringResource(R.string.restrictions, deviceRestrictions),
-                    entries = deviceRestrictionEntries,
+                    subtitle = stringResource(R.string.restrictions),
+                    entries = mapOf(
+                        DEVICE_ONLY_ON_WIFI to stringResource(R.string.connected_to_wifi),
+                        DEVICE_NETWORK_NOT_METERED to stringResource(R.string.network_not_metered),
+                        DEVICE_CHARGING to stringResource(R.string.charging),
+                        DEVICE_BATTERY_NOT_LOW to stringResource(R.string.battery_not_low),
+                    ),
                     onValueChanged = {
                         // Post to event looper to allow the preference to be updated.
                         ContextCompat.getMainExecutor(context).execute { LibraryUpdateJob.setupTask(context) }
@@ -253,8 +235,11 @@ class SettingsLibraryScreen : SearchableSettings {
                 Preference.PreferenceItem.MultiSelectListPreference(
                     pref = libraryUpdateMangaRestrictionPref,
                     title = stringResource(R.string.pref_library_update_manga_restriction),
-                    subtitle = mangaRestrictions,
-                    entries = mangaRestrictionEntries,
+                    entries = mapOf(
+                        MANGA_HAS_UNREAD to stringResource(R.string.pref_update_only_completely_read),
+                        MANGA_NON_READ to stringResource(R.string.pref_update_only_started),
+                        MANGA_NON_COMPLETED to stringResource(R.string.pref_update_only_non_completed),
+                    ),
                 ),
                 Preference.PreferenceItem.TextPreference(
                     title = stringResource(R.string.categories),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt
index 0ac7d20a9..1b7fa736c 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt
@@ -49,7 +49,6 @@ class SettingsSecurityScreen : SearchableSettings {
             Preference.PreferenceItem.ListPreference(
                 pref = securityPreferences.lockAppAfter(),
                 title = stringResource(R.string.lock_when_idle),
-                subtitle = "%s",
                 enabled = authSupported && useAuth,
                 entries = LockAfterValues
                     .associateWith {
@@ -72,7 +71,6 @@ class SettingsSecurityScreen : SearchableSettings {
             Preference.PreferenceItem.ListPreference(
                 pref = securityPreferences.secureScreen(),
                 title = stringResource(R.string.secure_screen),
-                subtitle = "%s",
                 entries = SecurityPreferences.SecureScreenMode.values()
                     .associateWith { stringResource(it.titleResId) },
             ),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt
index 0ff1755b5..8c13cfb20 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt
@@ -39,7 +39,7 @@ fun <T> ListPreferenceWidget(
 
     TextPreferenceWidget(
         title = title,
-        subtitle = subtitle?.format(entries[value]),
+        subtitle = subtitle,
         icon = icon,
         onPreferenceClick = { showDialog(true) },
     )
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt
index 83a91468d..6aab4fd7c 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/MultiSelectListPreferenceWidget.kt
@@ -33,7 +33,7 @@ fun MultiSelectListPreferenceWidget(
 
     TextPreferenceWidget(
         title = preference.title,
-        subtitle = preference.subtitle,
+        subtitle = preference.subtitleProvider(values, preference.entries),
         icon = preference.icon,
         onPreferenceClick = { showDialog(true) },
     )