diff --git a/web/modals/modal.css b/web/modals/modal.css
--- a/web/modals/modal.css
+++ b/web/modals/modal.css
@@ -30,6 +30,10 @@
   padding: 32px 32px 0 32px;
 }
 
+div.modalHeaderCentered {
+  justify-content: center;
+}
+
 h2.title {
   font-size: 20px;
   font-weight: 500;
diff --git a/web/modals/modal.react.js b/web/modals/modal.react.js
--- a/web/modals/modal.react.js
+++ b/web/modals/modal.react.js
@@ -17,6 +17,7 @@
   +onClose: () => void,
   +withCloseButton?: boolean,
   +size?: ModalSize,
+  +modalHeaderCentered?: boolean,
 };
 
 type ModalProps = {
@@ -32,6 +33,7 @@
     name,
     icon,
     withCloseButton = true,
+    modalHeaderCentered = false,
   } = props;
 
   const modalContainerClasses = classNames(css.modalContainer, {
@@ -39,6 +41,11 @@
     [css.modalContainerSmall]: size === 'small',
   });
 
+  const modalHeader = classNames({
+    [css.modalHeader]: true,
+    [css.modalHeaderCentered]: modalHeaderCentered,
+  });
+
   const cornerCloseButton = React.useMemo(() => {
     if (!withCloseButton) {
       return null;
@@ -60,7 +67,7 @@
   return (
     <ModalOverlay onClose={onClose}>
       <div className={modalContainerClasses}>
-        <div className={css.modalHeader}>
+        <div className={modalHeader}>
           <h2 className={css.title}>
             {headerIcon}
             {name}
diff --git a/web/modals/terms-and-privacy-modal.css b/web/modals/terms-and-privacy-modal.css
new file mode 100644
--- /dev/null
+++ b/web/modals/terms-and-privacy-modal.css
@@ -0,0 +1,31 @@
+.container {
+  color: var(--modal-fg);
+  padding: 20px 32px;
+  text-align: center;
+  line-height: var(--line-height-text);
+}
+
+.button {
+  display: flex;
+  flex-direction: column;
+  width: 110px;
+  padding: 5px 0 16px;
+  margin: auto;
+  justify-content: space-around;
+}
+
+.link {
+  color: var(--purple-link);
+}
+
+.error {
+  margin-top: 16px;
+  font-size: var(--s-font-14);
+  color: var(--error);
+  font-style: italic;
+  text-align: center;
+}
+
+.buttonContent {
+  height: 20px;
+}
diff --git a/web/modals/terms-and-privacy-modal.react.js b/web/modals/terms-and-privacy-modal.react.js
new file mode 100644
--- /dev/null
+++ b/web/modals/terms-and-privacy-modal.react.js
@@ -0,0 +1,96 @@
+// @flow
+
+import * as React from 'react';
+
+import {
+  policyAcknowledgment,
+  policyAcknowledgmentActionTypes,
+} from 'lib/actions/user-actions.js';
+import { policyTypes } from 'lib/facts/policies.js';
+import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js';
+import {
+  useDispatchActionPromise,
+  useServerCall,
+} from 'lib/utils/action-utils.js';
+import { acknowledgePolicy } from 'lib/utils/policy-acknowledge-utlis.js';
+
+import Button, { buttonThemes } from '../components/button.react.js';
+import LoadingIndicator from '../loading-indicator.react.js';
+import { useSelector } from '../redux/redux-utils.js';
+import Modal from './modal.react';
+import css from './terms-and-privacy-modal.css';
+
+const loadingStatusSelector = createLoadingStatusSelector(
+  policyAcknowledgmentActionTypes,
+);
+
+const disabledOnClose = () => undefined;
+
+function TermsAndPrivacyModal(): React.Node {
+  const loading = useSelector(loadingStatusSelector);
+  const [acknowledgmentError, setAcknowledgmentError] = React.useState('');
+  const sendAcknowledgmentRequest = useServerCall(policyAcknowledgment);
+  const dispatchActionPromise = useDispatchActionPromise();
+
+  const onAccept = React.useCallback(() => {
+    acknowledgePolicy(
+      policyTypes.tosAndPrivacyPolicy,
+      dispatchActionPromise,
+      sendAcknowledgmentRequest,
+      setAcknowledgmentError,
+    );
+  }, [dispatchActionPromise, sendAcknowledgmentRequest]);
+
+  const buttonContent = React.useMemo(() => {
+    if (loading === 'loading') {
+      return <LoadingIndicator status="loading" size="medium" />;
+    }
+    return 'I accept';
+  }, [loading]);
+
+  return (
+    <Modal
+      withCloseButton={false}
+      modalHeaderCentered={true}
+      onClose={disabledOnClose}
+      name="Terms of Service and Privacy Policy"
+      size="large"
+    >
+      <div className={css.container}>
+        We recently updated our{' '}
+        <a
+          href="https://comm.app/terms"
+          target="_blank"
+          rel="noreferrer"
+          className={css.link}
+        >
+          Terms of Service
+        </a>
+        {' & '}
+        <a
+          href="https://comm.app/privacy"
+          target="_blank"
+          rel="noreferrer"
+          className={css.link}
+        >
+          Privacy Policy
+        </a>
+        . In order to continue using Comm, we&apos;re asking you to read
+        through, acknowledge, and accept the updated policies.
+      </div>
+
+      <div className={css.button}>
+        <Button
+          variant="filled"
+          buttonColor={buttonThemes.standard}
+          onClick={onAccept}
+        >
+          <div className={css.buttonContent}>{buttonContent}</div>
+        </Button>
+        <div className={css.error}>{acknowledgmentError}</div>
+      </div>
+    </Modal>
+  );
+}
+
+export default TermsAndPrivacyModal;
diff --git a/web/theme.css b/web/theme.css
--- a/web/theme.css
+++ b/web/theme.css
@@ -190,4 +190,5 @@
   --typeahead-overlay-shadow-secondary: rgba(0, 0, 0, 0.4);
   --spoiler-text-color: #33332c;
   --spoiler-background-color: #33332c;
+  --purple-link: var(--violet-light-100);
 }