diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -1533,6 +1533,41 @@
   return threadInfo.community === communityID || threadInfo.id === communityID;
 }
 
+type RoleAndMemberCount = {
+  [roleName: string]: number,
+};
+
+function useRoleMemberCountsForCommunity(
+  threadInfo: ThreadInfo,
+): RoleAndMemberCount {
+  return React.useMemo(() => {
+    const roleIDsToNames = {};
+
+    Object.keys(threadInfo.roles).forEach(roleID => {
+      roleIDsToNames[roleID] = threadInfo.roles[roleID].name;
+    });
+
+    const roleNamesToMemberCount: RoleAndMemberCount = {};
+
+    threadInfo.members.forEach(({ role: roleID }) => {
+      invariant(roleID, 'Community member should have a role');
+      const roleName = roleIDsToNames[roleID];
+
+      roleNamesToMemberCount[roleName] =
+        (roleNamesToMemberCount[roleName] ?? 0) + 1;
+    });
+
+    // For all community roles with no members, add them to the list with 0
+    Object.keys(roleIDsToNames).forEach(roleName => {
+      if (roleNamesToMemberCount[roleIDsToNames[roleName]] === undefined) {
+        roleNamesToMemberCount[roleIDsToNames[roleName]] = 0;
+      }
+    });
+
+    return roleNamesToMemberCount;
+  }, [threadInfo]);
+}
+
 export {
   threadHasPermission,
   viewerIsMember,
@@ -1598,4 +1633,5 @@
   chatNameMaxLength,
   patchThreadInfoToIncludeMentionedMembersOfParent,
   threadInfoInsideCommunity,
+  useRoleMemberCountsForCommunity,
 };
diff --git a/native/roles/community-roles-screen.react.js b/native/roles/community-roles-screen.react.js
--- a/native/roles/community-roles-screen.react.js
+++ b/native/roles/community-roles-screen.react.js
@@ -1,11 +1,17 @@
 // @flow
 
 import * as React from 'react';
+import { View, Text } from 'react-native';
+import { ScrollView } from 'react-native-gesture-handler';
 
+import { useRoleMemberCountsForCommunity } from 'lib/shared/thread-utils.js';
 import type { ThreadInfo } from 'lib/types/thread-types.js';
 
+import RolePanelEntry from './role-panel-entry.react.js';
 import type { RolesNavigationProp } from './roles-navigator.react.js';
+import Button from '../components/button.react.js';
 import type { NavigationRoute } from '../navigation/route-names.js';
+import { useStyles } from '../themes/colors.js';
 
 export type CommunityRolesScreenParams = {
   +threadInfo: ThreadInfo,
@@ -16,9 +22,117 @@
   +route: NavigationRoute<'CommunityRolesScreen'>,
 };
 
-// eslint-disable-next-line no-unused-vars
 function CommunityRolesScreen(props: CommunityRolesScreenProps): React.Node {
-  return null;
+  const { threadInfo } = props.route.params;
+  const styles = useStyles(unboundStyles);
+
+  const roleNamesToMembers = useRoleMemberCountsForCommunity(threadInfo);
+
+  const rolePanelList = React.useMemo(() => {
+    const rolePanelEntries = [];
+
+    Object.keys(roleNamesToMembers).forEach(roleName => {
+      rolePanelEntries.push(
+        <RolePanelEntry
+          key={roleName}
+          roleName={roleName}
+          memberCount={roleNamesToMembers[roleName]}
+        />,
+      );
+    });
+
+    return rolePanelEntries;
+  }, [roleNamesToMembers]);
+
+  const navigateToCreateRole = React.useCallback(() => {}, []);
+
+  return (
+    <View>
+      <View style={styles.rolesInfoContainer}>
+        <Text style={styles.rolesInfoTextFirstLine}>
+          Roles help you group community members together and assign them
+          certain permissions. The Admins and Members roles are set by default
+          and cannot be edited or deleted.
+        </Text>
+        <Text style={styles.rolesInfoTextSecondLine}>
+          When people join the community, they are automatically assigned the
+          Members role.
+        </Text>
+      </View>
+      <View style={styles.rolesPanel}>
+        <View style={styles.rolePanelHeadersContainer}>
+          <Text style={styles.rolePanelHeaderLeft}>ROLES</Text>
+          <Text style={styles.rolePanelHeaderRight}>MEMBERS</Text>
+        </View>
+        <ScrollView style={styles.rolePanelList}>{rolePanelList}</ScrollView>
+        <View style={styles.buttonContainer}>
+          <Button
+            style={styles.createRoleButton}
+            onPress={navigateToCreateRole}
+          >
+            <Text style={styles.createRoleButtonText}>Create role</Text>
+          </Button>
+        </View>
+      </View>
+    </View>
+  );
 }
 
