diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
index f7f6bff50d06c0190407b3872c65f193bbf2a56d..be304ce31c545f63455b93d24b567d73b9b7f57d 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactList.kt
@@ -33,6 +33,8 @@ import androidx.compose.foundation.rememberScrollbarAdapter
 import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Surface
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.PersonAdd
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -65,9 +67,11 @@ fun ContactList(
                 modifier = Modifier.fillMaxWidth().height(HEADER_SIZE + 1.dp),
             ) {
                 SearchTextField(
-                    filterBy,
+                    placeholder = i18n("contacts.search.title"),
+                    icon = Icons.Filled.PersonAdd,
+                    searchValue = filterBy,
                     onValueChange = setFilterBy,
-                    onContactAdd = onContactAdd,
+                    onAddButtonClicked = onContactAdd,
                 )
             }
 
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt
index cbfa8141ef5e7afc5e69408b09763e246320da71..6d3e564dfb76184dfd532e37f97ecea736287ae5 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/contact/SearchTextField.kt
@@ -27,17 +27,23 @@ import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Text
 import androidx.compose.material.TextField
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.PersonAdd
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
 import org.briarproject.briar.desktop.ui.ColoredIconButton
 import org.briarproject.briar.desktop.utils.AccessibilityUtils.description
 import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 
 @Composable
-fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onContactAdd: () -> Unit) {
+fun SearchTextField(
+    placeholder: String,
+    icon: ImageVector,
+    searchValue: String,
+    onValueChange: (String) -> Unit,
+    onAddButtonClicked: () -> Unit,
+) {
     TextField(
         value = searchValue,
         onValueChange = onValueChange,
@@ -45,7 +51,7 @@ fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onCont
         textStyle = LocalTextStyle.current.copy(
             color = MaterialTheme.colors.onSurface
         ),
-        placeholder = { Text(i18n("contacts.search.title"), style = MaterialTheme.typography.body1) },
+        placeholder = { Text(placeholder, style = MaterialTheme.typography.body1) },
         shape = RoundedCornerShape(0.dp),
         leadingIcon = {
             val padding = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 32.dp, end = 4.dp)
@@ -53,10 +59,10 @@ fun SearchTextField(searchValue: String, onValueChange: (String) -> Unit, onCont
         },
         trailingIcon = {
             ColoredIconButton(
-                icon = Icons.Filled.PersonAdd,
+                icon = icon,
                 iconSize = 20.dp,
                 contentDescription = i18n("access.contacts.add"),
-                onClick = onContactAdd,
+                onClick = onAddButtonClicked,
                 modifier = Modifier.padding(end = 8.dp)
             )
         },
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt
index 067be793c12e28e53802991fcc409da846cb6806..437b5bdc06c3883e69aea292099aebe2ec3a558d 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsList.kt
@@ -18,40 +18,81 @@
 
 package org.briarproject.briar.desktop.forums
 
+import androidx.compose.foundation.VerticalScrollbar
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.rememberScrollbarAdapter
+import androidx.compose.foundation.selection.selectableGroup
 import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Scaffold
+import androidx.compose.material.Surface
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.AddComment
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
 import org.briarproject.bramble.api.sync.GroupId
+import org.briarproject.briar.desktop.contact.SearchTextField
 import org.briarproject.briar.desktop.theme.surfaceVariant
+import org.briarproject.briar.desktop.ui.Constants
 import org.briarproject.briar.desktop.ui.Constants.COLUMN_WIDTH
 import org.briarproject.briar.desktop.ui.HorizontalDivider
+import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 
 @Composable
 fun ForumsList(
-    list: List<ForumsItem>,
+    list: State<List<GroupItem>>,
     isSelected: (GroupId) -> Boolean,
+    filterBy: State<String>,
+    onFilterSet: (String) -> Unit,
     onGroupIdSelected: (GroupId) -> Unit,
+    onAddButtonClicked: () -> Unit,
 ) {
-    // TODO AddForumDialog (and search bar?)
-    Scaffold(
+    val scrollState = rememberLazyListState()
+    Surface(
         modifier = Modifier.fillMaxHeight().width(COLUMN_WIDTH),
-        backgroundColor = MaterialTheme.colors.surfaceVariant,
-        content = {
-            LazyColumn {
-                items(list) { item ->
-                    GroupsCard(
-                        item = item,
-                        onGroupIdSelected = onGroupIdSelected,
-                        selected = isSelected(item.forum.id)
-                    )
-                    HorizontalDivider()
+        color = MaterialTheme.colors.surfaceVariant
+    ) {
+        Column {
+            Column(
+                modifier = Modifier.fillMaxWidth().height(Constants.HEADER_SIZE + 1.dp),
+            ) {
+                SearchTextField(
+                    placeholder = i18n("forum.search.title"),
+                    icon = Icons.Filled.AddComment,
+                    searchValue = filterBy.value,
+                    onValueChange = onFilterSet,
+                    onAddButtonClicked = onAddButtonClicked,
+                )
+            }
+            Box(modifier = Modifier.fillMaxSize()) {
+                LazyColumn(
+                    state = scrollState,
+                    modifier = Modifier.selectableGroup()
+                ) {
+                    items(list.value) { item ->
+                        GroupsCard(
+                            item = item,
+                            onGroupIdSelected = onGroupIdSelected,
+                            selected = isSelected(item.id)
+                        )
+                        HorizontalDivider()
+                    }
                 }
+                VerticalScrollbar(
+                    adapter = rememberScrollbarAdapter(scrollState),
+                    modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight()
+                )
             }
-        },
-    )
+        }
+    }
 }
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt
index f1a6509dcc55bbbb9d733857aa31c1683988d1de..77f448ddf8fe2cf9b6b89081d4f918d9020ed666 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsScreen.kt
@@ -36,7 +36,10 @@ fun ForumsScreen(
         ForumsList(
             list = viewModel.groupList,
             isSelected = viewModel::isSelected,
+            filterBy = viewModel.filterBy,
+            onFilterSet = viewModel::setFilterBy,
             onGroupIdSelected = viewModel::selectGroup,
+            onAddButtonClicked = {},
         )
         VerticalDivider()
         Column(modifier = Modifier.weight(1f).fillMaxHeight()) {
diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt
index ea998d3efbeab7d963227a3953013d01321961ca..b92f33acdbf46732c515a1a01e6af62cacd17b8b 100644
--- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt
+++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/forums/ForumsViewModel.kt
@@ -19,6 +19,7 @@
 package org.briarproject.briar.desktop.forums
 
 import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
 import androidx.compose.runtime.mutableStateListOf
 import androidx.compose.runtime.mutableStateOf
 import org.briarproject.bramble.api.db.TransactionManager
@@ -30,6 +31,7 @@ import org.briarproject.briar.api.forum.ForumManager
 import org.briarproject.briar.desktop.threading.BriarExecutors
 import org.briarproject.briar.desktop.utils.clearAndAddAll
 import org.briarproject.briar.desktop.viewmodel.EventListenerDbViewModel
+import org.briarproject.briar.desktop.viewmodel.asState
 import javax.inject.Inject
 
 class ForumsViewModel
@@ -43,11 +45,19 @@ constructor(
 ) : EventListenerDbViewModel(briarExecutors, lifecycleManager, db, eventBus) {
 
     private val _fullGroupList = mutableStateListOf<ForumsItem>()
-    val groupList: List<ForumsItem> = _fullGroupList
+    val groupList = derivedStateOf {
+        val filter = _filterBy.value
+        _fullGroupList.filter { item ->
+            item.name.contains(filter, ignoreCase = true)
+        }.sortedByDescending { it.timestamp }
+    }
 
     private val _selectedGroupId = mutableStateOf<GroupId?>(null)
     val selectedGroupId: State<GroupId?> = _selectedGroupId
 
+    private val _filterBy = mutableStateOf("")
+    val filterBy = _filterBy.asState()
+
     override fun onInit() {
         super.onInit()
         loadGroups()
@@ -76,4 +86,8 @@ constructor(
     }
 
     fun isSelected(privateGroupId: GroupId) = _selectedGroupId.value == privateGroupId
+
+    fun setFilterBy(filter: String) {
+        _filterBy.value = filter
+    }
 }
diff --git a/briar-desktop/src/main/resources/strings/BriarDesktop.properties b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
index f82aa0b2a7cd06a2848f8e52ef6a6f7f05661d17..47f782afa9cbb2ce90d4da10f13541003588c5c2 100644
--- a/briar-desktop/src/main/resources/strings/BriarDesktop.properties
+++ b/briar-desktop/src/main/resources/strings/BriarDesktop.properties
@@ -101,6 +101,7 @@ conversation.change.alias.dialog.title=Change contact name
 conversation.change.alias.dialog.description=Please enter a new name for this contact (only visible to you):
 
 # Forums
+forum.search.title=Forums
 group.card.posts={0, plural, one {{0} post} other {{0} posts}}
 
 # Private Groups