Skip to content

Commit db17055

Browse files
feat: replace next-seo with custom solution (vercel#660)
* replace next-seo with custom solution * Updated check Co-authored-by: LFades <[email protected]>
1 parent 65c9d39 commit db17055

File tree

8 files changed

+176
-25
lines changed

8 files changed

+176
-25
lines changed

site/components/common/Head/Head.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
import { FC } from 'react'
2-
import NextHead from 'next/head'
3-
import { DefaultSeo } from 'next-seo'
4-
import config from '@config/seo.json'
1+
import type { VFC } from 'react'
2+
import { SEO } from '@components/common'
53

6-
const Head: FC = () => {
4+
const Head: VFC = () => {
75
return (
8-
<>
9-
<DefaultSeo {...config} />
10-
<NextHead>
11-
<meta name="viewport" content="width=device-width, initial-scale=1" />
12-
<link rel="manifest" href="/site.webmanifest" key="site-manifest" />
13-
</NextHead>
14-
</>
6+
<SEO>
7+
<meta
8+
key="viewport"
9+
name="viewport"
10+
content="width=device-width, initial-scale=1"
11+
/>
12+
<link rel="manifest" href="/site.webmanifest" key="site-manifest" />
13+
</SEO>
1514
)
1615
}
1716

site/components/common/SEO/SEO.tsx

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import Head from 'next/head'
2+
import { FC, Fragment, ReactNode } from 'react'
3+
import config from '@config/seo_meta.json'
4+
5+
const storeUrl =
6+
process.env.NEXT_PUBLIC_STORE_URL || process.env.NEXT_PUBLIC_VERCEL_URL
7+
const storeBaseUrl = storeUrl ? `https://${storeUrl}` : null
8+
9+
interface OgImage {
10+
url?: string
11+
width?: string
12+
height?: string
13+
alt?: string
14+
}
15+
16+
interface Props {
17+
title?: string
18+
description?: string
19+
robots?: string
20+
openGraph?: {
21+
title?: string
22+
type?: string
23+
locale?: string
24+
description?: string
25+
site_name?: string
26+
url?: string
27+
images?: OgImage[]
28+
}
29+
children?: ReactNode
30+
}
31+
32+
const ogImage = ({ url, width, height, alt }: OgImage, index: number) => {
33+
// generate full URL for OG image url with store base URL
34+
const imgUrl = storeBaseUrl ? new URL(url!, storeBaseUrl).toString() : url
35+
return (
36+
<Fragment key={`og:image:${index}`}>
37+
<meta
38+
key={`og:image:url:${index}`}
39+
property="og:image"
40+
content={imgUrl}
41+
/>
42+
<meta
43+
key={`og:image:width:${index}`}
44+
property="og:image:width"
45+
content={width}
46+
/>
47+
<meta
48+
key={`og:image:height:${index}`}
49+
property="og:image:height"
50+
content={height}
51+
/>
52+
<meta
53+
key={`og:image:alt:${index}`}
54+
property="og:image:alt"
55+
content={alt}
56+
/>
57+
</Fragment>
58+
)
59+
}
60+
61+
const SEO: FC<Props> = ({
62+
title,
63+
description,
64+
openGraph,
65+
robots,
66+
children,
67+
}) => {
68+
/**
69+
* @see https://nextjs.org/docs/api-reference/next/head
70+
*
71+
* meta or any other elements need to be contained as direct children of the Head element,
72+
* or wrapped into maximum one level of <React.Fragment> or arrays
73+
* otherwise the tags won't be correctly picked up on client-side navigations.
74+
*
75+
* The `key` property makes the tag is only rendered once,
76+
*/
77+
return (
78+
<Head>
79+
<title key="title">
80+
{title ? `${config.titleTemplate.replace(/%s/g, title)}` : config.title}
81+
</title>
82+
<meta
83+
key="description"
84+
name="description"
85+
content={description || config.description}
86+
/>
87+
<meta
88+
key="og:type"
89+
property="og:type"
90+
content={openGraph?.type ?? config.openGraph.type}
91+
/>
92+
<meta
93+
key="og:title"
94+
property="og:title"
95+
content={
96+
openGraph?.title ?? config.openGraph.title ?? title ?? config.title
97+
}
98+
/>
99+
<meta
100+
key="og:description"
101+
property="og:description"
102+
content={
103+
openGraph?.description ??
104+
config.openGraph.description ??
105+
description ??
106+
config.description
107+
}
108+
/>
109+
<meta
110+
key="og:site_name"
111+
property="og:site_name"
112+
content={openGraph?.site_name ?? config.openGraph.site_name}
113+
/>
114+
<meta
115+
key="og:url"
116+
property="og:url"
117+
content={openGraph?.url ?? config.openGraph.url}
118+
></meta>
119+
{openGraph?.locale && (
120+
<meta key="og:locale" property="og:locale" content={openGraph.locale} />
121+
)}
122+
{openGraph?.images?.length
123+
? openGraph.images.map((img, index) => ogImage(img, index))
124+
: ogImage(config.openGraph.images[0], 0)}
125+
{config.twitter.cardType && (
126+
<meta
127+
key="twitter:card"
128+
name="twitter:card"
129+
content={config.twitter.cardType}
130+
/>
131+
)}
132+
{config.twitter.site && (
133+
<meta
134+
key="twitter:site"
135+
name="twitter:site"
136+
content={config.twitter.site}
137+
/>
138+
)}
139+
{config.twitter.handle && (
140+
<meta
141+
key="twitter:creator"
142+
name="twitter:creator"
143+
content={config.twitter.handle}
144+
/>
145+
)}
146+
<meta key="robots" name="robots" content={robots ?? 'index,follow'} />
147+
<meta
148+
key="googlebot"
149+
name="googlebot"
150+
content={robots ?? 'index,follow'}
151+
></meta>
152+
{children}
153+
</Head>
154+
)
155+
}
156+
157+
export default SEO

site/components/common/SEO/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './SEO'

site/components/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export { default as Searchbar } from './Searchbar'
77
export { default as UserNav } from './UserNav'
88
export { default as Head } from './Head'
99
export { default as I18nWidget } from './I18nWidget'
10+
export { default as SEO } from './SEO'

site/components/product/ProductView/ProductView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import cn from 'clsx'
22
import Image from 'next/image'
3-
import { NextSeo } from 'next-seo'
43
import s from './ProductView.module.css'
54
import { FC } from 'react'
65
import type { Product } from '@commerce/types/product'
76
import usePrice from '@framework/product/use-price'
87
import { WishlistButton } from '@components/wishlist'
98
import { ProductSlider, ProductCard } from '@components/product'
109
import { Container, Text } from '@components/ui'
10+
import { SEO } from '@components/common'
1111
import ProductSidebar from '../ProductSidebar'
1212
import ProductTag from '../ProductTag'
1313
interface ProductViewProps {
@@ -89,7 +89,7 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
8989
</div>
9090
</section>
9191
</Container>
92-
<NextSeo
92+
<SEO
9393
title={product.name}
9494
description={product.description}
9595
openGraph={{
@@ -99,8 +99,8 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
9999
images: [
100100
{
101101
url: product.images[0]?.url!,
102-
width: 800,
103-
height: 600,
102+
width: '800',
103+
height: '600',
104104
alt: product.name,
105105
},
106106
],

site/config/seo.json renamed to site/config/seo_meta.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
"title": "ACME Storefront | Powered by Next.js Commerce",
77
"description": "Next.js Commerce - https://www.nextjs.org/commerce",
88
"type": "website",
9-
"locale": "en_IE",
109
"url": "https://nextjs.org/commerce",
1110
"site_name": "Next.js Commerce",
1211
"images": [
1312
{
1413
"url": "/card.png",
15-
"width": 800,
16-
"height": 600,
14+
"width": "800",
15+
"height": "600",
1716
"alt": "Next.js Commerce"
1817
}
1918
]

site/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
"lodash.random": "^3.2.0",
3535
"lodash.throttle": "^4.1.1",
3636
"next": "^12.0.8",
37-
"next-seo": "^4.28.1",
3837
"next-themes": "^0.0.15",
3938
"postcss": "^8.3.5",
4039
"postcss-nesting": "^8.0.1",

yarn.lock

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4763,11 +4763,6 @@ natural-compare@^1.4.0:
47634763
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
47644764
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
47654765

4766-
next-seo@^4.28.1:
4767-
version "4.29.0"
4768-
resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-4.29.0.tgz#d281e95ba47914117cc99e9e468599f0547d9b9b"
4769-
integrity sha512-xmwzcz4uHaYJ8glbuhs6FSBQ7z3irmdPYdJJ5saWm72Uy3o+mPKGaPCXQetTCE6/xxVnpoDV4yFtFlEjUcljSg==
4770-
47714766
next-themes@^0.0.15:
47724767
version "0.0.15"
47734768
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.0.15.tgz#ab0cee69cd763b77d41211f631e108beab39bf7d"

0 commit comments

Comments
 (0)