diff --git a/frontend/app/components/Domain/Announcement/AnnouncementDialog.vue b/frontend/app/components/Domain/Announcement/AnnouncementDialog.vue
new file mode 100644
index 000000000..3f38c91c2
--- /dev/null
+++ b/frontend/app/components/Domain/Announcement/AnnouncementDialog.vue
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+
+ {{ announcement.meta?.title }}
+
+
+ {{ $d(announcement.date) }}
+
+
+
+
+ {{ $globals.icons.alertCircle }}
+
+
+
+
+
+
+
+
+ {{ $t("announcements.all-announcements") }}
+
+
+
+
+ {{ $globals.icons.calendar }}
+
+ {{ $d(currentAnnouncement.date) }}
+
+ {{ currentAnnouncement.meta?.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/app/components/Domain/Announcement/Announcements/2026-03-27_1_welcome.vue b/frontend/app/components/Domain/Announcement/Announcements/2026-03-27_1_welcome.vue
new file mode 100644
index 000000000..3134e1856
--- /dev/null
+++ b/frontend/app/components/Domain/Announcement/Announcements/2026-03-27_1_welcome.vue
@@ -0,0 +1,62 @@
+
+
+
+ Welcome to Mealie! If this is your first time seeing announcements, here's what to expect.
+
+
+ Announcements are reserved for things like:
+
+ - Important new features
+ - Major changes
+ - Anything that might require additional user actions (such as migration scripts)
+
+
+
+ While we generally keep everything in our GitHub release notes,
+ sometimes certain changes require some extra attention.
+
+
+ Announcements are English-only; they're one-off messages from the maintainers, not a replacement for our release notes. Some elements may still be translated.
+
+
+
+ You can opt out of announcements in your user settings:
+
+
+ {{ $t("profile.user-settings") }}
+
+
+
+ As {{ user?.admin ? "an admin" : "a household manager" }}, you can disable announcements for your entire household:
+
+
+ {{ $t("profile.household-settings") }}
+
+
+
+ {{ user?.admin ? "You can also" : "As a group manager, you can" }} disable announcements for your entire group:
+
+
+ {{ $t("profile.group-settings") }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/app/components/Domain/Announcement/Announcements/announcements.test.ts b/frontend/app/components/Domain/Announcement/Announcements/announcements.test.ts
new file mode 100644
index 000000000..e1aa007dd
--- /dev/null
+++ b/frontend/app/components/Domain/Announcement/Announcements/announcements.test.ts
@@ -0,0 +1,37 @@
+import { describe, test, expect } from "vitest";
+
+const announcementFiles = import.meta.glob<{ default: unknown }>(
+ "~/components/Domain/Announcement/Announcements/*.vue",
+);
+
+// Expected format: YYYY-MM-DD_N_slug e.g. 2026-03-27_1_welcome
+const FILE_FORMAT = /^\d{4}-\d{2}-\d{2}_\d+_.+$/;
+
+describe("Announcement files", () => {
+ const filenames = Object.keys(announcementFiles).map(path =>
+ path.split("/").at(-1)!.replace(".vue", ""),
+ );
+
+ test("directory is not empty", () => {
+ expect(filenames.length).toBeGreaterThan(0);
+ });
+
+ test("all filenames match YYYY-MM-DD_N_slug format", () => {
+ for (const name of filenames) {
+ expect(name, `"${name}" does not match the expected format`).toMatch(FILE_FORMAT);
+ }
+ });
+
+ test("all date prefixes are valid dates", () => {
+ for (const name of filenames) {
+ const datePart = name.split("_", 1)[0]!;
+ const date = new Date(datePart);
+ expect(isNaN(date.getTime()), `"${name}" has an invalid date prefix "${datePart}"`).toBe(false);
+ }
+ });
+
+ test("all filenames are unique", () => {
+ const unique = new Set(filenames);
+ expect(unique.size).toBe(filenames.length);
+ });
+});
diff --git a/frontend/app/components/Domain/Group/GroupPreferencesEditor.vue b/frontend/app/components/Domain/Group/GroupPreferencesEditor.vue
index 1bfd0c935..e00c163a3 100644
--- a/frontend/app/components/Domain/Group/GroupPreferencesEditor.vue
+++ b/frontend/app/components/Domain/Group/GroupPreferencesEditor.vue
@@ -1,11 +1,38 @@
-
-
+
+
+
+
+
+ {{ $t("group.private-group-description") }}
+
+
+
+
+
+
+
+
+ {{ $t("announcements.show-announcements-setting-description") }}
+
+
+
@@ -14,5 +41,3 @@ import type { ReadGroupPreferences } from "~/lib/api/types/user";
const preferences = defineModel({ required: true });
-
-
diff --git a/frontend/app/components/Domain/Household/HouseholdPreferencesEditor.vue b/frontend/app/components/Domain/Household/HouseholdPreferencesEditor.vue
index bcbed74a8..6c24e033a 100644
--- a/frontend/app/components/Domain/Household/HouseholdPreferencesEditor.vue
+++ b/frontend/app/components/Domain/Household/HouseholdPreferencesEditor.vue
@@ -18,6 +18,20 @@
+
+
+
+
+ {{ $t("announcements.show-announcements-setting-description") }}
+
+
+
+
@@ -117,6 +118,24 @@
+ showAnnouncementsDialog = !showAnnouncementsDialog"
+ >
+
+
+
+ {{ $globals.icons.bullhornVariant }}
+
+
+
+
@@ -139,8 +158,10 @@