diff --git a/app/javascript/mastodon/features/compose/components/compose_form.jsx b/app/javascript/mastodon/features/compose/components/compose_form.jsx
index 468125afb..2cd88aab4 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.jsx
+++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx
@@ -10,6 +10,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 
 import { length } from 'stringz';
 
+import { missingAltTextModal } from 'mastodon/initial_state';
+
 import AutosuggestInput from '../../../components/autosuggest_input';
 import AutosuggestTextarea from '../../../components/autosuggest_textarea';
 import { Button } from '../../../components/button';
@@ -65,6 +67,7 @@ class ComposeForm extends ImmutablePureComponent {
     autoFocus: PropTypes.bool,
     withoutNavigation: PropTypes.bool,
     anyMedia: PropTypes.bool,
+    missingAltText: PropTypes.bool,
     isInReply: PropTypes.bool,
     singleColumn: PropTypes.bool,
     lang: PropTypes.string,
@@ -117,7 +120,7 @@ class ComposeForm extends ImmutablePureComponent {
       return;
     }
 
-    this.props.onSubmit();
+    this.props.onSubmit(missingAltTextModal && this.props.missingAltText);
 
     if (e) {
       e.preventDefault();
diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
index bda2edba6..15ccabf74 100644
--- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js
+++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
@@ -9,7 +9,9 @@ import {
   changeComposeSpoilerText,
   insertEmojiCompose,
   uploadCompose,
-} from '../../../actions/compose';
+} from 'mastodon/actions/compose';
+import { openModal } from 'mastodon/actions/modal';
+
 import ComposeForm from '../components/compose_form';
 
 const mapStateToProps = state => ({
@@ -26,6 +28,7 @@ const mapStateToProps = state => ({
   isChangingUpload: state.getIn(['compose', 'is_changing_upload']),
   isUploading: state.getIn(['compose', 'is_uploading']),
   anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
+  missingAltText: state.getIn(['compose', 'media_attachments']).some(media => ['image', 'gifv'].includes(media.get('type')) && (media.get('description') ?? '').length === 0),
   isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
   lang: state.getIn(['compose', 'language']),
   maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
@@ -37,8 +40,15 @@ const mapDispatchToProps = (dispatch) => ({
     dispatch(changeCompose(text));
   },
 
-  onSubmit () {
-    dispatch(submitCompose());
+  onSubmit (missingAltText) {
+    if (missingAltText) {
+      dispatch(openModal({
+        modalType: 'CONFIRM_MISSING_ALT_TEXT',
+        modalProps: {},
+      }));
+    } else {
+      dispatch(submitCompose());
+    }
   },
 
   onClearSuggestions () {
diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx b/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx
index ab567c697..929b1a240 100644
--- a/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx
+++ b/app/javascript/mastodon/features/ui/components/confirmation_modals/confirmation_modal.tsx
@@ -56,14 +56,6 @@ export const ConfirmationModal: React.FC<
 
       
         
-          {secondary && (
-            <>
-              
-
-              
-            >
-          )}
-
           
 
+          {secondary && (
+            <>
+              
+              
+            >
+          )}
+
           {/* eslint-disable-next-line jsx-a11y/no-autofocus -- we are in a modal and thus autofocusing is justified */}