-
Notifications
You must be signed in to change notification settings - Fork 619
[MNY-216] Update SEO metadata on chain pages, Add FAQ section #8135
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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
WalkthroughAdds a cached server API (fetchChainSeo + ChainSeo type), integrates fetched SEO into chain page metadata and rendering (including FAQs), refactors getChainMetadata → synchronous getCustomChainMetadata and updates callers, converts some server components from async to sync, and applies minor UI styling tweaks across chain-related components. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User as User
participant Router as Next.js App Router
participant Meta as generateMetadata
participant SEO as fetchChainSeo
participant Layout as ChainPageLayout
participant Utils as getCustomChainMetadata
User->>Router: Navigate to /chain/[chain_id]
Router->>Meta: generateMetadata(params)
Meta->>SEO: fetchChainSeo(chainId)
SEO-->>Meta: ChainSeo | undefined
alt SEO available
Meta-->>Router: Metadata (from ChainSeo)
else SEO missing
Meta-->>Router: undefined
end
Router->>Layout: render page
Layout->>Utils: getCustomChainMetadata(chainId)
Utils-->>Layout: customChainMetadata | null
Layout->>SEO: fetchChainSeo(chainId)
SEO-->>Layout: ChainSeo | undefined
alt ChainSeo contains FAQs
Layout-->>Router: Render page including FaqSection
else
Layout-->>Router: Render page without FAQs
end
Router-->>User: HTML (OG tags from SEO, optional FAQs)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Pre-merge checks and finishing touches❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
Comment |
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8135 +/- ##
=======================================
Coverage 56.28% 56.28%
=======================================
Files 906 906
Lines 59192 59192
Branches 4174 4174
=======================================
Hits 33316 33316
Misses 25771 25771
Partials 105 105
🚀 New features to boost your workflow:
|
size-limit report 📦
|
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: 5
🧹 Nitpick comments (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx (1)
14-16: Align Page props type with Next.js API.Next.js always passes
paramssynchronously; typing it asPromiseobscures the real shape and invites accidental misuse. Please restore the plain object type.-type Props = { - params: Promise<{ chain_id: string }>; -}; +type Props = { + params: { chain_id: string }; +};
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx(2 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx(3 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
🧠 Learnings (6)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{ts,tsx} : Export default async functions without `'use client';` – they run on the Node edge.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx
📚 Learning: 2025-05-29T00:46:09.063Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7188
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx:15-15
Timestamp: 2025-05-29T00:46:09.063Z
Learning: In the accounts component at apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx, the 3-column grid layout (md:grid-cols-3) is intentionally maintained even when rendering only one StatCard, as part of the design structure for this component.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-08-29T23:44:47.512Z
Learnt from: MananTank
PR: thirdweb-dev/js#7951
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx:38-38
Timestamp: 2025-08-29T23:44:47.512Z
Learning: The ContractPageLayout component in apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx is not the root layout - it's nested within the dashboard layout which already handles footer positioning with min-h-dvh and AppFooter placement. The ContractPageLayout needs flex flex-col grow to properly participate in the parent's flex layout.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/layout.tsx : Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
🧬 Code graph analysis (5)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
getCustomChainMetadata(917-928)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
getCustomChainMetadata(917-928)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts (1)
ChainSeo(4-29)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (2)
getChain(102-125)getCustomChainMetadata(917-928)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts (1)
fetchChainSeo(31-50)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (2)
getChain(102-125)getCustomChainMetadata(917-928)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts (1)
fetchChainSeo(31-50)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx (1)
FaqSection(9-29)
⏰ 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: E2E Tests (pnpm, webpack)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx (1)
20-22: Typography adjustment looks goodThe lighter weight aligns with the updated dashboard headings while preserving hierarchy. No issues spotted.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx (1)
14-14: Consistent spacing with updated chain cardsThe rounded-xl + gap adjustments line up with the rest of the refreshed chain UI while keeping the content grid intact.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx (1)
9-9: Typography update aligns with new overview stylingDropping the medium weight matches the lighter section headings introduced elsewhere and keeps emphasis on the value content.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx (1)
29-40: Synchronous metadata access keeps server row lightweightUsing
getCustomChainMetadatainline removes the async boundary and still exposes the optional flags the row needs.apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx (1)
21-32: Card now relies on shared custom metadata helperSwitching the card to the shared synchronous helper keeps the metadata story consistent between list views without extra awaits.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
917-919: Helper rename lands cleanlyThe renamed helper returns the same memoized data without the async wrapper, matching the new call sites.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts
Show resolved
Hide resolved
...d/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
Outdated
Show resolved
Hide resolved
...p)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
Show resolved
Hide resolved
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
Outdated
Show resolved
Hide resolved
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
Outdated
Show resolved
Hide resolved
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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx(2 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx(3 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx
🧠 Learnings (10)
📚 Learning: 2025-08-29T23:44:47.512Z
Learnt from: MananTank
PR: thirdweb-dev/js#7951
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx:38-38
Timestamp: 2025-08-29T23:44:47.512Z
Learning: The ContractPageLayout component in apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx is not the root layout - it's nested within the dashboard layout which already handles footer positioning with min-h-dvh and AppFooter placement. The ContractPageLayout needs flex flex-col grow to properly participate in the parent's flex layout.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/layout.tsx : Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Never hard-code colors – always go through Tailwind variables.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-05-29T00:46:09.063Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7188
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx:15-15
Timestamp: 2025-05-29T00:46:09.063Z
Learning: In the accounts component at apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx, the 3-column grid layout (md:grid-cols-3) is intentionally maintained even when rendering only one StatCard, as part of the design structure for this component.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use the `container` class with a `max-w-7xl` cap for page width consistency.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Merge class names with `cn` from `@/lib/utils` to keep conditional logic readable.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-05-26T16:27:26.443Z
Learnt from: MananTank
PR: thirdweb-dev/js#7152
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx:304-313
Timestamp: 2025-05-26T16:27:26.443Z
Learning: The `useChainSlug` hook returns `string | number`, not `string | undefined` as previously assumed. It does not return undefined values.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (2)
getChain(102-125)getCustomChainMetadata(917-928)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts (1)
fetchChainSeo(31-50)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
getCustomChainMetadata(917-928)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (2)
getChain(102-125)getCustomChainMetadata(917-928)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts (1)
fetchChainSeo(31-50)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx (1)
FaqSection(9-29)
🔇 Additional comments (6)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx (1)
20-22: Typography adjustment looks goodThe shift to
font-mediummatches the updated visual language elsewhere and keeps the explorer cards consistent. 👍apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx (1)
14-14: Rounded-xl & gap adjustments look goodThe tighter large-screen gap and softer radius align well with the dashboard design language.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx (1)
21-24: Use the resolved chainId when fetching custom metadataCasting
params.chain_idto a number returnsNaNon slug routes (e.g./chain/base), so every custom CTA silently disappears. Use the canonicalchain.chainIdyou just fetched instead.- const customChainMetadata = getCustomChainMetadata(Number(params.chain_id)); + const customChainMetadata = getCustomChainMetadata(chain.chainId);apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx (1)
9-9: Typographic tweak aligns with the updated hierarchyDropping the extra weight keeps this subheading consistent with the lighter typography used elsewhere in the refreshed chain page. Looks good.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx (1)
26-38: Revised card layout reads cleanlyAppreciate the tightened card styling—using the bordered avatar for product icons plus the font-medium headline keeps the entry visually balanced while staying on design tokens. Nicely done.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx (1)
29-40: Synchronous metadata lookup simplifies the row componentSwitching to
getCustomChainMetadataand shedding the async wrapper keeps the render path lean while preserving the existing feature checks. 👍
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
Show resolved
Hide resolved
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 (4)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (4)
25-26: Mark this layout as server-onlyAdd the server-only marker to prevent accidental client bundling.
As per coding guidelines
+import "server-only"; import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
31-36: Do not type params as a Promise; remove unnecessary awaitsNext.js passes params as a plain object; modeling it as a Promise is misleading and unnecessary.
-type Params = Promise<{ chain_id: string }>; +type Params = { chain_id: string }; export async function generateMetadata(props: { - params: Params; -}): Promise<Metadata | undefined> { - const params = await props.params; + params: Params; +}): Promise<Metadata | undefined> { + const { params } = props;export default async function ChainPageLayout(props: { children: React.ReactNode; params: Params; }) { - const params = await props.params; + const { params } = props;Also applies to: 67-73
38-40: Avoid redundant Number() conversionchain.chainId is already a number; drop the cast.
- const chainSeo = await fetchChainSeo(Number(chain.chainId)).catch( + const chainSeo = await fetchChainSeo(chain.chainId).catch( () => undefined, );
47-56: Set OpenGraph URL to the chain page URL (not site root)Improves share previews by pointing OG/Twitter to the actual chain page.
- metadataBase: new URL("https://thirdweb.com"), + metadataBase: new URL("https://thirdweb.com"), openGraph: { title: chainSeo.og.title, description: chainSeo.og.description, siteName: "thirdweb", type: "website", - url: "https://thirdweb.com", + url: `https://thirdweb.com/${chain.slug}`, }, twitter: { title: chainSeo.og.title, description: chainSeo.og.description, card: "summary_large_image", creator: "@thirdweb", site: "@thirdweb", },Also applies to: 57-63
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx(2 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx(3 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
🧠 Learnings (8)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Never hard-code colors – always go through Tailwind variables.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-05-29T00:46:09.063Z
Learnt from: jnsdls
PR: thirdweb-dev/js#7188
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx:15-15
Timestamp: 2025-05-29T00:46:09.063Z
Learning: In the accounts component at apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/accounts/components/accounts-count.tsx, the 3-column grid layout (md:grid-cols-3) is intentionally maintained even when rendering only one StatCard, as part of the design structure for this component.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-08-29T23:44:47.512Z
Learnt from: MananTank
PR: thirdweb-dev/js#7951
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx:38-38
Timestamp: 2025-08-29T23:44:47.512Z
Learning: The ContractPageLayout component in apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx is not the root layout - it's nested within the dashboard layout which already handles footer positioning with min-h-dvh and AppFooter placement. The ContractPageLayout needs flex flex-col grow to properly participate in the parent's flex layout.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use the `container` class with a `max-w-7xl` cap for page width consistency.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Merge class names with `cn` from `@/lib/utils` to keep conditional logic readable.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/layout.tsx : Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
getCustomChainMetadata(917-928)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (2)
getChain(102-125)getCustomChainMetadata(917-928)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts (1)
fetchChainSeo(31-50)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
getCustomChainMetadata(917-928)
⏰ 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: E2E Tests (pnpm, webpack)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (11)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx (1)
20-20: Typography tweak looks consistentThe lighter heading weight still keeps hierarchy clear and aligns with the rest of the refreshed section styling.
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx (1)
14-49: Adjustment keeps overview grid consistent with the new card radius/spacing spec.The tighter
lg:gap-6and larger radius line up with the refreshed design tokens used elsewhere in the chain page—looks good.apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx (1)
26-49: Card/CTA refinements look solid.The bordered tile + centered icon treatment reads cleaner, and restoring
font-mediumon the link resolves the earlier Tailwind issue while keeping the overlay affordance.apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx (1)
21-109: Switch to synchronous metadata integration is ✅.Pulling
getCustomChainMetadatadirectly keeps the card render simple and preserves the gas-sponsored badge logic without the extra async hop.apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
917-928: Renamed helper aligns with its actual behavior.Making the function synchronous under the
getCustomChainMetadataname matches its usage pattern throughout the stack.apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx (1)
29-99: Row update tracks the new metadata utility nicely.Dropping the async signature and reusing
getCustomChainMetadatakeeps the table render tight while preserving the gas-sponsored badge.apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (5)
51-52: OG title mapping fix looks goodopenGraph/twitter titles now correctly use chainSeo.og.title.
Also applies to: 58-59
140-140: Header image source change LGTMUsing customChainMetadata.headerImgUrl is appropriate.
148-148: Gas Sponsored gating LGTMConditioning on customChainMetadata.gasSponsored is correct.
Also applies to: 170-170
178-180: Description fallback LGTMGracefully prefers custom copy with SEO fallback.
82-86: Guard SEO fetch to avoid layout crash on API errorsThis call can throw and take down the layout; mirror the try/catch used in generateMetadata.
- const chainSeo = await fetchChainSeo(chain.chainId); + const chainSeo = await fetchChainSeo(chain.chainId).catch(() => undefined); const client = getClientThirdwebClient(undefined); const description = customChainMetadata?.about || chainSeo?.description;
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
Show resolved
Hide resolved
Merge activity
|
<!--
## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"
If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):
## Notes for the reviewer
Anything important to call out? Be sure to also clarify these in your comments.
## How to test
Unit tests, playground, etc.
-->
<!-- start pr-codex -->
---
## PR-Codex overview
This PR focuses on refactoring and enhancing the codebase for better readability and functionality. It primarily involves renaming functions, updating component styles, and implementing a new SEO fetching mechanism.
### Detailed summary
- Renamed `getChainMetadata` to `getCustomChainMetadata`.
- Updated styles in several components for consistency.
- Added `fetchChainSeo` function for improved SEO handling.
- Replaced `chainMetadata` with `customChainMetadata` in various components.
- Introduced `FaqSection` component for displaying FAQs.
- Adjusted metadata generation to utilize fetched SEO data.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
<!-- end pr-codex -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
- New Features
- Chain pages now show an interactive FAQ section when SEO data includes FAQs.
- SEO data is fetched and cached externally to provide richer titles, descriptions, and OpenGraph/Twitter previews.
- Bug Fixes / Behavior
- Metadata generation now depends on external SEO (may be unavailable), affecting preview availability and link consistency.
- Chain header images and gas-sponsored indicators now use the updated metadata source.
- Style
- More compact Supported Products cards, adjusted overview spacing/corner radius, lighter explorer/info-item typography, and refined link styling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
c2e5f65 to
64f5e47
Compare
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx (1)
1-8: Add server-only directive, explicit return type, and className support on root
- Server component should start with
import "server-only";.- Add an explicit return type.
- Expose a
classNameprop on the root element per dashboard guideline and merge viacn().As per coding guidelines
Example update:
import "server-only"; import Link from "next/link"; import type { ChainMetadataWithServices } from "@/types/chain"; import { cn } from "@/lib/utils"; export function SupportedProductsSection(props: { services: ChainMetadataWithServices["services"]; className?: string; }): JSX.Element | null { const { services, className } = props; // ... return ( <section className={cn(className)}> {/* ... */} </section> ); }
🧹 Nitpick comments (13)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx (2)
26-26: Add focus-visible ring to support the stretched-link patternThe link’s ::before covers the card. Add a visible focus state on the card for keyboard users.
As per coding guidelines
- className="relative rounded-xl border bg-card p-4 hover:border-active-border" + className="relative rounded-xl border bg-card p-4 hover:border-active-border focus-within:ring-2 focus-within:ring-ring"
29-33: Hide decorative icon from screen readersMark the product icon as decorative to avoid redundant announcements.
- <product.icon className="size-4 text-muted-foreground" /> + <product.icon aria-hidden="true" className="size-4 text-muted-foreground" />apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx (1)
1-15: Add server-only directive, className prop, and explicit return typeAligns with dashboard server-component guidelines and improves composability.
As per coding guidelines
+import "server-only"; +import type { JSX } from "react"; +import { cn } from "@/lib/utils"; -export function PrimaryInfoItem(props: { - title: string; - titleIcon?: React.ReactNode; - children: React.ReactNode; -}) { +export function PrimaryInfoItem(props: { + title: string; + titleIcon?: React.ReactNode; + children: React.ReactNode; + className?: string; +}): JSX.Element { return ( - <div> + <div className={cn(props.className)}> <div className="flex items-center gap-2"> <h3 className="text-base text-muted-foreground">{props.title}</h3> {props.titleIcon} </div> {props.children} </div> ); }apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx (1)
1-9: Mark as server-only, expose className, and add explicit return typeImproves typing and adherence to dashboard component conventions.
As per coding guidelines
+import "server-only"; import { CircleAlertIcon, TicketCheckIcon } from "lucide-react"; import Link from "next/link"; import type { JSX } from "react"; import { CopyTextButton } from "@/components/ui/CopyTextButton"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import type { ChainSupportedService } from "@/types/chain"; import { ChainIcon } from "../../../components/server/chain-icon"; import { getCustomChainMetadata } from "../../../utils"; +import { cn } from "@/lib/utils"; type ChainListCardProps = { favoriteButton: JSX.Element | undefined; chainId: number; chainSlug: string; chainName: string; enabledServices: ChainSupportedService[]; currencySymbol: string; isDeprecated: boolean; iconUrl?: string; + className?: string; }; -export function ChainListCard({ +export function ChainListCard({ isDeprecated, chainId, chainName, chainSlug, currencySymbol, enabledServices, favoriteButton, iconUrl, -}: ChainListCardProps) { + className, +}: ChainListCardProps): JSX.Element { const customChainMetadata = getCustomChainMetadata(chainId); return ( - <div className="relative h-full"> + <div className={cn("relative h-full", className)}> <Card className="h-full w-full transition-colors hover:border-active-border">Also applies to: 10-19, 21-31, 34-35
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx (1)
1-6: Use correct Props shape for Next.js params and mark server-onlyparams is not a Promise; also add explicit return type and server-only directive.
As per coding guidelines
+import "server-only"; import { CircleAlertIcon } from "lucide-react"; import { getRawAccount } from "@/api/account/get-account"; import { getClientThirdwebClient } from "@/constants/thirdweb-client.client"; import { getChain, getCustomChainMetadata } from "../../utils"; import { fetchChainSeo } from "./apis/chain-seo"; +import type { JSX } from "react"; @@ -type Props = { - params: Promise<{ chain_id: string }>; -}; +type Props = { params: { chain_id: string } }; -export default async function Page(props: Props) { - const params = await props.params; +export default async function Page(props: Props): Promise<JSX.Element> { + const { params } = props;Also applies to: 14-19
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx (3)
9-16: Expose className on FaqSection and add explicit return typesImproves composability and typings across dashboard components.
As per coding guidelines
-export function FaqSection(props: { faqs: ChainSeo["faqs"] }) { +export function FaqSection(props: { + faqs: ChainSeo["faqs"]; + className?: string; +}): JSX.Element { return ( - <div className="py-10"> + <div className={cn("py-10", props.className)}> @@ -function FaqItem(props: { +function FaqItem(props: { title: string; description: string; className?: string; -}) { +}): JSX.Element {Also applies to: 31-35
42-52: Prevent accidental form submissionSet Button type="button" to avoid submit behavior if nested in a form.
- <Button + <Button + type="button" variant="ghost" onClick={() => setIsOpen(!isOpen)} aria-controls={contentId} aria-expanded={isOpen}
17-24: Stabilize list keysTitles may collide; include index to avoid React key warnings.
- {props.faqs.map((faq, faqIndex) => ( + {props.faqs.map((faq, faqIndex) => ( <FaqItem - key={faq.title} + key={`${faq.title}-${faqIndex}`} title={faq.title} description={faq.description} className={cn(faqIndex === props.faqs.length - 1 && "border-b-0")} /> ))}apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (5)
1-1: Mark this layout as a Server ComponentAdd the server-only marker at the top to prevent accidental client imports and align with our dashboard server component guidelines.
As per coding guidelines
+import "server-only"; import { ChevronDownIcon, TicketCheckIcon } from "lucide-react";
31-31: Nit: Clarify the Params alias (and consider avoiding Promise-typed params)The generic name makes tracing harder, and Next normally passes params synchronously. Consider a more specific alias (e.g., ChainPageParams) and decoupling from Promise for future compatibility.
42-44: Prefer a minimal fallback over returning undefined metadataReturning undefined drops all metadata. Provide a minimal canonical fallback so pages still have reasonable titles when SEO data is unavailable.
- if (!chainSeo) { - return undefined; - } + if (!chainSeo) { + return { + title: chain.name, + metadataBase: new URL("https://thirdweb.com"), + }; + }
47-63: Set canonical per-chain URL and include images in OG/TwitterUse the specific chain URL for OG previews and wire through image(s) if provided by the SEO API.
return { title: chainSeo.title, description: chainSeo.description, metadataBase: new URL("https://thirdweb.com"), + // Optional but recommended for SEO + alternates: { + canonical: `/chain/${chain.slug}`, + }, openGraph: { title: chainSeo.og.title, description: chainSeo.og.description, siteName: "thirdweb", type: "website", - url: "https://thirdweb.com", + url: `https://thirdweb.com/chain/${chain.slug}`, + images: chainSeo.og?.image ? [chainSeo.og.image] : undefined, }, twitter: { title: chainSeo.og.title, description: chainSeo.og.description, card: "summary_large_image", creator: "@thirdweb", site: "@thirdweb", + images: chainSeo.og?.image ? [chainSeo.og.image] : undefined, }, };If you’d rather avoid alternates, at least update openGraph.url as above. Based on learnings
212-218: Use design tokens instead of raw HSL colorsReplace hard-coded HSL with our design tokens (e.g., bg-accent and text-accent-foreground) for theme consistency and dark mode support.
As per coding guidelines
- return ( - <div className="flex items-center gap-2 rounded-full bg-[hsla(335,57%,51%,0.2)] px-2.5 py-1 text-[hsl(334,81.12%,69.65%)]"> + return ( + <div className="flex items-center gap-2 rounded-full bg-accent px-2.5 py-1 text-accent-foreground"> <TicketCheckIcon className="size-4" /> <span className="font-medium text-xs">Gas Sponsored</span> </div> );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (11)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsx(1 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx(2 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx(4 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx(3 hunks)apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-row.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/ChainOverviewSection.tsx
- apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/explorer-section.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.tsapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/primary-info-item.tsxapps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx
🧠 Learnings (9)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use `NavLink` (`@/components/ui/NavLink`) for internal navigation so active states are handled automatically.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Never hard-code colors – always go through Tailwind variables.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx
📚 Learning: 2025-08-29T23:44:47.512Z
Learnt from: MananTank
PR: thirdweb-dev/js#7951
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx:38-38
Timestamp: 2025-08-29T23:44:47.512Z
Learning: The ContractPageLayout component in apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/_layout/contract-page-layout.tsx is not the root layout - it's nested within the dashboard layout which already handles footer positioning with min-h-dvh and AppFooter placement. The ContractPageLayout needs flex flex-col grow to properly participate in the parent's flex layout.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Keep `queryKey` stable and descriptive for cache hits.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Use descriptive, stable `queryKeys` for React Query cache hits
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/layout.tsx : Building layout shells (`layout.tsx`) and top-level pages that mainly assemble data.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx
📚 Learning: 2025-05-26T16:27:26.443Z
Learnt from: MananTank
PR: thirdweb-dev/js#7152
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/nfts/components/table.tsx:304-313
Timestamp: 2025-05-26T16:27:26.443Z
Learning: The `useChainSlug` hook returns `string | number`, not `string | undefined` as previously assumed. It does not return undefined values.
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx
🧬 Code graph analysis (3)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/chainlist/components/server/chainlist-card.tsx (1)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (1)
getCustomChainMetadata(917-928)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/layout.tsx (1)
generateMetadata(28-39)apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (2)
getChain(102-125)getCustomChainMetadata(917-928)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx (2)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/utils.ts (2)
getChain(102-125)getCustomChainMetadata(917-928)apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx (1)
FaqSection(9-29)
⏰ 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). (7)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Size
- GitHub Check: Build Packages
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (11)
apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/SupportedProductsSection.tsx (1)
35-41: Resolved: invalid Tailwind class; also verify external vs internal link
- The previous “text-medium” issue is fixed by using
font-medium. LGTM.- If
product.linkis internal, useNavLinkper dashboard guideline; if external, consider a plain<a>overnext/link.As per coding guidelines
#!/bin/bash # Inspect product links to confirm internal vs external usage. set -euo pipefail # Find the products definition and show link fields rg -nP -C2 'export\s+const\s+products\b|link\s*:' --type=ts --type=tsx # If most links start with "/", prefer NavLink. If they start with http(s), <a> is fine.apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/apis/chain-seo.ts (1)
31-47: Handle SEO API network failures gracefullyWrap in try/catch to avoid taking down the page when upstream hiccups; return undefined on errors.
export const fetchChainSeo = unstable_cache( async (chainId: number) => { - const url = new URL( - `https://seo-pages-generator-5814.zeet-nftlabs.zeet.app/chain/${chainId}`, - ); - const res = await fetch(url, { - headers: { - "Content-Type": "application/json", - }, - }); - - if (!res.ok) { - return undefined; - } - - return res.json() as Promise<ChainSeo>; + try { + const url = new URL( + `https://seo-pages-generator-5814.zeet-nftlabs.zeet.app/chain/${chainId}`, + ); + const res = await fetch(url, { + headers: { "Content-Type": "application/json" }, + }); + if (!res.ok) { + return undefined; + } + return (await res.json()) as ChainSeo; + } catch { + return undefined; + } }, ["chain-seo"], { revalidate: 60 * 60 * 24 }, // 24 hours );apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/page.tsx (1)
21-23: Fix custom metadata lookup for slug routesUse the canonical chainId from getChain; Number(params.chain_id) is NaN on slug URLs.
- const customChainMetadata = getCustomChainMetadata(Number(params.chain_id)); + const customChainMetadata = getCustomChainMetadata(Number(chain.chainId));apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/components/server/faq-section.tsx (1)
36-46: Fix setter name typoRename to setIsOpen for clarity; update the onClick to match.
- const [isOpen, setIsOpenn] = useState(false); + const [isOpen, setIsOpen] = useState(false); @@ - <Button + <Button variant="ghost" - onClick={() => setIsOpenn(!isOpen)} + onClick={() => setIsOpen(!isOpen)} aria-controls={contentId} aria-expanded={isOpen}apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/(chainPage)/layout.tsx (7)
25-26: Imports refactor looks goodSwitch to getCustomChainMetadata and using fetchChainSeo is appropriate here.
38-40: Good: SEO fetch is guarded in generateMetadataCatching failures avoids metadata generation crashes on SEO API outages.
51-56: OG title mapping fix is correctMapping openGraph.title to chainSeo.og.title resolves the prior bug.
85-85: Description fallback logic is goodPreferring customChainMetadata.about over SEO description aligns with the objectives.
140-141: Header image source switch looks rightPassing customChainMetadata?.headerImgUrl into ChainHeader is consistent with the refactor.
178-181: Description rendering LGTMWhitespace handling and responsive typography look good.
83-85: Guard fetchChainSeo in layout to avoid runtime crashes on SEO API outagesUnlike generateMetadata, this call isn’t caught. A thrown fetch will crash the layout render.
- const chainSeo = await fetchChainSeo(chain.chainId); + const chainSeo = await fetchChainSeo(chain.chainId).catch(() => undefined);

PR-Codex overview
This PR focuses on refactoring and enhancing components related to chain metadata and SEO within the dashboard application. It includes updates to function names, UI adjustments, and the introduction of a new FAQ section.
Detailed summary
getChainMetadatatogetCustomChainMetadata.ChainOverviewSectionandSupportedProductsSection.FaqSectioncomponent for displaying FAQs.fetchChainSeoto improve SEO handling.ChainPageLayoutandPagecomponents.Summary by CodeRabbit
New Features
Bug Fixes / Behavior
Style