+const unboundStyles = {
+  rolesInfoContainer: {
+    backgroundColor: 'panelForeground',
+    padding: 16,
+  },
+  rolesInfoTextFirstLine: {
+    color: 'panelBackgroundLabel',
+    fontSize: 14,
+    marginBottom: 14,
+  },
+  rolesInfoTextSecondLine: {
+    color: 'panelBackgroundLabel',
+    fontSize: 14,
+  },
+  rolesPanel: {
+    marginTop: 30,
+  },
+  rolePanelHeadersContainer: {
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+    alignItems: 'center',
+    paddingHorizontal: 8,
+  },
+  rolePanelHeaderLeft: {
+    color: 'panelBackgroundLabel',
+    fontSize: 14,
+  },
+  rolePanelHeaderRight: {
+    color: 'panelBackgroundLabel',
+    fontSize: 14,
+    marginRight: 72,
+  },
+  rolePanelList: {
+    backgroundColor: 'panelForeground',
+    marginTop: 8,
+    padding: 4,
+    maxHeight: 325,
+  },
+  buttonContainer: {
+    backgroundColor: 'panelForeground',
+    padding: 2,
+  },
+  createRoleButton: {
+    justifyContent: 'center',
+    alignItems: 'center',
+    margin: 10,
+    backgroundColor: 'purpleButton',
+    height: 48,
+    borderRadius: 10,
+  },
+  createRoleButtonText: {
+    color: 'whiteText',
+    fontSize: 16,
+    fontWeight: '500',
+  },
+};
+
 export default CommunityRolesScreen;
diff --git a/native/roles/role-panel-entry.react.js b/native/roles/role-panel-entry.react.js
new file mode 100644
--- /dev/null
+++ b/native/roles/role-panel-entry.react.js
@@ -0,0 +1,50 @@
+// @flow
+
+import * as React from 'react';
+import { View, Text } from 'react-native';
+
+import CommIcon from '../components/comm-icon.react.js';
+import { useStyles } from '../themes/colors.js';
+
+type RolePanelEntryProps = {
+  +roleName: string,
+  +memberCount: number,
+};
+
+function RolePanelEntry(props: RolePanelEntryProps): React.Node {
+  const { roleName, memberCount } = props;
+  const styles = useStyles(unboundStyles);
+
+  return (
+    <View style={styles.rolePanelEntry}>
+      <Text style={styles.rolePanelNameEntry}>{roleName}</Text>
+      <Text style={styles.rolePanelCountEntry}>
+        {memberCount}
+        <CommIcon name="user-filled" size={14} />
+      </Text>
+    </View>
+  );
+}
+
+const unboundStyles = {
+  rolePanelEntry: {
+    flexDirection: 'row',
+    justifyContent: 'space-between',
+    alignItems: 'center',
+    padding: 8,
+  },
+  rolePanelNameEntry: {
+    color: 'panelForegroundLabel',
+    fontWeight: '600',
+    fontSize: 14,
+  },
+  rolePanelCountEntry: {
+    color: 'panelForegroundLabel',
+    fontWeight: '600',
+    fontSize: 14,
+    marginRight: 72,
+    padding: 8,
+  },
+};
+
+export default RolePanelEntry;