fix: emit one json-ld script per schema object

A single ld+json script holding a top-level array crashes naive
structured-data consumers (Safari extension content scripts) that read
parsed['@context'] without checking for arrays. Emit one script per
object so every payload has a top-level @context, and escape < in the
root layout like the marketing pages already did.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
eehkay
2026-07-18 09:15:57 -07:00
co-authored by Claude Fable 5
parent 33008d33ad
commit 9595698b5b
2 changed files with 22 additions and 7 deletions
+11 -3
View File
@@ -44,11 +44,19 @@ export default async function MarketingSlugPage({ params }: MarketingSlugPagePro
path: `/${page.slug}`, path: `/${page.slug}`,
faq: page.faq, faq: page.faq,
}); });
const safeStructuredData = JSON.stringify(structuredData).replace(/</g, '\\u003c');
return ( return (
<> <>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: safeStructuredData }} /> {/* One script per object: single-object payloads with a top-level
@context survive naive JSON-LD consumers that choke on arrays. */}
{structuredData.map((data, index) => (
<script
key={`${String(data['@type'])}-${index}`}
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(data).replace(/</g, '\\u003c'),
}}
/>
))}
<ComparisonPage page={page} isLoggedIn={Boolean(session?.user)} /> <ComparisonPage page={page} isLoggedIn={Boolean(session?.user)} />
</> </>
); );
+8 -1
View File
@@ -120,10 +120,17 @@ export default function RootLayout({
suppressHydrationWarning suppressHydrationWarning
> >
<body className="antialiased min-h-screen bg-background font-sans"> <body className="antialiased min-h-screen bg-background font-sans">
{/* One script per object: single-object payloads with a top-level
@context survive naive JSON-LD consumers that choke on arrays. */}
{structuredData.map((data) => (
<script <script
key={String(data['@type'])}
type="application/ld+json" type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }} dangerouslySetInnerHTML={{
__html: JSON.stringify(data).replace(/</g, '\\u003c'),
}}
/> />
))}
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange> <ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
<svg aria-hidden="true" className="fixed h-0 w-0"> <svg aria-hidden="true" className="fixed h-0 w-0">
<filter id="openframe-noise"> <filter id="openframe-noise">