diff --git a/web/chat/thread-menu.react.js b/web/chat/thread-menu.react.js
--- a/web/chat/thread-menu.react.js
+++ b/web/chat/thread-menu.react.js
@@ -28,6 +28,7 @@
 import SidebarPromoteModal from '../modals/chat/sidebar-promote-modal.react';
 import { useModalContext } from '../modals/modal-provider.react';
 import ConfirmLeaveThreadModal from '../modals/threads/confirm-leave-thread-modal.react';
+import ComposeSubchannelModal from '../modals/threads/create/compose-subchannel-modal.react';
 import ThreadMembersModal from '../modals/threads/members/members-modal.react';
 import ThreadNotificationsModal from '../modals/threads/notifications/notifications-modal.react';
 import ThreadSettingsModal from '../modals/threads/settings/thread-settings-modal.react';
@@ -144,6 +145,17 @@
     );
   }, [hasSubchannels, onClickViewSubchannels]);
 
+  const onClickCreateSubchannel = React.useCallback(
+    () =>
+      pushModal(
+        <ComposeSubchannelModal
+          parentThreadInfo={threadInfo}
+          onClose={popModal}
+        />,
+      ),
+    [popModal, pushModal, threadInfo],
+  );
+
   const createSubchannelsItem = React.useMemo(() => {
     if (!canCreateSubchannels) {
       return null;
@@ -153,9 +165,10 @@
         key="newSubchannel"
         text="Create new subchannel"
         icon="plus-circle"
+        onClick={onClickCreateSubchannel}
       />
     );
-  }, [canCreateSubchannels]);
+  }, [canCreateSubchannels, onClickCreateSubchannel]);
 
   const dispatchActionPromise = useDispatchActionPromise();
   const callLeaveThread = useServerCall(leaveThread);
