aethex-forge/client/components/SEO.tsx
2025-10-19 21:31:09 +00:00

95 lines
2.2 KiB
TypeScript

import { useEffect } from "react";
export type SEOProps = {
pageTitle: string;
description?: string;
image?: string | null;
canonical?: string | null;
noIndex?: boolean;
};
function upsertMeta(selector: string, attrs: Record<string, string>) {
let el = document.querySelector(selector) as HTMLElement | null;
if (!el) {
const tag = selector.startsWith("meta[")
? "meta"
: selector.startsWith("link[")
? "link"
: "meta";
el = document.createElement(tag);
document.head.appendChild(el);
}
Object.entries(attrs).forEach(([k, v]) => (el as any).setAttribute(k, v));
}
export default function SEO({
pageTitle,
description,
image,
canonical,
noIndex,
}: SEOProps) {
useEffect(() => {
const title = `AeThex | ${pageTitle}`;
document.title = title;
if (canonical) {
upsertMeta('link[rel="canonical"]', {
rel: "canonical",
href: canonical,
});
upsertMeta('meta[property="og:url"]', {
property: "og:url",
content: canonical,
});
}
if (description) {
upsertMeta('meta[name="description"]', {
name: "description",
content: description,
});
upsertMeta('meta[property="og:description"]', {
property: "og:description",
content: description,
});
upsertMeta('meta[name="twitter:description"]', {
name: "twitter:description",
content: description,
});
}
upsertMeta('meta[property="og:title"]', {
property: "og:title",
content: title,
});
upsertMeta('meta[name="twitter:title"]', {
name: "twitter:title",
content: title,
});
if (image) {
upsertMeta('meta[property="og:image"]', {
property: "og:image",
content: image,
});
upsertMeta('meta[name="twitter:image"]', {
name: "twitter:image",
content: image,
});
}
if (noIndex) {
upsertMeta('meta[name="robots"]', {
name: "robots",
content: "noindex, nofollow",
});
upsertMeta('meta[name="googlebot"]', {
name: "googlebot",
content: "noindex, nofollow",
});
}
}, [pageTitle, description, image, canonical, noIndex]);
return null;
}