Final
This commit is contained in:
84
Pottery-website/components/SEO.tsx
Normal file
84
Pottery-website/components/SEO.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user