From 6a45439a9b5b22a9dfc54e5d01b0a3b40b5d6381 Mon Sep 17 00:00:00 2001 From: sanusishafii989 Date: Wed, 26 Aug 2026 04:04:14 +0100 Subject: [PATCH 1/2] feat(seo): add hreflang tags, canonical URLs, and central Seo helper Add a reusable component in src/utils/seo.tsx that emits canonical URLs, hreflang alternates (en, es, x-default), Open Graph, and Twitter Card metadata on every page. Future pages inherit correct SEO by simply dropping in . - Create src/utils/seo.tsx with Seo component (uses useLocation for self-referencing canonical + locale-aware hreflang) - Add /es/* locale-prefixed routes in App.tsx via LocaleSetter wrapper - Audit all 18 pages: every route now has title, description, canonical, hreflang, og:*, and twitter:* metadata - Replace ad-hoc Helmet blocks on About, Blog, Careers, Ecosystem, Stellar with centralized Seo component - Add SEO metadata to 12 pages that had none: Faq, Privacy, Newsletter, UseCases, Roadmap, Grants, CaseStudies, Contributors, Vitals, NotFound, Press, Home - Add /es/* routes to sitemap knownRoutes Closes #125 --- public/feed.xml | 2 +- public/sitemap.xml | 44 +++++++--- scripts/sitemap.ts | 14 ++++ src/App.tsx | 161 ++++++++++++++++++++++++++++++++++++- src/pages/About.tsx | 20 ++--- src/pages/Blog.tsx | 29 ++++--- src/pages/Careers.tsx | 20 ++--- src/pages/CaseStudies.tsx | 14 ++++ src/pages/Contributors.tsx | 5 ++ src/pages/Ecosystem.tsx | 21 ++--- src/pages/Faq.tsx | 5 ++ src/pages/Grants.tsx | 5 ++ src/pages/Newsletter.tsx | 12 +-- src/pages/NotFound.tsx | 5 ++ src/pages/Press.tsx | 5 ++ src/pages/Privacy.tsx | 5 ++ src/pages/Roadmap.tsx | 5 ++ src/pages/Stellar.tsx | 24 ++---- src/pages/UseCases.tsx | 5 ++ src/pages/Vitals.tsx | 5 ++ src/utils/seo.tsx | 93 +++++++++++++++++++++ 21 files changed, 399 insertions(+), 100 deletions(-) create mode 100644 src/utils/seo.tsx diff --git a/public/feed.xml b/public/feed.xml index c575d12..1f49e57 100644 --- a/public/feed.xml +++ b/public/feed.xml @@ -5,7 +5,7 @@ https://usewraith.xyz/blog Notes on stealth payments, private infrastructure, and the Wraith ecosystem. en-us - Sat, 08 Aug 2026 18:36:39 GMT + Wed, 26 Aug 2026 02:49:58 GMT diff --git a/public/sitemap.xml b/public/sitemap.xml index 839e573..8c72aa5 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -2,79 +2,97 @@ https://usewraith.xyz - 2026-08-08 + 2026-08-26 daily 1.0 https://usewraith.xyz/faq - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/privacy - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/use-cases - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/roadmap - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/case-studies - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/stellar - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/careers - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/press - 2026-08-08 + 2026-08-26 + weekly + 0.8 + + + https://usewraith.xyz/grants + 2026-08-26 + weekly + 0.8 + + + https://usewraith.xyz/grants/showcase + 2026-08-26 + weekly + 0.8 + + + https://usewraith.xyz/contributors + 2026-08-26 weekly 0.8 https://usewraith.xyz/case-studies/payroll-processor - 2026-08-08 + 2026-08-26 weekly 0.7 https://usewraith.xyz/blog - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/compare - 2026-08-08 + 2026-08-26 weekly 0.8 https://usewraith.xyz/newsletter - 2026-08-08 + 2026-08-26 weekly 0.8 diff --git a/scripts/sitemap.ts b/scripts/sitemap.ts index de0b2e3..4cbf677 100644 --- a/scripts/sitemap.ts +++ b/scripts/sitemap.ts @@ -18,6 +18,20 @@ const knownRoutes = [ '/stellar', '/careers', '/press', + '/grants', + '/grants/showcase', + '/contributors', + '/es', + '/es/faq', + '/es/privacy', + '/es/use-cases', + '/es/roadmap', + '/es/case-studies', + '/es/stellar', + '/es/careers', + '/es/grants', + '/es/grants/showcase', + '/es/contributors', ]; function getCaseStudyRoutes(): string[] { diff --git a/src/App.tsx b/src/App.tsx index c8436f2..090a19e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import { lazy, Suspense } from 'react'; +import { lazy, Suspense, useEffect, type ReactNode } from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import Header from './components/Header'; import Hero from './components/Hero'; @@ -7,6 +7,8 @@ import Layout from './components/Layout'; import TrustStrip from './components/TrustStrip'; import PartnerStrip from './components/PartnerStrip'; import { ThemeProvider } from './context/ThemeContext'; +import { Seo } from './utils/seo'; +import { changeLocale, type Locale } from './i18n'; // Lazy load below-the-fold homepage components const StealthAnimation = lazy(() => import('./components/StealthAnimation')); @@ -37,9 +39,21 @@ const NotFound = lazy(() => import('./pages/NotFound')); const Contributors = lazy(() => import('./pages/Contributors')); const Blog = lazy(() => import('./pages/Blog')); +/** Sets the i18n locale on mount for locale-prefixed routes. */ +function LocaleSetter({ locale, children }: { locale: Locale; children: ReactNode }) { + useEffect(() => { + changeLocale(locale); + }, [locale]); + return <>{children}; +} + function Home() { return (
+ Skip to content @@ -141,6 +155,151 @@ export default function App() { } /> + + {/* Spanish locale-prefixed routes */} + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + + + } + /> + + + + + + } + /> + + + + + + } + /> + + + + + + } + /> + + + + + + } + /> + + + + + + } + /> + + + + + + } + /> + } /> diff --git a/src/pages/About.tsx b/src/pages/About.tsx index c536d5a..557660f 100644 --- a/src/pages/About.tsx +++ b/src/pages/About.tsx @@ -1,4 +1,4 @@ -import { Helmet } from 'react-helmet-async'; +import { Seo } from '../utils/seo'; import teamData from '../data/team.json'; const socialIcon = (type: 'github' | 'twitter') => { @@ -19,20 +19,10 @@ const socialIcon = (type: 'github' | 'twitter') => { export default function About() { return ( <> - - About – Wraith Protocol - - - - - - + {/* ── Hero ──────────────────────────────────────────────────────────── */}
diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx index 3b5a384..0beb43f 100644 --- a/src/pages/Blog.tsx +++ b/src/pages/Blog.tsx @@ -1,4 +1,4 @@ -import { Helmet } from 'react-helmet-async'; +import { Seo } from '../utils/seo'; import { Link, useParams } from 'react-router-dom'; import { getAllPosts, getPostBySlug } from '../utils/blog'; import BlogToc from '../components/BlogToc'; @@ -8,19 +8,17 @@ function BlogList() { return (
- - Blog – Wraith Protocol - + - +

Blog

@@ -82,9 +80,10 @@ function BlogPostDetail({ slug }: { slug: string }) { if (!post) { return (
- - Post Not Found – Wraith Protocol - +

Post Not Found

The requested blog post could not be found.

@@ -98,10 +97,10 @@ function BlogPostDetail({ slug }: { slug: string }) { return (
- - {post.title} – Wraith Protocol - {post.excerpt && } - +
- - Careers – Wraith Protocol - - - - - - +
diff --git a/src/pages/CaseStudies.tsx b/src/pages/CaseStudies.tsx index 177abba..56512ca 100644 --- a/src/pages/CaseStudies.tsx +++ b/src/pages/CaseStudies.tsx @@ -3,6 +3,7 @@ import { Link, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { entries } from '../data/case-studies.json'; import Layout from '../components/Layout'; +import { Seo } from '../utils/seo'; type CaseStudy = { id: string; @@ -67,6 +68,11 @@ function CaseStudyDetail({ study }: { study: CaseStudy }) { return ( +