forked from crowetic/commerce
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 type { VFC } from 'react'
|
||||||
import NextHead from 'next/head'
|
import { SEO } from '@components/common'
|
||||||
import { DefaultSeo } from 'next-seo'
|
|
||||||
import config from '@config/seo.json'
|
|
||||||
|
|
||||||
const Head: FC = () => {
|
const Head: VFC = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<SEO>
|
||||||
<DefaultSeo {...config} />
|
<meta
|
||||||
<NextHead>
|
key="viewport"
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1"
|
||||||
|
/>
|
||||||
<link rel="manifest" href="/site.webmanifest" key="site-manifest" />
|
<link rel="manifest" href="/site.webmanifest" key="site-manifest" />
|
||||||
</NextHead>
|
</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 UserNav } from './UserNav'
|
||||||
export { default as Head } from './Head'
|
export { default as Head } from './Head'
|
||||||
export { default as I18nWidget } from './I18nWidget'
|
export { default as I18nWidget } from './I18nWidget'
|
||||||
|
export { default as SEO } from './SEO'
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import cn from 'clsx'
|
import cn from 'clsx'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import { NextSeo } from 'next-seo'
|
|
||||||
import s from './ProductView.module.css'
|
import s from './ProductView.module.css'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import type { Product } from '@commerce/types/product'
|
import type { Product } from '@commerce/types/product'
|
||||||
@ -8,6 +7,7 @@ import usePrice from '@framework/product/use-price'
|
|||||||
import { WishlistButton } from '@components/wishlist'
|
import { WishlistButton } from '@components/wishlist'
|
||||||
import { ProductSlider, ProductCard } from '@components/product'
|
import { ProductSlider, ProductCard } from '@components/product'
|
||||||
import { Container, Text } from '@components/ui'
|
import { Container, Text } from '@components/ui'
|
||||||
|
import { SEO } from '@components/common'
|
||||||
import ProductSidebar from '../ProductSidebar'
|
import ProductSidebar from '../ProductSidebar'
|
||||||
import ProductTag from '../ProductTag'
|
import ProductTag from '../ProductTag'
|
||||||
interface ProductViewProps {
|
interface ProductViewProps {
|
||||||
@ -89,7 +89,7 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Container>
|
</Container>
|
||||||
<NextSeo
|
<SEO
|
||||||
title={product.name}
|
title={product.name}
|
||||||
description={product.description}
|
description={product.description}
|
||||||
openGraph={{
|
openGraph={{
|
||||||
@ -99,8 +99,8 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
|||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: product.images[0]?.url!,
|
url: product.images[0]?.url!,
|
||||||
width: 800,
|
width: '800',
|
||||||
height: 600,
|
height: '600',
|
||||||
alt: product.name,
|
alt: product.name,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -6,14 +6,13 @@
|
|||||||
"title": "ACME Storefront | Powered by Next.js Commerce",
|
"title": "ACME Storefront | Powered by Next.js Commerce",
|
||||||
"description": "Next.js Commerce - https://www.nextjs.org/commerce",
|
"description": "Next.js Commerce - https://www.nextjs.org/commerce",
|
||||||
"type": "website",
|
"type": "website",
|
||||||
"locale": "en_IE",
|
|
||||||
"url": "https://nextjs.org/commerce",
|
"url": "https://nextjs.org/commerce",
|
||||||
"site_name": "Next.js Commerce",
|
"site_name": "Next.js Commerce",
|
||||||
"images": [
|
"images": [
|
||||||
{
|
{
|
||||||
"url": "/card.png",
|
"url": "/card.png",
|
||||||
"width": 800,
|
"width": "800",
|
||||||
"height": 600,
|
"height": "600",
|
||||||
"alt": "Next.js Commerce"
|
"alt": "Next.js Commerce"
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -34,7 +34,6 @@
|
|||||||
"lodash.random": "^3.2.0",
|
"lodash.random": "^3.2.0",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"next": "^12.0.8",
|
"next": "^12.0.8",
|
||||||
"next-seo": "^4.28.1",
|
|
||||||
"next-themes": "^0.0.15",
|
"next-themes": "^0.0.15",
|
||||||
"postcss": "^8.3.5",
|
"postcss": "^8.3.5",
|
||||||
"postcss-nesting": "^8.0.1",
|
"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"
|
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
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:
|
next-themes@^0.0.15:
|
||||||
version "0.0.15"
|
version "0.0.15"
|
||||||
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.15.tgz#ab0cee69cd763b77d41211f631e108beab39bf7d"
|
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.15.tgz#ab0cee69cd763b77d41211f631e108beab39bf7d"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user