@@ -245,16 +258,13 @@
   const menuItems = React.useMemo(() => {
     const separator = <hr key="separator" className={css.separator} />;
 
-    // TODO: Enable menu items when the modals are implemented
-    const SHOW_CREATE_SUBCHANNELS = false;
-
     const items = [
       settingsItem,
       notificationsItem,
       membersItem,
       sidebarItem,
       viewSubchannelsItem,
-      SHOW_CREATE_SUBCHANNELS && createSubchannelsItem,
+      createSubchannelsItem,
       leaveThreadItem && separator,
       canPromoteSidebar && promoteSidebar,
       leaveThreadItem,
diff --git a/web/modals/threads/create/compose-subchannel-modal.css b/web/modals/threads/create/compose-subchannel-modal.css
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/create/compose-subchannel-modal.css
@@ -0,0 +1,30 @@
+div.modalHeader {
+  padding: 15px;
+  font-weight: var(--semi-bold);
+  color: var(--compose-subchannel-header-fg);
+  background-color: var(--compose-subchannel-header-bg);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 30px;
+}
+
+div.modalHeaderParentName {
+  color: var(--compose-subchannel-mark-color);
+  display: inline;
+}
+
+div.container {
+  width: 383px;
+  overflow-y: auto;
+}
+
+div.stepContainer {
+  position: relative;
+  height: 100vh;
+  max-height: 533px;
+}
+
+div.stepItem {
+  padding: 20px;
+}
diff --git a/web/modals/threads/create/compose-subchannel-modal.react.js b/web/modals/threads/create/compose-subchannel-modal.react.js
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/create/compose-subchannel-modal.react.js
@@ -0,0 +1,190 @@
+// @flow
+import * as React from 'react';
+
+import type { ThreadInfo } from 'lib/types/thread-types';
+import { trimText } from 'lib/utils/text-utils';
+
+import Stepper from '../../../components/stepper.react';
+import Modal from '../../modal.react';
+import css from './compose-subchannel-modal.css';
+import SubchannelMembers from './steps/subchannel-members.react';
+import SubchannelSettings from './steps/subchannel-settings.react';
+import type { VisibilityType } from './steps/subchannel-settings.react';
+
+type Props = {
+  +onClose: () => void,
+  +parentThreadInfo: ThreadInfo,
+};
+
+type Steps = 'settings' | 'members';
+
+type HeaderProps = {
+  +parentThreadName: string,
+};
+
+function ComposeSubchannelHeader(props: HeaderProps): React.Node {
+  const { parentThreadName } = props;
+  return (
+    <div className={css.modalHeader}>
+      <div>
+        {'within '}
+        <div className={css.modalHeaderParentName}>{parentThreadName}</div>
+      </div>
+    </div>
+  );
+}
+
+function ComposeSubchannelModal(props: Props): React.Node {
+  const { parentThreadInfo, onClose } = props;
+  const { uiName: parentThreadName } = parentThreadInfo;
+
+  const [activeStep, setActiveStep] = React.useState<Steps>('settings');
+
+  const [channelName, setChannelName] = React.useState<string>('');
+  const [visibilityType, setVisibilityType] = React.useState<VisibilityType>(
+    'open',
+  );
+  const [announcement, setAnnouncement] = React.useState<boolean>(false);
+  const [selectedUsers, setSelectedUsers] = React.useState<
+    $ReadOnlySet<string>,
+  >(new Set());
+  const [searchUserText, setSearchUserText] = React.useState<string>('');
+
+  const onChangeChannelName = React.useCallback(
+    (event: SyntheticEvent<HTMLInputElement>) => {
+      const target = event.currentTarget;
+      setChannelName(target.value);
+    },
+    [],
+  );
+
+  const onOpenVisibilityTypeSelected = React.useCallback(
+    () => setVisibilityType('open'),
+    [],
+  );
+
+  const onSecretVisibilityTypeSelected = React.useCallback(
+    () => setVisibilityType('secret'),
+    [],
+  );
+
+  const onAnnouncementSelected = React.useCallback(
+    () => setAnnouncement(!announcement),
+    [announcement],
+  );
+
+  const toggleUserSelection = React.useCallback((userID: string) => {
+    setSelectedUsers((users: $ReadOnlySet<string>) => {
+      const newUsers = new Set(users);
+      if (newUsers.has(userID)) {
+        newUsers.delete(userID);
+      } else {
+        newUsers.add(userID);
+      }
+      return newUsers;
+    });
+  }, []);
+
+  const subchannelSettings = React.useMemo(
+    () => (
+      <SubchannelSettings
+        channelName={channelName}
+        visibilityType={visibilityType}
+        announcement={announcement}
+        onChangeChannelName={onChangeChannelName}
+        onOpenTypeSelect={onOpenVisibilityTypeSelected}
+        onSecretTypeSelect={onSecretVisibilityTypeSelected}
+        onAnnouncementSelected={onAnnouncementSelected}
+      />
+    ),
+    [
+      channelName,
+      visibilityType,
+      announcement,
+      onChangeChannelName,
+      onOpenVisibilityTypeSelected,
+      onSecretVisibilityTypeSelected,
+      onAnnouncementSelected,
+    ],
+  );
+
+  const stepperButtons = React.useMemo(
+    () => ({
+      settings: {
+        nextProps: {
+          content: 'Next',
+          disabled: !channelName.trim(),
+          onClick: () => {
+            setChannelName(channelName.trim());
+            setActiveStep('members');
+          },
+        },
+      },
+      members: {
+        prevProps: {
+          content: 'Back',
+          onClick: () => setActiveStep('settings'),
+        },
+        nextProps: {
+          content: 'Create',
+          onClick: () => {
+            // TODO: make form logic
+          },
+        },
+      },
+    }),
+    [channelName],
+  );
+
+  const subchannelMembers = React.useMemo(
+    () => (
+      <SubchannelMembers
+        parentThreadInfo={parentThreadInfo}
+        selectedUsers={selectedUsers}
+        searchText={searchUserText}
+        setSearchText={setSearchUserText}
+        toggleUserSelection={toggleUserSelection}
+      />
+    ),
+    [
+      selectedUsers,
+      toggleUserSelection,
+      parentThreadInfo,
+      searchUserText,
+      setSearchUserText,
+    ],
+  );
+
+  const modalName =
+    activeStep === 'members'
+      ? `Create channel - ${trimText(channelName, 11)}`
+      : 'Create channel';
+
+  return (
+    <Modal name={modalName} onClose={onClose} size="fit-content">
+      <ComposeSubchannelHeader parentThreadName={parentThreadName} />
+      <div className={css.container}>
+        <div className={css.stepItem}>
+          <Stepper.Container
+            className={css.stepContainer}
+            activeStep={activeStep}
+          >
+            <Stepper.Item
+              content={subchannelSettings}
+              name="settings"
+              nextProps={stepperButtons.settings.nextProps}
+            />
+            <Stepper.Item
+              content={subchannelMembers}
+              name="members"
+              prevProps={stepperButtons.members.prevProps}
+              nextProps={stepperButtons.members.nextProps}
+            />
+          </Stepper.Container>
+        </div>
+      </div>
+    </Modal>
+  );
+}
+
+export default ComposeSubchannelModal;
diff --git a/web/modals/threads/create/steps/subchannel-members-list.react.js b/web/modals/threads/create/steps/subchannel-members-list.react.js
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/create/steps/subchannel-members-list.react.js
@@ -0,0 +1,97 @@
+// @flow
+
+import * as React from 'react';
+import { useSelector } from 'react-redux';
+
+import { stringForUser } from 'lib/shared/user-utils';
+import type { ThreadInfo } from 'lib/types/thread-types';
+
+import AddMembersList from '../../../components/add-members-list.react';
+
+type Props = {
+  +searchText: string,
+  +searchResult: $ReadOnlySet<string>,
+  +communityThreadInfo: ThreadInfo,
+  +parentThreadInfo: ThreadInfo,
+  +selectedUsers: $ReadOnlySet<string>,
+  +toggleUserSelection: (userID: string) => void,
+};
+
+function Memberlist(props: Props): React.Node {
+  const {
+    searchText,
+    searchResult,
+    communityThreadInfo,
+    parentThreadInfo,
+    selectedUsers,
+    toggleUserSelection,
+  } = props;
+
+  const { members: parentMembers } = parentThreadInfo;
+
+  const {
+    members: communityMembers,
+    name: communityName,
+  } = communityThreadInfo;
+
+  const currentUserId = useSelector(state => state.currentUserInfo.id);
+
+  const parentMembersSet = React.useMemo(
+    () => new Set(parentThreadInfo.members.map(user => user.id)),
+    [parentThreadInfo],
+  );
+
+  const parentMemberList = React.useMemo(
+    () =>
+      parentMembers
+        .filter(
+          user =>
+            user.id !== currentUserId &&
+            (searchResult.has(user.id) || searchText.length === 0),
+        )
+        .map(user => ({ id: user.id, username: stringForUser(user) })),
+
+    [parentMembers, currentUserId, searchResult, searchText],
+  );
+
+  const otherMemberList = React.useMemo(
+    () =>
+      communityMembers
+        .filter(
+          user =>
+            !parentMembersSet.has(user.id) &&
+            user.id !== currentUserId &&
+            (searchResult.has(user.id) || searchText.length === 0),
+        )
+        .map(user => ({ id: user.id, username: stringForUser(user) })),
+    [
+      communityMembers,
+      parentMembersSet,
+      currentUserId,
+      searchResult,
+      searchText,
+    ],
+  );
+
+  const sortedGroupedUserList = React.useMemo(
+    () =>
+      [
+        { header: 'Users in parent channel', userInfos: parentMemberList },
+        {
+          header: `All users in ${communityName ?? 'community'}`,
+          userInfos: otherMemberList,
+        },
+      ].filter(item => item.userInfos.length),
+    [parentMemberList, otherMemberList, communityName],
+  );
+
+  return (
+    <AddMembersList
+      switchUser={toggleUserSelection}
+      pendingUsersToAdd={selectedUsers}
+      sortedGroupedUsersList={sortedGroupedUserList}
+    />
+  );
+}
+
+export default Memberlist;
diff --git a/web/modals/threads/create/steps/subchannel-members.css b/web/modals/threads/create/steps/subchannel-members.css
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/create/steps/subchannel-members.css
@@ -0,0 +1,10 @@
+.members {
+  overflow-y: auto;
+}
+
+.searchBar {
+  background-color: var(--modal-bg);
+  position: sticky;
+  padding: 2.5px 0;
+  top: 0;
+}
diff --git a/web/modals/threads/create/steps/subchannel-members.react.js b/web/modals/threads/create/steps/subchannel-members.react.js
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/create/steps/subchannel-members.react.js
@@ -0,0 +1,64 @@
+// @flow
+
+import * as React from 'react';
+import { useSelector } from 'react-redux';
+
+import { userStoreSearchIndex } from 'lib/selectors/user-selectors';
+import { useAncestorThreads } from 'lib/shared/ancestor-threads';
+import type { ThreadInfo } from 'lib/types/thread-types';
+
+import Search from '../../../../components/search.react';
+import MembersList from './subchannel-members-list.react';
+import css from './subchannel-members.css';
+
+type SubchannelMembersProps = {
+  +parentThreadInfo: ThreadInfo,
+  +selectedUsers: $ReadOnlySet<string>,
+  +searchText: string,
+  +setSearchText: string => void,
+  +toggleUserSelection: (userID: string) => void,
+};
+
+function SubchannelMembers(props: SubchannelMembersProps): React.Node {
+  const {
+    toggleUserSelection,
+    searchText,
+    setSearchText,
+    parentThreadInfo,
+    selectedUsers,
+  } = props;
+
+  const ancestorThreads = useAncestorThreads(parentThreadInfo);
+
+  const communityThread = ancestorThreads[0] ?? parentThreadInfo;
+
+  const userSearchIndex = useSelector(userStoreSearchIndex);
+  const searchResult = React.useMemo(
+    () => new Set(userSearchIndex.getSearchResults(searchText)),
+    [userSearchIndex, searchText],
+  );
+
+  return (
+    <>
+      <div className={css.searchBar}>
+        <Search
+          searchText={searchText}
+          onChangeText={setSearchText}
+          placeholder="Search"
+        />
+      </div>
+      <div className={css.members}>
+        <MembersList
+          communityThreadInfo={communityThread}
+          parentThreadInfo={parentThreadInfo}
+          selectedUsers={selectedUsers}
+          searchResult={searchResult}
+          searchText={searchText}
+          toggleUserSelection={toggleUserSelection}
+        />
+      </div>
+    </>
+  );
+}
+
+export default SubchannelMembers;
diff --git a/web/modals/threads/create/steps/subchannel-settings.css b/web/modals/threads/create/steps/subchannel-settings.css
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/create/steps/subchannel-settings.css
@@ -0,0 +1,8 @@
+.wrapper {
+  color: var(--enum-option-icon-color);
+}
+
+.label {
+  padding: 20px 0;
+  color: var(--compose-subchannel-label-color);
+}
diff --git a/web/modals/threads/create/steps/subchannel-settings.react.js b/web/modals/threads/create/steps/subchannel-settings.react.js
new file mode 100644
--- /dev/null
+++ b/web/modals/threads/create/steps/subchannel-settings.react.js
@@ -0,0 +1,123 @@
+// @flow
+
+import * as React from 'react';
+
+import { threadTypeDescriptions } from 'lib/shared/thread-utils';
+import { threadTypes } from 'lib/types/thread-types';
+
+import CommIcon from '../../../../CommIcon.react';
+import EnumSettingsOption from '../../../../components/enum-settings-option.react';
+import SWMansionIcon from '../../../../SWMansionIcon.react';
+import Input from '../../../input.react';
+import css from './subchannel-settings.css';
+
+const { COMMUNITY_OPEN_SUBTHREAD, COMMUNITY_SECRET_SUBTHREAD } = threadTypes;
+
+const openStatements = [
+  {
+    statement: threadTypeDescriptions[COMMUNITY_OPEN_SUBTHREAD],
+    isStatementValid: true,
+    styleStatementBasedOnValidity: false,
+  },
+];
+
+const secretStatements = [
+  {
+    statement: threadTypeDescriptions[COMMUNITY_SECRET_SUBTHREAD],
+    isStatementValid: true,
+    styleStatementBasedOnValidity: false,
+  },
+];
+
+const announcementStatements = [
+  {
+    statement: 'Admins can create Announcement channels.',
+    isStatementValid: true,
+    styleStatementBasedOnValidity: false,
+  },
+];
+
+export type VisibilityType = 'open' | 'secret';
+
+type Props = {
+  +channelName: string,
+  +onChangeChannelName: (SyntheticEvent<HTMLInputElement>) => void,
+  +visibilityType: VisibilityType,
+  +onOpenTypeSelect: () => void,
+  +onSecretTypeSelect: () => void,
+  +announcement: boolean,
+  +onAnnouncementSelected: () => void,
+};
+
+function SubchannelSettings(props: Props): React.Node {
+  const {
+    channelName,
+    onChangeChannelName,
+    visibilityType,
+    onOpenTypeSelect,
+    onSecretTypeSelect,
+    announcement,
+    onAnnouncementSelected,
+  } = props;
+
+  const globeIcon = React.useMemo(
+    () => <SWMansionIcon icon="globe-1" size={24} />,
+    [],
+  );
+
+  const lockIcon = React.useMemo(
+    () => <SWMansionIcon icon="lock-on" size={24} />,
+    [],
+  );
+
+  const flagIcon = React.useMemo(
+    () => <CommIcon icon="megaphone" size={24} />,
+    [],
+  );
+
+  return (
+    <>
+      <Input
+        type="text"
+        onChange={onChangeChannelName}
+        placeholder="Channel name"
+        value={channelName}
+      />
+
+      <div className={css.wrapper}>
+        <div className={css.label}>Visibility</div>
+        <EnumSettingsOption
+          title="Open"
+          statements={openStatements}
+          onSelect={onOpenTypeSelect}
+          selected={visibilityType === 'open'}
+          icon={globeIcon}
+          iconPosition="top"
+        />
+        <EnumSettingsOption
+          title="Secret"
+          statements={secretStatements}
+          onSelect={onSecretTypeSelect}
+          selected={visibilityType === 'secret'}
+          icon={lockIcon}
+          iconPosition="top"
+        />
+      </div>
+
+      <div className={css.wrapper}>
+        <div className={css.label}>Optional settings</div>
+        <EnumSettingsOption
+          title="Announcement"
+          statements={announcementStatements}
+          onSelect={onAnnouncementSelected}
+          selected={announcement}
+          icon={flagIcon}
+          iconPosition="top"
+          type="checkbox"
+        />
+      </div>
+    </>
+  );
+}
+
+export default SubchannelSettings;
diff --git a/web/theme.css b/web/theme.css
--- a/web/theme.css
+++ b/web/theme.css
@@ -182,4 +182,9 @@
   --inline-sidebar-bg: var(--shades-black-70);
   --inline-sidebar-bg-hover: var(--shades-black-80);
   --inline-sidebar-color: var(--fg);
+  --compose-subchannel-header-fg: var(--shades-black-60);
+  --compose-subchannel-header-bg: var(--shades-black-80);
+  --compose-subchannel-label-color: var(--shades-black-60);
+  --compose-subchannel-mark-color: var(--violet-light-100);
+  --enum-option-icon-color: var(--violet-dark-100);
 }