improvement(seo): extract shared withFilteredNoindex helper (#5392) · simstudioai/sim@4fe372b · GitHub
Skip to content

Commit 4fe372b

Browse files
authored
improvement(seo): extract shared withFilteredNoindex helper (#5392)
The `{ ...base, ...(isFiltered && { robots: { index: false, follow: true } }) }` faceted-navigation noindex pattern was duplicated verbatim across five catalog pages (integrations, models, blog, careers, pricing). Extracted to lib/landing/seo.ts alongside buildLandingMetadata, giving the pattern a name and a single point of change. Pure refactor — verified byte-identical rendered output (robots meta + canonical) on baseline and filtered variants of all five pages before and after.
1 parent 8937005 commit 4fe372b

6 files changed

Lines changed: 110 additions & 88 deletions

File tree

apps/sim/app/(landing)/blog/page.tsx

Lines changed: 29 additions & 26 deletions

apps/sim/app/(landing)/careers/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Metadata } from 'next'
22
import type { SearchParams } from 'nuqs/server'
3-
import { buildLandingMetadata } from '@/lib/landing/seo'
3+
import { buildLandingMetadata, withFilteredNoindex } from '@/lib/landing/seo'
44
import Careers from '@/app/(landing)/careers/careers'
55
import { ALL_FILTER_VALUE, careersSearchParamsCache } from '@/app/(landing)/careers/search-params'
66

@@ -26,7 +26,7 @@ export async function generateMetadata({
2626
'Sim careers, Sim jobs, AI workspace jobs, AI agent engineering jobs, open source jobs',
2727
})
2828

29-
return { ...base, ...(isFiltered && { robots: { index: false, follow: true } }) }
29+
return withFilteredNoindex(base, isFiltered)
3030
}
3131

