diff --git a/app/javascript/mastodon/components/dismissable_banner.jsx b/app/javascript/mastodon/components/dismissable_banner.jsx
deleted file mode 100644
index 5aecc88b16..0000000000
--- a/app/javascript/mastodon/components/dismissable_banner.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-import PropTypes from 'prop-types';
-import { PureComponent } from 'react';
-
-import { injectIntl, defineMessages } from 'react-intl';
-
-import { bannerSettings } from 'mastodon/settings';
-
-import { IconButton } from './icon_button';
-
-const messages = defineMessages({
-  dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
-});
-
-class DismissableBanner extends PureComponent {
-
-  static propTypes = {
-    id: PropTypes.string.isRequired,
-    children: PropTypes.node,
-    intl: PropTypes.object.isRequired,
-  };
-
-  state = {
-    visible: !bannerSettings.get(this.props.id),
-  };
-
-  handleDismiss = () => {
-    const { id } = this.props;
-    this.setState({ visible: false }, () => bannerSettings.set(id, true));
-  };
-
-  render () {
-    const { visible } = this.state;
-
-    if (!visible) {
-      return null;
-    }
-
-    const { children, intl } = this.props;
-
-    return (
-      <div className='dismissable-banner'>
-        <div className='dismissable-banner__message'>
-          {children}
-        </div>
-
-        <div className='dismissable-banner__action'>
-          <IconButton icon='times' title={intl.formatMessage(messages.dismiss)} onClick={this.handleDismiss} />
-        </div>
-      </div>
-    );
-  }
-
-}
-
-export default injectIntl(DismissableBanner);
diff --git a/app/javascript/mastodon/components/dismissable_banner.tsx b/app/javascript/mastodon/components/dismissable_banner.tsx
new file mode 100644
index 0000000000..d5cdb07503
--- /dev/null
+++ b/app/javascript/mastodon/components/dismissable_banner.tsx
@@ -0,0 +1,47 @@
+import type { PropsWithChildren } from 'react';
+import { useCallback, useState } from 'react';
+
+import { defineMessages, useIntl } from 'react-intl';
+
+import { bannerSettings } from 'mastodon/settings';
+
+import { IconButton } from './icon_button';
+
+const messages = defineMessages({
+  dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
+});
+
+interface Props {
+  id: string;
+}
+
+export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
+  id,
+  children,
+}) => {
+  const [visible, setVisible] = useState(!bannerSettings.get(id));
+  const intl = useIntl();
+
+  const handleDismiss = useCallback(() => {
+    setVisible(false);
+    bannerSettings.set(id, true);
+  }, [id]);
+
+  if (!visible) {
+    return null;
+  }
+
+  return (
+    <div className='dismissable-banner'>
+      <div className='dismissable-banner__message'>{children}</div>
+
+      <div className='dismissable-banner__action'>
+        <IconButton
+          icon='times'
+          title={intl.formatMessage(messages.dismiss)}
+          onClick={handleDismiss}
+        />
+      </div>
+    </div>
+  );
+};
diff --git a/app/javascript/mastodon/features/community_timeline/index.jsx b/app/javascript/mastodon/features/community_timeline/index.jsx
index 7e3b9babe9..2d94cabed2 100644
--- a/app/javascript/mastodon/features/community_timeline/index.jsx
+++ b/app/javascript/mastodon/features/community_timeline/index.jsx
@@ -7,7 +7,7 @@ import { Helmet } from 'react-helmet';
 
 import { connect } from 'react-redux';
 
-import DismissableBanner from 'mastodon/components/dismissable_banner';
+import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 import { domain } from 'mastodon/initial_state';
 
 import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
diff --git a/app/javascript/mastodon/features/explore/links.jsx b/app/javascript/mastodon/features/explore/links.jsx
index 49c667f027..8b199bf47c 100644
--- a/app/javascript/mastodon/features/explore/links.jsx
+++ b/app/javascript/mastodon/features/explore/links.jsx
@@ -7,7 +7,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
 
 import { fetchTrendingLinks } from 'mastodon/actions/trends';
-import DismissableBanner from 'mastodon/components/dismissable_banner';
+import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 import { LoadingIndicator } from 'mastodon/components/loading_indicator';
 
 import Story from './components/story';
diff --git a/app/javascript/mastodon/features/explore/statuses.jsx b/app/javascript/mastodon/features/explore/statuses.jsx
index eb2fe777a6..3271929db5 100644
--- a/app/javascript/mastodon/features/explore/statuses.jsx
+++ b/app/javascript/mastodon/features/explore/statuses.jsx
@@ -9,7 +9,7 @@ import { connect } from 'react-redux';
 import { debounce } from 'lodash';
 
 import { fetchTrendingStatuses, expandTrendingStatuses } from 'mastodon/actions/trends';
-import DismissableBanner from 'mastodon/components/dismissable_banner';
+import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 import StatusList from 'mastodon/components/status_list';
 import { getStatusList } from 'mastodon/selectors';
 
diff --git a/app/javascript/mastodon/features/explore/tags.jsx b/app/javascript/mastodon/features/explore/tags.jsx
index f558b48a60..1a4d259690 100644
--- a/app/javascript/mastodon/features/explore/tags.jsx
+++ b/app/javascript/mastodon/features/explore/tags.jsx
@@ -7,7 +7,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
 
 import { fetchTrendingHashtags } from 'mastodon/actions/trends';
-import DismissableBanner from 'mastodon/components/dismissable_banner';
+import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
 import { LoadingIndicator } from 'mastodon/components/loading_indicator';
 
diff --git a/app/javascript/mastodon/features/firehose/index.jsx b/app/javascript/mastodon/features/firehose/index.jsx
index 9ba4fd5b2b..e5b47d3fe0 100644
--- a/app/javascript/mastodon/features/firehose/index.jsx
+++ b/app/javascript/mastodon/features/firehose/index.jsx
@@ -10,7 +10,7 @@ import { addColumn } from 'mastodon/actions/columns';
 import { changeSetting } from 'mastodon/actions/settings';
 import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
 import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
-import DismissableBanner from 'mastodon/components/dismissable_banner';
+import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 import initialState, { domain } from 'mastodon/initial_state';
 import { useAppDispatch, useAppSelector } from 'mastodon/store';
 
diff --git a/app/javascript/mastodon/features/home_timeline/components/explore_prompt.jsx b/app/javascript/mastodon/features/home_timeline/components/explore_prompt.jsx
index a6993c6418..2af85b6d54 100644
--- a/app/javascript/mastodon/features/home_timeline/components/explore_prompt.jsx
+++ b/app/javascript/mastodon/features/home_timeline/components/explore_prompt.jsx
@@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
 import { Link } from 'react-router-dom';
 
 import background from 'mastodon/../images/friends-cropped.png';
-import DismissableBanner from 'mastodon/components/dismissable_banner';
+import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 
 
 export const ExplorePrompt = () => (
diff --git a/app/javascript/mastodon/features/public_timeline/index.jsx b/app/javascript/mastodon/features/public_timeline/index.jsx
index 352baa8336..3bfb25ba73 100644
--- a/app/javascript/mastodon/features/public_timeline/index.jsx
+++ b/app/javascript/mastodon/features/public_timeline/index.jsx
@@ -7,7 +7,7 @@ import { Helmet } from 'react-helmet';
 
 import { connect } from 'react-redux';
 
-import DismissableBanner from 'mastodon/components/dismissable_banner';
+import { DismissableBanner } from 'mastodon/components/dismissable_banner';
 import { domain } from 'mastodon/initial_state';
 
 import { addColumn, removeColumn, moveColumn } from '../../actions/columns';