From 568f10761e96c0eb110403300a7350aa28101aa1 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 14:20:46 +0200
Subject: [PATCH] Add comprehensive SEO optimization with dynamic meta tags and
social sharing support (#23)
* Initial plan
* Add SEO optimization with dynamic meta tags for all pages
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
* Fix SEO meta tags to use useSeoMeta correctly without useEffect
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
* Add SEO verification documentation
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
* Add comprehensive SEO examples documentation
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
* Refactor routing to use HomePage component and update blog post fetching limit
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
Co-authored-by: highperfocused
---
SEO_EXAMPLES.md | 253 +++++++++++++++++++
SEO_VERIFICATION.md | 152 +++++++++++
index.html | 17 +-
src/AppRouter.tsx | 5 +-
src/components/LatestInHashtag.tsx | 6 +-
src/hooks/useBlogPostsByHashtag.ts | 4 +-
src/hooks/useSearch.ts | 4 +-
src/pages/ArticlePage.tsx | 55 ++++
src/pages/BookmarksPage.tsx | 7 +
src/pages/CreatePostPage.tsx | 7 +
src/pages/EditPostPage.tsx | 7 +
src/pages/EventPage.tsx | 33 +++
src/pages/FollowingPage.tsx | 7 +
src/pages/{BlogHomePage.tsx => HomePage.tsx} | 26 +-
src/pages/Index.tsx | 15 +-
src/pages/NotePage.tsx | 31 +++
src/pages/ProfilePage.tsx | 46 ++++
src/pages/SearchResultsPage.tsx | 19 ++
18 files changed, 676 insertions(+), 18 deletions(-)
create mode 100644 SEO_EXAMPLES.md
create mode 100644 SEO_VERIFICATION.md
rename src/pages/{BlogHomePage.tsx => HomePage.tsx} (51%)
diff --git a/SEO_EXAMPLES.md b/SEO_EXAMPLES.md
new file mode 100644
index 0000000..7d9af53
--- /dev/null
+++ b/SEO_EXAMPLES.md
@@ -0,0 +1,253 @@
+# SEO Implementation Examples
+
+This document shows concrete examples of how the SEO meta tags appear on different pages of zelo.news.
+
+## Example 1: Article Page
+
+When viewing an article titled "The Future of Decentralized Social Media" by Alice:
+
+### What Gets Rendered:
+```html
+
+ The Future of Decentralized Social Media - Alice - zelo.news
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### What This Looks Like When Shared:
+
+**WhatsApp Preview:**
+```
+┌─────────────────────────────────────────┐
+│ [Article Image] │
+├─────────────────────────────────────────┤
+│ The Future of Decentralized Social │
+│ Media │
+│ Exploring how Nostr and other │
+│ decentralized protocols are reshaping │
+│ social media... │
+│ zelo.news │
+└─────────────────────────────────────────┘
+```
+
+**Twitter/X Preview:**
+```
+┌─────────────────────────────────────────┐
+│ [Large Article Image] │
+├─────────────────────────────────────────┤
+│ The Future of Decentralized Social │
+│ Media │
+│ Exploring how Nostr and other │
+│ decentralized protocols... │
+│ 🔗 zelo.news │
+└─────────────────────────────────────────┘
+```
+
+## Example 2: Profile Page
+
+When viewing Bob's profile who has published 15 articles:
+
+### What Gets Rendered:
+```html
+
+ Bob - Profile - zelo.news
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### What This Looks Like When Shared:
+
+**Facebook/LinkedIn Preview:**
+```
+┌─────────────────────────────────────────┐
+│ [Bob's Profile Banner or Avatar] │
+├─────────────────────────────────────────┤
+│ Bob on zelo.news │
+│ Bitcoin enthusiast and writer exploring │
+│ decentralized systems. • 15 articles │
+│ published │
+│ 🔗 ZELO.NEWS │
+└─────────────────────────────────────────┘
+```
+
+## Example 3: Home Page
+
+When viewing the home page:
+
+### What Gets Rendered:
+```html
+
+ zelo.news - Decentralized News on Nostr
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Example 4: Search Results Page
+
+When searching for "bitcoin":
+
+### What Gets Rendered:
+```html
+
+ Search: bitcoin - zelo.news
+
+
+
+
+```
+
+## Example 5: Note Page (Short Post)
+
+When viewing a short text note from Charlie:
+
+### What Gets Rendered:
+```html
+
+ Charlie's note - zelo.news
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Benefits of This Implementation
+
+### 1. Better Search Engine Rankings
+- Unique titles for every page
+- Descriptive meta descriptions
+- Proper semantic HTML structure
+- Keywords in meta tags
+
+### 2. Rich Social Media Previews
+When users share links on:
+- **WhatsApp**: Shows article image, title, and description
+- **Facebook**: Rich preview with image, title, and description
+- **Twitter/X**: Large image card with content preview
+- **LinkedIn**: Professional preview with article details
+- **Discord**: Embedded preview with image and text
+- **Slack**: Link unfurling with full preview
+- **Telegram**: Rich message preview
+
+### 3. Dynamic Content Handling
+- Meta tags update automatically when content loads
+- Fallback values ensure something always displays
+- Social media crawlers execute JavaScript and see updated tags
+
+### 4. Professional Appearance
+- Consistent branding across all social platforms
+- Author attribution on all content
+- Timestamp information for articles
+- Category tags for better discovery
+
+## Testing Your Implementation
+
+### Quick Test with Twitter
+1. Copy any article URL from your deployed site
+2. Paste it into a new tweet
+3. Twitter will show a preview - you should see the article image, title, and description
+
+### Full Test with Facebook Debugger
+1. Go to https://developers.facebook.com/tools/debug/
+2. Enter any page URL from your deployed site
+3. Click "Scrape Again"
+4. Review the preview - should show all meta tags and image
+
+### Verify in Browser
+1. Open any page
+2. Right-click → Inspect
+3. Go to Elements tab
+4. Look at `` section
+5. Should see all meta tags listed above
+
+## Common Issues and Solutions
+
+### Issue: Social preview not updating
+**Solution**: Use the social media debuggers to force a re-scrape
+- Facebook: https://developers.facebook.com/tools/debug/
+- Twitter: https://cards-dev.twitter.com/validator
+- LinkedIn: https://www.linkedin.com/post-inspector/
+
+### Issue: Image not showing in preview
+**Solution**: Ensure images use absolute URLs (https://...) not relative (/image.jpg)
+
+### Issue: Old title/description showing
+**Solution**: Clear browser cache or test in incognito mode
+
+## Conclusion
+
+The SEO implementation provides:
+✅ Professional social media previews on all platforms
+✅ Better search engine visibility
+✅ Dynamic updates for Nostr content
+✅ Proper handling of images and descriptions
+✅ Consistent branding across the web
+
+All without requiring server-side rendering - it works entirely in the browser with the @unhead library!
diff --git a/SEO_VERIFICATION.md b/SEO_VERIFICATION.md
new file mode 100644
index 0000000..26cc9b1
--- /dev/null
+++ b/SEO_VERIFICATION.md
@@ -0,0 +1,152 @@
+# SEO Optimization Verification
+
+This document describes the SEO optimizations implemented in zelo.news and how to verify they work correctly.
+
+## What Was Implemented
+
+### 1. Dynamic Page Titles
+Each page now has a unique, descriptive title that updates based on content:
+- **Article Pages**: `[Article Title] - [Author Name] - zelo.news`
+- **Profile Pages**: `[Username] - Profile - zelo.news`
+- **Home Page**: `zelo.news - Decentralized News on Nostr`
+- **Search Pages**: `Search: [query] - zelo.news` or `Articles tagged #[tag] - zelo.news`
+- **Other Pages**: Descriptive titles for bookmarks, following, etc.
+
+### 2. Meta Descriptions
+Every page includes a relevant description:
+- **Articles**: Uses the article summary, or first 160 characters of content
+- **Profiles**: Uses the user's "about" text, or a default description
+- **Notes/Events**: Uses the first 160 characters of the content
+- **Search**: Includes result count and search term
+
+### 3. Open Graph Tags (for Facebook, LinkedIn, WhatsApp, etc.)
+All pages include Open Graph meta tags:
+- `og:title` - Page/content title
+- `og:description` - Page/content description
+- `og:type` - "article" for content pages, "website" for home
+- `og:url` - Current page URL
+- `og:image` - Article image, profile picture, or default icon
+- `og:site_name` - "zelo.news"
+
+Article pages also include:
+- `article:published_time` - Publication timestamp
+- `article:author` - Author name
+- `article:tag` - Article hashtags
+
+### 4. Twitter Card Tags
+All pages include Twitter Card meta tags for Twitter/X sharing:
+- `twitter:card` - "summary_large_image" for articles, "summary" for others
+- `twitter:title` - Page/content title
+- `twitter:description` - Page/content description
+- `twitter:image` - Article image, profile picture, or default icon
+- `twitter:site` - "@zelo_news"
+
+### 5. Additional SEO Features
+- Keywords meta tag in base HTML
+- Author meta tag in base HTML
+- `robots: noindex` for personal pages (bookmarks, following, search results, editor)
+- Proper semantic HTML structure
+
+## How to Verify
+
+### Method 1: Browser DevTools
+1. Open the application in a browser
+2. Open DevTools (F12)
+3. Go to the Elements/Inspector tab
+4. Look at the `` section
+5. You should see dynamically injected meta tags from @unhead
+
+### Method 2: View Page Source
+1. Right-click on any page and select "View Page Source"
+2. Look for `` tags in the ``
+3. Default tags will be visible in the static HTML
+4. Dynamic tags are injected by JavaScript after page load
+
+### Method 3: Social Media Sharing Debuggers
+These tools show what social media platforms will see:
+
+**Facebook/Open Graph Debugger:**
+- Visit: https://developers.facebook.com/tools/debug/
+- Enter a page URL from your deployed site
+- Click "Scrape Again" to see the latest meta tags
+- Should show article title, description, and image
+
+**Twitter Card Validator:**
+- Visit: https://cards-dev.twitter.com/validator
+- Enter a page URL from your deployed site
+- Should show a preview of how the link will appear on Twitter
+
+**LinkedIn Post Inspector:**
+- Visit: https://www.linkedin.com/post-inspector/
+- Enter a page URL from your deployed site
+- Should show how the link will appear on LinkedIn
+
+### Method 4: Browser Extensions
+Install a meta tag viewer extension:
+- **SEO Meta in 1 Click** (Chrome/Edge)
+- **META SEO inspector** (Firefox)
+- These will show all meta tags on the current page
+
+## Example: Article Page Meta Tags
+
+When viewing an article page, the following meta tags should be present:
+
+```html
+[Article Title] - [Author Name] - zelo.news
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Testing Dynamic Content Loading
+
+The SEO implementation handles dynamically loaded content:
+
+1. **Initial Load**: Default meta tags from `index.html` are visible
+2. **After Data Loads**: Meta tags are updated with actual content
+3. **Social Media Crawlers**: They execute JavaScript and will see the updated tags
+
+To verify this works:
+1. Open a page (e.g., an article page)
+2. Open DevTools > Network tab
+3. Reload the page
+4. Watch the meta tags in the Elements tab - they should update as data loads
+
+## Notes
+
+- **Server-Side Rendering**: Currently not implemented. Social media crawlers execute JavaScript, so they will see the dynamically set meta tags.
+- **@unhead Library**: This library is SSR-compatible and properly manages meta tags.
+- **Image URLs**: Uses absolute URLs so social media platforms can fetch images.
+- **Default Fallbacks**: All pages have sensible defaults if content is not available.
+
+## Files Modified
+
+- `index.html` - Updated default meta tags
+- `src/pages/ArticlePage.tsx` - Added article-specific SEO
+- `src/pages/ProfilePage.tsx` - Added profile-specific SEO
+- `src/pages/BlogHomePage.tsx` - Added home page SEO
+- `src/pages/SearchResultsPage.tsx` - Added search page SEO (noindex)
+- `src/pages/BookmarksPage.tsx` - Added bookmarks page SEO (noindex)
+- `src/pages/FollowingPage.tsx` - Added following page SEO (noindex)
+- `src/pages/CreatePostPage.tsx` - Added create page SEO (noindex)
+- `src/pages/EditPostPage.tsx` - Added edit page SEO (noindex)
+- `src/pages/NotePage.tsx` - Added note page SEO
+- `src/pages/EventPage.tsx` - Added event page SEO
diff --git a/index.html b/index.html
index 53f6696..b2d803f 100644
--- a/index.html
+++ b/index.html
@@ -3,8 +3,10 @@
- zelo.news - Your Source for Decentralized News
-
+ zelo.news - Decentralized News on Nostr
+
+
+
@@ -17,8 +19,15 @@
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/AppRouter.tsx b/src/AppRouter.tsx
index 5b5b084..17c36a5 100644
--- a/src/AppRouter.tsx
+++ b/src/AppRouter.tsx
@@ -1,8 +1,6 @@
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { ScrollToTop } from "./components/ScrollToTop";
import { BlogLayout } from "./components/BlogLayout";
-
-import BlogHomePage from "./pages/BlogHomePage";
import CreatePostPage from "./pages/CreatePostPage";
import EditPostPage from "./pages/EditPostPage";
import SearchResultsPage from "./pages/SearchResultsPage";
@@ -12,6 +10,7 @@ import Nip05ProfilePage from "./pages/Nip05ProfilePage";
import ArticleByDTagPage from "./pages/ArticleByDTagPage";
import { NIP19Page } from "./pages/NIP19Page";
import NotFound from "./pages/NotFound";
+import HomePage from "./pages/HomePage";
export function AppRouter() {
return (
@@ -19,7 +18,7 @@ export function AppRouter() {
- } />
+ } />
} />
} />
} />
diff --git a/src/components/LatestInHashtag.tsx b/src/components/LatestInHashtag.tsx
index 1a7a4a6..aecf584 100644
--- a/src/components/LatestInHashtag.tsx
+++ b/src/components/LatestInHashtag.tsx
@@ -15,7 +15,7 @@ const INITIAL_POSTS_COUNT = 3;
export function LatestInHashtag({ hashtag, icon }: LatestInHashtagProps) {
const navigate = useNavigate();
- const { data: posts, isLoading } = useBlogPostsByHashtag(hashtag);
+ const { data: posts, isLoading } = useBlogPostsByHashtag(hashtag, 4);
// Loading state
if (isLoading) {
@@ -60,9 +60,9 @@ export function LatestInHashtag({ hashtag, icon }: LatestInHashtagProps) {
Latest in #{hashtag}
-
+ {/*
{posts.length} {posts.length === 1 ? 'article' : 'articles'} in this category
-