Improve SEO metadata and add sitemap

This commit is contained in:
Calum Muir 2025-12-21 20:51:37 +00:00
parent 357194edad
commit 7e3b72d8f3
12 changed files with 168 additions and 9 deletions

View file

@ -3,24 +3,27 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Highland Group RDA | Enriching Lives Through Horses</title> <title>Highland Group RDA - Home</title>
<meta <meta
name="description" name="description"
content="Highland Group RDA brings meaningful, positive change to people with disabilities through equine activities in the Highlands." content="Highland Group RDA provides horse riding for people with disabilities in the Highlands, offering safe, fun, and therapeutic equestrian activities."
/> />
<link rel="canonical" href="https://rda-2026.calumamuir.workers.dev" /> <link rel="canonical" href="https://highlandgrouprda.org.uk" />
<meta property="og:title" content="Highland Group RDA" /> <meta property="og:title" content="Highland Group RDA - Home" />
<meta <meta
property="og:description" property="og:description"
content="Enriching lives through horses in the Highlands since 1975." content="Highland Group RDA provides horse riding for people with disabilities in the Highlands, offering safe, fun, and therapeutic equestrian activities."
/> />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta <meta property="og:url" content="https://highlandgrouprda.org.uk" />
property="og:url"
content="https://rda-2026.calumamuir.workers.dev"
/>
<meta property="og:image" content="/Samantha-and-Connolly.jpg" /> <meta property="og:image" content="/Samantha-and-Connolly.jpg" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Highland Group RDA - Home" />
<meta
name="twitter:description"
content="Highland Group RDA provides horse riding for people with disabilities in the Highlands, offering safe, fun, and therapeutic equestrian activities."
/>
<meta name="twitter:image" content="/Samantha-and-Connolly.jpg" />
<meta name="theme-color" content="#e2e2e2" /> <meta name="theme-color" content="#e2e2e2" />
<link rel="preload" as="image" href="/Samantha-and-Connolly.jpg" /> <link rel="preload" as="image" href="/Samantha-and-Connolly.jpg" />
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />

View file

@ -1,2 +1,4 @@
User-agent: * User-agent: *
Allow: / Allow: /
Sitemap: https://highlandgrouprda.org.uk/sitemap.xml

36
client/public/sitemap.xml Normal file
View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://highlandgrouprda.org.uk/</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/about</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/news</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/news/volunteers-summer-2024</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/news/summer-awards-2024</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/news/race-night</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/news/important-news</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/support</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/contact</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/privacy</loc>
</url>
<url>
<loc>https://highlandgrouprda.org.uk/facebook</loc>
</url>
</urlset>

66
client/src/lib/seo.ts Normal file
View file

@ -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<HTMLMetaElement>(
`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<HTMLLinkElement>(
'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]);
};

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { usePageMeta } from "@/lib/seo";
const navigationItems = [ const navigationItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
@ -11,6 +12,12 @@ const navigationItems = [
]; ];
export const About = (): JSX.Element => { 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 ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main className="w-full"> <main className="w-full">

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { usePageMeta } from "@/lib/seo";
const navigationItems = [ const navigationItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
@ -11,6 +12,12 @@ const navigationItems = [
]; ];
export const Contact = (): JSX.Element => { export const Contact = (): JSX.Element => {
usePageMeta({
title: "Contact",
path: "/contact",
description:
"Contact Highland Group RDA for sessions, volunteering, and support.",
});
return ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main className="w-full"> <main className="w-full">

View file

@ -2,6 +2,7 @@ import React from "react";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { usePageMeta } from "@/lib/seo";
const navigationItems = [ const navigationItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
@ -35,6 +36,7 @@ const cardData = [
]; ];
export const Desktop = (): JSX.Element => { export const Desktop = (): JSX.Element => {
usePageMeta({ title: "Home", path: "/" });
return ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main className="w-full"> <main className="w-full">

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { usePageMeta } from "@/lib/seo";
const navigationItems = [ const navigationItems = [
{ label: "Home", href: "/" }, { 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"; "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 => { export const Facebook = (): JSX.Element => {
usePageMeta({
title: "Facebook",
path: "/facebook",
description:
"Follow Highland Group RDA on Facebook for the latest updates and events.",
});
return ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main className="w-full"> <main className="w-full">

View file

@ -2,6 +2,7 @@ import React from "react";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { formatNewsDate, newsArticles } from "@/lib/news"; import { formatNewsDate, newsArticles } from "@/lib/news";
import { usePageMeta } from "@/lib/seo";
const navigationItems = [ const navigationItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
@ -12,6 +13,12 @@ const navigationItems = [
]; ];
export const News = (): JSX.Element => { export const News = (): JSX.Element => {
usePageMeta({
title: "News",
path: "/news",
description:
"News, events, and updates from Highland Group RDA and our community.",
});
return ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main className="w-full"> <main className="w-full">

View file

@ -3,6 +3,7 @@ import { useRoute } from "wouter";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { formatNewsDate, getNewsArticle } from "@/lib/news"; import { formatNewsDate, getNewsArticle } from "@/lib/news";
import { usePageMeta } from "@/lib/seo";
import NotFound from "@/pages/not-found"; import NotFound from "@/pages/not-found";
const navigationItems = [ const navigationItems = [
@ -21,6 +22,13 @@ export const NewsArticle = (): JSX.Element => {
return <NotFound />; return <NotFound />;
} }
usePageMeta({
title: article.title,
path: `/news/${article.slug}`,
description: article.summary,
image: article.image,
});
return ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main className="w-full"> <main className="w-full">

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { usePageMeta } from "@/lib/seo";
const navigationItems = [ const navigationItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
@ -11,6 +12,12 @@ const navigationItems = [
]; ];
export const Privacy = (): JSX.Element => { 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 ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<SiteHeader <SiteHeader

View file

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { SiteHeader } from "@/components/SiteHeader"; import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter"; import { SiteFooter } from "@/components/SiteFooter";
import { usePageMeta } from "@/lib/seo";
const navigationItems = [ const navigationItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
@ -11,6 +12,12 @@ const navigationItems = [
]; ];
export const SupportUs = (): JSX.Element => { export const SupportUs = (): JSX.Element => {
usePageMeta({
title: "Support Us",
path: "/support",
description:
"Support Highland Group RDA through donations, volunteering, and fundraising.",
});
return ( return (
<div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]"> <div className="flex flex-col min-h-screen items-center bg-[#e2e2e2]">
<main className="w-full"> <main className="w-full">