mirror of
https://github.com/vercel/commerce.git
synced 2025-03-14 14:42:31 +00:00
feat: replace next-seo with custom solution (#660)
* replace next-seo with custom solution * Updated check Co-authored-by: LFades <luis@vercel.com>
This commit is contained in:
parent
65c9d39ae6
commit
db170558d5
@ -1,17 +1,16 @@
|
||||
import { FC } from 'react'
|
||||
import NextHead from 'next/head'
|
||||
import { DefaultSeo } from 'next-seo'
|
||||
import config from '@config/seo.json'
|
||||
import type { VFC } from 'react'
|
||||
import { SEO } from '@components/common'
|
||||
|
||||
const Head: FC = () => {
|
||||
const Head: VFC = () => {
|
||||
return (
|
||||
<>
|
||||
<DefaultSeo {...config} />
|
||||
<NextHead>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="manifest" href="/site.webmanifest" key="site-manifest" />
|
||||
</NextHead>
|
||||
</>
|
||||
<SEO>
|
||||
<meta
|
||||
key="viewport"
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1"
|
||||
/>
|
||||
<link rel="manifest" href="/site.webmanifest" key="site-manifest" />
|
||||
</SEO>
|
||||
)
|
||||
}
|
||||
|
||||
|
157
site/components/common/SEO/SEO.tsx
Normal file
157
site/components/common/SEO/SEO.tsx
Normal file
@ -0,0 +1,157 @@
|
||||
import Head from 'next/head'
|
||||
import { FC, Fragment, ReactNode } from 'react'
|
||||
import config from '@config/seo_meta.json'
|
||||
|
||||
const storeUrl =
|
||||
process.env.NEXT_PUBLIC_STORE_URL || process.env.NEXT_PUBLIC_VERCEL_URL
|
||||
const storeBaseUrl = storeUrl ? `https://${storeUrl}` : null
|
||||
|
||||
interface OgImage {
|
||||
url?: string
|
||||
width?: string
|
||||
height?: string
|
||||
alt?: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
description?: string
|
||||
robots?: string
|
||||
openGraph?: {
|
||||
title?: string
|
||||
type?: string
|
||||
locale?: string
|
||||
description?: string
|
||||
site_name?: string
|
||||
url?: string
|
||||
images?: OgImage[]
|
||||
}
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
const ogImage = ({ url, width, height, alt }: OgImage, index: number) => {
|
||||
// generate full URL for OG image url with store base URL
|
||||
const imgUrl = storeBaseUrl ? new URL(url!, storeBaseUrl).toString() : url
|
||||
return (
|
||||
<Fragment key={`og:image:${index}`}>
|
||||
<meta
|
||||
key={`og:image:url:${index}`}
|
||||
property="og:image"
|
||||
content={imgUrl}
|
||||
/>
|
||||
<meta
|
||||
key={`og:image:width:${index}`}
|
||||
property="og:image:width"
|
||||
content={width}
|
||||
/>
|
||||
<meta
|
||||
key={`og:image:height:${index}`}
|
||||
property="og:image:height"
|
||||
content={height}
|
||||
/>
|
||||
<meta
|
||||
key={`og:image:alt:${index}`}
|
||||
property="og:image:alt"
|
||||
content={alt}
|
||||
/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
const SEO: FC<Props> = ({
|
||||
title,
|
||||
description,
|
||||
openGraph,
|
||||
robots,
|
||||
children,
|
||||
}) => {
|
||||
/**
|
||||
* @see https://nextjs.org/docs/api-reference/next/head
|
||||
*
|
||||
* meta or any other elements need to be contained as direct children of the Head element,
|
||||
* or wrapped into maximum one level of <React.Fragment> or arrays
|
||||
* otherwise the tags won't be correctly picked up on client-side navigations.
|
||||
*
|
||||
* The `key` property makes the tag is only rendered once,
|
||||
*/
|
||||
return (
|
||||
<Head>
|
||||
<title key="title">
|
||||
{title ? `${config.titleTemplate.replace(/%s/g, title)}` : config.title}
|
||||
</title>
|
||||
<meta
|
||||
key="description"
|
||||
name="description"
|
||||
content={description || config.description}
|
||||
/>
|
||||
<meta
|
||||
key="og:type"
|
||||
property="og:type"
|
||||
content={openGraph?.type ?? config.openGraph.type}
|
||||
/>
|
||||
<meta
|
||||
key="og:title"
|
||||
property="og:title"
|
||||
content={
|
||||
openGraph?.title ?? config.openGraph.title ?? title ?? config.title
|
||||
}
|
||||
/>
|
||||
<meta
|
||||
key="og:description"
|
||||
property="og:description"
|
||||
content={
|
||||
openGraph?.description ??
|
||||
config.openGraph.description ??
|
||||
description ??
|
||||
config.description
|
||||
}
|
||||
/>
|
||||
<meta
|
||||
key="og:site_name"
|
||||
property="og:site_name"
|
||||
content={openGraph?.site_name ?? config.openGraph.site_name}
|
||||
/>
|
||||
<meta
|
||||
key="og:url"
|
||||
property="og:url"
|
||||
content={openGraph?.url ?? config.openGraph.url}
|
||||
></meta>
|
||||
{openGraph?.locale && (
|
||||
<meta key="og:locale" property="og:locale" content={openGraph.locale} />
|
||||
)}
|
||||
{openGraph?.images?.length
|
||||
? openGraph.images.map((img, index) => ogImage(img, index))
|
||||
: ogImage(config.openGraph.images[0], 0)}
|
||||
{config.twitter.cardType && (
|
||||
<meta
|
||||
key="twitter:card"
|
||||
name="twitter:card"
|
||||
content={config.twitter.cardType}
|
||||
/>
|
||||
)}
|
||||
{config.twitter.site && (
|
||||
<meta
|
||||
key="twitter:site"
|
||||
name="twitter:site"
|
||||
content={config.twitter.site}
|
||||
/>
|
||||
)}
|
||||
{config.twitter.handle && (
|
||||
<meta
|
||||
key="twitter:creator"
|
||||
name="twitter:creator"
|
||||
content={config.twitter.handle}
|
||||
/>
|
||||
)}
|
||||
<meta key="robots" name="robots" content={robots ?? 'index,follow'} />
|
||||
<meta
|
||||
key="googlebot"
|
||||
name="googlebot"
|
||||
content={robots ?? 'index,follow'}
|
||||
></meta>
|
||||
{children}
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
|
||||
export default SEO
|
1
site/components/common/SEO/index.ts
Normal file
1
site/components/common/SEO/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './SEO'
|
@ -7,3 +7,4 @@ export { default as Searchbar } from './Searchbar'
|
||||
export { default as UserNav } from './UserNav'
|
||||
export { default as Head } from './Head'
|
||||
export { default as I18nWidget } from './I18nWidget'
|
||||
export { default as SEO } from './SEO'
|
||||
|
@ -1,6 +1,5 @@
|
||||
import cn from 'clsx'
|
||||
import Image from 'next/image'
|
||||
import { NextSeo } from 'next-seo'
|
||||
import s from './ProductView.module.css'
|
||||
import { FC } from 'react'
|
||||
import type { Product } from '@commerce/types/product'
|
||||
@ -8,6 +7,7 @@ import usePrice from '@framework/product/use-price'
|
||||
import { WishlistButton } from '@components/wishlist'
|
||||
import { ProductSlider, ProductCard } from '@components/product'
|
||||
import { Container, Text } from '@components/ui'
|
||||
import { SEO } from '@components/common'
|
||||
import ProductSidebar from '../ProductSidebar'
|
||||
import ProductTag from '../ProductTag'
|
||||
interface ProductViewProps {
|
||||
@ -89,7 +89,7 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
||||
</div>
|
||||
</section>
|
||||
</Container>
|
||||
<NextSeo
|
||||
<SEO
|
||||
title={product.name}
|
||||
description={product.description}
|
||||
openGraph={{
|
||||
@ -99,8 +99,8 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
||||
images: [
|
||||
{
|
||||
url: product.images[0]?.url!,
|
||||
width: 800,
|
||||
height: 600,
|
||||
width: '800',
|
||||
height: '600',
|
||||
alt: product.name,
|
||||
},
|
||||
],
|
||||
|
@ -6,14 +6,13 @@
|
||||
"title": "ACME Storefront | Powered by Next.js Commerce",
|
||||
"description": "Next.js Commerce - https://www.nextjs.org/commerce",
|
||||
"type": "website",
|
||||
"locale": "en_IE",
|
||||
"url": "https://nextjs.org/commerce",
|
||||
"site_name": "Next.js Commerce",
|
||||
"images": [
|
||||
{
|
||||
"url": "/card.png",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"width": "800",
|
||||
"height": "600",
|
||||
"alt": "Next.js Commerce"
|
||||
}
|
||||
]
|
@ -34,7 +34,6 @@
|
||||
"lodash.random": "^3.2.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"next": "^12.0.8",
|
||||
"next-seo": "^4.28.1",
|
||||
"next-themes": "^0.0.15",
|
||||
"postcss": "^8.3.5",
|
||||
"postcss-nesting": "^8.0.1",
|
||||
|
@ -4763,11 +4763,6 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
next-seo@^4.28.1:
|
||||
version "4.29.0"
|
||||
resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.29.0.tgz#d281e95ba47914117cc99e9e468599f0547d9b9b"
|
||||
integrity sha512-xmwzcz4uHaYJ8glbuhs6FSBQ7z3irmdPYdJJ5saWm72Uy3o+mPKGaPCXQetTCE6/xxVnpoDV4yFtFlEjUcljSg==
|
||||
|
||||
next-themes@^0.0.15:
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.15.tgz#ab0cee69cd763b77d41211f631e108beab39bf7d"
|
||||
|
Loading…
x
Reference in New Issue
Block a user