Files
ai-wpa/components/seo/SEO.tsx
2025-08-30 18:18:57 +05:30

201 lines
5.1 KiB
TypeScript

/**
* SEO Component System
* Provides comprehensive SEO meta tags and Open Graph support
*/
import Head from 'next/head'
export interface SEOProps {
title?: string
description?: string
canonical?: string
openGraph?: {
type?: 'website' | 'article' | 'profile'
title?: string
description?: string
image?: string
url?: string
siteName?: string
}
twitter?: {
card?: 'summary' | 'summary_large_image' | 'app' | 'player'
site?: string
creator?: string
title?: string
description?: string
image?: string
}
additionalMetaTags?: Array<{
name?: string
property?: string
content: string
}>
additionalLinkTags?: Array<{
rel: string
href: string
type?: string
sizes?: string
}>
noindex?: boolean
nofollow?: boolean
}
const defaultSEOConfig = {
title: 'NextJS Boilerplate',
description:
'A production-ready NextJS boilerplate with TypeScript, Tailwind CSS, and authentication',
openGraph: {
type: 'website' as const,
siteName: 'NextJS Boilerplate',
},
twitter: {
card: 'summary_large_image' as const,
},
}
export function SEO({
title,
description,
canonical,
openGraph,
twitter,
additionalMetaTags = [],
additionalLinkTags = [],
noindex = false,
nofollow = false,
}: SEOProps) {
const seo = {
title: title || defaultSEOConfig.title,
description: description || defaultSEOConfig.description,
openGraph: {
...defaultSEOConfig.openGraph,
...openGraph,
},
twitter: {
...defaultSEOConfig.twitter,
...twitter,
},
}
// Create robots meta content
const robotsContent = []
if (noindex) robotsContent.push('noindex')
if (nofollow) robotsContent.push('nofollow')
if (robotsContent.length === 0) robotsContent.push('index', 'follow')
return (
<Head>
{/* Basic Meta Tags */}
<title>{seo.title}</title>
<meta name="description" content={seo.description} />
<meta name="robots" content={robotsContent.join(', ')} />
{/* Canonical URL */}
{canonical && <link rel="canonical" href={canonical} />}
{/* Open Graph */}
<meta property="og:type" content={seo.openGraph.type} />
<meta property="og:title" content={seo.openGraph.title || seo.title} />
<meta property="og:description" content={seo.openGraph.description || seo.description} />
{seo.openGraph.image && <meta property="og:image" content={seo.openGraph.image} />}
{seo.openGraph.url && <meta property="og:url" content={seo.openGraph.url} />}
{seo.openGraph.siteName && <meta property="og:site_name" content={seo.openGraph.siteName} />}
{/* Twitter Card */}
<meta name="twitter:card" content={seo.twitter.card} />
{seo.twitter.site && <meta name="twitter:site" content={seo.twitter.site} />}
{seo.twitter.creator && <meta name="twitter:creator" content={seo.twitter.creator} />}
<meta name="twitter:title" content={seo.twitter.title || seo.title} />
<meta name="twitter:description" content={seo.twitter.description || seo.description} />
{seo.twitter.image && <meta name="twitter:image" content={seo.twitter.image} />}
{/* Additional Meta Tags */}
{additionalMetaTags.map((tag, index) => (
<meta
key={index}
{...(tag.name ? { name: tag.name } : { property: tag.property })}
content={tag.content}
/>
))}
{/* Additional Link Tags */}
{additionalLinkTags.map((tag, index) => (
<link key={index} {...tag} />
))}
</Head>
)
}
/**
* Article SEO component for topic posts
*/
interface ArticleSEOProps extends Omit<SEOProps, 'openGraph'> {
article: {
publishedTime?: string
modifiedTime?: string
author?: string
section?: string
tags?: string[]
}
openGraph?: SEOProps['openGraph']
}
export function ArticleSEO({ article, openGraph, ...props }: ArticleSEOProps) {
const articleOpenGraph = {
...openGraph,
type: 'article' as const,
}
const additionalMetaTags = [
...(props.additionalMetaTags || []),
...(article.publishedTime
? [
{
property: 'article:published_time',
content: article.publishedTime,
},
]
: []),
...(article.modifiedTime
? [
{
property: 'article:modified_time',
content: article.modifiedTime,
},
]
: []),
...(article.author
? [
{
property: 'article:author',
content: article.author,
},
]
: []),
...(article.section
? [
{
property: 'article:section',
content: article.section,
},
]
: []),
...(article.tags
? article.tags.map((tag) => ({
property: 'article:tag',
content: tag,
}))
: []),
]
return <SEO {...props} openGraph={articleOpenGraph} additionalMetaTags={additionalMetaTags} />
}
/**
* Hook to generate SEO-friendly URLs
*/
export function useSEOUrl(baseUrl: string, path?: string) {
const cleanPath = path?.startsWith('/') ? path : `/${path || ''}`
return `${baseUrl}${cleanPath}`
}