diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5f825744bc9ff3fe9e3ebb52e80ac8f622480bb1..fe40b48d52b06b44bfd28b12722e96b5023eee39 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -15,6 +15,11 @@ Added
 * Add Ukrainian locale (contributed by Sergiy Gorichenko from Fre(i)e Software GmbH).
 * Add third gender to gender choices
 
+Changed
+~~~~~~~
+
+* Restructure group page and show more information about members.
+
 Fixed
 ~~~~~~~
 
diff --git a/aleksis/core/static/public/style.scss b/aleksis/core/static/public/style.scss
index 4ae42c291d77c2dddfb3550dc454a0b78a188da4..7aaff6575cbd675a891944a6767291854cc76f78 100644
--- a/aleksis/core/static/public/style.scss
+++ b/aleksis/core/static/public/style.scss
@@ -990,3 +990,8 @@ p.ical-description {
   margin: 0;
   font-weight: 300;
 }
+
+.table-circle, .table-circle .materialize-circle {
+  height: 4em;
+  width: 4em;
+}
diff --git a/aleksis/core/tables.py b/aleksis/core/tables.py
index f08460bd8654907cb56f655e01014342f8e59ab5..a58c3fc443b3ad66c8df31109cbb6276192518ef 100644
--- a/aleksis/core/tables.py
+++ b/aleksis/core/tables.py
@@ -1,5 +1,6 @@
 from textwrap import wrap
 
+from django.template.loader import render_to_string
 from django.utils.translation import gettext_lazy as _
 
 import django_tables2 as tables
@@ -31,12 +32,65 @@ class PersonsTable(tables.Table):
     """Table to list persons."""
 
     class Meta:
+        model = Person
         attrs = {"class": "highlight"}
+        fields = []
 
     first_name = tables.LinkColumn("person_by_id", args=[A("id")])
     last_name = tables.LinkColumn("person_by_id", args=[A("id")])
 
 
+class FullPersonsTable(PersonsTable):
+    """Table to list persons."""
+
+    photo = tables.Column(verbose_name=_("Photo"), accessor="pk", orderable=False)
+    address = tables.Column(verbose_name=_("Address"), accessor="pk", orderable=False)
+
+    class Meta(PersonsTable.Meta):
+        fields = (
+            "photo",
+            "short_name",
+            "date_of_birth",
+            "sex",
+            "email",
+            "user__username",
+        )
+        sequence = ("photo", "first_name", "last_name", "short_name", "...")
+
+    def render_photo(self, value, record):
+        return render_to_string(
+            "core/partials/avatar_content.html",
+            {
+                "person_or_user": record,
+                "class": "materialize-circle table-circle",
+                "img_class": "materialize-circle",
+            },
+            self.request,
+        )
+
+    def render_address(self, value, record):
+        return render_to_string(
+            "core/partials/address.html",
+            {
+                "person": record,
+            },
+            self.request,
+        )
+
+    def before_render(self, request):
+        """Hide columns if user has no permission to view them."""
+        if not self.request.user.has_perm("core.view_person_rule"):
+            self.columns.hide("date_of_birth")
+            self.columns.hide("sex")
+        if not self.request.user.has_perm("core.view_contact_details_rule"):
+            self.columns.hide("email")
+        if not self.request.user.has_perm("core.view_address"):
+            self.columns.hide("street")
+            self.columns.hide("housenumber")
+            self.columns.hide("postal_code")
+            self.columns.hide("place")
+
+
 class GroupsTable(tables.Table):
     """Table to list groups."""
 
diff --git a/aleksis/core/templates/core/group/full.html b/aleksis/core/templates/core/group/full.html
index 3c6e5488d6174e30847eabcfda2e97b244112938..34b5e647590806e72666701ae0bd1ca1196d9638 100644
--- a/aleksis/core/templates/core/group/full.html
+++ b/aleksis/core/templates/core/group/full.html
@@ -9,7 +9,9 @@
 {% block browser_title %}{{ group.name }}{% endblock %}
 
 {% block content %}
-  <h1>{{ group.name }} <small class="grey-text">{{ group.short_name }}</small></h1>
+  <h1>{{ group.name }}
+    <small class="grey-text">{{ group.short_name }}</small>
+  </h1>
 
   {% has_perm 'core.edit_group_rule' user group as can_change_group %}
   {% has_perm 'core.change_group_preferences_rule' user group as can_change_group_preferences %}
