-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add structured data schema for HomePage #1015
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
Conversation
WalkthroughAdds a new HomePageSchema client component that injects combined JSON-LD into the home page, a web-schema utility exporting multiple schema factory functions, integrates the component into HomePage rendering, and adds a "@/data/*" tsconfig path alias used for testimonials import. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant NextApp as Next.js App
participant HomePage as HomePage
participant SchemaComp as HomePageSchema (client)
participant SchemaUtils as web-schema.ts
participant DOM as Document
participant Crawler as Search Engine
User->>NextApp: GET /
NextApp->>HomePage: render page
HomePage->>SchemaComp: mount HomePageSchema
SchemaComp->>SchemaUtils: call createOrganization/WebSite/SoftwareApplication/... (use testimonials)
SchemaUtils-->>SchemaComp: return schema objects
SchemaComp->>DOM: inject <script id="homepage-structured-data" type="application/ld+json">JSON-LD</script>
Crawler-->>DOM: fetch and parse JSON-LD
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 0
🧹 Nitpick comments (8)
apps/web/utils/web-schema.ts (5)
49-60: Honor the function parameter when selecting prioritized testimonials.You’re searching
allTestimonialsfor Roger even when a customtestimonialsarray is passed. This makes the function impure relative to its input.Apply:
- const rogerTestimonial = allTestimonials.find( + const rogerTestimonial = testimonialsToUse.find( (t) => t.handle === "@_rogermattos", ); - const selectedTestimonials = rogerTestimonial + const selectedTestimonials = rogerTestimonial ? [ rogerTestimonial, - ...testimonialsToUse + ...testimonialsToUse .filter((t) => t.handle !== "@_rogermattos") .slice(0, 4), ] : testimonialsToUse.slice(0, 5);
106-126: Use Offer.priceSpecification for monthly pricing instead of eligibleQuantity.
eligibleQuantityisn’t the right construct for billing period. PreferUnitPriceSpecificationwith a monthly unit.{ "@type": "Offer", price: "8.16", priceCurrency: "USD", name: "Pro Plan", priceValidUntil: "2025-12-31", description: "Full features for professional use", - eligibleQuantity: { - "@type": "QuantitativeValue", - unitText: "month", - }, + priceSpecification: { + "@type": "UnitPriceSpecification", + price: "8.16", + priceCurrency: "USD", + unitText: "MONTH" + } },Also confirm whether the “Pro” price or term changes before 2025‑12‑31; if so, update or remove
priceValidUntilto avoid Search Console warnings.
127-134: Align reviewCount with the testimonial source used.
reviewCountshould reflect the dataset you base reviews on.- reviewCount: allTestimonials.length.toString(), + reviewCount: testimonialsToUse.length.toString(),
15-20: Deduplicate social profiles in sameAs.Both Twitter and X point to the same presence. Keep one to avoid noise.
sameAs: [ "https://github.com/capsoftware/cap", - "https://twitter.com/cap", "https://x.com/cap", "https://www.producthunt.com/products/cap-3", ],
255-267: Consistent @id pattern for LocalBusiness.Use a fragment identifier like the other entities for consistency.
- "@id": "https://cap.so", + "@id": "https://cap.so/#localbusiness",apps/web/components/pages/HomePage/index.tsx (1)
10-11: Minor: unify data imports to use the new alias.Since we’re touching imports here, consider switching
../../../data/homepage-copyto@/data/homepage-copyfor consistency with the new@/data/*alias.apps/web/components/pages/HomePage/HomePageSchema.tsx (2)
1-1: File naming: prefer kebab-case for TS/TSX files.Rename to
home-page-schema.tsxto match the repo guideline.
47-58: Optional: emit a single object with @graph.Current array is valid. Using
{"@context":"https://schema.org","@graph":[...]}can reduce repetition and slightly shrink payload.- const schemas = [ - createOrganizationSchema(), - createWebSiteSchema(), - createSoftwareApplicationSchema(testimonials), - createProductSchema(), - createBreadcrumbSchema([{ name: "Home", url: "https://cap.so" }]), - createFAQSchema(homePageFAQs), - ]; - return JSON.stringify(schemas); + const graph = [ + createOrganizationSchema(), + createWebSiteSchema(), + createSoftwareApplicationSchema(testimonials), + createProductSchema(), + createBreadcrumbSchema([{ name: "Home", url: "https://cap.so" }]), + createFAQSchema(homePageFAQs), + ]; + return JSON.stringify({ "@context": "https://schema.org", "@graph": graph });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
apps/web/public/cap-logo.pngis excluded by!**/*.png
📒 Files selected for processing (4)
apps/web/components/pages/HomePage/HomePageSchema.tsx(1 hunks)apps/web/components/pages/HomePage/index.tsx(2 hunks)apps/web/tsconfig.json(1 hunks)apps/web/utils/web-schema.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for client-side server state and data fetching in the web app
Mutations should call Server Actions and perform precise cache updates with setQueryData/setQueriesData, avoiding broad invalidations
Prefer Server Components for initial data and pass initialData to client components for React Query hydration
Files:
apps/web/components/pages/HomePage/index.tsxapps/web/components/pages/HomePage/HomePageSchema.tsxapps/web/utils/web-schema.ts
{apps/web,packages/ui}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
{apps/web,packages/ui}/**/*.{ts,tsx}: Use Tailwind CSS exclusively for styling in the web app and shared React UI components
Component naming: React components in PascalCase; hooks in camelCase starting with 'use'
Files:
apps/web/components/pages/HomePage/index.tsxapps/web/components/pages/HomePage/HomePageSchema.tsxapps/web/utils/web-schema.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use strict TypeScript and avoid any; prefer shared types from packages
**/*.{ts,tsx}: Use Biome to format/lint TypeScript with a 2-space indent
TypeScript file names should be kebab-case (e.g., user-menu.tsx)
Files:
apps/web/components/pages/HomePage/index.tsxapps/web/components/pages/HomePage/HomePageSchema.tsxapps/web/utils/web-schema.ts
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
React/Solid components should be named using PascalCase
Files:
apps/web/components/pages/HomePage/index.tsxapps/web/components/pages/HomePage/HomePageSchema.tsx
🧠 Learnings (1)
📚 Learning: 2025-09-08T16:48:20.727Z
Learnt from: CR
PR: CapSoftware/Cap#0
File: AGENTS.md:0-0
Timestamp: 2025-09-08T16:48:20.727Z
Learning: Applies to **/tauri.ts : Do not edit auto-generated file: tauri.ts
Applied to files:
apps/web/tsconfig.json
🧬 Code graph analysis (3)
apps/web/components/pages/HomePage/index.tsx (1)
apps/web/components/pages/HomePage/HomePageSchema.tsx (1)
HomePageSchema(60-68)
apps/web/components/pages/HomePage/HomePageSchema.tsx (2)
apps/web/utils/web-schema.ts (5)
createOrganizationSchema(1-26)createWebSiteSchema(28-39)createSoftwareApplicationSchema(44-139)createBreadcrumbSchema(141-152)createFAQSchema(175-188)apps/web/data/testimonials.ts (1)
testimonials(1-211)
apps/web/utils/web-schema.ts (1)
apps/web/data/testimonials.ts (1)
testimonials(1-211)
🪛 ast-grep (0.38.6)
apps/web/components/pages/HomePage/HomePageSchema.tsx
[warning] 64-64: 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)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
- GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
- GitHub Check: Analyze (rust)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
apps/web/tsconfig.json (1)
29-31: Alias addition looks good.The "@/data/*" path mapping is correct and aligns with how it’s used in this PR.
apps/web/utils/web-schema.ts (2)
200-211: Verify hardcoded aggregate rating for Product.
reviewCount: "250"andratingValue: "4.8"are static. If not sourced from verifiable reviews, consider removing or wiring to a source of truth to avoid rich‑results issues.
41-47: Incorrect —Testimonialis exported from apps/web/data/testimonials.tsapps/web/data/testimonials.ts exports
Testimonial(line 213), so the import in apps/web/utils/web-schema.ts is valid; the suggested diff is unnecessary.Likely an incorrect or invalid review comment.
apps/web/components/pages/HomePage/index.tsx (1)
24-25: LGTM: injecting structured data at the top of the page.Placement before Header is good for visibility.
Ensure
HomePageSchemaisn’t rendered elsewhere to avoid duplicate JSON‑LD on the same route.apps/web/components/pages/HomePage/HomePageSchema.tsx (1)
60-67: Safe JSON-LD injection pattern.Using
next/scriptwithtype="application/ld+json"andJSON.stringify(...)is the recommended approach; no XSS risk here since input is static and serialized.If any schema content ever comes from user input, keep it JSON‑only and continue serializing with
JSON.stringify(no HTML).
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.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/web/components/pages/HomePage/HomePageSchema.tsx (3)
1-1: Make this a Server Component (no client runtime needed)This component only renders static JSON‑LD. Drop the client boundary to avoid unnecessary hydration and JS on the page.
Apply this diff:
-"use client"; +// Server Component: no client runtime required
14-45: Optional: move FAQs to a data moduleMinor tidy-up: consider moving homePageFAQs to apps/web/data and importing it (similar to testimonials) for reuse and easier edits.
1-69: Rename file to kebab‑case per repo conventionTypeScript file names should be kebab‑case. Suggest: home-page-schema.tsx.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/web/components/pages/HomePage/HomePageSchema.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
apps/web/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for client-side server state and data fetching in the web app
Mutations should call Server Actions and perform precise cache updates with setQueryData/setQueriesData, avoiding broad invalidations
Prefer Server Components for initial data and pass initialData to client components for React Query hydration
Files:
apps/web/components/pages/HomePage/HomePageSchema.tsx
{apps/web,packages/ui}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
{apps/web,packages/ui}/**/*.{ts,tsx}: Use Tailwind CSS exclusively for styling in the web app and shared React UI components
Component naming: React components in PascalCase; hooks in camelCase starting with 'use'
Files:
apps/web/components/pages/HomePage/HomePageSchema.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use strict TypeScript and avoid any; prefer shared types from packages
**/*.{ts,tsx}: Use Biome to format/lint TypeScript with a 2-space indent
TypeScript file names should be kebab-case (e.g., user-menu.tsx)
Files:
apps/web/components/pages/HomePage/HomePageSchema.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
React/Solid components should be named using PascalCase
Files:
apps/web/components/pages/HomePage/HomePageSchema.tsx
🧬 Code graph analysis (1)
apps/web/components/pages/HomePage/HomePageSchema.tsx (2)
apps/web/utils/web-schema.ts (5)
createOrganizationSchema(1-26)createWebSiteSchema(28-39)createSoftwareApplicationSchema(44-139)createBreadcrumbSchema(141-152)createFAQSchema(175-188)apps/web/data/testimonials.ts (1)
testimonials(1-211)
🪛 ast-grep (0.38.6)
apps/web/components/pages/HomePage/HomePageSchema.tsx
[warning] 64-64: 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)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
- GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
- GitHub Check: Analyze (rust)
🔇 Additional comments (2)
apps/web/components/pages/HomePage/HomePageSchema.tsx (2)
47-55: Nice composition of multiple JSON‑LD entitiesGood call composing Organization, WebSite, SoftwareApplication, Product, BreadcrumbList, and FAQPage for richer coverage. IDs and publisher references look consistent.
60-68: Inline Script usage LGTM once escaping is addedThe Script tag with a stable id is fine for JSON‑LD. After applying the escaping above, this block is good to ship.
If you keep this as a Server Component, verify that Next.js in this repo supports using next/script in RSC (it generally does). If not, render this component within a server-rendered part of the tree (e.g., page/layout) so the JSON‑LD is present in the initial HTML.
| return JSON.stringify(schemas); | ||
| }; |
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.
Escape JSON before injecting to prevent </script> break‑out (XSS)
JSON.stringify doesn’t escape </script>. If any string (e.g., testimonials) ever contains </script>, the tag can terminate early and allow injection. Escape unsafe chars before embedding.
Apply this diff:
- return JSON.stringify(schemas);
+ // Escape unsafe characters to avoid </script> early termination and XSS
+ return JSON.stringify(schemas)
+ .replace(/</g, "\\u003c")
+ .replace(/>/g, "\\u003e")
+ .replace(/&/g, "\\u0026");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return JSON.stringify(schemas); | |
| }; | |
| // Escape unsafe characters to avoid </script> early termination and XSS | |
| return JSON.stringify(schemas) | |
| .replace(/</g, "\\u003c") | |
| .replace(/>/g, "\\u003e") | |
| .replace(/&/g, "\\u0026"); |
🤖 Prompt for AI Agents
In apps/web/components/pages/HomePage/HomePageSchema.tsx around lines 57 to 58,
the code returns JSON.stringify(schemas) which does not escape sequences like
</script> and can allow script tag break-out; update the return to HTML-escape
unsafe characters in the JSON payload before injecting into a script tag (at
minimum replace the sequences </script> (and also </ and the characters U+2028
and U+2029) with safe equivalents such as <\/script> and escaped unicode), or
use a small safe serializer/utility that performs these replacements, and return
that escaped string instead of raw JSON.stringify output.
This pull request adds structured data (schema.org JSON-LD) to the Home Page for SEO purposes.
Summary by CodeRabbit
New Features
Chores