refactor: use metadata export for seo
This commit is contained in:
@ -2,6 +2,9 @@ import { Authors, allAuthors } from 'contentlayer/generated'
|
||||
import { Mdx } from '@/components/MDXComponents'
|
||||
import AuthorLayout from '@/layouts/AuthorLayout'
|
||||
import { coreContent } from 'pliny/utils/contentlayer'
|
||||
import { genPageMetadata } from 'app/seo'
|
||||
|
||||
export const metadata = genPageMetadata({ title: 'About' })
|
||||
|
||||
export default function Page() {
|
||||
const author = allAuthors.find((p) => p.slug === 'default') as Authors
|
||||
|
@ -4,6 +4,61 @@ import { sortedBlogPost, coreContent } from 'pliny/utils/contentlayer'
|
||||
import { allBlogs, allAuthors } from 'contentlayer/generated'
|
||||
import type { Authors, Blog } from 'contentlayer/generated'
|
||||
import PostLayout from '@/layouts/PostLayout'
|
||||
import { Metadata } from 'next'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: { slug: string[] }
|
||||
}): Promise<Metadata | undefined> {
|
||||
const slug = params.slug.join('/')
|
||||
const post = allBlogs.find((p) => p.slug === slug)
|
||||
const authorList = post?.authors || ['default']
|
||||
const authorDetails = authorList.map((author) => {
|
||||
const authorResults = allAuthors.find((p) => p.slug === author)
|
||||
return coreContent(authorResults as Authors)
|
||||
})
|
||||
if (!post) {
|
||||
return
|
||||
}
|
||||
|
||||
const publishedAt = new Date(post.date).toISOString()
|
||||
const modifiedAt = new Date(post.lastmod || post.date).toISOString()
|
||||
const authors = authorDetails.map((author) => author.name)
|
||||
let imageList = [siteMetadata.socialBanner]
|
||||
if (post.images) {
|
||||
imageList = typeof post.images === 'string' ? [post.images] : post.images
|
||||
}
|
||||
const ogImages = imageList.map((img) => {
|
||||
return {
|
||||
url: img.includes('http') ? img : siteMetadata.siteUrl + img,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
title: post.title,
|
||||
description: post.summary,
|
||||
openGraph: {
|
||||
title: post.title,
|
||||
description: post.summary,
|
||||
siteName: siteMetadata.title,
|
||||
locale: 'en_US',
|
||||
type: 'article',
|
||||
publishedTime: publishedAt,
|
||||
modifiedTime: modifiedAt,
|
||||
url: './',
|
||||
images: ogImages,
|
||||
authors: authors.length > 0 ? authors : [siteMetadata.author],
|
||||
},
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
title: post.title,
|
||||
description: post.summary,
|
||||
images: imageList,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const generateStaticParams = async () => {
|
||||
const paths = allBlogs.map((p) => ({ slug: p.slug.split('/') }))
|
||||
|
@ -2,9 +2,12 @@ import ListLayout from '@/layouts/ListLayout'
|
||||
import { sortedBlogPost } from 'pliny/utils/contentlayer'
|
||||
import { allBlogs } from 'contentlayer/generated'
|
||||
import type { Blog } from 'contentlayer/generated'
|
||||
import { genPageMetadata } from 'app/seo'
|
||||
|
||||
const POSTS_PER_PAGE = 5
|
||||
|
||||
export const metadata = genPageMetadata({ title: 'Blog' })
|
||||
|
||||
export default function BlogPage() {
|
||||
const posts = sortedBlogPost(allBlogs) as Blog[]
|
||||
const pageNumber = 1
|
||||
|
@ -8,14 +8,61 @@ import SectionContainer from '@/components/SectionContainer'
|
||||
import Footer from '@/components/Footer'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import { ThemeProviders } from './theme-providers'
|
||||
import { Metadata } from 'next'
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
metadataBase: new URL(siteMetadata.siteUrl),
|
||||
title: {
|
||||
default: siteMetadata.title,
|
||||
template: `%s | ${siteMetadata.title}`,
|
||||
},
|
||||
description: siteMetadata.description,
|
||||
openGraph: {
|
||||
title: siteMetadata.title,
|
||||
description: siteMetadata.description,
|
||||
url: './',
|
||||
siteName: siteMetadata.title,
|
||||
images: [siteMetadata.socialBanner],
|
||||
locale: 'en_US',
|
||||
type: 'website',
|
||||
},
|
||||
alternates: {
|
||||
canonical: './',
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
googleBot: {
|
||||
index: true,
|
||||
follow: true,
|
||||
'max-video-preview': -1,
|
||||
'max-image-preview': 'large',
|
||||
'max-snippet': -1,
|
||||
},
|
||||
},
|
||||
twitter: {
|
||||
title: siteMetadata.title,
|
||||
card: 'summary_large_image',
|
||||
images: [siteMetadata.socialBanner],
|
||||
},
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang={siteMetadata.language} className={`${inter.className} scroll-smooth`}>
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/static/favicons/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicons/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicons/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/static/favicons/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/static/favicons/safari-pinned-tab.svg" color="#5bbad5" />
|
||||
<meta name="msapplication-TileColor" content="#000000" />
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#fff" />
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000" />
|
||||
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
|
||||
<body className="bg-white text-black antialiased dark:bg-gray-900 dark:text-white">
|
||||
<ThemeProviders>
|
||||
{/* <Analytics analyticsConfig={siteMetadata.analytics} /> */}
|
||||
|
@ -1,5 +1,8 @@
|
||||
import projectsData from '@/data/projectsData'
|
||||
import Card from '@/components/Card'
|
||||
import { genPageMetadata } from 'app/seo'
|
||||
|
||||
export const metadata = genPageMetadata({ title: 'Projects' })
|
||||
|
||||
export default function Projects() {
|
||||
return (
|
||||
|
28
app/seo.tsx
Normal file
28
app/seo.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { Metadata } from 'next'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
|
||||
interface PageSEOProps {
|
||||
title: string
|
||||
description?: string
|
||||
image?: string
|
||||
}
|
||||
|
||||
export function genPageMetadata({ title, description, image }: PageSEOProps): Metadata {
|
||||
return {
|
||||
title,
|
||||
openGraph: {
|
||||
title: `${title} | ${siteMetadata.title}`,
|
||||
description: description || siteMetadata.description,
|
||||
url: './',
|
||||
siteName: siteMetadata.title,
|
||||
images: image ? [image] : [siteMetadata.socialBanner],
|
||||
locale: 'en_US',
|
||||
type: 'website',
|
||||
},
|
||||
twitter: {
|
||||
title: `${title} | ${siteMetadata.title}`,
|
||||
card: 'summary_large_image',
|
||||
images: image ? [image] : [siteMetadata.socialBanner],
|
||||
},
|
||||
}
|
||||
}
|
@ -2,9 +2,11 @@ import { getAllTags } from 'pliny/utils/contentlayer'
|
||||
import Link from '@/components/Link'
|
||||
// import { PageSEO } from '@/components/SEO'
|
||||
import Tag from '@/components/Tag'
|
||||
import siteMetadata from '@/data/siteMetadata'
|
||||
import { kebabCase } from 'pliny/utils/kebabCase'
|
||||
import { allBlogs } from 'contentlayer/generated'
|
||||
import { genPageMetadata } from 'app/seo'
|
||||
|
||||
export const metadata = genPageMetadata({ title: 'Tags', description: 'Things I blog about' })
|
||||
|
||||
export default async function Page() {
|
||||
const tags = await getAllTags(allBlogs)
|
||||
|
Reference in New Issue
Block a user