diff --git a/src/views/components/common/WhatsNewPopup.tsx b/src/views/components/common/WhatsNewPopup.tsx
new file mode 100644
index 00000000..db6ee9d0
--- /dev/null
+++ b/src/views/components/common/WhatsNewPopup.tsx
@@ -0,0 +1,148 @@
+import type { IconProps } from '@phosphor-icons/react';
+import { CloudX, Copy, Exam, MapPinArea, Palette } from '@phosphor-icons/react';
+import { ExtensionStore } from '@shared/storage/ExtensionStore';
+import Text from '@views/components/common/Text/Text';
+import useWhatsNewPopUp from '@views/hooks/useWhatsNew';
+import React, { useEffect, useState } from 'react';
+
+/**
+ * This is the version of the 'What's New' features popup.
+ *
+ * It is used to check if the popup has already been shown to the user or not
+ *
+ * It should be incremented every time the "What's New" popup is updated.
+ */
+const WHATSNEW_POPUP_VERSION = 1;
+
+const WHATSNEW_VIDEO_URL = 'https://cdn.longhorns.dev/whats-new-v2.1.2.mp4';
+
+type Feature = {
+ id: string;
+ icon: React.ForwardRefExoticComponent;
+ title: string | JSX.Element;
+ description: string;
+};
+
+const NEW_FEATURES = [
+ {
+ id: 'custom-course-colors',
+ icon: Palette,
+ title: 'Custom Course Colors',
+ description: 'Paint your schedule in your favorite color theme',
+ },
+ {
+ id: 'quick-copy',
+ icon: Copy,
+ title: 'Quick Copy',
+ description: 'Quickly copy a course unique number to your clipboard',
+ },
+ {
+ id: 'updated-grades',
+ icon: Exam,
+ title: 'Updated Grades',
+ description: 'Fall 2024 grades are now available in the grade distribution',
+ },
+ {
+ id: 'ut-map',
+ icon: MapPinArea,
+ title: (
+
+ UTRP Map
+
+ BETA
+
+
+ ),
+ description: 'Find directions to your classes with our beta map feature in the settings page',
+ },
+] as const satisfies readonly Feature[];
+
+/**
+ * WhatsNewPopupContent component.
+ *
+ * This component displays the content of the WhatsNew dialog.
+ * It shows the new features that have been added to the extension.
+ *
+ * @returns A JSX of WhatsNewPopupContent component.
+ */
+export default function WhatsNewPopupContent(): JSX.Element {
+ const [videoError, setVideoError] = useState(false);
+
+ return (
+
+
+
+ Failed to load video. Please try again later.
+
+
+
+ ) : (
+
+ )}
+
+
+
+ );
+}
+
+/**
+ * WhatsNewDialog component.
+ *
+ * This component is responsible for checking if the extension has already been updated
+ * and if so, it displays the WhatsNew dialog. Then it updates the state to show that the
+ * dialog has been shown.
+ *
+ * @returns An empty fragment.
+ *
+ * @remarks
+ * The component uses the `useWhatsNew` hook to show the WhatsNew dialog and the
+ * `useEffect` hook to perform the check on component mount. It also uses the `ExtensionStore`
+ * to view the state of the dialog.
+ */
+export function WhatsNewDialog(): null {
+ const showPopUp = useWhatsNewPopUp();
+
+ useEffect(() => {
+ const checkUpdate = async () => {
+ const version = await ExtensionStore.get('lastWhatsNewPopupVersion');
+ if (version !== WHATSNEW_POPUP_VERSION) {
+ await ExtensionStore.set('lastWhatsNewPopupVersion', WHATSNEW_POPUP_VERSION);
+ showPopUp();
+ }
+ };
+
+ checkUpdate();
+
+ // This is on purpose
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ return null;
+}
diff --git a/src/views/hooks/useWhatsNew.tsx b/src/views/hooks/useWhatsNew.tsx
new file mode 100644
index 00000000..d803cc87
--- /dev/null
+++ b/src/views/hooks/useWhatsNew.tsx
@@ -0,0 +1,46 @@
+import { Button } from '@views/components/common/Button';
+import Text from '@views/components/common/Text/Text';
+import WhatsNewPopupContent from '@views/components/common/WhatsNewPopup';
+import { useDialog } from '@views/contexts/DialogContext';
+import React from 'react';
+
+import { LogoIcon } from '../components/common/LogoIcon';
+import useChangelog from './useChangelog';
+
+/**
+ * Custom hook that provides a function to display a what's new dialog.
+ *
+ * @returns A function that, when called, shows a dialog with the changelog.
+ */
+export default function useWhatsNewPopUp(): () => void {
+ const showDialog = useDialog();
+ const showChangeLog = useChangelog();
+ const { version } = chrome.runtime.getManifest();
+
+ const showPopUp = () => {
+ showDialog(close => ({
+ className: 'w-[830px] flex flex-col items-center gap-spacing-7 p-spacing-8',
+ title: (
+