feat(core): add canonical URLs and hreflang alternates for SEO#2856
feat(core): add canonical URLs and hreflang alternates for SEO#2856
Conversation
🦋 Changeset detectedLatest commit: eb1b56e The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| params: Promise<{ locale: string }>; | ||
| } | ||
|
|
||
| export async function generateMetadata({ params }: Props): Promise<Metadata> { |
There was a problem hiding this comment.
Were we not setting any of the metadata on this page previously?
There was a problem hiding this comment.
Ah I guess we are setting default metadata in the layout file and this just adds/overrwrides any additional fields?
|
Confirmed seeing the generated canonical and alternate urls locally |
core/app/[locale]/layout.tsx
Outdated
| const { pageTitle, metaDescription, metaKeywords } = data.site.settings?.seo || {}; | ||
|
|
||
| return { | ||
| metadataBase: new URL(buildConfig.get('urls').vanityUrl), |
There was a problem hiding this comment.
This will only be the default channel, but for domain-based localization I think this will break, right? For example if the merchant configured it to be (channel id/url) 123/us.example.com + 456/es.example.com + etc...
There was a problem hiding this comment.
Looking into this, any ideas?
There was a problem hiding this comment.
You can query for the vanityUrl on each request 🤔
core/lib/seo/canonical.ts
Outdated
| export function getMetadataAlternates(options: CanonicalUrlOptions) { | ||
| const { path, locale, includeAlternates = true } = options; | ||
|
|
||
| const vanityUrl = buildConfig.get('urls').vanityUrl.replace(/\/$/, ''); |
668d0c2 to
d9c5b72
Compare
Add canonical meta tags to gift certificates, contact, public wishlist, and compare pages. Update changeset with migration steps for all pages. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add JSDoc param and returns type annotations - Replace for...of loop with reduce for array iteration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
d9c5b72 to
2261682
Compare
chanceaclark
left a comment
There was a problem hiding this comment.
Overall the approach is way better, just some comments around simplifying the code with the URL object.
b711e89 to
eb1b56e
Compare
What/Why?
Add canonical URLs and hreflang alternates for SEO. Pages now set
alternates.canonicalandalternates.languagesingenerateMetadatavia the newgetMetadataAlternateshelper incore/lib/seo/canonical.ts. The default locale uses no path prefix; other locales use/{locale}/path. The root locale layout setsmetadataBaseto the configured vanity URL so canonical URLs resolve correctly.Testing
Migration steps
Step 1: Root layout metadata base
Set
metadataBasein the root locale layout so canonical URLs resolve to your vanity URL.Update
core/app/[locale]/layout.tsx:import { Providers } from '~/app/providers'; + import { buildConfig } from '~/build-config/reader'; import { client } from '~/client'; ... return { + metadataBase: new URL(buildConfig.get('urls').vanityUrl), title: {Step 2: GraphQL fragment updates
Add the
pathfield to brand, blog post, and product queries so metadata can build canonical URLs.Update
core/app/[locale]/(default)/(faceted)/brand/[slug]/page-data.ts:site { brand(entityId: $entityId) { name + path seo {Update
core/app/[locale]/(default)/blog/[blogId]/page-data.ts:author htmlBody name + path publishedDate {Update
core/app/[locale]/(default)/product/[slug]/page-data.ts(in the metadata query):site { product(entityId: $entityId) { name + path defaultImage {Step 3: Page metadata alternates
Add the
getMetadataAlternatesimport and setalternatesingenerateMetadatafor each page. Ensurecore/lib/seo/canonical.tsexists (it is included in this release).Update
core/app/[locale]/(default)/page.tsx(home):For entity pages (product, category, brand, blog, blog post, webpage), add the import and include
alternatesin the existinggenerateMetadatareturn value using the entitypath(or breadcrumb-derived path for category and webpage). Example for a brand page:Step 4: Gift certificates pages
Update
core/app/[locale]/(default)/gift-certificates/page.tsx:Update
core/app/[locale]/(default)/gift-certificates/balance/page.tsx:Add
generateMetadatatocore/app/[locale]/(default)/gift-certificates/purchase/page.tsx:Step 5: Contact page
Update
core/app/[locale]/(default)/webpages/[id]/contact/page.tsx:Step 6: Public wishlist page
Update
core/app/[locale]/(default)/wishlist/[token]/page.tsx:Step 7: Compare page
Update
core/app/[locale]/(default)/compare/page.tsx: