Files
hotschpotsh/Pottery-website/components/SEO.tsx
2026-03-23 19:00:17 -05:00

85 lines
3.0 KiB
TypeScript

import React, { useEffect } from 'react';
// TODO: Update SITE_URL to your actual domain before deploying
export const SITE_URL = 'https://knuthceramics.com';
export const SITE_NAME = 'KNUTH Ceramics';
const DEFAULT_OG_IMAGE = `${SITE_URL}/landingpage/artelier.png`;
type Schema = Record<string, unknown>;
interface SEOProps {
title: string;
description: string;
canonical?: string;
schema?: Schema | Schema[];
ogImage?: string;
ogType?: 'website' | 'article' | 'product';
}
function setMeta(selector: string, attr: string, value: string) {
let el = document.querySelector<HTMLMetaElement>(selector);
if (!el) {
el = document.createElement('meta');
const [attrName, attrVal] = selector.replace('meta[', '').replace(']', '').split('="');
el.setAttribute(attrName, attrVal.replace('"', ''));
document.head.appendChild(el);
}
el.setAttribute(attr, value);
}
const SEO: React.FC<SEOProps> = ({
title,
description,
canonical,
schema,
ogImage = DEFAULT_OG_IMAGE,
ogType = 'website',
}) => {
const schemaStr = schema ? JSON.stringify(schema) : null;
useEffect(() => {
const fullTitle = title.includes(SITE_NAME) ? title : `${title} | ${SITE_NAME}`;
document.title = fullTitle;
setMeta('meta[name="description"]', 'content', description);
setMeta('meta[property="og:title"]', 'content', fullTitle);
setMeta('meta[property="og:description"]', 'content', description);
setMeta('meta[property="og:type"]', 'content', ogType);
setMeta('meta[property="og:image"]', 'content', ogImage);
setMeta('meta[property="og:site_name"]', 'content', SITE_NAME);
setMeta('meta[name="twitter:card"]', 'content', 'summary_large_image');
setMeta('meta[name="twitter:title"]', 'content', fullTitle);
setMeta('meta[name="twitter:description"]', 'content', description);
setMeta('meta[name="twitter:image"]', 'content', ogImage);
let canonicalEl = document.querySelector<HTMLLinkElement>('link[rel="canonical"]');
if (!canonicalEl) {
canonicalEl = document.createElement('link');
canonicalEl.setAttribute('rel', 'canonical');
document.head.appendChild(canonicalEl);
}
canonicalEl.setAttribute('href', canonical ?? `${SITE_URL}${window.location.pathname}`);
// Remove previous page-level schemas, inject new ones
document.querySelectorAll('script[data-seo="page"]').forEach(el => el.remove());
if (schemaStr) {
const schemas = Array.isArray(JSON.parse(schemaStr)) ? JSON.parse(schemaStr) : [JSON.parse(schemaStr)];
schemas.forEach((s: Schema) => {
const script = document.createElement('script');
script.setAttribute('type', 'application/ld+json');
script.setAttribute('data-seo', 'page');
script.textContent = JSON.stringify(s);
document.head.appendChild(script);
});
}
return () => {
document.querySelectorAll('script[data-seo="page"]').forEach(el => el.remove());
};
}, [title, description, canonical, ogImage, ogType, schemaStr]);
return null;
};
export default SEO;