@@ -44,39 +46,57 @@
   <table>
     <tr>
       <th>
-        <i class="material-icons iconify center" data-icon="mdi:shape-outline" title="{% trans "Group type" %}"></i>
+        <i class="material-icons iconify left" data-icon="mdi:shape-outline"></i>
+        {% trans "Group type" %}
       </th>
       <td>
-        {{ group.group_type }}
+        {{ group.group_type|default:"–" }}
       </td>
     </tr>
     <tr>
       <th>
-        <i class="material-icons iconify center" data-icon="mdi:format-vertical-align-top" title="{% trans "Parent groups" %}"></i>
+        <i class="material-icons iconify left" data-icon="mdi:format-vertical-align-top"></i>
+        {% trans "Parent groups" %}
       </th>
       <td>
-        {{ group.parent_groups.all|join:", " }}
+        {{ group.parent_groups.all|join:", "|default:"–" }}
       </td>
     </tr>
   </table>
 
   {% if can_view_group_stats %}
     <h2>{% blocktrans %}Statistics{% endblocktrans %}</h2>
-    <ul>
-      <li>
-        {% trans "Count of members" %}: {{ stats.members }}
-      </li>
+    <table>
+      <tr>
+        <th>
+          <i class="material-icons iconify left" data-icon="mdi:account-group-outline"></i>
+          {% trans "Count of members" %}
+        </th>
+        <td>{{ stats.members }}</td>
+      </tr>
       {% if stats.age_avg %}
-        <li>
-          {% trans "Average age" %}: {{ stats.age_avg|floatformat }}
-        </li>
+        <tr>
+          <th>
+            <i class="material-icons iconify left" data-icon="mdi:cake-variant-outline"></i>
+            {% trans "Average age" %}
+          </th>
+          <td>{{ stats.age_avg|floatformat }}</td>
+        </tr>
       {% endif %}
       {% if stats.age_range_min %}
-        <li>
-          {% trans "Age range" %}: {{ stats.age_range_min }} {% trans "years to" %} {{ stats.age_range_max }} {% trans "years "%}
-        </li>
+        <tr>
+          <th>
+            <i class="material-icons iconify left" data-icon="mdi:calendar-range-outline"></i>
+            {% trans "Age range" %}
+          </th>
+          <td>
+            {% blocktrans with min=stats.age_range_min max=stats.age_range_max %}
+              {{ min }} years to {{ max }} years
+            {% endblocktrans %}
+          </td>
+        </tr>
       {% endif %}
-    </ul>
+    </table>
   {% endif %}
 
   <h2>{% blocktrans %}Owners{% endblocktrans %}</h2>
diff --git a/aleksis/core/templates/core/partials/address.html b/aleksis/core/templates/core/partials/address.html
new file mode 100644
index 0000000000000000000000000000000000000000..74a01bde2494d94256432923f5f27c279da2e564
--- /dev/null
+++ b/aleksis/core/templates/core/partials/address.html
@@ -0,0 +1,2 @@
+{{ person.street }} {{ person.housenumber }}<br>
+{{ person.postal_code }} {{ person.place }}
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 62ac22aa692ad5075ead9cf0d0bdd139f2fbccaf..185cf225841c7b7cf2e4c3a99f5cea34e332550c 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -115,6 +115,7 @@ from .registries import (
 from .tables import (
     AdditionalFieldsTable,
     DashboardWidgetTable,
+    FullPersonsTable,
     GroupGlobalPermissionTable,
     GroupObjectPermissionTable,
     GroupsTable,
@@ -358,7 +359,7 @@ def group(request: HttpRequest, id_: int) -> HttpResponse:
     members = group.members.all()
 
     # Build table
-    members_table = PersonsTable(members)
+    members_table = FullPersonsTable(members)
     RequestConfig(request).configure(members_table)
     context["members_table"] = members_table
 
@@ -366,7 +367,7 @@ def group(request: HttpRequest, id_: int) -> HttpResponse:
     owners = group.owners.all()
 
     # Build table
-    owners_table = PersonsTable(owners)
+    owners_table = FullPersonsTable(owners)
     RequestConfig(request).configure(owners_table)
     context["owners_table"] = owners_table