diff --git a/src/components/Feed.tsx b/src/components/Feed.tsx
index 5cc31e5e..bed6ad8a 100644
--- a/src/components/Feed.tsx
+++ b/src/components/Feed.tsx
@@ -4,6 +4,7 @@ import { useQueryClient } from '@tanstack/react-query';
import { ComposeBox } from '@/components/ComposeBox';
import { NoteCard } from '@/components/NoteCard';
import { PullToRefresh } from '@/components/PullToRefresh';
+import { FeedEmptyState } from '@/components/FeedEmptyState';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { Loader2 } from 'lucide-react';
@@ -234,11 +235,20 @@ export function Feed({ kinds, tagFilters, header, hideCompose, emptyMessage }: F
)}
) : (
-
-
- {emptyMessage ?? 'No posts yet. Follow some people or switch to the Global tab to discover content.'}
-
-
+ setActiveTab('global')
+ : undefined
+ }
+ />
)}
diff --git a/src/components/FeedEmptyState.tsx b/src/components/FeedEmptyState.tsx
new file mode 100644
index 00000000..65ab45a7
--- /dev/null
+++ b/src/components/FeedEmptyState.tsx
@@ -0,0 +1,36 @@
+import { cn } from '@/lib/utils';
+
+interface FeedEmptyStateProps {
+ /** Primary empty-state message. */
+ message: string;
+ /** Called when the user clicks "Switch to Global". Omit to hide the button. */
+ onSwitchToGlobal?: () => void;
+ className?: string;
+}
+
+/**
+ * Consistent empty state for Follows/Global feed tabs across all feed pages.
+ *
+ * - Follows tab: pass `onSwitchToGlobal` to render a "Switch to Global" CTA.
+ * - Global tab: omit `onSwitchToGlobal`; the message should guide the user
+ * to check their relay connections.
+ */
+export function FeedEmptyState({
+ message,
+ onSwitchToGlobal,
+ className,
+}: FeedEmptyStateProps) {
+ return (
+
+
{message}
+ {onSwitchToGlobal && (
+
+ )}
+
+ );
+}
diff --git a/src/pages/EventsFeedPage.tsx b/src/pages/EventsFeedPage.tsx
index 741e4926..bf959f30 100644
--- a/src/pages/EventsFeedPage.tsx
+++ b/src/pages/EventsFeedPage.tsx
@@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
import { useInView } from 'react-intersection-observer';
import { useQueryClient } from '@tanstack/react-query';
import { ArrowLeft, CalendarDays, Loader2 } from 'lucide-react';
+import { FeedEmptyState } from '@/components/FeedEmptyState';
import { useSeoMeta } from '@unhead/react';
import type { NostrEvent } from '@nostrify/nostrify';
@@ -151,19 +152,14 @@ export function EventsFeedPage() {
)}
) : (
-
-
-
- {activeTab === 'follows'
- ? 'No events from people you follow yet. Try the Global tab.'
- : 'No calendar events found. Check your relay connections or try again later.'}
-
- {activeTab === 'follows' && (
-
- )}
-
+ setActiveTab('global') : undefined}
+ />
)}
diff --git a/src/pages/PhotosFeedPage.tsx b/src/pages/PhotosFeedPage.tsx
index 6cd6871a..cbde4e18 100644
--- a/src/pages/PhotosFeedPage.tsx
+++ b/src/pages/PhotosFeedPage.tsx
@@ -8,10 +8,10 @@
import { useState, useEffect, useMemo } from 'react';
import { Link } from 'react-router-dom';
-import { ArrowLeft, Camera } from 'lucide-react';
+import { ArrowLeft, Camera, Loader2 } from 'lucide-react';
import { useSeoMeta } from '@unhead/react';
import { useInView } from 'react-intersection-observer';
-import { Loader2 } from 'lucide-react';
+import { FeedEmptyState } from '@/components/FeedEmptyState';
import type { NostrEvent } from '@nostrify/nostrify';
import { useAppContext } from '@/hooks/useAppContext';
import { useCurrentUser } from '@/hooks/useCurrentUser';
@@ -133,15 +133,14 @@ export function PhotosFeedPage() {
{showSkeleton ? (
) : photoEvents.length === 0 ? (
-
-
-
- No photos yet.
- {activeTab === 'follows'
- ? ' Follow some photographers or switch to Global.'
- : ' Check your relay connections or come back soon.'}
-