diff --git a/aleksis/core/filters.py b/aleksis/core/filters.py
index 12265c33c6b9e0d94a9dfb5e87d9111c721fc4ad..c0159567101fe2620deb0f116019ca68e4cf9652 100644
--- a/aleksis/core/filters.py
+++ b/aleksis/core/filters.py
@@ -9,3 +9,12 @@ class GroupFilter(FilterSet):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         self.form.layout = Layout(Row("name", "short_name"))
+
+
+class PersonFilter(FilterSet):
+    first_name = CharFilter(lookup_expr="icontains")
+    last_name = CharFilter(lookup_expr="icontains")
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.form.layout = Layout(Row("first_name", "last_name"))
diff --git a/aleksis/core/templates/core/group/full.html b/aleksis/core/templates/core/group/full.html
index ba5bba0aa1208565331d748d106b4d2e982f7918..16d8bf22c3ccc4e2e738099dded4b445ff1dd417 100644
--- a/aleksis/core/templates/core/group/full.html
+++ b/aleksis/core/templates/core/group/full.html
@@ -3,7 +3,7 @@
 {% extends "core/base.html" %}
 {% load rules %}
 
-{% load i18n static %}
+{% load i18n static material_form %}
 {% load render_table from django_tables2 %}
 
 {% block browser_title %}{{ group.name }}{% endblock %}
@@ -60,9 +60,19 @@
   </table>
 
   <h5>{% blocktrans %}Owners{% endblocktrans %}</h5>
+  <form method="get">
+    {% form form=owners_filter.form %}{% endform %}
+    {% trans "Search" as caption %}
+    {% include "core/partials/save_button.html" with caption=caption icon="search" %}
+  </form>
   {% render_table owners_table %}
 
   <h5>{% blocktrans %}Members{% endblocktrans %}</h5>
+  <form method="get">
+    {% form form=members_filter.form %}{% endform %}
+    {% trans "Search" as caption %}
+    {% include "core/partials/save_button.html" with caption=caption icon="search" %}
+  </form>
   {% render_table members_table %}
 
 {% endblock %}
diff --git a/aleksis/core/templates/core/group/list.html b/aleksis/core/templates/core/group/list.html
index fab23516a458f55347ebcc152ee6ffac20e004d5..5dff8c86ade16e1b9f77aee7e2494fddb7ea0ba3 100644
--- a/aleksis/core/templates/core/group/list.html
+++ b/aleksis/core/templates/core/group/list.html
@@ -2,7 +2,7 @@
 
 {% extends "core/base.html" %}
 
-{% load i18n %}
+{% load i18n material_form %}
 {% load render_table from django_tables2 %}
 
 {% block browser_title %}{% blocktrans %}Groups{% endblocktrans %}{% endblock %}
@@ -14,5 +14,11 @@
     {% trans "Create group" %}
   </a>
 
+  <form method="get">
+    {% form form=groups_filter.form %}{% endform %}
+    {% trans "Search" as caption %}
+    {% include "core/partials/save_button.html" with caption=caption icon="search" %}
+  </form>
+
   {% render_table groups_table %}
 {% endblock %}
diff --git a/aleksis/core/templates/core/person/full.html b/aleksis/core/templates/core/person/full.html
index 01dccc43e3b580eeef858dfe0d4602bae97b793e..7daa2ca1e2972eb04a0c2c06d64875e1530e65b1 100644
--- a/aleksis/core/templates/core/person/full.html
+++ b/aleksis/core/templates/core/person/full.html
@@ -2,7 +2,7 @@
 
 {% extends "core/base.html" %}
 
-{% load i18n static rules %}
+{% load i18n static rules material_form %}
 {% load render_table from django_tables2 %}
 
 {% block browser_title %}{{ person.first_name }} {{ person.last_name }}{% endblock %}
@@ -134,6 +134,11 @@
   {% has_perm 'core.view_person_groups' user person as can_view_groups %}
   {% if can_view_groups %}
     <h5>{% blocktrans %}Groups{% endblocktrans %}</h5>
