From 1df746d1c6c4eb4ba7892878e99d99936355f07e Mon Sep 17 00:00:00 2001 From: Raymond Jacobson Date: Fri, 6 Mar 2026 17:17:39 -0800 Subject: [PATCH] Improve GEO --- packages/web/index.html | 1 + packages/web/scripts/workers-site/index.js | 43 ++++++++++++++++-- .../pages/landing-2026/components/FAQ2026.tsx | 20 +++++++++ packages/web/src/ssr/metaTags.ts | 45 +++++++++++++++++-- 4 files changed, 101 insertions(+), 8 deletions(-) diff --git a/packages/web/index.html b/packages/web/index.html index ab34e8b1c65..6277b6147be 100644 --- a/packages/web/index.html +++ b/packages/web/index.html @@ -58,6 +58,7 @@ + Audius — Free Music Streaming for Artists, Labels & Fans diff --git a/packages/web/scripts/workers-site/index.js b/packages/web/scripts/workers-site/index.js index 3ec9f8ef79e..a50c699351d 100644 --- a/packages/web/scripts/workers-site/index.js +++ b/packages/web/scripts/workers-site/index.js @@ -49,7 +49,7 @@ function isCrawler(val) { return false } const crawlerTest = - /forcessr|ahrefs(bot|siteaudit)|altavista|baiduspider|bingbot|discordbot|duckduckbot|embedly|facebookexternalhit|gigabot|googlebot|google-inspectiontool|ia_archiver|linkbot|linkedinbot|meta-externalfetcher|msnbot|nextgensearchbot|reaper|slackbot|snap|telegrambot|twitterbot|whatsapp|whatsup|yahoo|yandex|yeti|yodaobot|zend|zoominfobot/i + /forcessr|ahrefs(bot|siteaudit)|altavista|anthropic-ai|baiduspider|bingbot|chatgpt-user|claude-web|cohere-ai|discordbot|duckduckbot|embedly|facebookexternalhit|gigabot|googlebot|google-extended|google-inspectiontool|gptbot|ia_archiver|linkbot|linkedinbot|meta-externalfetcher|msnbot|nextgensearchbot|perplexitybot|reaper|slackbot|snap|telegrambot|twitterbot|whatsapp|whatsup|yahoo|yandex|yeti|yodaobot|zend|zoominfobot/i return crawlerTest.test(val) } @@ -139,14 +139,49 @@ class SEOHandlerHead { if (!metadata || !name || !metadata.data || metadata.data.length === 0) { // We didn't parse this to anything we have custom tags for, so just return the default tags - const tags = ` + const baseUrl = `https://${self.host}` + const schemaLd = JSON.stringify({ + '@context': 'https://schema.org', + '@graph': [ + { + '@type': 'Organization', + '@id': `${baseUrl}/#organization`, + name: 'Audius', + url: baseUrl, + sameAs: [ + 'https://twitter.com/audius', + 'https://discord.gg/audius', + 'https://github.com/AudiusProject' + ] + }, + { + '@type': 'WebSite', + '@id': `${baseUrl}/#website`, + url: baseUrl, + name: 'Audius', + publisher: { '@id': `${baseUrl}/#organization` } + }, + { + '@type': 'SoftwareApplication', + name: 'Audius', + applicationCategory: 'MusicApplication', + operatingSystem: 'Web, iOS, Android' + } + ] + }) + const tags = `Audius — Free Music Streaming for Artists, Labels & Fans + + + + - + - ` + + ` element.append(tags, { html: true }) return } diff --git a/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx b/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx index 3974bcc53b4..e74d44b6d75 100644 --- a/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx +++ b/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx @@ -1,5 +1,7 @@ import { useState } from 'react' +import { Helmet } from 'react-helmet' + import styles from './FAQ2026.module.css' /** Inline chevron so we control color (white default, purple on hover) without Harmony theme override */ @@ -63,8 +65,26 @@ export const FAQ2026 = (_props: FAQ2026Props) => { }) } + const faqPageSchema = { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: faqItems.map((item) => ({ + '@type': 'Question', + name: item.question, + acceptedAnswer: { + '@type': 'Answer', + text: item.answer + } + })) + } + return (
+ + +

Frequently Asked Questions diff --git a/packages/web/src/ssr/metaTags.ts b/packages/web/src/ssr/metaTags.ts index 28b7e01b260..45305c6b372 100644 --- a/packages/web/src/ssr/metaTags.ts +++ b/packages/web/src/ssr/metaTags.ts @@ -97,8 +97,43 @@ export const getExploreInfo = (type?: string): ExploreInfo => { } /** - * Default meta tag context (homepage / landing) - * GEO: Title and canonical so AI engines and crawlers get strong signals. + * Homepage schema markup for Organization, WebSite, SoftwareApplication + */ +const getHomepageStructuredData = () => { + const publicUrl = getPublicUrl() + return { + '@context': 'https://schema.org', + '@graph': [ + { + '@type': 'Organization', + '@id': `${publicUrl}/#organization`, + name: 'Audius', + url: publicUrl, + sameAs: [ + 'https://twitter.com/audius', + 'https://discord.gg/audius', + 'https://github.com/AudiusProject' + ] + }, + { + '@type': 'WebSite', + '@id': `${publicUrl}/#website`, + url: publicUrl, + name: 'Audius', + publisher: { '@id': `${publicUrl}/#organization` } + }, + { + '@type': 'SoftwareApplication', + name: 'Audius', + applicationCategory: 'MusicApplication', + operatingSystem: 'Web, iOS, Android' + } + ] + } +} + +/** + * Default meta tag context */ export const getDefaultContext = () => { const publicUrl = getPublicUrl() @@ -108,10 +143,12 @@ export const getDefaultContext = () => { 'Audius is a music streaming and sharing platform that puts power back into the hands of content creators.', ogDescription: 'Audius is a music streaming and sharing platform that puts power back into the hands of content creators.', - canonicalUrl: `${publicUrl}/`, image: DEFAULT_IMAGE_URL, imageAlt: 'The Audius Platform', - thumbnail: true + thumbnail: true, + canonicalUrl: `${publicUrl}/`, + webUrl: `${publicUrl}/`, + structuredData: getHomepageStructuredData() } }