From 56dae8f104ba7a5ae21d805aaa170a815e74d15f Mon Sep 17 00:00:00 2001 From: Prospector <6166773+Prospector@users.noreply.github.com> Date: Thu, 7 May 2026 06:07:41 -0700 Subject: [PATCH] chore: give projects, orgs, and collections canonical URLs to hopefully improve SEO (#6014) * Give projects, collections, and orgs canonical URLs * prepr --- apps/frontend/src/pages/[type]/[id].vue | 14 ++++++++++++++ apps/frontend/src/pages/collection/[id].vue | 10 ++++++++++ apps/frontend/src/pages/organization/[id].vue | 10 ++++++++++ 3 files changed, 34 insertions(+) diff --git a/apps/frontend/src/pages/[type]/[id].vue b/apps/frontend/src/pages/[type]/[id].vue index 763bca362..f56440a58 100644 --- a/apps/frontend/src/pages/[type]/[id].vue +++ b/apps/frontend/src/pages/[type]/[id].vue @@ -2317,6 +2317,18 @@ const canCreateServerFrom = computed(() => { return project.value.project_type === 'modpack' && project.value.server_side !== 'unsupported' }) +const createCanonicalUrl = () => + project.value ? `https://modrinth.com/project/${project.value.id}` : undefined + +useHead({ + link: [ + { + rel: 'canonical', + href: createCanonicalUrl, + }, + ], +}) + if (!route.name.startsWith('type-id-settings')) { useSeoMeta({ title: () => title.value, @@ -2324,6 +2336,7 @@ if (!route.name.startsWith('type-id-settings')) { ogTitle: () => title.value, ogDescription: () => project.value?.description ?? '', ogImage: () => project.value?.icon_url ?? 'https://cdn.modrinth.com/placeholder.png', + ogUrl: createCanonicalUrl, robots: () => project.value?.status === 'approved' || project.value?.status === 'archived' ? 'all' @@ -2332,6 +2345,7 @@ if (!route.name.startsWith('type-id-settings')) { } else { useSeoMeta({ robots: 'noindex', + ogUrl: createCanonicalUrl, }) } diff --git a/apps/frontend/src/pages/collection/[id].vue b/apps/frontend/src/pages/collection/[id].vue index 6b6fd3326..0a67a4fe1 100644 --- a/apps/frontend/src/pages/collection/[id].vue +++ b/apps/frontend/src/pages/collection/[id].vue @@ -657,6 +657,7 @@ watch( [collection, creator], ([col, cre]) => { if (col && cre) { + const canonicalUrl = col ? `https://modrinth.com/collection/${col.id}` : undefined useSeoMeta({ title: formatMessage(messages.collectionTitle, { name: col.name }), description: formatMessage(messages.collectionDescription, { @@ -667,8 +668,17 @@ watch( ogTitle: formatMessage(messages.collectionTitle, { name: col.name }), ogDescription: col.description, ogImage: col.icon_url ?? 'https://cdn.modrinth.com/placeholder.png', + ogUrl: canonicalUrl, robots: col.status === 'listed' ? 'all' : 'noindex', }) + useHead({ + link: [ + { + rel: 'canonical', + href: canonicalUrl, + }, + ], + }) } }, { immediate: true }, diff --git a/apps/frontend/src/pages/organization/[id].vue b/apps/frontend/src/pages/organization/[id].vue index 362c9227c..2e5e1881a 100644 --- a/apps/frontend/src/pages/organization/[id].vue +++ b/apps/frontend/src/pages/organization/[id].vue @@ -551,6 +551,7 @@ watch( if (org) { const title = `${org.name} - Organization` const description = `${org.description} - View the organization ${org.name} on Modrinth` + const canonicalUrl = org ? `https://modrinth.com/organization/${org.id}` : undefined useSeoMeta({ title, @@ -558,6 +559,15 @@ watch( ogTitle: title, ogDescription: org.description, ogImage: org.icon_url ?? 'https://cdn.modrinth.com/placeholder.png', + ogUrl: canonicalUrl, + }) + useHead({ + link: [ + { + rel: 'canonical', + href: canonicalUrl, + }, + ], }) } },