+    <form method="get">
+      {% form form=groups_filter.form %}{% endform %}
+      {% trans "Search" as caption %}
+      {% include "core/partials/save_button.html" with caption=caption icon="search" %}
+    </form>
     {% render_table groups_table %}
   {% endif %}
 {% endblock %}
diff --git a/aleksis/core/templates/core/person/list.html b/aleksis/core/templates/core/person/list.html
index 1103199e002bda1394b3028df4483c69804268d8..816e7254b8bc58d8a6a155afdd97d17310042509 100644
--- a/aleksis/core/templates/core/person/list.html
+++ b/aleksis/core/templates/core/person/list.html
@@ -2,7 +2,7 @@
 
 {% extends "core/base.html" %}
 
-{% load i18n rules %}
+{% load i18n rules material_form %}
 {% load render_table from django_tables2 %}
 
 {% block browser_title %}{% blocktrans %}Persons{% endblocktrans %}{% endblock %}
@@ -18,5 +18,11 @@
     </a>
   {% endif %}
 
+  <form method="get">
+    {% form form=persons_filter.form %}{% endform %}
+    {% trans "Search" as caption %}
+    {% include "core/partials/save_button.html" with caption=caption icon="search" %}
+  </form>
+
   {% render_table persons_table %}
 {% endblock %}
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 33db765bb8b709b2725d6679718dc37b56f0a73b..4a3876cfa457843acf5042080424bbf8802e0a4e 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -19,7 +19,7 @@ from haystack.views import SearchView
 from health_check.views import MainView
 from rules.contrib.views import PermissionRequiredMixin, permission_required
 
-from .filters import GroupFilter
+from .filters import GroupFilter, PersonFilter
 from .forms import (
     AnnouncementForm,
     ChildGroupsForm,
@@ -143,8 +143,12 @@ def persons(request: HttpRequest) -> HttpResponse:
         request.user, "core.view_person", Person.objects.filter(is_active=True)
     )
 
+    # Get filter
+    persons_filter = PersonFilter(request.GET, queryset=persons)
+    context["persons_filter"] = persons_filter
+
     # Build table
-    persons_table = PersonsTable(persons)
+    persons_table = PersonsTable(persons_filter.qs)
     RequestConfig(request).configure(persons_table)
     context["persons_table"] = persons_table
 
@@ -164,8 +168,12 @@ def person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse:
     # Get groups where person is member of
     groups = Group.objects.filter(members=person)
 
+    # Get filter
+    groups_filter = GroupFilter(request.GET, queryset=groups)
+    context["groups_filter"] = groups_filter
+
     # Build table
-    groups_table = GroupsTable(groups)
+    groups_table = GroupsTable(groups_filter.qs)
     RequestConfig(request).configure(groups_table)
     context["groups_table"] = groups_table
 
@@ -186,16 +194,24 @@ def group(request: HttpRequest, id_: int) -> HttpResponse:
     # Get members
     members = group.members.filter(is_active=True)
 
+    # Get filter
+    members_filter = PersonFilter(request.GET, queryset=members)
+    context["members_filter"] = members_filter
+
     # Build table
-    members_table = PersonsTable(members)
+    members_table = PersonsTable(members_filter.qs)
     RequestConfig(request).configure(members_table)
     context["members_table"] = members_table
 
     # Get owners
     owners = group.owners.filter(is_active=True)
 
+    # Get filter
+    owners_filter = PersonFilter(request.GET, queryset=owners)
+    context["owners_filter"] = owners_filter
+
     # Build table
-    owners_table = PersonsTable(owners)
+    owners_table = PersonsTable(owners_filter.qs)
     RequestConfig(request).configure(owners_table)
     context["owners_table"] = owners_table
 
@@ -210,8 +226,12 @@ def groups(request: HttpRequest) -> HttpResponse:
     # Get all groups
     groups = get_objects_for_user(request.user, "core.view_group", Group)
 
+    # Get filter
+    groups_filter = GroupFilter(request.GET, queryset=groups)
+    context["groups_filter"] = groups_filter
+
     # Build table
-    groups_table = GroupsTable(groups)
+    groups_table = GroupsTable(group_filter.qs)
     RequestConfig(request).configure(groups_table)
     context["groups_table"] = groups_table