diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt
index e3893a2fe..c711eaed6 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt
@@ -146,79 +146,75 @@ private fun ExtensionContent(
             }
         }
 
-        items(
-            items = state.items,
-            contentType = {
-                when (it) {
-                    is ExtensionUiModel.Header -> "header"
-                    is ExtensionUiModel.Item -> "item"
-                }
-            },
-            key = {
-                when (it) {
-                    is ExtensionUiModel.Header -> "extensionHeader-${it.hashCode()}"
-                    is ExtensionUiModel.Item -> "extension-${it.hashCode()}"
-                }
-            },
-        ) { item ->
-            when (item) {
-                is ExtensionUiModel.Header.Resource -> {
-                    val action: @Composable RowScope.() -> Unit =
-                        if (item.textRes == R.string.ext_updates_pending) {
-                            {
-                                Button(onClick = { onClickUpdateAll() }) {
-                                    Text(
-                                        text = stringResource(R.string.ext_update_all),
-                                        style = LocalTextStyle.current.copy(
-                                            color = MaterialTheme.colorScheme.onPrimary,
-                                        ),
-                                    )
-                                }
-                            }
-                        } else {
-                            {}
-                        }
-                    ExtensionHeader(
-                        textRes = item.textRes,
-                        modifier = Modifier.animateItemPlacement(),
-                        action = action,
-                    )
-                }
-                is ExtensionUiModel.Header.Text -> {
-                    ExtensionHeader(
-                        text = item.text,
-                        modifier = Modifier.animateItemPlacement(),
-                    )
-                }
-                is ExtensionUiModel.Item -> {
-                    ExtensionItem(
-                        modifier = Modifier.animateItemPlacement(),
-                        item = item,
-                        onClickItem = {
-                            when (it) {
-                                is Extension.Available -> onInstallExtension(it)
-                                is Extension.Installed -> onOpenExtension(it)
-                                is Extension.Untrusted -> { trustState = it }
-                            }
-                        },
-                        onLongClickItem = onLongClickItem,
-                        onClickItemCancel = onClickItemCancel,
-                        onClickItemAction = {
-                            when (it) {
-                                is Extension.Available -> onInstallExtension(it)
-                                is Extension.Installed -> {
-                                    if (it.hasUpdate) {
-                                        onUpdateExtension(it)
-                                    } else {
-                                        onOpenExtension(it)
+        state.items.forEach { (header, items) ->
+            item(
+                contentType = "header",
+                key = "extensionHeader-${header.hashCode()}",
+            ) {
+                when (header) {
+                    is ExtensionUiModel.Header.Resource -> {
+                        val action: @Composable RowScope.() -> Unit =
+                            if (header.textRes == R.string.ext_updates_pending) {
+                                {
+                                    Button(onClick = { onClickUpdateAll() }) {
+                                        Text(
+                                            text = stringResource(R.string.ext_update_all),
+                                            style = LocalTextStyle.current.copy(
+                                                color = MaterialTheme.colorScheme.onPrimary,
+                                            ),
+                                        )
                                     }
                                 }
-                                is Extension.Untrusted -> { trustState = it }
+                            } else {
+                                {}
                             }
-                        },
-                    )
+                        ExtensionHeader(
+                            textRes = header.textRes,
+                            modifier = Modifier.animateItemPlacement(),
+                            action = action,
+                        )
+                    }
+                    is ExtensionUiModel.Header.Text -> {
+                        ExtensionHeader(
+                            text = header.text,
+                            modifier = Modifier.animateItemPlacement(),
+                        )
+                    }
                 }
             }
+
+            items(
+                items = items,
+                contentType = { "item" },
+                key = { "extension-${it.hashCode()}" },
+            ) { item ->
+                ExtensionItem(
+                    modifier = Modifier.animateItemPlacement(),
+                    item = item,
+                    onClickItem = {
+                        when (it) {
+                            is Extension.Available -> onInstallExtension(it)
+                            is Extension.Installed -> onOpenExtension(it)
+                            is Extension.Untrusted -> { trustState = it }
+                        }
+                    },
+                    onLongClickItem = onLongClickItem,
+                    onClickItemCancel = onClickItemCancel,
+                    onClickItemAction = {
+                        when (it) {
+                            is Extension.Available -> onInstallExtension(it)
+                            is Extension.Installed -> {
+                                if (it.hasUpdate) {
+                                    onUpdateExtension(it)
+                                } else {
+                                    onOpenExtension(it)
+                                }
+                            }
+                            is Extension.Untrusted -> { trustState = it }
+                        }
+                    },
+                )
+            }
         }
     }
     if (trustState != null) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt
index 184e47951..daa889b11 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt
@@ -40,7 +40,7 @@ class ExtensionsScreenModel(
 
     init {
         val context = Injekt.get<Application>()
-        val extensionMapper: (Map<String, InstallStep>) -> ((Extension) -> ExtensionUiModel) = { map ->
+        val extensionMapper: (Map<String, InstallStep>) -> ((Extension) -> ExtensionUiModel.Item) = { map ->
             {
                 ExtensionUiModel.Item(it, map[it.pkgName] ?: InstallStep.Idle)
             }
@@ -80,38 +80,31 @@ class ExtensionsScreenModel(
             ) { query, downloads, (_updates, _installed, _available, _untrusted) ->
                 val searchQuery = query ?: ""
 
-                val languagesWithExtensions = _available
-                    .filter(queryFilter(searchQuery))
-                    .groupBy { it.lang }
-                    .toSortedMap(LocaleHelper.comparator)
-                    .flatMap { (lang, exts) ->
-                        listOf(
-                            ExtensionUiModel.Header.Text(LocaleHelper.getSourceDisplayName(lang, context)),
-                            *exts.map(extensionMapper(downloads)).toTypedArray(),
-                        )
-                    }
-
-                val items = mutableListOf<ExtensionUiModel>()
+                val itemsGroups: ItemGroups = mutableMapOf()
 
                 val updates = _updates.filter(queryFilter(searchQuery)).map(extensionMapper(downloads))
                 if (updates.isNotEmpty()) {
-                    items.add(ExtensionUiModel.Header.Resource(R.string.ext_updates_pending))
-                    items.addAll(updates)
+                    itemsGroups[ExtensionUiModel.Header.Resource(R.string.ext_updates_pending)] = updates
                 }
 
                 val installed = _installed.filter(queryFilter(searchQuery)).map(extensionMapper(downloads))
                 val untrusted = _untrusted.filter(queryFilter(searchQuery)).map(extensionMapper(downloads))
                 if (installed.isNotEmpty() || untrusted.isNotEmpty()) {
-                    items.add(ExtensionUiModel.Header.Resource(R.string.ext_installed))
-                    items.addAll(installed)
-                    items.addAll(untrusted)
+                    itemsGroups[ExtensionUiModel.Header.Resource(R.string.ext_installed)] = installed + untrusted
                 }
 
+                val languagesWithExtensions = _available
+                    .filter(queryFilter(searchQuery))
+                    .groupBy { it.lang }
+                    .toSortedMap(LocaleHelper.comparator)
+                    .map { (lang, exts) ->
+                        ExtensionUiModel.Header.Text(LocaleHelper.getSourceDisplayName(lang, context)) to exts.map(extensionMapper(downloads))
+                    }
                 if (languagesWithExtensions.isNotEmpty()) {
-                    items.addAll(languagesWithExtensions)
+                    itemsGroups.putAll(languagesWithExtensions)
                 }
 
-                items
+                itemsGroups
             }
                 .collectLatest {
                     mutableState.update { state ->
@@ -140,10 +133,10 @@ class ExtensionsScreenModel(
         coroutineScope.launchIO {
             with(state.value) {
                 if (isEmpty) return@launchIO
-                items
+                items.values
+                    .flatten()
                     .mapNotNull {
                         when {
-                            it !is ExtensionUiModel.Item -> null
                             it.extension !is Extension.Installed -> null
                             !it.extension.hasUpdate -> null
                             else -> it.extension
@@ -216,14 +209,16 @@ class ExtensionsScreenModel(
 data class ExtensionsState(
     val isLoading: Boolean = true,
     val isRefreshing: Boolean = false,
-    val items: List<ExtensionUiModel> = emptyList(),
+    val items: ItemGroups = mutableMapOf(),
     val updates: Int = 0,
 ) {
     val isEmpty = items.isEmpty()
 }
 
-sealed interface ExtensionUiModel {
-    sealed interface Header : ExtensionUiModel {
+typealias ItemGroups = MutableMap<ExtensionUiModel.Header, List<ExtensionUiModel.Item>>
+
+object ExtensionUiModel {
+    sealed interface Header {
         data class Resource(@StringRes val textRes: Int) : Header
         data class Text(val text: String) : Header
     }
@@ -231,5 +226,5 @@ sealed interface ExtensionUiModel {
     data class Item(
         val extension: Extension,
         val installStep: InstallStep,
-    ) : ExtensionUiModel
+    )
 }