UNPKG

@ai-growth/nextjs

Version:

Seamlessly integrate Sanity CMS with Next.js applications for automated blog routing and rendering

122 lines (121 loc) 5.24 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; /** * @fileoverview SEO Head Component * * This component provides comprehensive SEO functionality including * meta tags, Open Graph, Twitter Cards, and JSON-LD structured data. */ import React from 'react'; import Head from 'next/head'; import { processSEO, sanitizeSEO } from '../utils/seo'; import { escapeJsonLD } from '../utils/seo/structured-data'; /** * SEO Head component that generates comprehensive SEO metadata * * @example * ```tsx * <SEOHead * content={post} * siteSettings={siteSettings} * template="article" * url="https://example.com/blog/post-slug" * /> * ``` */ export const SEOHead = (props) => { // Process SEO data and generate results const seoResult = processSEO(props); // Sanitize SEO data for optimal display const sanitizedSEO = sanitizeSEO({ title: seoResult.title, description: seoResult.description, canonical: seoResult.canonical, keywords: props.seo?.keywords || [], noIndex: props.seo?.noIndex || false, noFollow: props.seo?.noFollow || false }); return (_jsxs(Head, { children: [renderMetaTags(seoResult.metaTags), _jsx("title", { children: sanitizedSEO.title }), sanitizedSEO.canonical && (_jsx("link", { rel: "canonical", href: sanitizedSEO.canonical })), props.seo?.alternateLanguages?.map((lang, index) => (_jsx("link", { rel: "alternate", hrefLang: `${lang.language}${lang.region ? `-${lang.region}` : ''}`, href: lang.url }, index))), seoResult.structuredData.map((data, index) => (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: escapeJsonLD(data) } }, index))), _jsx("link", { rel: "dns-prefetch", href: "//fonts.googleapis.com" }), _jsx("link", { rel: "dns-prefetch", href: "//cdn.sanity.io" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" })] })); }; /** * Render meta tags from the generated list * * @param metaTags - Array of meta tags to render * @returns JSX elements for meta tags */ function renderMetaTags(metaTags) { return metaTags.map((tag, index) => { const key = `meta-${index}`; // Handle charset meta tag if (tag.charset) { return _jsx("meta", { charSet: tag.charset }, key); } // Handle http-equiv meta tag if (tag.httpEquiv) { return _jsx("meta", { httpEquiv: tag.httpEquiv, content: tag.content }, key); } // Handle property meta tag (Open Graph) if (tag.property) { return _jsx("meta", { property: tag.property, content: tag.content }, key); } // Handle name meta tag (standard and Twitter) if (tag.name) { return _jsx("meta", { name: tag.name, content: tag.content }, key); } // Fallback for any other meta tag type return _jsx("meta", { content: tag.content }, key); }); } /** * SEO Head component with error boundary */ export const SafeSEOHead = (props) => { try { return _jsx(SEOHead, { ...props }); } catch (error) { console.error('SEO Head error:', error); // Fallback SEO with basic meta tags return (_jsxs(Head, { children: [_jsx("title", { children: props.content?.title || props.siteSettings?.title || 'AI Growth' }), _jsx("meta", { name: "description", content: props.content?.excerpt || props.siteSettings?.description || '' }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("meta", { charSet: "utf-8" })] })); } }; /** * Hook for accessing SEO data without rendering * * @param props - SEO Head props * @returns Processed SEO result */ export function useSEOData(props) { return React.useMemo(() => { try { return processSEO(props); } catch (error) { console.error('SEO processing error:', error); return { metaTags: [], structuredData: [], title: props.content?.title || 'AI Growth', description: props.content?.excerpt || '', canonical: props.url || '', image: undefined }; } }, [props]); } /** * Utility component for JSON-LD structured data only * * @param props - Structured data props */ export const StructuredDataOnly = ({ data }) => (_jsx(Head, { children: data.map((item, index) => (_jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: { __html: escapeJsonLD(item) } }, index))) })); /** * Utility component for basic meta tags only * * @param props - Basic meta props */ export const BasicSEO = ({ title, description, canonical, noIndex }) => (_jsxs(Head, { children: [_jsx("title", { children: title }), description && _jsx("meta", { name: "description", content: description }), canonical && _jsx("link", { rel: "canonical", href: canonical }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("meta", { charSet: "utf-8" }), noIndex && _jsx("meta", { name: "robots", content: "noindex, nofollow" })] })); export default SEOHead;