From 7e3b72d8f3057f694ac221a62f6354c0fee545f1 Mon Sep 17 00:00:00 2001 From: Calum Muir Date: Sun, 21 Dec 2025 20:51:37 +0000 Subject: [PATCH] Improve SEO metadata and add sitemap --- client/index.html | 21 +++++----- client/public/robots.txt | 2 + client/public/sitemap.xml | 36 +++++++++++++++++ client/src/lib/seo.ts | 66 ++++++++++++++++++++++++++++++++ client/src/pages/About.tsx | 7 ++++ client/src/pages/Contact.tsx | 7 ++++ client/src/pages/Desktop.tsx | 2 + client/src/pages/Facebook.tsx | 7 ++++ client/src/pages/News.tsx | 7 ++++ client/src/pages/NewsArticle.tsx | 8 ++++ client/src/pages/Privacy.tsx | 7 ++++ client/src/pages/SupportUs.tsx | 7 ++++ 12 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 client/public/sitemap.xml create mode 100644 client/src/lib/seo.ts diff --git a/client/index.html b/client/index.html index e2b1b6c..29dfb5e 100644 --- a/client/index.html +++ b/client/index.html @@ -3,24 +3,27 @@ - Highland Group RDA | Enriching Lives Through Horses + Highland Group RDA - Home - - + + - + + + + diff --git a/client/public/robots.txt b/client/public/robots.txt index c2a49f4..195c82c 100644 --- a/client/public/robots.txt +++ b/client/public/robots.txt @@ -1,2 +1,4 @@ User-agent: * Allow: / + +Sitemap: https://highlandgrouprda.org.uk/sitemap.xml diff --git a/client/public/sitemap.xml b/client/public/sitemap.xml new file mode 100644 index 0000000..d962077 --- /dev/null +++ b/client/public/sitemap.xml @@ -0,0 +1,36 @@ + + + + https://highlandgrouprda.org.uk/ + + + https://highlandgrouprda.org.uk/about + + + https://highlandgrouprda.org.uk/news + + + https://highlandgrouprda.org.uk/news/volunteers-summer-2024 + + + https://highlandgrouprda.org.uk/news/summer-awards-2024 + + + https://highlandgrouprda.org.uk/news/race-night + + + https://highlandgrouprda.org.uk/news/important-news + + + https://highlandgrouprda.org.uk/support + + + https://highlandgrouprda.org.uk/contact + + + https://highlandgrouprda.org.uk/privacy + + + https://highlandgrouprda.org.uk/facebook + + diff --git a/client/src/lib/seo.ts b/client/src/lib/seo.ts new file mode 100644 index 0000000..083af90 --- /dev/null +++ b/client/src/lib/seo.ts @@ -0,0 +1,66 @@ +import { useEffect } from "react"; + +const SITE_NAME = "Highland Group RDA"; +const BASE_URL = "https://highlandgrouprda.org.uk"; +const DEFAULT_DESCRIPTION = + "Highland Group RDA provides horse riding for people with disabilities in the Highlands, offering safe, fun, and therapeutic equestrian activities."; +const DEFAULT_IMAGE = "/Samantha-and-Connolly.jpg"; + +type PageMeta = { + title: string; + description?: string; + path?: string; + image?: string; +}; + +const setMeta = (attr: "name" | "property", key: string, value: string) => { + let tag = document.head.querySelector( + `meta[${attr}="${key}"]`, + ); + if (!tag) { + tag = document.createElement("meta"); + tag.setAttribute(attr, key); + document.head.appendChild(tag); + } + tag.setAttribute("content", value); +}; + +const setCanonical = (href: string) => { + let link = document.head.querySelector( + 'link[rel="canonical"]', + ); + if (!link) { + link = document.createElement("link"); + link.setAttribute("rel", "canonical"); + document.head.appendChild(link); + } + link.setAttribute("href", href); +}; + +export const usePageMeta = ({ + title, + description = DEFAULT_DESCRIPTION, + path = "/", + image = DEFAULT_IMAGE, +}: PageMeta): void => { + useEffect(() => { + const pageTitle = `${SITE_NAME} - ${title}`; + const url = `${BASE_URL}${path === "/" ? "" : path}`; + const imageUrl = image.startsWith("http") + ? image + : `${BASE_URL}${image}`; + + document.title = pageTitle; + setMeta("name", "description", description); + setMeta("property", "og:title", pageTitle); + setMeta("property", "og:description", description); + setMeta("property", "og:type", "website"); + setMeta("property", "og:url", url); + setMeta("property", "og:image", imageUrl); + setMeta("name", "twitter:card", "summary_large_image"); + setMeta("name", "twitter:title", pageTitle); + setMeta("name", "twitter:description", description); + setMeta("name", "twitter:image", imageUrl); + setCanonical(url); + }, [title, description, path, image]); +}; diff --git a/client/src/pages/About.tsx b/client/src/pages/About.tsx index 6b1ccda..216eb72 100644 --- a/client/src/pages/About.tsx +++ b/client/src/pages/About.tsx @@ -1,6 +1,7 @@ import React from "react"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteFooter } from "@/components/SiteFooter"; +import { usePageMeta } from "@/lib/seo"; const navigationItems = [ { label: "Home", href: "/" }, @@ -11,6 +12,12 @@ const navigationItems = [ ]; export const About = (): JSX.Element => { + usePageMeta({ + title: "About", + path: "/about", + description: + "Learn about Highland Group RDA, our mission, and how we support riders across the Highlands.", + }); return (
diff --git a/client/src/pages/Contact.tsx b/client/src/pages/Contact.tsx index b1eaeac..2a7a771 100644 --- a/client/src/pages/Contact.tsx +++ b/client/src/pages/Contact.tsx @@ -1,6 +1,7 @@ import React from "react"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteFooter } from "@/components/SiteFooter"; +import { usePageMeta } from "@/lib/seo"; const navigationItems = [ { label: "Home", href: "/" }, @@ -11,6 +12,12 @@ const navigationItems = [ ]; export const Contact = (): JSX.Element => { + usePageMeta({ + title: "Contact", + path: "/contact", + description: + "Contact Highland Group RDA for sessions, volunteering, and support.", + }); return (
diff --git a/client/src/pages/Desktop.tsx b/client/src/pages/Desktop.tsx index c73e48f..852ba9b 100644 --- a/client/src/pages/Desktop.tsx +++ b/client/src/pages/Desktop.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Card, CardContent } from "@/components/ui/card"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteFooter } from "@/components/SiteFooter"; +import { usePageMeta } from "@/lib/seo"; const navigationItems = [ { label: "Home", href: "/" }, @@ -35,6 +36,7 @@ const cardData = [ ]; export const Desktop = (): JSX.Element => { + usePageMeta({ title: "Home", path: "/" }); return (
diff --git a/client/src/pages/Facebook.tsx b/client/src/pages/Facebook.tsx index dfc7103..bf218f5 100644 --- a/client/src/pages/Facebook.tsx +++ b/client/src/pages/Facebook.tsx @@ -1,6 +1,7 @@ import React from "react"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteFooter } from "@/components/SiteFooter"; +import { usePageMeta } from "@/lib/seo"; const navigationItems = [ { label: "Home", href: "/" }, @@ -17,6 +18,12 @@ const facebookEmbedUrl = "https://www.facebook.com/plugins/page.php?href=https%3A%2F%2Fwww.facebook.com%2Fprofile.php%3Fid%3D100064729054822&tabs=timeline&width=500&height=900&small_header=false&adapt_container_width=true&hide_cover=false&show_facepile=true"; export const Facebook = (): JSX.Element => { + usePageMeta({ + title: "Facebook", + path: "/facebook", + description: + "Follow Highland Group RDA on Facebook for the latest updates and events.", + }); return (
diff --git a/client/src/pages/News.tsx b/client/src/pages/News.tsx index fef7f5a..263b70b 100644 --- a/client/src/pages/News.tsx +++ b/client/src/pages/News.tsx @@ -2,6 +2,7 @@ import React from "react"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteFooter } from "@/components/SiteFooter"; import { formatNewsDate, newsArticles } from "@/lib/news"; +import { usePageMeta } from "@/lib/seo"; const navigationItems = [ { label: "Home", href: "/" }, @@ -12,6 +13,12 @@ const navigationItems = [ ]; export const News = (): JSX.Element => { + usePageMeta({ + title: "News", + path: "/news", + description: + "News, events, and updates from Highland Group RDA and our community.", + }); return (
diff --git a/client/src/pages/NewsArticle.tsx b/client/src/pages/NewsArticle.tsx index d305464..f28b008 100644 --- a/client/src/pages/NewsArticle.tsx +++ b/client/src/pages/NewsArticle.tsx @@ -3,6 +3,7 @@ import { useRoute } from "wouter"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteFooter } from "@/components/SiteFooter"; import { formatNewsDate, getNewsArticle } from "@/lib/news"; +import { usePageMeta } from "@/lib/seo"; import NotFound from "@/pages/not-found"; const navigationItems = [ @@ -21,6 +22,13 @@ export const NewsArticle = (): JSX.Element => { return ; } + usePageMeta({ + title: article.title, + path: `/news/${article.slug}`, + description: article.summary, + image: article.image, + }); + return (
diff --git a/client/src/pages/Privacy.tsx b/client/src/pages/Privacy.tsx index e2a73f3..e635321 100644 --- a/client/src/pages/Privacy.tsx +++ b/client/src/pages/Privacy.tsx @@ -1,6 +1,7 @@ import React from "react"; import { SiteHeader } from "@/components/SiteHeader"; import { SiteFooter } from "@/components/SiteFooter"; +import { usePageMeta } from "@/lib/seo"; const navigationItems = [ { label: "Home", href: "/" }, @@ -11,6 +12,12 @@ const navigationItems = [ ]; export const Privacy = (): JSX.Element => { + usePageMeta({ + title: "Privacy Policy", + path: "/privacy", + description: + "Privacy policy for Highland Group RDA, including our use of Cloudflare Web Analytics.", + }); return (
{ + usePageMeta({ + title: "Support Us", + path: "/support", + description: + "Support Highland Group RDA through donations, volunteering, and fundraising.", + }); return (