UNPKG

metaforge-seo

Version:

Lightweight SEO metadata manager for React + Vite apps.

136 lines 3.25 kB
import React, { useEffect } from 'react'; export const SEO = props => { const { title, description, keywords, canonicalUrl, lang = 'en', ogTitle, ogDescription, ogImage, ogUrl, twitterCard = 'summary_large_image', twitterSite, twitterCreator, noindex, nofollow, jsonLd } = props; useEffect(() => { document.documentElement.lang = lang; const tags = [title && { tag: 'title', props: { innerText: title } }, description && { tag: 'meta', props: { name: 'description', content: description } }, keywords && { tag: 'meta', props: { name: 'keywords', content: keywords } }, canonicalUrl && { tag: 'link', props: { rel: 'canonical', href: canonicalUrl } }, ogTitle && { tag: 'meta', props: { property: 'og:title', content: ogTitle || title || '' } }, ogDescription && { tag: 'meta', props: { property: 'og:description', content: ogDescription || description || '' } }, ogImage && { tag: 'meta', props: { property: 'og:image', content: ogImage } }, ogUrl && { tag: 'meta', props: { property: 'og:url', content: ogUrl } }, twitterCard && { tag: 'meta', props: { name: 'twitter:card', content: twitterCard } }, twitterSite && { tag: 'meta', props: { name: 'twitter:site', content: twitterSite } }, twitterCreator && { tag: 'meta', props: { name: 'twitter:creator', content: twitterCreator } }, (noindex || nofollow) && { tag: 'meta', props: { name: 'robots', content: `${noindex ? 'noindex' : 'index'}, ${nofollow ? 'nofollow' : 'follow'}` } }].filter(Boolean); tags.forEach(({ tag, props }) => { const el = document.createElement(tag); Object.entries(props).forEach(([key, value]) => { if (key === 'innerText') { el.innerText = value; } else { el.setAttribute(key, value); } }); document.head.appendChild(el); }); if (jsonLd) { const script = document.createElement('script'); script.type = 'application/ld+json'; script.innerText = JSON.stringify({ '@context': jsonLd.context, '@type': jsonLd.type, ...jsonLd.data }); document.head.appendChild(script); } return () => { tags.forEach(({ tag, props }) => { const selector = props.name ? `${tag}[name='${props.name}']` : props.property ? `${tag}[property='${props.property}']` : props.rel ? `${tag}[rel='${props.rel}']` : tag; const elements = document.head.querySelectorAll(selector); elements.forEach(el => el.remove()); }); if (jsonLd) { const scripts = document.head.querySelectorAll("script[type='application/ld+json']"); scripts.forEach(el => el.remove()); } }; }, [props]); return null; }; SEO.displayName = 'SEO'; export default SEO;