Repository navigation
feat(ui): add comprehensive SEO improvements #1492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -52,8 +52,65 @@ export default async function FeaturePage({ params }: PageProps) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? demoComponents[feature.demoComponent] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const breadcrumbSchema = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@context": "https://schema.org", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "BreadcrumbList", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| itemListElement: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ListItem", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "Home", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| item: "https://llmgateway.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ListItem", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "Features", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| item: "https://llmgateway.io/features", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ListItem", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: feature.title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| item: `https://llmgateway.io/features/${slug}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const featureSchema = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@context": "https://schema.org", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "WebPage", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: feature.title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: feature.longDescription, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainEntity: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "SoftwareApplication", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: `LLM Gateway - ${feature.title}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| applicationCategory: "DeveloperApplication", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| operatingSystem: "Web", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: feature.longDescription, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| offers: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "Offer", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| price: "0", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| priceCurrency: "USD", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| featureList: feature.benefits.map((b) => b.title), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <script | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="application/ld+json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dangerouslySetInnerHTML={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __html: JSON.stringify(breadcrumbSchema), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <script | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="application/ld+json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dangerouslySetInnerHTML={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __html: JSON.stringify(featureSchema), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+113
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Next.js App Router + JSON-LD (with
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const breadcrumbSchema = { | |
| "@context": "https://schema.org", | |
| "@type": "BreadcrumbList", | |
| itemListElement: [ | |
| { | |
| "@type": "ListItem", | |
| position: 1, | |
| name: "Home", | |
| item: "https://llmgateway.io", | |
| }, | |
| { | |
| "@type": "ListItem", | |
| position: 2, | |
| name: "Features", | |
| item: "https://llmgateway.io/features", | |
| }, | |
| { | |
| "@type": "ListItem", | |
| position: 3, | |
| name: feature.title, | |
| item: `https://llmgateway.io/features/${slug}`, | |
| }, | |
| ], | |
| }; | |
| const featureSchema = { | |
| "@context": "https://schema.org", | |
| "@type": "WebPage", | |
| name: feature.title, | |
| description: feature.longDescription, | |
| mainEntity: { | |
| "@type": "SoftwareApplication", | |
| name: `LLM Gateway - ${feature.title}`, | |
| applicationCategory: "DeveloperApplication", | |
| operatingSystem: "Web", | |
| description: feature.longDescription, | |
| offers: { | |
| "@type": "Offer", | |
| price: "0", | |
| priceCurrency: "USD", | |
| }, | |
| featureList: feature.benefits.map((b) => b.title), | |
| }, | |
| }; | |
| return ( | |
| <> | |
| <script | |
| type="application/ld+json" | |
| dangerouslySetInnerHTML={{ | |
| __html: JSON.stringify(breadcrumbSchema), | |
| }} | |
| /> | |
| <script | |
| type="application/ld+json" | |
| dangerouslySetInnerHTML={{ | |
| __html: JSON.stringify(featureSchema), | |
| }} | |
| /> | |
| const breadcrumbSchema = { | |
| "@context": "https://schema.org", | |
| "@type": "BreadcrumbList", | |
| itemListElement: [ | |
| { | |
| "@type": "ListItem", | |
| position: 1, | |
| name: "Home", | |
| item: "https://llmgateway.io", | |
| }, | |
| { | |
| "@type": "ListItem", | |
| position: 2, | |
| name: "Features", | |
| item: "https://llmgateway.io/features", | |
| }, | |
| { | |
| "@type": "ListItem", | |
| position: 3, | |
| name: feature.title, | |
| item: `https://llmgateway.io/features/${slug}`, | |
| }, | |
| ], | |
| }; | |
| const featureSchema = { | |
| "@context": "https://schema.org", | |
| "@type": "WebPage", | |
| name: feature.title, | |
| description: feature.longDescription, | |
| mainEntity: { | |
| "@type": "SoftwareApplication", | |
| name: `LLM Gateway - ${feature.title}`, | |
| applicationCategory: "DeveloperApplication", | |
| operatingSystem: "Web", | |
| description: feature.longDescription, | |
| offers: { | |
| "@type": "Offer", | |
| price: "0", | |
| priceCurrency: "USD", | |
| }, | |
| featureList: feature.benefits.map((b) => b.title), | |
| }, | |
| }; | |
| return ( | |
| <> | |
| <script | |
| type="application/ld+json" | |
| dangerouslySetInnerHTML={{ | |
| __html: JSON.stringify(breadcrumbSchema).replace(/</g, "\\u003c"), | |
| }} | |
| /> | |
| <script | |
| type="application/ld+json" | |
| dangerouslySetInnerHTML={{ | |
| __html: JSON.stringify(featureSchema).replace(/</g, "\\u003c"), | |
| }} | |
| /> |
🧰 Tools
🪛 ast-grep (0.40.5)
[warning] 103-103: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
[warning] 109-109: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
[error] 104-104: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
[error] 110-110: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🤖 Prompt for AI Agents
In `@apps/ui/src/app/features/`[slug]/page.tsx around lines 55 - 113, The JSON-LD
injected via dangerouslySetInnerHTML for breadcrumbSchema and featureSchema is
not escaping `<`, triggering the Biome security/noDangerouslySetInnerHtml lint;
fix it by serializing the objects with JSON.stringify and then escaping `<`
characters (e.g., replace `<` with `\u003c`) before assigning to __html for both
breadcrumbSchema and featureSchema so the script content cannot be used for tag
injection; update the places where dangerouslySetInnerHTML is set to use the
escaped string produced from breadcrumbSchema and featureSchema.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -26,8 +26,82 @@ export default async function GuidePage({ params }: GuidePageProps) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notFound(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const articleSchema = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@context": "https://schema.org", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "TechArticle", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headline: guide.title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: guide.description || "LLM Gateway integration guide", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| datePublished: guide.date, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dateModified: guide.date, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| author: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "Organization", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "LLM Gateway", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: "https://llmgateway.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| publisher: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "Organization", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "LLM Gateway", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: "https://llmgateway.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logo: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ImageObject", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: "https://llmgateway.io/favicon/android-chrome-512x512.png", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mainEntityOfPage: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "WebPage", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@id": `https://llmgateway.io/guides/${slug}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...(guide.image && { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ImageObject", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: guide.image.src.startsWith("http") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? guide.image.src | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : `https://llmgateway.io${guide.image.src}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: guide.image.width, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: guide.image.height, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const breadcrumbSchema = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@context": "https://schema.org", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "BreadcrumbList", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| itemListElement: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ListItem", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "Home", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| item: "https://llmgateway.io", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ListItem", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: "Guides", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| item: "https://llmgateway.io/guides", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "@type": "ListItem", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| position: 3, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: guide.title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| item: `https://llmgateway.io/guides/${slug}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <script | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="application/ld+json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dangerouslySetInnerHTML={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __html: JSON.stringify(articleSchema), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <script | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="application/ld+json" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dangerouslySetInnerHTML={{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| __html: JSON.stringify(breadcrumbSchema), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+93
to
+104
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's check the Biome configuration in the repo
find . -maxdepth 3 -name "biome.json" -o -name "biome.jsonc" | head -5Repository: theopenco/llmgateway Length of output: 76 🏁 Script executed: # Search for any linting rules related to dangerouslySetInnerHTML
rg "dangerouslySetInnerHTML" --type json --type jsonc -A 2 -B 2Repository: theopenco/llmgateway Length of output: 93 🏁 Script executed: # Check if there are other uses of dangerouslySetInnerHTML in the codebase to understand the pattern
rg "dangerouslySetInnerHTML" --type tsx --type ts | head -20Repository: theopenco/llmgateway Length of output: 91 🌐 Web query:
💡 Result: Recommended pattern (Next.js App Router)1) Render JSON-LD as an inline // app/products/[id]/page.tsx
export default async function Page({ params }: { params: { id: string } }) {
const product = await getProduct(params.id)
const jsonLd = {
"@context": "https://schema.org",
"@type": "Product",
name: product.name,
image: product.image,
description: product.description,
}
return (
<section>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
}}
/>
{/* page content */}
</section>
)
}2) Put the script in 3) Prefer JSON-LD over microdata/RDFa. 4) Validate your markup. Optional (but common) refinement: type your JSON-LD with Sources[1] Next.js Docs – “Guides: JSON-LD” (updated Sep 30, 2025) (nextjs.org) Citations:
🏁 Script executed: cat ./biome.json | head -100Repository: theopenco/llmgateway Length of output: 1838 🏁 Script executed: # Check the actual file in question to see current implementation
cat -n apps/ui/src/app/guides/[slug]/page.tsx | sed -n '85,115p'Repository: theopenco/llmgateway Length of output: 1100 🌐 Web query:
💡 Result: Key difference: HTML parsing/execution vs text + JSON parsing
Important gotchas (where people get hurt)
Practical recommendation
Sources: React escaping in JSX [1]; React 🏁 Script executed: # Check Biome's security rules more carefully
rg "dangerouslySetInnerHTML|noDangerousInnerHtml" --type jsonRepository: theopenco/llmgateway Length of output: 46 🏁 Script executed: # Search for any eslint/biome configurations that might override rules
rg "dangerouslySetInnerHTML" --type js --type json -i | head -20Repository: theopenco/llmgateway Length of output: 46 Add escaping to JSON-LD to prevent script breakout, but keep The current code uses Per Next.js official guidance, add escaping to safely handle the ✅ Correct fix (escaping, not removal) <script
type="application/ld+json"
dangerouslySetInnerHTML={{
- __html: JSON.stringify(articleSchema),
+ __html: JSON.stringify(articleSchema).replace(/</g, "\\u003c"),
}}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
- __html: JSON.stringify(breadcrumbSchema),
+ __html: JSON.stringify(breadcrumbSchema).replace(/</g, "\\u003c"),
}}
/>📝 Committable suggestion
Suggested change
🧰 Tools🪛 ast-grep (0.40.5)[warning] 94-94: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks. (react-unsafe-html-injection) [warning] 100-100: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks. (react-unsafe-html-injection) 🪛 Biome (2.1.2)[error] 95-95: Avoid passing content using the dangerouslySetInnerHTML prop. Setting content using code can expose users to cross-site scripting (XSS) attacks (lint/security/noDangerouslySetInnerHtml) [error] 101-101: Avoid passing content using the dangerouslySetInnerHTML prop. Setting content using code can expose users to cross-site scripting (XSS) attacks (lint/security/noDangerouslySetInnerHtml) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <HeroRSC navbarOnly /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="min-h-screen bg-white text-black dark:bg-black dark:text-white pt-30"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <main className="container mx-auto px-4 py-8"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,29 +23,101 @@ export const dynamic = "force-dynamic"; | |
|
|
||
| export const metadata: Metadata = { | ||
| metadataBase: new URL("https://llmgateway.io"), | ||
| title: "LLM Gateway", | ||
| title: { | ||
| default: "LLM Gateway - Unified API for Multiple LLM Providers", | ||
| template: "%s | LLM Gateway", | ||
| }, | ||
| description: | ||
| "Route, manage, and analyze your LLM requests across multiple providers with a unified API interface.", | ||
| "Route, manage, and analyze your LLM requests across multiple providers with a unified API interface. Access OpenAI, Anthropic, Google, and 19+ providers through one API.", | ||
| keywords: [ | ||
| "LLM", | ||
| "API Gateway", | ||
| "OpenAI", | ||
| "Anthropic", | ||
| "Claude", | ||
| "GPT-4", | ||
| "AI API", | ||
| "LLM Routing", | ||
| "Multi-provider LLM", | ||
| "AI Gateway", | ||
| ], | ||
| authors: [{ name: "LLM Gateway" }], | ||
| creator: "LLM Gateway", | ||
| publisher: "LLM Gateway", | ||
| icons: { | ||
| icon: "/favicon/favicon.ico?v=2", | ||
| icon: [ | ||
| { url: "/favicon/favicon.ico", sizes: "any" }, | ||
| { url: "/favicon/favicon-16x16.png", sizes: "16x16", type: "image/png" }, | ||
| { url: "/favicon/favicon-32x32.png", sizes: "32x32", type: "image/png" }, | ||
| ], | ||
| apple: [{ url: "/favicon/apple-touch-icon.png", sizes: "180x180" }], | ||
| }, | ||
| manifest: "/favicon/site.webmanifest", | ||
| alternates: { | ||
| canonical: "./", | ||
| }, | ||
| openGraph: { | ||
| title: "LLM Gateway", | ||
| title: "LLM Gateway - Unified API for Multiple LLM Providers", | ||
| description: | ||
| "Route, manage, and analyze your LLM requests across multiple providers with a unified API interface.", | ||
| "Route, manage, and analyze your LLM requests across multiple providers with a unified API interface. Access OpenAI, Anthropic, Google, and 19+ providers through one API.", | ||
| images: ["/opengraph.png?v=1"], | ||
| type: "website", | ||
| url: "https://llmgateway.io", | ||
| siteName: "LLM Gateway", | ||
| locale: "en_US", | ||
| }, | ||
| twitter: { | ||
| card: "summary_large_image", | ||
| title: "LLM Gateway", | ||
| title: "LLM Gateway - Unified API for Multiple LLM Providers", | ||
| description: | ||
| "Route, manage, and analyze your LLM requests across multiple providers with a unified API interface.", | ||
| images: ["/opengraph.png?v=1"], | ||
| creator: "@llmgateway", | ||
| }, | ||
| robots: { | ||
| index: true, | ||
| follow: true, | ||
| googleBot: { | ||
| index: true, | ||
| follow: true, | ||
| "max-video-preview": -1, | ||
| "max-image-preview": "large", | ||
| "max-snippet": -1, | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| const organizationSchema = { | ||
| "@context": "https://schema.org", | ||
| "@type": "Organization", | ||
| name: "LLM Gateway", | ||
| url: "https://llmgateway.io", | ||
| logo: "https://llmgateway.io/favicon/android-chrome-512x512.png", | ||
| description: | ||
| "Route, manage, and analyze your LLM requests across multiple providers with a unified API interface.", | ||
| sameAs: [ | ||
| "https://twitter.com/llmgateway", | ||
| "https://github.com/llmgateway/llmgateway", | ||
| ], | ||
| contactPoint: { | ||
| "@type": "ContactPoint", | ||
| email: "contact@llmgateway.io", | ||
| contactType: "customer support", | ||
| }, | ||
| }; | ||
|
|
||
| const websiteSchema = { | ||
| "@context": "https://schema.org", | ||
| "@type": "WebSite", | ||
| name: "LLM Gateway", | ||
| url: "https://llmgateway.io", | ||
| potentialAction: { | ||
| "@type": "SearchAction", | ||
| target: { | ||
| "@type": "EntryPoint", | ||
| urlTemplate: "https://llmgateway.io/models?search={search_term_string}", | ||
| }, | ||
| "query-input": "required name=search_term_string", | ||
| }, | ||
| }; | ||
|
Comment on lines
+90
to
122
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Check models page for search parameter handling
cat apps/ui/src/app/models/page.tsxRepository: theopenco/llmgateway Length of output: 1343 🏁 Script executed: # Search for search parameter usage across the codebase
rg "searchParams|search_term" apps/ui/src/app/Repository: theopenco/llmgateway Length of output: 3570 🏁 Script executed: # Check for any GitHub repository reference
rg "github.com/llmgateway" --type ts --type tsxRepository: theopenco/llmgateway Length of output: 91 🏁 Script executed: # Check git remote to verify actual repository
git config --get remote.origin.url 2>/dev/null || echo "Not a git repo or no origin"Repository: theopenco/llmgateway Length of output: 107 🏁 Script executed: # Check the AllModels component for search functionality
find apps/ui/src/components -name "*all-models*" -o -name "*AllModels*"Repository: theopenco/llmgateway Length of output: 108 🏁 Script executed: # Search for search implementation in models components
rg -n "search" apps/ui/src/components/models/ -A 3 -B 1Repository: theopenco/llmgateway Length of output: 14002 Fix two issues in the JSON-LD schemas: incorrect GitHub URL and mismatched search parameter.
The 🤖 Prompt for AI Agents |
||
|
|
||
|
|
@@ -54,6 +126,20 @@ export default function RootLayout({ children }: { children: ReactNode }) { | |
|
|
||
| return ( | ||
| <html lang="en" suppressHydrationWarning> | ||
| <head> | ||
| <script | ||
| type="application/ld+json" | ||
| dangerouslySetInnerHTML={{ | ||
| __html: JSON.stringify(organizationSchema), | ||
| }} | ||
| /> | ||
| <script | ||
| type="application/ld+json" | ||
| dangerouslySetInnerHTML={{ | ||
| __html: JSON.stringify(websiteSchema), | ||
| }} | ||
| /> | ||
| </head> | ||
| <body | ||
| className={`${inter.variable} ${geistMono.variable} min-h-screen antialiased`} | ||
| > | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add fallback values for image dimensions to match
generateMetadatabehavior.If
entry.imageexists but lackswidthorheightproperties, the schema will containundefinedvalues, potentially causing malformed JSON-LD. ThegenerateMetadatafunction already provides defaults (800, 400) for the same scenario.Proposed fix
...(entry.image && { image: { "@type": "ImageObject", url: entry.image.src.startsWith("http") ? entry.image.src : `https://llmgateway.io${entry.image.src}`, - width: entry.image.width, - height: entry.image.height, + width: entry.image.width || 800, + height: entry.image.height || 400, }, }),📝 Committable suggestion
🤖 Prompt for AI Agents