3232
export default function Page({ searchParams }: { searchParams: Promise<SearchParams> }) {

apps/sim/app/(landing)/integrations/(shell)/page.tsx

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
type Integration,
99
POPULAR_WORKFLOWS,
1010
} from '@/lib/integrations'
11+
import { withFilteredNoindex } from '@/lib/landing/seo'
1112
import { JsonLd } from '@/app/(landing)/components/json-ld'
1213
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
1314
import { IntegrationCard } from '@/app/(landing)/integrations/components/integration-card'
@@ -92,32 +93,34 @@ export async function generateMetadata({
9293
const { q, category } = await integrationsSearchParamsCache.parse(searchParams)
9394
const isFiltered = Boolean(q || category)
9495

95-
return {
96-
title: 'Integrations',
97-
description: `Connect ${INTEGRATION_COUNT}+ apps and services in Sim's AI workspace. Build agents that automate real work with ${TOP_NAMES.join(', ')}, and more.`,
98-
keywords: [
99-
'AI workspace integrations',
100-
'AI agent integrations',
101-
'AI agent builder integrations',
102-
...TOP_NAMES.flatMap((n) => [`${n} integration`, `${n} automation`]),
103-
...allIntegrations.slice(0, 20).map((i) => `${i.name} automation`),
104-
],
105-
// og:image/twitter:image come from the sibling opengraph-image.tsx -
106-
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
107-
openGraph: {
108-
title: 'Integrations | Sim AI Workspace',
109-
description: `Connect ${INTEGRATION_COUNT}+ apps in Sim's AI workspace. Build agents that link ${TOP_NAMES.join(', ')}, and every tool your team uses.`,
110-
url: `${baseUrl}/integrations`,
111-
type: 'website',
112-
},
113-
twitter: {
114-
card: 'summary_large_image',
115-
title: 'Integrations | Sim',
116-
description: `Connect ${INTEGRATION_COUNT}+ apps in Sim's AI workspace.`,
96+
return withFilteredNoindex(
97+
{
98+
title: 'Integrations',
99+
description: `Connect ${INTEGRATION_COUNT}+ apps and services in Sim's AI workspace. Build agents that automate real work with ${TOP_NAMES.join(', ')}, and more.`,
100+
keywords: [
101+
'AI workspace integrations',
102+
'AI agent integrations',
103+
'AI agent builder integrations',
104+
...TOP_NAMES.flatMap((n) => [`${n} integration`, `${n} automation`]),
105+
...allIntegrations.slice(0, 20).map((i) => `${i.name} automation`),
106+
],
107+
// og:image/twitter:image come from the sibling opengraph-image.tsx -
108+
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
109+
openGraph: {
110+
title: 'Integrations | Sim AI Workspace',
111+
description: `Connect ${INTEGRATION_COUNT}+ apps in Sim's AI workspace. Build agents that link ${TOP_NAMES.join(', ')}, and every tool your team uses.`,
112+
url: `${baseUrl}/integrations`,
113+
type: 'website',
114+
},
115+
twitter: {
116+
card: 'summary_large_image',
117+
title: 'Integrations | Sim',
118+
description: `Connect ${INTEGRATION_COUNT}+ apps in Sim's AI workspace.`,
119+
},
120+
alternates: { canonical: `${baseUrl}/integrations` },
117121
},
118-
alternates: { canonical: `${baseUrl}/integrations` },
119-
...(isFiltered && { robots: { index: false, follow: true } }),
120-
}
122+
isFiltered
123+
)
121124
}
122125

123126
export default async function IntegrationsPage({

apps/sim/app/(landing)/models/(shell)/page.tsx

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from 'next'
22
import type { SearchParams } from 'nuqs/server'
33
import { SITE_URL } from '@/lib/core/utils/urls'
4+
import { withFilteredNoindex } from '@/lib/landing/seo'
45
import { JsonLd } from '@/app/(landing)/components/json-ld'
56
import { LandingFAQ } from '@/app/(landing)/components/landing-faq'
67
import { ModelComparisonCharts } from '@/app/(landing)/models/components/model-comparison-charts'
@@ -74,40 +75,42 @@ export async function generateMetadata({
7475
const { q, provider } = await modelsSearchParamsCache.parse(searchParams)
7576
const isFiltered = Boolean(q || provider)
7677

77-
return {
78-
title: 'AI Models Directory',
79-
description: `Compare ${TOTAL_MODELS}+ AI models across ${TOTAL_MODEL_PROVIDERS} providers in Sim's AI workspace. Compare pricing, context windows, and capabilities for your agents.`,
80-
keywords: [
81-
'AI models directory',
82-
'AI model comparison',
83-
'LLM model list',
84-
'model pricing',
85-
'context window comparison',
86-
'OpenAI models',
87-
'Anthropic models',
88-
'Google Gemini models',
89-
'xAI Grok models',
90-
'Mistral models',
91-
...TOP_MODEL_PROVIDERS.map((provider) => `${provider} models`),
92-
],
93-
// og:image/twitter:image come from the sibling opengraph-image.tsx -
94-
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
95-
openGraph: {
96-
title: 'AI Models Directory | Sim',
97-
description: `Explore ${TOTAL_MODELS}+ AI models across ${TOTAL_MODEL_PROVIDERS} providers with pricing, context windows, and capability details.`,
98-
url: `${baseUrl}/models`,
99-
type: 'website',
100-
},
101-
twitter: {
102-
card: 'summary_large_image',
103-
title: 'AI Models Directory | Sim',
104-
description: `Search ${TOTAL_MODELS}+ AI models across ${TOTAL_MODEL_PROVIDERS} providers.`,
105-
},
106-
alternates: {
107-
canonical: `${baseUrl}/models`,
78+
return withFilteredNoindex(
79+
{
80+
title: 'AI Models Directory',
81+
description: `Compare ${TOTAL_MODELS}+ AI models across ${TOTAL_MODEL_PROVIDERS} providers in Sim's AI workspace. Compare pricing, context windows, and capabilities for your agents.`,
82+
keywords: [
83+
'AI models directory',
84+
'AI model comparison',
85+
'LLM model list',
86+
'model pricing',
87+
'context window comparison',
88+
'OpenAI models',
89+
'Anthropic models',
90+
'Google Gemini models',
91+
'xAI Grok models',
92+
'Mistral models',
93+
...TOP_MODEL_PROVIDERS.map((provider) => `${provider} models`),
94+
],
95+
// og:image/twitter:image come from the sibling opengraph-image.tsx -
96+
// Next serves it at a hash-suffixed URL, so hardcoding it here 404s.
97+
openGraph: {
98+
title: 'AI Models Directory | Sim',
99+
description: `Explore ${TOTAL_MODELS}+ AI models across ${TOTAL_MODEL_PROVIDERS} providers with pricing, context windows, and capability details.`,
100+
url: `${baseUrl}/models`,
101+
type: 'website',
102+
},
103+
twitter: {
104+
card: 'summary_large_image',
105+
title: 'AI Models Directory | Sim',
106+
description: `Search ${TOTAL_MODELS}+ AI models across ${TOTAL_MODEL_PROVIDERS} providers.`,
107+
},
108+
alternates: {
109+
canonical: `${baseUrl}/models`,
110+
},
108111
},
109-
...(isFiltered && { robots: { index: false, follow: true } }),
110-
}
112+
isFiltered
113+
)
111114
}
112115

113116
export default async function ModelsPage({

apps/sim/app/(landing)/pricing/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Metadata } from 'next'
22
import type { SearchParams } from 'nuqs/server'
3-
import { buildLandingMetadata } from '@/lib/landing/seo'
3+
import { buildLandingMetadata, withFilteredNoindex } from '@/lib/landing/seo'
44
import Pricing from '@/app/(landing)/pricing/pricing'
55
import { pricingSearchParamsCache } from '@/app/(landing)/pricing/search-params'
66

@@ -29,7 +29,7 @@ export async function generateMetadata({
2929
'Sim pricing, AI workspace pricing, AI agent platform pricing, build AI agents, Pro plan, Max plan, Enterprise plan, open-source AI agents, LLM pricing',
3030
})
3131

32-
return { ...base, ...(isFiltered && { robots: { index: false, follow: true } }) }
32+
return withFilteredNoindex(base, isFiltered)
3333
}
3434

3535
export default async function Page({ searchParams }: { searchParams: Promise<SearchParams> }) {

apps/sim/lib/landing/seo.ts

Lines changed: 13 additions & 0 deletions

0 commit comments

Comments
 (0)