tailwind-nextjs-blog/components/SEO.tsx

209 lines
5.0 KiB
TypeScript
Raw Permalink Normal View History

2022-10-17 23:37:01 +08:00
import Head from 'next/head';
import { useRouter } from 'next/router';
import siteMetadata from '@/data/siteMetadata';
import { AuthorFrontMatter } from 'types/AuthorFrontMatter';
import { PostFrontMatter } from 'types/PostFrontMatter';
2022-07-17 21:40:41 +08:00
interface CommonSEOProps {
2022-10-17 23:37:01 +08:00
title: string;
description: string;
ogType: string;
ogImage:
| string
| {
2022-10-17 23:37:01 +08:00
'@type': string;
url: string;
}[];
twImage: string;
canonicalUrl?: string;
}
const CommonSEO = ({
title,
description,
ogType,
ogImage,
twImage,
canonicalUrl,
}: CommonSEOProps) => {
2022-10-17 23:37:01 +08:00
const router = useRouter();
2022-07-17 21:40:41 +08:00
return (
<Head>
<title>{title}</title>
<meta name="robots" content="follow, index" />
<meta name="description" content={description} />
2022-10-17 23:37:01 +08:00
<meta
property="og:url"
content={`${siteMetadata.siteUrl}${router.asPath}`}
/>
2022-07-17 21:40:41 +08:00
<meta property="og:type" content={ogType} />
<meta property="og:site_name" content={siteMetadata.title} />
<meta property="og:description" content={description} />
<meta property="og:title" content={title} />
{Array.isArray(ogImage) ? (
2022-10-17 23:37:01 +08:00
ogImage.map(({ url }) => (
<meta property="og:image" content={url} key={url} />
))
2022-07-17 21:40:41 +08:00
) : (
<meta property="og:image" content={ogImage} key={ogImage} />
)}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content={siteMetadata.twitter} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={twImage} />
<link
rel="canonical"
2022-10-17 23:37:01 +08:00
href={
canonicalUrl
? canonicalUrl
: `${siteMetadata.siteUrl}${router.asPath}`
}
2022-07-17 21:40:41 +08:00
/>
</Head>
2022-10-17 23:37:01 +08:00
);
};
2022-07-17 21:40:41 +08:00
interface PageSEOProps {
2022-10-17 23:37:01 +08:00
title: string;
description: string;
}
export const PageSEO = ({ title, description }: PageSEOProps) => {
2022-10-17 23:37:01 +08:00
const ogImageUrl = siteMetadata.siteUrl + siteMetadata.socialBanner;
const twImageUrl = siteMetadata.siteUrl + siteMetadata.socialBanner;
2022-07-17 21:40:41 +08:00
return (
<CommonSEO
title={title}
description={description}
ogType="website"
ogImage={ogImageUrl}
twImage={twImageUrl}
/>
2022-10-17 23:37:01 +08:00
);
};
2022-07-17 21:40:41 +08:00
export const TagSEO = ({ title, description }: PageSEOProps) => {
2022-10-17 23:37:01 +08:00
const ogImageUrl = siteMetadata.siteUrl + siteMetadata.socialBanner;
const twImageUrl = siteMetadata.siteUrl + siteMetadata.socialBanner;
const router = useRouter();
2022-07-17 21:40:41 +08:00
return (
<>
<CommonSEO
title={title}
description={description}
ogType="website"
ogImage={ogImageUrl}
twImage={twImageUrl}
/>
<Head>
<link
rel="alternate"
type="application/rss+xml"
title={`${description} - RSS feed`}
href={`${siteMetadata.siteUrl}${router.asPath}/feed.xml`}
/>
</Head>
</>
2022-10-17 23:37:01 +08:00
);
};
2022-07-17 21:40:41 +08:00
interface BlogSeoProps extends PostFrontMatter {
2022-10-17 23:37:01 +08:00
authorDetails?: AuthorFrontMatter[];
url: string;
}
2022-07-17 21:40:41 +08:00
export const BlogSEO = ({
authorDetails,
title,
summary,
date,
lastmod,
url,
images = [],
canonicalUrl,
}: BlogSeoProps) => {
2022-10-17 23:37:01 +08:00
const publishedAt = new Date(date).toISOString();
const modifiedAt = new Date(lastmod || date).toISOString();
const imagesArr =
2022-07-17 21:40:41 +08:00
images.length === 0
? [siteMetadata.socialBanner]
: typeof images === 'string'
? [images]
2022-10-17 23:37:01 +08:00
: images;
2022-07-17 21:40:41 +08:00
const featuredImages = imagesArr.map((img) => {
return {
'@type': 'ImageObject',
url: `${siteMetadata.siteUrl}${img}`,
2022-10-17 23:37:01 +08:00
};
});
2022-07-17 21:40:41 +08:00
2022-10-17 23:37:01 +08:00
let authorList;
2022-07-17 21:40:41 +08:00
if (authorDetails) {
authorList = authorDetails.map((author) => {
return {
'@type': 'Person',
name: author.name,
2022-10-17 23:37:01 +08:00
};
});
2022-07-17 21:40:41 +08:00
} else {
authorList = {
'@type': 'Person',
name: siteMetadata.author,
2022-10-17 23:37:01 +08:00
};
2022-07-17 21:40:41 +08:00
}
const structuredData = {
'@context': 'https://schema.org',
'@type': 'Article',
mainEntityOfPage: {
'@type': 'WebPage',
'@id': url,
},
headline: title,
image: featuredImages,
datePublished: publishedAt,
dateModified: modifiedAt,
author: authorList,
publisher: {
'@type': 'Organization',
name: siteMetadata.author,
logo: {
'@type': 'ImageObject',
url: `${siteMetadata.siteUrl}${siteMetadata.siteLogo}`,
},
},
description: summary,
2022-10-17 23:37:01 +08:00
};
2022-07-17 21:40:41 +08:00
2022-10-17 23:37:01 +08:00
const twImageUrl = featuredImages[0].url;
2022-07-17 21:40:41 +08:00
return (
<>
<CommonSEO
title={title}
description={summary}
ogType="article"
ogImage={featuredImages}
twImage={twImageUrl}
canonicalUrl={canonicalUrl}
/>
<Head>
2022-10-17 23:37:01 +08:00
{date && (
<meta property="article:published_time" content={publishedAt} />
)}
{lastmod && (
<meta property="article:modified_time" content={modifiedAt} />
)}
2022-07-17 21:40:41 +08:00
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(structuredData, null, 2),
}}
/>
</Head>
</>
2022-10-17 23:37:01 +08:00
);
};