diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt
index f4890d2174d24954689ce44f558e53ddca4bd6ad..3deba3f0a49a3d3763a6e6217336eef86ce97f88 100644
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/MeteroidRouter.kt
@@ -40,6 +40,7 @@ import androidx.navigation3.runtime.rememberSavedStateNavEntryDecorator
 import androidx.navigation3.ui.NavDisplay
 import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator
 import de.chaosdorf.meteroid.ui.*
+import de.chaosdorf.meteroid.ui.history.HistoryRoute
 import de.chaosdorf.meteroid.ui.purchase.PurchaseRoute
 import de.chaosdorf.meteroid.viewmodel.*
 import kotlinx.coroutines.flow.update
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/HistoryRoute.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/HistoryRoute.kt
deleted file mode 100644
index 0e95ec62db45fb3f3a302caee0598b37cc90fb94..0000000000000000000000000000000000000000
--- a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/HistoryRoute.kt
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2013-2025 Chaosdorf e.V.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package de.chaosdorf.meteroid.ui
-
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.AttachMoney
-import androidx.compose.material.icons.filled.QuestionMark
-import androidx.compose.material3.Icon
-import androidx.compose.material3.ListItem
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.unit.dp
-import coil3.compose.rememberAsyncImagePainter
-import de.chaosdorf.meteroid.model.Drink
-import de.chaosdorf.meteroid.model.Transaction
-import de.chaosdorf.meteroid.theme.secondaryGradient
-import de.chaosdorf.meteroid.ui.common.PriceBadge
-import de.chaosdorf.meteroid.viewmodel.HistoryViewModel
-import de.chaosdorf.meteroid.viewmodel.Navigator
-import kotlinx.datetime.TimeZone
-import kotlinx.datetime.toJavaLocalDateTime
-import kotlinx.datetime.toLocalDateTime
-import java.math.BigDecimal
-import java.time.format.DateTimeFormatter
-import java.time.format.FormatStyle
-
-@Composable
-fun HistoryRoute(
-  viewModel: HistoryViewModel,
-  navigator: Navigator,
-  contentPadding: PaddingValues,
-) {
-  val transactions by viewModel.transactions.collectAsState()
-
-  LazyColumn(contentPadding = contentPadding) {
-    items(
-      transactions,
-      key = { "transaction-${it.transaction.serverId}-${it.transaction.transactionId}" },
-    ) { (transaction, drink) ->
-      TransactionHistoryItem(transaction, drink)
-    }
-  }
-}
-
-@Composable
-fun TransactionHistoryItem(
-  transaction: Transaction,
-  drink: Drink?,
-  modifier: Modifier = Modifier
-) {
-  val timestamp = transaction.timestamp.toLocalDateTime(TimeZone.currentSystemDefault())
-  val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
-
-  ListItem(
-    headlineContent = {
-      val label = when {
-        drink != null -> drink.name
-        transaction.difference > BigDecimal.ZERO -> "Deposit"
-        else -> "Unknown"
-      }
-      Text(label)
-    },
-    supportingContent = {
-      Text(formatter.format(timestamp.toJavaLocalDateTime()))
-    },
-    leadingContent = {
-      Box(
-        modifier = Modifier
-          .size(48.dp)
-          .clip(CircleShape)
-          .aspectRatio(1.0f)
-          .background(MaterialTheme.colorScheme.secondaryGradient.verticalGradient())
-      ) {
-        if (drink != null) {
-          val thumbPainter = rememberAsyncImagePainter(
-            drink.logoUrl
-          )
-          val originalPainter = rememberAsyncImagePainter(
-            drink.originalLogoUrl,
-            error = thumbPainter
-          )
-
-          Image(
-            painter = originalPainter,
-            contentDescription = null,
-            contentScale = ContentScale.Fit,
-            modifier = Modifier
-              .align(Alignment.Center)
-              .fillMaxSize()
-          )
-        } else if (transaction.difference > BigDecimal.ZERO) {
-          Icon(
-            Icons.Default.AttachMoney,
-            contentDescription = null,
-            modifier = Modifier.align(Alignment.Center)
-          )
-        } else {
-          Icon(
-            Icons.Default.QuestionMark,
-            contentDescription = null,
-            modifier = Modifier.align(Alignment.Center)
-          )
-        }
-      }
-    },
-    trailingContent = {
-      PriceBadge(
-        transaction.difference,
-        modifier = Modifier.padding(horizontal = 8.dp)
-      )
-    },
-    modifier = modifier,
-  )
-}
-
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/HistoryRoute.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/HistoryRoute.kt
new file mode 100644
index 0000000000000000000000000000000000000000..450c6d1051d86de48b8f1ceee2f2fac18c34c55c
--- /dev/null
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/HistoryRoute.kt
@@ -0,0 +1,53 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2025 Chaosdorf e.V.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package de.chaosdorf.meteroid.ui.history
+
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import de.chaosdorf.meteroid.viewmodel.HistoryViewModel
+import de.chaosdorf.meteroid.viewmodel.Navigator
+
+@Composable
+fun HistoryRoute(
+  viewModel: HistoryViewModel,
+  navigator: Navigator,
+  contentPadding: PaddingValues,
+) {
+  val transactions by viewModel.transactions.collectAsState()
+
+  LazyColumn(contentPadding = contentPadding) {
+    items(
+      transactions,
+      key = { "transaction-${it.transaction.serverId}-${it.transaction.transactionId}" },
+      contentType = { if (it.drink != null) "transaction-drink" else "transaction-generic" }
+    ) { (transaction, drink) ->
+      TransactionHistoryItem(transaction, drink)
+    }
+  }
+}
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionHistoryItem.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionHistoryItem.kt
new file mode 100644
index 0000000000000000000000000000000000000000..935011db8c75ae14638cdabe8a9b2cbeb536cc6f
--- /dev/null
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionHistoryItem.kt
@@ -0,0 +1,85 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2025 Chaosdorf e.V.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package de.chaosdorf.meteroid.ui.history
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.ListItem
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import de.chaosdorf.meteroid.model.Drink
+import de.chaosdorf.meteroid.model.Transaction
+import de.chaosdorf.meteroid.ui.common.PriceBadge
+import kotlinx.datetime.TimeZone
+import kotlinx.datetime.toJavaLocalDateTime
+import kotlinx.datetime.toLocalDateTime
+import java.math.BigDecimal
+import java.time.format.DateTimeFormatter
+import java.time.format.FormatStyle
+
+@Composable
+fun TransactionHistoryItem(
+  transaction: Transaction,
+  drink: Drink?,
+  modifier: Modifier = Modifier,
+) {
+  val timestamp = remember(transaction.timestamp) {
+    val timestamp = transaction.timestamp.toLocalDateTime(TimeZone.Companion.currentSystemDefault())
+    val formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
+    formatter.format(timestamp.toJavaLocalDateTime())
+  }
+  val deposit = remember(transaction.difference) {
+    transaction.difference > BigDecimal.ZERO
+  }
+
+  val label = remember(drink, deposit) {
+    when {
+      drink != null -> drink.name
+      deposit -> "Deposit"
+      else -> "Unknown"
+    }
+  }
+
+  ListItem(
+    headlineContent = {
+      Text(label)
+    },
+    supportingContent = {
+      Text(timestamp)
+    },
+    leadingContent = {
+      TransactionIcon(drink, deposit)
+    },
+    trailingContent = {
+      PriceBadge(
+        transaction.difference,
+        modifier = Modifier.padding(horizontal = 8.dp)
+      )
+    },
+    modifier = modifier,
+  )
+}
diff --git a/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionIcon.kt b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionIcon.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9c2c371f34d9dad0dd5515437a9a23d596c6acac
--- /dev/null
+++ b/app/src/main/kotlin/de/chaosdorf/meteroid/ui/history/TransactionIcon.kt
@@ -0,0 +1,86 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013-2025 Chaosdorf e.V.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package de.chaosdorf.meteroid.ui.history
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.AttachMoney
+import androidx.compose.material.icons.filled.QuestionMark
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import coil3.compose.rememberAsyncImagePainter
+import de.chaosdorf.meteroid.model.Drink
+import de.chaosdorf.meteroid.theme.secondaryGradient
+
+@Composable
+fun TransactionIcon(
+  drink: Drink?,
+  deposit: Boolean,
+) {
+  Box(
+    modifier = Modifier
+      .size(48.dp)
+      .clip(CircleShape)
+      .aspectRatio(1.0f)
+      .background(MaterialTheme.colorScheme.secondaryGradient.verticalGradient())
+  ) {
+    if (drink != null) {
+      val thumbPainter = rememberAsyncImagePainter(
+        drink.logoUrl,
+      )
+      val originalPainter = rememberAsyncImagePainter(
+        drink.originalLogoUrl,
+        error = thumbPainter,
+      )
+
+      Image(
+        painter = originalPainter,
+        contentDescription = null,
+        contentScale = ContentScale.Companion.Fit,
+        modifier = Modifier
+          .align(Alignment.Companion.Center)
+          .fillMaxSize()
+      )
+    } else {
+      Icon(
+        if (deposit) Icons.Default.AttachMoney else Icons.Default.QuestionMark,
+        contentDescription = null,
+        modifier = Modifier.align(Alignment.Companion.Center)
+      )
+    }
+  }
+}