Skip to content

Conversation

@MananTank
Copy link
Member

@MananTank MananTank commented Dec 16, 2025


PR-Codex overview

This PR focuses on enhancing the BridgeWidget component and its associated functionalities, including UI improvements, new options for token selection, and error handling enhancements. It also updates the metadata and documentation for better user guidance.

Detailed summary

  • Removed PayIcon and replaced with BringToFrontIcon.
  • Added showThirdwebBranding option in UniversalBridgeEmbed.
  • Improved token sorting in useBridgeSupportedChains.
  • Updated bridgeFeatureCards with new titles and icons.
  • Enhanced BridgeWidgetPlayground with new options for theme and branding.
  • Refactored error handling in BuyAndSwapEmbed.
  • Added new query parameters for default token amounts in documentation.
  • Improved UI elements in various components for better user experience.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Bridge Widget playground with interactive preview, code generation (script/component/iframe), theming, token/amount prefills, and preview modes.
    • Unified buy/swap UI via a single Bridge widget and a showThirdwebBranding toggle.
    • Richer wallet/connect configuration and ability to pass className/style to the widget.
  • Documentation

    • Added docs for token amounts, branding, and iframe examples.
  • Style

    • Small typography and spacing refinements.
  • Chores

    • Navigation and feature card updates; one image test skipped; icon updates.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 16, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
docs-v2 Ready Ready Preview, Comment Dec 16, 2025 8:52pm
nebula Ready Ready Preview, Comment Dec 16, 2025 8:52pm
thirdweb_playground Ready Ready Preview, Comment Dec 16, 2025 8:52pm
thirdweb-www Ready Ready Preview, Comment Dec 16, 2025 8:52pm
wallet-ui Ready Ready Preview, Comment Dec 16, 2025 8:52pm

@linear
Copy link

linear bot commented Dec 16, 2025

@changeset-bot
Copy link

changeset-bot bot commented Dec 16, 2025

⚠️ No Changeset found

Latest commit: aad3e64

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

Replace legacy buy/swap tab UI with a unified BridgeWidget entry point, add/forward a new showThirdwebBranding prop, extend BridgeWidget props (connectOptions, className, style), and add a Bridge Widget playground (UI, codegen, iframe builder), docs, nav entry, and small styling/lint fixes.

Changes

Cohort / File(s) Change Summary
Dashboard Bridge Embed
apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx, apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx, apps/dashboard/src/app/bridge/widget/page.tsx
Consolidate Buy/Swap flows to BridgeWidget; remove internal tabs and TabButton usage; add showThirdwebBranding?: boolean prop and forward it; parse/forward buy/sell amount query params as strings; rewire onError/onCancel/onSuccess analytics callbacks.
BridgeWidget Core Props & Styling
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
Add public className?: string, style?: React.CSSProperties, and a comprehensive connectOptions surface; propagate connectOptions to child widgets and adjust EmbedContainer/TabButton sizing/styling.
Playground — Main Page & Nav
apps/playground-web/src/app/bridge/bridge-widget/page.tsx, apps/playground-web/src/app/navLinks.ts, apps/playground-web/src/app/bridge/page.tsx
Add Bridge Widget playground page and metadata; add nav link /bridge/bridge-widget; swap Overview icon to BringToFrontIcon.
Playground — Playground UI
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx, apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx, apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx, apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
Add BridgeWidgetPlayground with LeftSection (token selection, amounts, appearance, branding toggle) and RightSection (Preview/Code tabs); introduce BridgeWidgetPlaygroundOptions type; sync theme and prefill; preview supports component/script or iframe modes and passes branding/prefill/amounts.
Playground — Code & URL Utilities
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx, apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
Add CodeGen producing script/component/iframe snippets (including theme/branding/prefill/amounts) and buildIframeUrl helper to compose iframe URLs with query params for tokens, chains, amounts, theme, and branding.
Playground — UI Primitives
apps/playground-web/src/components/ui/tab-buttons.tsx
New TabButtons component with scrollable tabs, icons/tooltips, animated underline positioned via ResizeObserver, and optional styling hooks.
Playground — Page & Data Updates
apps/playground-web/src/app/bridge/bridge-widget/page.tsx, apps/playground-web/src/app/data/pages-metadata.ts, apps/playground-web/src/app/bridge/page.tsx
Add playground page export/metadata; update bridge feature cards icons/content to SquareIcon; update Overview icon.
Removed / Adjusted Icons & Stories
apps/playground-web/src/icons/PayIcon.tsx, packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
Remove PayIcon file; remove LightTheme story and adjust CustomTheme composition.
Docs / Portal
apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx
Document inputCurrencyAmount/outputCurrencyAmount query params and showThirdwebBranding, add examples and update persist-selection semantics.
Minor Styling / Lints / Small Fixes
apps/dashboard/src/app/.../erc20/erc20.tsx, apps/playground-web/src/components/blocks/FeatureCard.tsx, apps/playground-web/src/app/transactions/users/page.tsx, apps/playground-web/src/app/ai/components/ChatPageContent.tsx, apps/playground-web/src/hooks/chains.ts, apps/playground-web/src/app/x402/components/X402LeftSection.tsx, packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
Typography weight tweaks, stricter NaN check (Number.isNaN), sorting of bridge-supported-chains, lint suppression comment, small JSX refactor, update/skip image test and other minor cleanups.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant PlaygroundUI as BridgeWidgetPlayground
  participant CodeGen
  participant BridgeWidget
  participant Iframe as WidgetIframe

  User->>PlaygroundUI: configure options (theme, prefill, amounts, branding, integrationType)
  PlaygroundUI->>PlaygroundUI: update options state
  alt integrationType == "component" or "script"
    PlaygroundUI->>BridgeWidget: render with props (theme, buy/sell prefill, currency, showThirdwebBranding, connectOptions)
    BridgeWidget-->>User: interactive widget (buy/swap flows)
    BridgeWidget->>PlaygroundUI: emit events (onSuccess/onError/onCancel)
  else integrationType == "iframe"
    PlaygroundUI->>CodeGen: build iframe URL via buildIframeUrl(options)
    CodeGen->>Iframe: provide iframe src
    Iframe-->>User: iframe loads remote widget with query params (tokens, amounts, theme, branding)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas that may need extra attention:
    • BridgeWidgetProps additions and propagation of connectOptions/className/style to children.
    • Callback/analytics wiring changes in BuyAndSwapEmbed and UniversalBridgeEmbed (payloads, pageType preservation).
    • Code generation templates and buildIframeUrl query encoding (amounts as strings, branding flag).
    • Playground Left/Right components: keyed resets, token selection defaults (native vs token addresses), and theme override application.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.03% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive PR description uses template comments rather than filling out required sections; only PR-Codex summary is provided without explicit 'Notes for reviewer' or 'How to test' sections. Fill out the description template by completing 'Notes for the reviewer' and 'How to test' sections with relevant testing instructions and important context.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR title clearly and concisely describes the main change: adding Bridge Widget playgrounds for three integration modes (script, iframe, react).
Linked Issues check ✅ Passed All objectives from MNY-226 are met: separate pages/tabs for script/iframe/component modes [MNY-226], iframe renders actual iframe while script/component render component directly [MNY-226], token amount configuration via query parameters added [MNY-226], and configurable options for branding/theming [MNY-226].
Out of Scope Changes check ✅ Passed All changes are directly aligned with MNY-226 objectives: playground UI enhancements, token/chain sorting, icon replacements, nav links, and component integration are all necessary for the bridge widget playground feature.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mny-226

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added Dashboard Involves changes to the Dashboard. Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase. packages SDK Involves changes to the thirdweb SDK labels Dec 16, 2025
Copy link
Member Author


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (8)
apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1)

66-79: Prefer a type guard over non-null assertion.

The non-null assertion bypasses TypeScript's type safety. While the assertion appears safe due to conditional rendering, a type guard makes the safety explicit and prevents potential runtime errors if the component structure changes.

As per coding guidelines, avoid type-safety bypasses unless unavoidable.

Apply this diff to use a type guard:

 const handleTokenChange = (token: TokenMetadata) => {
+  if (selectedChain === undefined) {
+    return;
+  }
+
   setSelectedToken({
     address: token.address,
-    // biome-ignore lint/style/noNonNullAssertion: ok
-    chainId: selectedChain!,
+    chainId: selectedChain,
   });

   setOptions((v) => ({
     ...v,
     tokenAddress: token.address as `0x${string}`,
     tokenSymbol: token.symbol ?? "",
     tokenDecimals: token.decimals ?? 18,
   }));
 };
apps/playground-web/src/app/data/pages-metadata.ts (1)

120-147: Consider using distinct icons for better visual scanning.

Using SquareIcon uniformly across five bridge widgets reduces visual distinction between card types. Different icons help users quickly scan and identify the widget they need.

Additionally, note the inconsistency: the Transaction Button card (line 154) still uses RectangleHorizontalIcon while other bridge cards were updated to SquareIcon.

If icon uniformity is a deliberate design decision, consider documenting the rationale. Otherwise, restoring distinct icons (e.g., credit card for Buy Widget, arrows for Bridge Widget) would improve the user experience.

apps/playground-web/src/hooks/chains.ts (1)

7-16: Consider reusing the BridgeChain type from packages.

The local BridgeChain type might duplicate packages/thirdweb/src/bridge/types/Chain.ts. If the package type matches these requirements, importing it would reduce duplication.

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (1)

119-152: Consider extracting chainId resolution logic.

The pattern of extracting buyChainId from quote types is repeated across error, cancel, and success handlers (lines 122-127, 155-160, 187-192). This could be refactored into a helper function to reduce duplication and improve maintainability.

Example helper:

+function extractBuyChainId(quote: BuyOrOnrampPrepareResult | undefined): number | undefined {
+  if (!quote) return undefined;
+  return quote.type === "buy"
+    ? quote.intent.destinationChainId
+    : quote.type === "onramp"
+      ? quote.intent.chainId
+      : undefined;
+}

Then simplify the handlers:

 onError: (e, quote) => {
   const errorMessage = parseError(e);
-  const buyChainId =
-    quote?.type === "buy"
-      ? quote.intent.destinationChainId
-      : quote?.type === "onramp"
-        ? quote.intent.chainId
-        : undefined;
+  const buyChainId = extractBuyChainId(quote);
apps/portal/tsconfig.json (1)

38-38: Consider moving shared UI component to a common package.

Including ../playground-web/src/components/ui/tab-buttons.tsx directly from the portal app creates tight coupling between these applications. Shared components are typically better placed in a common UI package (e.g., packages/ui or similar) to:

  • Avoid build ordering issues
  • Improve reusability
  • Prevent circular dependencies
  • Clarify component ownership

If this is a temporary solution for prototyping, consider tracking this as technical debt.

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (2)

103-106: Replace inline style with Tailwind class.

Per coding guidelines, use Tailwind CSS instead of inline styles.

  <iframe
    src={buildIframeUrl(props.options)}
    height="750px"
    width="100%"
    title="Bridge Widget"
    className="fade-in-0 duration-500 animate-in rounded-xl"
-   style={{
-     border: "0",
-   }}
+   className="fade-in-0 duration-500 animate-in rounded-xl border-0"
  />

115-143: Consider reusing the shared TabButtons component.

A shared TabButtons component exists at @/components/ui/tab-buttons. This local implementation duplicates functionality. If the shared component's features (underline animation, tooltips) aren't needed here, the local version is acceptable, but consider using the shared one with hideBottomLine prop for consistency.

apps/playground-web/src/components/ui/tab-buttons.tsx (1)

107-145: Consider using requestAnimationFrame instead of setTimeout.

The setTimeout calls at lines 119-121 and 132-134 use arbitrary delays. Using requestAnimationFrame would be more reliable for visual updates and avoid potential timing issues:

- setTimeout(() => {
-   lineEl.style.transition = "transform 0.3s, width 0.3s";
- }, 0);
+ requestAnimationFrame(() => {
+   lineEl.style.transition = "transform 0.3s, width 0.3s";
+ });
  resizeObserver = new ResizeObserver(() => {
-   setTimeout(() => {
-     update();
-   }, 100);
+   requestAnimationFrame(update);
  });
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 4bab6f1 and 1f3130c.

📒 Files selected for processing (25)
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1 hunks)
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/widget/page.tsx (3 hunks)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/page.tsx (2 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (1 hunks)
  • apps/playground-web/src/app/transactions/users/page.tsx (2 hunks)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/FeatureCard.tsx (1 hunks)
  • apps/playground-web/src/components/ui/tab-buttons.tsx (1 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/icons/PayIcon.tsx (0 hunks)
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3 hunks)
  • apps/portal/tsconfig.json (1 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (8 hunks)
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/playground-web/src/icons/PayIcon.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/portal/tsconfig.json
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
apps/dashboard/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/dashboard/src/**/*.{ts,tsx}: Use NavLink for internal navigation with automatic active states in dashboard
Start server component files with import "server-only"; in Next.js
Read cookies/headers with next/headers in server components
Access server-only environment variables in server components
Perform heavy data fetching in server components
Implement redirect logic with redirect() from next/navigation in server components
Begin client component files with 'use client'; directive in Next.js
Handle interactive UI with React hooks (useState, useEffect, React Query, wallet hooks) in client components
Access browser APIs (localStorage, window, IntersectionObserver) in client components
Support fast transitions with prefetched data in client components
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header for API calls – never embed tokens in URLs
Return typed results (Project[], User[]) from server-side data fetches – avoid any
Wrap client-side API calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys in React Query for cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components – only use analytics client-side

Files:

  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

apps/dashboard/**/*.{ts,tsx}: Always import from the central UI library under @/components/ui/* for reusable core UI components like Button, Input, Select, Tabs, Card, Sidebar, Separator, Badge
Use NavLink from @/components/ui/NavLink for internal navigation to ensure active states are handled automatically
For notices and skeletons, rely on AnnouncementBanner, GenericLoadingPage, and EmptyStateCard components
Import icons from lucide-react or the project-specific …/icons exports; never embed raw SVG
Keep components pure; fetch data outside using server components or hooks and pass it down via props
Use Tailwind CSS as the styling system; avoid inline styles or CSS modules
Merge class names with cn from @/lib/utils to keep conditional logic readable
Stick to design tokens: use bg-card, border-border, text-muted-foreground and other Tailwind variables instead of hard-coded colors
Use spacing utilities (px-*, py-*, gap-*) instead of custom margins
Follow mobile-first responsive design with Tailwind helpers (max-sm, md, lg, xl)
Never hard-code colors; always use Tailwind variables
Combine class names via cn, and expose className prop if useful in components
Use React Query (@tanstack/react-query) for all client-side data fetching with typed hooks

Files:

  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{tsx,ts}: Import UI primitives from @/components/ui/_ (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in Dashboard and Playground apps
Use NavLink for internal navigation so active states are handled automatically
Use Tailwind CSS for styling – no inline styles or CSS modules
Merge class names with cn() from @/lib/utils to keep conditional logic readable
Stick to design tokens for styling: backgrounds (bg-card), borders (border-border), muted text (text-muted-foreground), etc.
Server Components: Read cookies/headers with next/headers, access server-only environment variables or secrets, perform heavy data fetching, implement redirect logic with redirect() from next/navigation, and start files with import 'server-only'; to prevent client bundling
Client Components: Begin files with 'use client'; before imports, handle interactive UI relying on React hooks (useState, useEffect, React Query, wallet hooks), access browser APIs (localStorage, window, IntersectionObserver, etc.), and support fast transitions with client-side data prefetching
For client-side data fetching: Wrap calls in React Query (@tanstack/react-query), use descriptive and stable queryKeys for cache hits, configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s), and keep tokens secret by calling internal API routes or server actions

Files:

  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: For server-side data fetching: Always call getAuthToken() to retrieve the JWT from cookies and inject the token as an Authorization: Bearer header – never embed it in the URL. Return typed results (Project[], User[], …) – avoid any
Never import posthog-js in server components; analytics reporting is client-side only

Files:

  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/page.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Use the container class with a max-w-7xl cap for consistent page width

Files:

  • apps/dashboard/src/app/bridge/widget/page.tsx
apps/dashboard/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Add className prop to the root element of every component to allow external overrides

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/components/**/*.{tsx,ts}: Group feature-specific components under feature/components/_ and expose a barrel index.ts when necessary
Expose a className prop on the root element of every component for styling overrides

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Add Storybook stories (*.stories.tsx) alongside new UI components for documentation

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

For new UI components, add Storybook stories (*.stories.tsx) alongside the code

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
🧬 Code graph analysis (7)
apps/playground-web/src/app/bridge/bridge-widget/page.tsx (2)
apps/playground-web/src/app/bridge/page.tsx (1)
  • Page (5-14)
apps/playground-web/src/components/blocks/APIHeader.tsx (1)
  • PageLayout (49-70)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/components/ui/tab-buttons.tsx (1)
  • TabButtons (16-96)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)
  • LeftSection (20-119)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)
  • RightSection (13-113)
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/lib/code-gen.ts (2)
  • quotes (17-19)
  • stringifyImports (21-29)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
  • buildIframeUrl (5-67)
apps/playground-web/src/app/bridge/components/CodeGen.tsx (1)
  • CodeGen (24-39)
apps/playground-web/src/hooks/chains.ts (1)
packages/thirdweb/src/bridge/types/Chain.ts (1)
  • BridgeChain (42-42)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (3)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
  • buildIframeUrl (5-67)
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1)
  • CodeGen (21-33)
apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3)
packages/thirdweb/src/exports/react.ts (1)
  • BridgeWidget (148-148)
apps/dashboard/src/@/constants/connect.ts (1)
  • appMetadata (1-5)
apps/dashboard/src/@/analytics/report.ts (9)
  • reportTokenBuyFailed (264-266)
  • reportAssetBuyFailed (342-350)
  • reportTokenBuyCancelled (276-278)
  • reportAssetBuyCancelled (360-367)
  • reportTokenBuySuccessful (252-254)
  • reportAssetBuySuccessful (221-228)
  • reportTokenSwapFailed (314-320)
  • reportTokenSwapSuccessful (288-290)
  • reportTokenSwapCancelled (330-332)
🪛 LanguageTool
apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx

[style] ~103-~103: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...9&outputChain=137" />

### Set token amounts You can set default toke...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

⏰ 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). (8)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Lint Packages
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Build Packages
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (26)
apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1)

87-87: Prefer Number.isNaN() over global isNaN().

The change to use Number.isNaN() is the idiomatic choice for numeric validation in modern TypeScript. While both functions produce the same result here (since Number(id) is called explicitly), Number.isNaN() is more explicit and avoids relying on legacy global coercion behavior. As per coding guidelines, this aligns with writing idiomatic TypeScript.

apps/playground-web/src/components/blocks/FeatureCard.tsx (1)

16-16: LGTM!

The font weight increase to font-semibold improves the visual hierarchy for card titles.

apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1)

286-286: LGTM – Typography refinement aligns with design system updates.

The change from font-bold to font-semibold is a deliberate design refinement that maintains readability while improving visual hierarchy for token metric values.

apps/playground-web/src/app/data/pages-metadata.ts (1)

120-125: LGTM: Explicit field structure improves clarity.

The addition of explicit title, link, and description fields makes the first Bridge Widget entry more consistent with the other entries and improves code readability.

apps/playground-web/src/hooks/chains.ts (1)

33-48: LGTM! Clean sorting implementation.

The sorting logic correctly prioritizes non-numeric chain names and handles edge cases appropriately. The optional chaining on line 36-37 safely handles empty strings, and localeCompare ensures locale-aware alphabetical ordering.

apps/playground-web/src/app/transactions/users/page.tsx (1)

34-97: LGTM! Unnecessary fragment removed.

The React fragment was redundant since UserTransactions returns a single element. Removing it improves code clarity.

packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1)

45-67: Direct rendering is appropriate for custom theme objects.

The change from using the Variant helper to directly rendering BridgeWidgetScript is correct. The Variant helper overrides the theme prop with string values ("dark"/"light"), which would defeat the purpose of demonstrating a custom theme object that already specifies type: "light" within its structure. This aligns with the other stories (BasicUsage, CurrencySet, NoThirdwebBranding) which use Variant because they rely on theme strings, while CustomTheme uses direct rendering since it uses a theme object.

apps/playground-web/src/app/bridge/page.tsx (1)

1-1: LGTM! Icon choice is semantically appropriate.

The change from PayIcon to BringToFrontIcon better represents bridge functionality.

Also applies to: 11-11

apps/playground-web/src/app/navLinks.ts (1)

190-193: LGTM! Navigation entry is consistent and well-placed.

The new "Bridge Widget" link follows the established pattern and is logically positioned in the menu structure.

apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (1)

64-65: LGTM! Comprehensive documentation with clear examples.

The new sections for token amounts and branding control are well-documented with working examples. The structure is clear and consistent with the existing documentation style.

Also applies to: 103-122, 175-180

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (1)

104-259: LGTM! BridgeWidget integration is well-structured.

The refactor from separate Buy/Swap widgets to BridgeWidget is comprehensive. Analytics reporting is properly preserved, callbacks are correctly wired, and error handling covers all necessary cases.

packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (2)

35-36: LGTM! API expansion is well-structured and documented.

The addition of className, style, and connectOptions props significantly enhances configurability while maintaining backward compatibility. The connectOptions documentation is comprehensive and follows the established pattern of other thirdweb components.

Also applies to: 202-311


471-479: LGTM! Explicit styling improves consistency.

The TabButton styling changes from implicit to explicit values (height: "36px", paddingInline/paddingBlock) improve predictability and maintainability.

apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (1)

28-28: LGTM! Proper prop threading.

The showThirdwebBranding prop is correctly added and forwarded to BuyAndSwapEmbed.

Also applies to: 38-38

apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)

9-66: LGTM! URL building logic is clean and efficient.

The implementation correctly:

  • Disables token persistence for playground consistency
  • Only adds non-default parameters to keep URLs clean
  • Properly handles optional fields with conditional logic
apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1)

1-33: LGTM!

Clean page setup following the existing pattern from other bridge pages. Proper use of ThirdwebProvider, PageLayout, and createMetadata. Structure is consistent with the codebase conventions.

apps/dashboard/src/app/bridge/widget/page.tsx (2)

44-47: Verify default behavior for showThirdwebBranding.

When the showThirdwebBranding search param is not provided, parse returns undefined (not true). If the intent is to show branding by default when the param is absent, consider providing a fallback:

  const showThirdwebBranding = parse(
    searchParams.showThirdwebBranding,
    (v) => v !== "false",
- );
+ ) ?? true;

Otherwise, ensure UniversalBridgeEmbed correctly interprets undefined as "show branding."


77-99: LGTM on amount handling.

The conditional string conversion for buyAmount and sellAmount correctly handles the undefined case, avoiding potential runtime errors from calling .toString() on undefined.

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)

56-93: LGTM on BridgeWidget integration.

The widget configuration correctly passes theme, prefill data, currency, and branding options. The key prop using JSON.stringify ensures the widget re-renders when prefill changes.

apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1)

39-73: LGTM on layout and tab structure.

Clean composition of LeftSection and RightSection with proper state management. The responsive layout with xl: breakpoints and tab switching logic work correctly.

apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)

20-118: LGTM on LeftSection structure.

Well-organized configuration UI with proper separation of concerns. Good use of collapsible sections, form controls, and conditional rendering for iframe-specific restrictions on color customization.

apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (2)

120-202: LGTM on React component code generation.

Clean implementation with proper import management, theme handling, and prop construction. The stringifyImports and stringifyProps utilities keep the code generation readable.


54-72: I'm unable to verify this review comment because the repository could not be accessed and web search is not available. To complete verification, I need to:

  1. Examine the actual code at lines 54-72 in apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  2. Compare with the React component generation at lines 131-133
  3. Check the BridgeWidget API documentation to confirm the expected theme structure
  4. Verify if spreading overrides at the root level vs. nesting under colors is indeed an error

Please provide access to the codebase or confirm the file contents so I can validate whether the theme configuration inconsistency exists and if the suggested fix is correct.

apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)

1-28: LGTM!

Clean type definition that properly leverages thirdweb/react types for consistency. The structure with integrationType, theme, prefill, and showThirdwebBranding aligns well with the component requirements.

apps/playground-web/src/components/ui/tab-buttons.tsx (2)

16-95: LGTM on TabButtons component structure.

Well-designed reusable component with good customization options through className props. The active underline animation and tooltip integration provide a polished UX.


5-7: Replace cross-boundary imports from portal with @/components/ui aliases.

These imports reach into the portal app's source directory, violating coding guidelines. Per project conventions, import from @/components/ui/* in playground-web. Verify whether these components exist in apps/playground-web/src/components/ui/ or need to be extracted to a shared workspace package.

- import { Button } from "../../../../portal/src/components/ui/button";
- import { ScrollShadow } from "../../../../portal/src/components/ui/ScrollShadow";
- import { ToolTipLabel } from "../../../../portal/src/components/ui/tooltip";
+ import { Button } from "@/components/ui/button";
+ import { ScrollShadow } from "@/components/ui/ScrollShadow";
+ import { ToolTipLabel } from "@/components/ui/tooltip";

@github-actions
Copy link
Contributor

github-actions bot commented Dec 16, 2025

size-limit report 📦

Path Size
@thirdweb-dev/nexus (esm) 105.66 KB (0%)
@thirdweb-dev/nexus (cjs) 319.47 KB (0%)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)

174-185: Token selection clears the amount field.

When selecting a new token, the current amount value is lost because the update doesn't spread the existing prefill data for the token type. Apply the same pattern used in chain selection (lines 153-154):

  onChange={(token) => {
    setOptions((v) => ({
      ...v,
      prefill: {
        ...v.prefill,
        [props.type]: {
+         ...v.prefill?.[props.type],
          chainId: token.chainId,
          tokenAddress: token.address,
        },
      },
    }));
  }}
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1)

22-37: Use resolvedTheme for accurate theme detection.

The theme value from useTheme() can be "system", which doesn't match the expected "dark" | "light" comparison. Use resolvedTheme instead:

  export function BridgeWidgetPlayground() {
-   const { theme } = useTheme();
+   const { resolvedTheme } = useTheme();

    const [options, setOptions] =
      useState<BridgeWidgetPlaygroundOptions>(defaultOptions);

    // change theme on global theme change
    useEffect(() => {
      setOptions((prev) => ({
        ...prev,
        theme: {
          ...prev.theme,
-         type: theme === "dark" ? "dark" : "light",
+         type: resolvedTheme === "dark" ? "dark" : "light",
        },
      }));
-   }, [theme]);
+   }, [resolvedTheme]);
🧹 Nitpick comments (7)
apps/playground-web/src/hooks/chains.ts (1)

35-48: Simplify regex check for numeric-starting names.

The current approach uses a.name[0]?.match(/^\d/) to detect if a name starts with a digit. Consider using /^\d/.test(a.name) instead, which is more idiomatic and directly tests the entire string without needing to access the first character.

Apply this diff:

  return data.sort((a, b) => {
-    const aStartsWithNumber = a.name[0]?.match(/^\d/);
-    const bStartsWithNumber = b.name[0]?.match(/^\d/);
+    const aStartsWithNumber = /^\d/.test(a.name);
+    const bStartsWithNumber = /^\d/.test(b.name);

    if (aStartsWithNumber && !bStartsWithNumber) {
      return 1;
    }

    if (!aStartsWithNumber && bStartsWithNumber) {
      return -1;
    }

    return a.name.localeCompare(b.name);
  });
apps/playground-web/src/app/data/pages-metadata.ts (1)

120-120: Consider using distinct icons for visual differentiation.

All five bridge widget cards now use SquareIcon, which reduces visual hierarchy and makes it harder for users to quickly scan and distinguish between options. Consider assigning unique icons (e.g., ArrowLeftRightIcon for Bridge, RepeatIcon for Swap, DollarSignIcon for Buy) to improve scannability.

Also applies to: 127-127, 133-133, 140-140, 147-147

packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (4)

13-20: Add explicit return types to story functions.

These exported story functions lack explicit return types. As per coding guidelines, TypeScript functions should have explicit return types.

Apply this diff to add return types:

-export function BasicUsage() {
+export function BasicUsage(): React.JSX.Element {
   return (
     <Variant
       clientId={storyClient.clientId}
       buy={{ chainId: 8453, amount: "0.1" }}
     />
   );
 }

-export function CurrencySet() {
+export function CurrencySet(): React.JSX.Element {
   return (
     <Variant
       clientId={storyClient.clientId}
       currency="JPY"
       buy={{ chainId: 8453, amount: "0.1" }}
     />
   );
 }

-export function NoThirdwebBranding() {
+export function NoThirdwebBranding(): React.JSX.Element {
   return (
     <Variant
       clientId={storyClient.clientId}
       theme="light"
       buy={{ chainId: 8453, amount: "0.1" }}
       showThirdwebBranding={false}
     />
   );
 }

Also applies to: 22-30, 32-41


43-69: Add explicit return type to CustomTheme story.

The CustomTheme function lacks an explicit return type. As per coding guidelines, TypeScript functions should have explicit return types.

Apply this diff:

-export function CustomTheme() {
+export function CustomTheme(): React.JSX.Element {
   return (
     <div

The refactored implementation (directly rendering BridgeWidgetScript with custom theme) looks good and is more appropriate for demonstrating custom theming than the previous Variant-based approach.


71-85: Add explicit return type to Variant helper.

The Variant helper function lacks an explicit return type. As per coding guidelines, TypeScript functions should have explicit return types.

Apply this diff:

-function Variant(props: BridgeWidgetScriptProps) {
+function Variant(props: BridgeWidgetScriptProps): React.JSX.Element {
   return (

17-17: Consider extracting repeated buy configuration.

The buy={{ chainId: 8453, amount: "0.1" }} configuration is repeated across all stories. Consider extracting it to a constant for maintainability.

Example:

const DEFAULT_BUY_CONFIG = { chainId: 8453, amount: "0.1" } as const;

export function BasicUsage(): React.JSX.Element {
  return (
    <Variant
      clientId={storyClient.clientId}
      buy={DEFAULT_BUY_CONFIG}
    />
  );
}

Also applies to: 27-27, 37-37, 55-55

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)

115-143: Consider reusing the shared TabButtons component.

A TabButtons component already exists at @/components/ui/tab-buttons.tsx with more features (icons, tooltips, disabled states, underline animation). Consider using it here for consistency, or if the simpler styling is intentional for this context, add a brief comment explaining the choice.

+"use client";
+import { useState } from "react";
+import { BridgeWidget, darkTheme, lightTheme } from "thirdweb/react";
+import { Button } from "@/components/ui/button";
++import { TabButtons } from "@/components/ui/tab-buttons";
+import { THIRDWEB_CLIENT } from "@/lib/client";

Then replace the local TabButtons usage with the shared one, or keep the local version if the simplified styling is intentional for this pill-button design.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 1f3130c and 23d69fd.

📒 Files selected for processing (24)
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1 hunks)
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/widget/page.tsx (3 hunks)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/page.tsx (2 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (1 hunks)
  • apps/playground-web/src/app/transactions/users/page.tsx (2 hunks)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/FeatureCard.tsx (1 hunks)
  • apps/playground-web/src/components/ui/tab-buttons.tsx (1 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/icons/PayIcon.tsx (0 hunks)
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (8 hunks)
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/playground-web/src/icons/PayIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Add Storybook stories (*.stories.tsx) alongside new UI components for documentation

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

For new UI components, add Storybook stories (*.stories.tsx) alongside the code

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
apps/dashboard/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/dashboard/src/**/*.{ts,tsx}: Use NavLink for internal navigation with automatic active states in dashboard
Start server component files with import "server-only"; in Next.js
Read cookies/headers with next/headers in server components
Access server-only environment variables in server components
Perform heavy data fetching in server components
Implement redirect logic with redirect() from next/navigation in server components
Begin client component files with 'use client'; directive in Next.js
Handle interactive UI with React hooks (useState, useEffect, React Query, wallet hooks) in client components
Access browser APIs (localStorage, window, IntersectionObserver) in client components
Support fast transitions with prefetched data in client components
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header for API calls – never embed tokens in URLs
Return typed results (Project[], User[]) from server-side data fetches – avoid any
Wrap client-side API calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys in React Query for cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components – only use analytics client-side

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

apps/dashboard/**/*.{ts,tsx}: Always import from the central UI library under @/components/ui/* for reusable core UI components like Button, Input, Select, Tabs, Card, Sidebar, Separator, Badge
Use NavLink from @/components/ui/NavLink for internal navigation to ensure active states are handled automatically
For notices and skeletons, rely on AnnouncementBanner, GenericLoadingPage, and EmptyStateCard components
Import icons from lucide-react or the project-specific …/icons exports; never embed raw SVG
Keep components pure; fetch data outside using server components or hooks and pass it down via props
Use Tailwind CSS as the styling system; avoid inline styles or CSS modules
Merge class names with cn from @/lib/utils to keep conditional logic readable
Stick to design tokens: use bg-card, border-border, text-muted-foreground and other Tailwind variables instead of hard-coded colors
Use spacing utilities (px-*, py-*, gap-*) instead of custom margins
Follow mobile-first responsive design with Tailwind helpers (max-sm, md, lg, xl)
Never hard-code colors; always use Tailwind variables
Combine class names via cn, and expose className prop if useful in components
Use React Query (@tanstack/react-query) for all client-side data fetching with typed hooks

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Add className prop to the root element of every component to allow external overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{tsx,ts}: Import UI primitives from @/components/ui/_ (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in Dashboard and Playground apps
Use NavLink for internal navigation so active states are handled automatically
Use Tailwind CSS for styling – no inline styles or CSS modules
Merge class names with cn() from @/lib/utils to keep conditional logic readable
Stick to design tokens for styling: backgrounds (bg-card), borders (border-border), muted text (text-muted-foreground), etc.
Server Components: Read cookies/headers with next/headers, access server-only environment variables or secrets, perform heavy data fetching, implement redirect logic with redirect() from next/navigation, and start files with import 'server-only'; to prevent client bundling
Client Components: Begin files with 'use client'; before imports, handle interactive UI relying on React hooks (useState, useEffect, React Query, wallet hooks), access browser APIs (localStorage, window, IntersectionObserver, etc.), and support fast transitions with client-side data prefetching
For client-side data fetching: Wrap calls in React Query (@tanstack/react-query), use descriptive and stable queryKeys for cache hits, configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s), and keep tokens secret by calling internal API routes or server actions

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/components/**/*.{tsx,ts}: Group feature-specific components under feature/components/_ and expose a barrel index.ts when necessary
Expose a className prop on the root element of every component for styling overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: For server-side data fetching: Always call getAuthToken() to retrieve the JWT from cookies and inject the token as an Authorization: Bearer header – never embed it in the URL. Return typed results (Project[], User[], …) – avoid any
Never import posthog-js in server components; analytics reporting is client-side only

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
🧬 Code graph analysis (6)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (2)
packages/thirdweb/src/script-exports/bridge-widget-script.tsx (1)
  • BridgeWidgetScript (69-79)
packages/thirdweb/src/stories/utils.tsx (1)
  • storyClient (14-16)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (6)
apps/playground-web/src/app/wallets/sign-in/components/CollapsibleSection.tsx (1)
  • CollapsibleSection (4-33)
packages/thirdweb/src/react/web/ui/components/formElements.tsx (1)
  • Label (20-28)
apps/playground-web/src/app/wallets/sign-in/components/ColorFormGroup.tsx (1)
  • ColorFormGroup (9-111)
apps/dashboard/src/@/components/ui/checkbox.tsx (1)
  • Checkbox (34-34)
apps/playground-web/src/app/x402/components/constants.ts (1)
  • token (4-9)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • NATIVE_TOKEN_ADDRESS (31-31)
apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (4)
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)
  • BridgeWidget (363-455)
packages/thirdweb/src/exports/react.ts (1)
  • BridgeWidget (148-148)
apps/dashboard/src/@/constants/connect.ts (1)
  • appMetadata (1-5)
apps/dashboard/src/@/analytics/report.ts (9)
  • reportTokenBuyFailed (264-266)
  • reportAssetBuyFailed (342-350)
  • reportTokenBuyCancelled (276-278)
  • reportAssetBuyCancelled (360-367)
  • reportTokenBuySuccessful (252-254)
  • reportAssetBuySuccessful (221-228)
  • reportTokenSwapFailed (314-320)
  • reportTokenSwapSuccessful (288-290)
  • reportTokenSwapCancelled (330-332)
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/components/ui/tab-buttons.tsx (1)
  • TabButtons (16-96)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)
  • LeftSection (20-119)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)
  • RightSection (13-113)
apps/playground-web/src/hooks/chains.ts (1)
packages/thirdweb/src/bridge/types/Chain.ts (1)
  • BridgeChain (42-42)
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (2)
packages/thirdweb/src/exports/react.ts (2)
  • ConnectButton_connectModalOptions (16-16)
  • SiweAuthOptions (12-12)
packages/thirdweb/src/wallets/types.ts (1)
  • AppMetadata (3-20)
⏰ 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). (5)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: Unit Tests
  • GitHub Check: Lint Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (15)
apps/playground-web/src/app/data/pages-metadata.ts (1)

120-125: LGTM!

The Bridge Widget entry now has explicit metadata fields that align with the feature card structure and accurately describe the widget's capabilities (bridging with cross-chain support and fiat onramp).

apps/playground-web/src/app/navLinks.ts (1)

190-193: LGTM!

The new Bridge Widget navigation link follows the established pattern and is correctly placed in the bridge section.

apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (2)

1-19: LGTM!

Imports are well-organized and appropriate for the component's functionality. Good use of thirdweb utilities and UI components from the shared library.


20-119: LGTM!

The LeftSection component properly manages theme state and conditionally disables color customization for iframe mode (which makes sense since iframes can't pass complex theme objects).

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (2)

103-259: LGTM!

The refactored BridgeWidget integration properly consolidates buy/swap flows and correctly propagates the new showThirdwebBranding prop. The error handling callbacks appropriately extract chain/token information from quotes and report to analytics.


227-256: Swap callbacks correctly access quote intent data.

The swap onError, onSuccess, and onCancel handlers safely access quote.intent properties since SwapPreparedQuote guarantees these fields are present when the callbacks are invoked.

packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (3)

34-36: LGTM!

Good addition of className and style props for styling flexibility, following React component conventions.


202-311: Comprehensive connectOptions configuration.

The connectOptions prop is well-documented with JSDoc comments and provides flexible wallet connection configuration. This aligns with existing patterns in the thirdweb SDK.


376-454: Props correctly forwarded to child widgets.

The className, style, and connectOptions props are properly applied to EmbedContainer and forwarded to SwapWidget/BuyWidget respectively.

apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1)

1-33: LGTM!

The page follows the established playground pattern with proper metadata configuration, ThirdwebProvider wrapping, and PageLayout usage with appropriate props.

apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (2)

10-20: LGTM!

Default options are properly structured with sensible defaults for the playground.


39-73: LGTM!

The layout structure with TabButtons and responsive flex layout is well-implemented. The tab click handlers correctly update the integrationType.

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (2)

1-32: LGTM!

Imports and theme object construction are correct. The conditional theme creation based on options.theme.type properly applies color overrides.


56-93: Good use of key prop for remounting on prefill changes.

Using key={JSON.stringify(props.options.prefill)} ensures the BridgeWidget remounts when prefill data changes, which is appropriate for resetting the widget state.

apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1)

85-98: The overlap between buy and swap.prefill properties is intentional and correct.

The BridgeWidget renders two separate tabs with distinct widgets: the Buy tab uses BuyWidget with individual props (amount, chainId, tokenAddress), while the Swap tab uses SwapWidget with a prefill object. When buyToken is specified in options, populating both properties allows:

  • The Buy tab to be pre-configured with the token for purchasing via onramp
  • The Swap tab to have the token pre-selected as the starting buyToken for swapping

This design ensures both tabs are properly initialized when a user specifies a buyToken, which is the expected behavior.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (3)
apps/playground-web/src/app/transactions/users/page.tsx (1)

32-32: Add explicit return type annotation.

The UserTransactions function is still missing an explicit return type, which is required by the coding guidelines.

Apply this diff:

-function UserTransactions() {
+function UserTransactions(): JSX.Element {
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1)

23-37: Use resolvedTheme for accurate theme detection.

The theme value from useTheme() can be "system", which doesn't match your expected "dark" | "light". Use resolvedTheme instead.

apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)

174-185: Token selection clears the amount field.

When selecting a new token, the current amount value is lost because the update doesn't spread the existing prefill data. This differs from the chain selection logic (lines 148-159) and amount input logic (lines 210-223) which both preserve other fields.

🧹 Nitpick comments (12)
apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1)

66-79: Make the suppression comment more descriptive or add a defensive guard.

The biome-ignore comment "ok" doesn't explain why the non-null assertion on line 70 is safe. While the assertion is technically safe (because TokenSelector only renders when selectedChain is defined), the vague comment harms maintainability.

Consider one of these approaches:

Option 1: Replace with a descriptive comment explaining the safety:

-    // biome-ignore lint/style/noNonNullAssertion: ok
+    // biome-ignore lint/style/noNonNullAssertion: TokenSelector only renders when selectedChain is defined
     chainId: selectedChain!,

Option 2: Add a defensive guard (preferred for robustness):

 const handleTokenChange = (token: TokenMetadata) => {
+  if (!selectedChain) return;
+
   setSelectedToken({
     address: token.address,
-    // biome-ignore lint/style/noNonNullAssertion: ok
     chainId: selectedChain!,
   });
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (2)

45-51: Consider extracting duplicated wrapper styling.

The div styling here is identical to the Variant helper's wrapper styling (lines 74-79). To reduce duplication, consider extracting a shared StoryWrapper component or const.

Example refactor:

const storyWrapperStyle = {
  display: "flex",
  flexDirection: "column",
  gap: "40px",
  alignItems: "center",
} as const;

export function CustomTheme() {
  return (
    <div style={storyWrapperStyle}>
      <BridgeWidgetScript
        // ... props
      />
    </div>
  );
}

function Variant(props: BridgeWidgetScriptProps) {
  return (
    <div style={storyWrapperStyle}>
      {/* ... */}
    </div>
  );
}

43-43: Add explicit return type annotation.

Per coding guidelines, TypeScript functions should have explicit return types.

As per coding guidelines, TypeScript functions should have explicit return types.

-export function CustomTheme() {
+export function CustomTheme(): JSX.Element {
apps/dashboard/src/app/bridge/widget/page.tsx (1)

44-47: Non-standard boolean parsing could confuse users.

The current logic treats any value other than the exact string "false" as true. This means "0", "no", "FALSE", "off", etc., would all be interpreted as true, which may not match user expectations.

Consider using a more explicit allow-list approach:

-  const showThirdwebBranding = parse(
-    searchParams.showThirdwebBranding,
-    (v) => v !== "false",
-  );
+  const showThirdwebBranding = parse(
+    searchParams.showThirdwebBranding,
+    (v) => v === "true" || v === "1",
+  ) ?? true; // default to true if not specified

Alternatively, if the current behavior is intentional, document it clearly in the query parameter documentation.

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (1)

119-217: Extract helper for buy token info to reduce duplication.

The logic for extracting buyChainId and buyTokenAddress from quotes is duplicated across onError, onCancel, and onSuccess callbacks. This increases maintenance burden and the risk of inconsistencies.

Consider extracting a helper function:

function getBuyTokenInfo(quote: BuyQuote | OnrampQuote | undefined) {
  if (!quote) return undefined;
  
  if (quote.type === "buy") {
    return {
      chainId: quote.intent.destinationChainId,
      tokenAddress: quote.intent.destinationTokenAddress,
    };
  }
  
  if (quote.type === "onramp") {
    return {
      chainId: quote.intent.chainId,
      tokenAddress: quote.intent.tokenAddress,
    };
  }
  
  return undefined;
}

Then use it in callbacks:

onError: (e, quote) => {
  const errorMessage = parseError(e);
  const tokenInfo = getBuyTokenInfo(quote);
  
  if (!tokenInfo) return;
  
  reportTokenBuyFailed({
    buyTokenChainId: tokenInfo.chainId,
    buyTokenAddress: tokenInfo.tokenAddress,
    pageType: props.pageType,
  });
  
  if (props.pageType === "asset") {
    reportAssetBuyFailed({
      assetType: "coin",
      chainId: tokenInfo.chainId,
      error: errorMessage,
      contractType: undefined,
      is_testnet: false,
    });
  }
},
apps/playground-web/src/app/data/pages-metadata.ts (1)

120-160: Consider unique icons for each bridge widget to improve visual distinction.

The bridgeFeatureCards currently uses SquareIcon for all five widgets (Bridge, Swap, Buy, Checkout, Transaction Widget) while the Transaction Button uses RectangleHorizontalIcon. This icon choice appears intentional—Transaction Button uses the same icon pattern as "Connect Button" in the wallets section, semantically distinguishing button-like components from widget components.

However, reusing the same icon for five distinct widgets reduces visual distinction compared to other feature card groups like walletsFeatureCards, which assigns unique icons to each item. Consider whether each bridge widget would benefit from a unique icon to improve user discoverability and visual hierarchy.

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (2)

115-143: Consider using the shared TabButtons component instead of duplicating.

There's already a TabButtons component at @/components/ui/tab-buttons with additional features (underline animation, icons, tooltips). This local implementation duplicates functionality and diverges in styling.

-import { Button } from "@/components/ui/button";
+import { TabButtons } from "@/components/ui/tab-buttons";

Then remove the local TabButtons function (lines 115-143) and update the usage at lines 35-48 to match the shared component's API.


103-105: Use Tailwind class instead of inline style.

Per coding guidelines, use Tailwind CSS only – no inline styles.

            className="fade-in-0 duration-500 animate-in rounded-xl"
-           style={{
-             border: "0",
-           }}
+           className="fade-in-0 duration-500 animate-in rounded-xl border-0"
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1)

45-60: Use functional state updates to avoid stale closure.

The onClick handlers capture options at render time. If multiple tabs are clicked rapidly, this could cause stale state issues. Use functional updates for consistency with other state updates in this codebase.

          {
            name: "Iframe",
-           onClick: () =>
-             setOptions({ ...options, integrationType: "iframe" }),
+           onClick: () =>
+             setOptions((prev) => ({ ...prev, integrationType: "iframe" })),
            isActive: options.integrationType === "iframe",
          },
          {
            name: "Script",
-           onClick: () =>
-             setOptions({ ...options, integrationType: "script" }),
+           onClick: () =>
+             setOptions((prev) => ({ ...prev, integrationType: "script" })),
            isActive: options.integrationType === "script",
          },
          {
            name: "React",
-           onClick: () =>
-             setOptions({ ...options, integrationType: "component" }),
+           onClick: () =>
+             setOptions((prev) => ({ ...prev, integrationType: "component" })),
            isActive: options.integrationType === "component",
          },
apps/playground-web/src/components/ui/tab-buttons.tsx (2)

5-7: Avoid deep relative imports to sibling app directories.

These imports reach into the portal app's source code, creating tight coupling between apps. Consider:

  1. Moving shared UI components to a shared package
  2. Or importing from @/components/ui/* if these components exist in playground-web
#!/bin/bash
# Check if these components exist locally in playground-web
fd -t f "button.tsx" apps/playground-web/src/components/ui/
fd -t f "ScrollShadow.tsx" apps/playground-web/src/components/ui/
fd -t f "tooltip.tsx" apps/playground-web/src/components/ui/

131-135: The 100ms delay in ResizeObserver may cause visible lag.

The arbitrary setTimeout(..., 100) delay before updating the underline position could cause a noticeable visual delay during resize. Consider removing the delay or reducing it significantly.

      resizeObserver = new ResizeObserver(() => {
-       setTimeout(() => {
-         update();
-       }, 100);
+       update();
      });

If debouncing is needed for performance, consider using requestAnimationFrame instead for smoother visual updates.

apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (1)

103-122: Token amounts documentation is clear and follows existing patterns.

The new section with practical examples effectively demonstrates how to use inputCurrencyAmount and outputCurrencyAmount. The structure is consistent with other sections in the document.

Consider adding a brief note about the expected amount format for completeness:

  • Are decimal values supported (e.g., 0.5)?
  • What units are expected (token units, not wei/smallest unit)?
  • Are there any minimum/maximum limits?

This would help developers avoid common mistakes when setting amounts.

📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 23d69fd and fd2c877.

📒 Files selected for processing (25)
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1 hunks)
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/widget/page.tsx (3 hunks)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/page.tsx (2 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (1 hunks)
  • apps/playground-web/src/app/transactions/users/page.tsx (2 hunks)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/FeatureCard.tsx (1 hunks)
  • apps/playground-web/src/components/ui/tab-buttons.tsx (1 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/icons/PayIcon.tsx (0 hunks)
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (8 hunks)
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (1 hunks)
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/playground-web/src/icons/PayIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/navLinks.ts
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
apps/dashboard/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/dashboard/src/**/*.{ts,tsx}: Use NavLink for internal navigation with automatic active states in dashboard
Start server component files with import "server-only"; in Next.js
Read cookies/headers with next/headers in server components
Access server-only environment variables in server components
Perform heavy data fetching in server components
Implement redirect logic with redirect() from next/navigation in server components
Begin client component files with 'use client'; directive in Next.js
Handle interactive UI with React hooks (useState, useEffect, React Query, wallet hooks) in client components
Access browser APIs (localStorage, window, IntersectionObserver) in client components
Support fast transitions with prefetched data in client components
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header for API calls – never embed tokens in URLs
Return typed results (Project[], User[]) from server-side data fetches – avoid any
Wrap client-side API calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys in React Query for cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components – only use analytics client-side

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
apps/dashboard/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

apps/dashboard/**/*.{ts,tsx}: Always import from the central UI library under @/components/ui/* for reusable core UI components like Button, Input, Select, Tabs, Card, Sidebar, Separator, Badge
Use NavLink from @/components/ui/NavLink for internal navigation to ensure active states are handled automatically
For notices and skeletons, rely on AnnouncementBanner, GenericLoadingPage, and EmptyStateCard components
Import icons from lucide-react or the project-specific …/icons exports; never embed raw SVG
Keep components pure; fetch data outside using server components or hooks and pass it down via props
Use Tailwind CSS as the styling system; avoid inline styles or CSS modules
Merge class names with cn from @/lib/utils to keep conditional logic readable
Stick to design tokens: use bg-card, border-border, text-muted-foreground and other Tailwind variables instead of hard-coded colors
Use spacing utilities (px-*, py-*, gap-*) instead of custom margins
Follow mobile-first responsive design with Tailwind helpers (max-sm, md, lg, xl)
Never hard-code colors; always use Tailwind variables
Combine class names via cn, and expose className prop if useful in components
Use React Query (@tanstack/react-query) for all client-side data fetching with typed hooks

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
apps/dashboard/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Add className prop to the root element of every component to allow external overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
apps/{dashboard,playground}/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{tsx,ts}: Import UI primitives from @/components/ui/_ (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in Dashboard and Playground apps
Use NavLink for internal navigation so active states are handled automatically
Use Tailwind CSS for styling – no inline styles or CSS modules
Merge class names with cn() from @/lib/utils to keep conditional logic readable
Stick to design tokens for styling: backgrounds (bg-card), borders (border-border), muted text (text-muted-foreground), etc.
Server Components: Read cookies/headers with next/headers, access server-only environment variables or secrets, perform heavy data fetching, implement redirect logic with redirect() from next/navigation, and start files with import 'server-only'; to prevent client bundling
Client Components: Begin files with 'use client'; before imports, handle interactive UI relying on React hooks (useState, useEffect, React Query, wallet hooks), access browser APIs (localStorage, window, IntersectionObserver, etc.), and support fast transitions with client-side data prefetching
For client-side data fetching: Wrap calls in React Query (@tanstack/react-query), use descriptive and stable queryKeys for cache hits, configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s), and keep tokens secret by calling internal API routes or server actions

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
apps/{dashboard,playground}/**/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/components/**/*.{tsx,ts}: Group feature-specific components under feature/components/_ and expose a barrel index.ts when necessary
Expose a className prop on the root element of every component for styling overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: For server-side data fetching: Always call getAuthToken() to retrieve the JWT from cookies and inject the token as an Authorization: Bearer header – never embed it in the URL. Return typed results (Project[], User[], …) – avoid any
Never import posthog-js in server components; analytics reporting is client-side only

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Add Storybook stories (*.stories.tsx) alongside new UI components for documentation

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

For new UI components, add Storybook stories (*.stories.tsx) alongside the code

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Place tests alongside code: foo.tsfoo.test.ts in the same directory
Use real function invocations with stub data in tests; avoid brittle mocks
Use Mock Service Worker (MSW) for fetch/HTTP call interception in tests
Keep tests deterministic and side-effect free
Use predefined test accounts from test/src/test-wallets.ts in tests
Use FORKED_ETHEREUM_CHAIN for mainnet interactions and ANVIL_CHAIN for isolated tests

**/*.test.{ts,tsx}: Co-locate tests with source files using the pattern foo.ts ↔ foo.test.ts
Use real function invocations with stub data in tests; avoid brittle mocks
For network interactions in tests, use Mock Service Worker (MSW) to intercept fetch/HTTP calls, mocking only scenarios that are hard to reproduce
Keep tests deterministic and side-effect free; Vitest is pre-configured

Files:

  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
apps/dashboard/**/page.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Use the container class with a max-w-7xl cap for consistent page width

Files:

  • apps/dashboard/src/app/bridge/widget/page.tsx
🧬 Code graph analysis (6)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (2)
packages/thirdweb/src/script-exports/bridge-widget-script.tsx (1)
  • BridgeWidgetScript (69-79)
packages/thirdweb/src/stories/utils.tsx (1)
  • storyClient (14-16)
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (2)
packages/thirdweb/src/exports/react.ts (2)
  • ConnectButton_connectModalOptions (16-16)
  • SiweAuthOptions (12-12)
packages/thirdweb/src/wallets/types.ts (1)
  • AppMetadata (3-20)
apps/dashboard/src/app/bridge/widget/page.tsx (1)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • NATIVE_TOKEN_ADDRESS (31-31)
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/components/ui/tab-buttons.tsx (1)
  • TabButtons (16-96)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)
  • LeftSection (20-119)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)
  • RightSection (13-113)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (3)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
  • buildIframeUrl (5-67)
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1)
  • CodeGen (21-33)
apps/playground-web/src/app/bridge/page.tsx (1)
apps/dashboard/src/@/icons/BridgeIcon.tsx (1)
  • BringToFrontIcon (1-1)
⏰ 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). (8)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Unit Tests
  • GitHub Check: Lint Packages
  • GitHub Check: Size
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (17)
apps/playground-web/src/app/bridge/page.tsx (1)

1-1: LGTM! Icon replacement aligns with dashboard usage.

The change from PayIcon to BringToFrontIcon is consistent with the dashboard codebase, which uses the same icon for bridge functionality. The new icon is semantically more appropriate for bridge/transfer operations.

Also applies to: 11-11

apps/playground-web/src/components/blocks/FeatureCard.tsx (1)

16-16: LGTM – Typography refinement applied correctly.

The font weight increase from font-medium (500) to font-semibold (600) improves visual hierarchy for the card titles and aligns with the PR's UI polish objectives.

apps/playground-web/src/app/transactions/users/page.tsx (1)

34-97: LGTM!

The refactoring to a self-closing CodeExample tag with inline props is cleaner and aligns with the PR's goal of simplifying the code-display pattern.

apps/dashboard/src/app/bridge/widget/page.tsx (1)

38-42: LGTM: Amount parsing and conversion.

The amount parsing uses onlyNumber to validate numeric inputs and correctly converts them to strings only when defined. The conditional assignment pattern ensures undefined values remain undefined rather than becoming "undefined".

Also applies to: 77-78, 87-90, 97-98

apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (1)

28-28: LGTM: Proper prop threading.

The showThirdwebBranding prop is correctly added to the component interface and forwarded to BuyAndSwapEmbed, maintaining consistency with the existing prop-forwarding pattern.

Also applies to: 38-38

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (2)

104-112: LGTM: BridgeWidget configuration.

The migration from separate buy/swap widgets to BridgeWidget consolidates the UI flow effectively. The connectOptions, theme, and showThirdwebBranding props are properly configured, and the overall structure aligns well with the PR objectives.

Also applies to: 257-259


221-256: LGTM: Swap callback implementation.

The swap configuration correctly uses persistTokenSelections and prefill from props, and the callbacks properly extract token information from the quote to report analytics events. The logic is cleaner than the buy callbacks since swap quotes have a consistent structure.

packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (1)

20-20: Remove this comment—the change is appropriate.

The URL is stable and accessible (HTTP 200), and this is an intentional integration test that verifies the MediaRenderer component can actually render a real image. The coding guideline about "stub data" refers to avoiding brittle mocks, not eliminating all external resources. Real function invocations with actual dependencies are appropriate here.

apps/playground-web/src/app/data/pages-metadata.ts (1)

16-16: LGTM!

The SquareIcon import is correctly added and used throughout the bridge feature cards.

apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)

20-118: LGTM on the overall LeftSection structure.

The component properly separates concerns with collapsible sections for Token Selection and Appearance. Theme handling, color overrides (disabled for iframe), and branding toggle are well implemented.

apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)

1-28: LGTM!

Well-structured type definition that follows coding guidelines by using type aliases and leveraging library types via indexed access (ThemeOverrides["colors"], SwapWidgetProps["showThirdwebBranding"]). The optional fields are appropriately marked.

apps/playground-web/src/components/ui/tab-buttons.tsx (2)

98-148: Overall useUnderline hook implementation is solid.

The hook properly handles cleanup of event listeners and ResizeObserver. Using useLayoutEffect is correct for synchronous DOM measurements before paint.


9-14: Remove unused TabLink type export.

The TabLink type is exported but never used within this file or imported elsewhere in the codebase. Delete it.

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)

57-93: Both the buy prop and swap.prefill.buyToken are intentionally configured to serve different tabs in the BridgeWidget. The buy prop configures the Buy tab (rendered via BuyWidget), while swap.prefill.buyToken configures the Swap tab (rendered via SwapWidget). This is the intended design pattern and does not constitute problematic duplication.

packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)

418-418: LGTM! connectOptions correctly propagated to child widgets.

The connectOptions configuration is properly passed through to both SwapWidget and BuyWidget, enabling consistent wallet connection behavior across tabs.

Also applies to: 446-446

apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (2)

175-180: Documentation of showThirdwebBranding parameter is accurate and implementation-verified.

The parameter name correctly matches the bridge widget implementation and is properly documented. Default behavior displays thirdweb branding, which can be hidden by setting showThirdwebBranding=false in the iframe URL.


58-65: Parameter names are correctly documented.

The documented parameters inputCurrencyAmount and outputCurrencyAmount match the actual implementation in the bridge widget code (confirmed in apps/dashboard/src/app/bridge/widget/page.tsx and apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts). The documentation accurately reflects the query parameters used to prefill token amounts in the iframe widget.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (1)

128-128: Capitalize the sentence.

The sentence should start with "If" (capital I) rather than "if".

Apply this diff:

-if the custom default token selection is specified using the query params mentioned above, it overrides the last used tokens to ensure that configuration specified in query params is always respected.
+If the custom default token selection is specified using the query params mentioned above, it overrides the last used tokens to ensure that configuration specified in query params is always respected.
♻️ Duplicate comments (4)
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1)

28-37: Use resolvedTheme for accurate theme detection.

The theme value from useTheme() can be "system", which doesn't accurately reflect the actual rendered theme. Use resolvedTheme instead to get the computed theme value.

Apply this diff:

 export function BridgeWidgetPlayground() {
-  const { theme } = useTheme();
+  const { resolvedTheme } = useTheme();

   const [options, setOptions] =
     useState<BridgeWidgetPlaygroundOptions>(defaultOptions);

   // change theme on global theme change
   useEffect(() => {
     setOptions((prev) => ({
       ...prev,
       theme: {
         ...prev.theme,
-        type: theme === "dark" ? "dark" : "light",
+        type: resolvedTheme === "dark" ? "dark" : "light",
       },
     }));
-  }, [theme]);
+  }, [resolvedTheme]);
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1)

35-214: Add explicit return type annotations to helper functions.

Per coding guidelines, all TypeScript functions must have explicit return types. The functions getCode, getCode_Script, getCode_ReactComponent, and getCode_Iframe should be annotated with : string.

Based on coding guidelines: "Write idiomatic TypeScript with explicit function declarations and return types."

Apply this diff:

-function getCode(options: BridgeWidgetPlaygroundOptions) {
+function getCode(options: BridgeWidgetPlaygroundOptions): string {
   if (options.integrationType === "script") {
     return getCode_Script(options);
   }
   ...
 }

-function getCode_Script(options: BridgeWidgetPlaygroundOptions) {
+function getCode_Script(options: BridgeWidgetPlaygroundOptions): string {
   const widgetOptions: Record<string, unknown> = {
     clientId: "your-thirdweb-client-id",
   };
   ...
 }

-function getCode_ReactComponent(options: BridgeWidgetPlaygroundOptions) {
+function getCode_ReactComponent(options: BridgeWidgetPlaygroundOptions): string {
   const imports = {
     thirdweb: ["createThirdwebClient"] as string[],
     "thirdweb/react": ["BridgeWidget"] as string[],
   };
   ...
 }

-function getCode_Iframe(options: BridgeWidgetPlaygroundOptions) {
+function getCode_Iframe(options: BridgeWidgetPlaygroundOptions): string {
   const src = buildIframeUrl(options);
   ...
 }
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)

174-185: Token selection clears the amount field.

When selecting a new token, the current amount value is lost because the update doesn't spread the existing prefill data. This differs from the chain selection logic (lines 148-159) which preserves other fields.

Apply this diff:

  onChange={(token) => {
    setOptions((v) => ({
      ...v,
      prefill: {
        ...v.prefill,
        [props.type]: {
+         ...v.prefill?.[props.type],
          chainId: token.chainId,
          tokenAddress: token.address,
        },
      },
    }));
  }}
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)

202-311: Extract connectOptions type definition to a separate types file.

As noted in a previous review, this 110-line inline type definition should be extracted to maintain single-responsibility and follow the pattern established by SwapWidgetConnectOptions in swap-widget/types.ts.

Based on coding guidelines requiring reuse of shared types from local types.ts barrels.

🧹 Nitpick comments (2)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1)

46-51: Extract shared styling to reduce duplication.

The wrapper div styling is identical to the Variant component's styling (lines 74-79). Consider extracting this to a shared constant or reusing the Variant component if appropriate.

Apply this diff to extract the shared styles:

+const wrapperStyle: React.CSSProperties = {
+  display: "flex",
+  flexDirection: "column",
+  gap: "40px",
+  alignItems: "center",
+};
+
 export function CustomTheme() {
   return (
-    <div
-      style={{
-        display: "flex",
-        flexDirection: "column",
-        gap: "40px",
-        alignItems: "center",
-      }}
-    >
+    <div style={wrapperStyle}>
       <BridgeWidgetScript
         clientId={storyClient.clientId}
         buy={{ chainId: 8453, amount: "0.1" }}
         theme={{
           type: "light",
           colors: {
             modalBg: "#FFFFF0",
             tertiaryBg: "#DBE4C9",
             borderColor: "#8AA624",
             secondaryText: "#3E3F29",
             accentText: "#E43636",
           },
         }}
       />
     </div>
   );
 }
 
 function Variant(props: BridgeWidgetScriptProps) {
-  return (
-    <div
-      style={{
-        display: "flex",
-        flexDirection: "column",
-        gap: "40px",
-        alignItems: "center",
-      }}
-    >
+  return (
+    <div style={wrapperStyle}>
       <BridgeWidgetScript {...props} theme="dark" />
       <BridgeWidgetScript {...props} theme="light" />
     </div>
   );
 }
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)

471-476: Consider extracting active/inactive colors for clarity.

The ternary expressions for color and border work correctly but could be more readable with extracted variables.

 function TabButton(props: {
   isActive: boolean;
   onClick: () => void;
   children: React.ReactNode;
 }) {
   const theme = useCustomTheme();
+  const textColor = props.isActive 
+    ? theme.colors.primaryText 
+    : theme.colors.secondaryText;
+  const borderColor = props.isActive 
+    ? theme.colors.secondaryText 
+    : theme.colors.borderColor;
+  
   return (
     <Button
       variant="secondary"
       onClick={props.onClick}
       style={{
         borderRadius: radius.full,
         fontSize: fontSize.sm,
         fontWeight: 500,
         paddingInline: spacing.md,
         paddingBlock: 0,
         height: "36px",
-        color: props.isActive
-          ? theme.colors.primaryText
-          : theme.colors.secondaryText,
-        border: `1px solid ${
-          props.isActive ? theme.colors.secondaryText : theme.colors.borderColor
-        }`,
+        color: textColor,
+        border: `1px solid ${borderColor}`,
       }}
     >
       {props.children}
     </Button>
   );
 }
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between fd2c877 and d20792c.

📒 Files selected for processing (25)
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1 hunks)
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/widget/page.tsx (3 hunks)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/page.tsx (2 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (1 hunks)
  • apps/playground-web/src/app/transactions/users/page.tsx (2 hunks)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/FeatureCard.tsx (1 hunks)
  • apps/playground-web/src/components/ui/tab-buttons.tsx (1 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/icons/PayIcon.tsx (0 hunks)
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (8 hunks)
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (2 hunks)
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/playground-web/src/icons/PayIcon.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/transactions/users/page.tsx
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Add Storybook stories (*.stories.tsx) alongside new UI components for documentation

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

For new UI components, add Storybook stories (*.stories.tsx) alongside the code

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
apps/dashboard/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/dashboard/src/**/*.{ts,tsx}: Use NavLink for internal navigation with automatic active states in dashboard
Start server component files with import "server-only"; in Next.js
Read cookies/headers with next/headers in server components
Access server-only environment variables in server components
Perform heavy data fetching in server components
Implement redirect logic with redirect() from next/navigation in server components
Begin client component files with 'use client'; directive in Next.js
Handle interactive UI with React hooks (useState, useEffect, React Query, wallet hooks) in client components
Access browser APIs (localStorage, window, IntersectionObserver) in client components
Support fast transitions with prefetched data in client components
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header for API calls – never embed tokens in URLs
Return typed results (Project[], User[]) from server-side data fetches – avoid any
Wrap client-side API calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys in React Query for cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components – only use analytics client-side

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

apps/dashboard/**/*.{ts,tsx}: Always import from the central UI library under @/components/ui/* for reusable core UI components like Button, Input, Select, Tabs, Card, Sidebar, Separator, Badge
Use NavLink from @/components/ui/NavLink for internal navigation to ensure active states are handled automatically
For notices and skeletons, rely on AnnouncementBanner, GenericLoadingPage, and EmptyStateCard components
Import icons from lucide-react or the project-specific …/icons exports; never embed raw SVG
Keep components pure; fetch data outside using server components or hooks and pass it down via props
Use Tailwind CSS as the styling system; avoid inline styles or CSS modules
Merge class names with cn from @/lib/utils to keep conditional logic readable
Stick to design tokens: use bg-card, border-border, text-muted-foreground and other Tailwind variables instead of hard-coded colors
Use spacing utilities (px-*, py-*, gap-*) instead of custom margins
Follow mobile-first responsive design with Tailwind helpers (max-sm, md, lg, xl)
Never hard-code colors; always use Tailwind variables
Combine class names via cn, and expose className prop if useful in components
Use React Query (@tanstack/react-query) for all client-side data fetching with typed hooks

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Add className prop to the root element of every component to allow external overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{tsx,ts}: Import UI primitives from @/components/ui/_ (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in Dashboard and Playground apps
Use NavLink for internal navigation so active states are handled automatically
Use Tailwind CSS for styling – no inline styles or CSS modules
Merge class names with cn() from @/lib/utils to keep conditional logic readable
Stick to design tokens for styling: backgrounds (bg-card), borders (border-border), muted text (text-muted-foreground), etc.
Server Components: Read cookies/headers with next/headers, access server-only environment variables or secrets, perform heavy data fetching, implement redirect logic with redirect() from next/navigation, and start files with import 'server-only'; to prevent client bundling
Client Components: Begin files with 'use client'; before imports, handle interactive UI relying on React hooks (useState, useEffect, React Query, wallet hooks), access browser APIs (localStorage, window, IntersectionObserver, etc.), and support fast transitions with client-side data prefetching
For client-side data fetching: Wrap calls in React Query (@tanstack/react-query), use descriptive and stable queryKeys for cache hits, configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s), and keep tokens secret by calling internal API routes or server actions

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/components/**/*.{tsx,ts}: Group feature-specific components under feature/components/_ and expose a barrel index.ts when necessary
Expose a className prop on the root element of every component for styling overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: For server-side data fetching: Always call getAuthToken() to retrieve the JWT from cookies and inject the token as an Authorization: Bearer header – never embed it in the URL. Return typed results (Project[], User[], …) – avoid any
Never import posthog-js in server components; analytics reporting is client-side only

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
🧬 Code graph analysis (6)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (2)
packages/thirdweb/src/script-exports/bridge-widget-script.tsx (1)
  • BridgeWidgetScript (69-79)
packages/thirdweb/src/stories/utils.tsx (1)
  • storyClient (14-16)
apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3)
packages/thirdweb/src/exports/react.ts (1)
  • BridgeWidget (148-148)
apps/dashboard/src/@/constants/connect.ts (1)
  • appMetadata (1-5)
apps/dashboard/src/@/analytics/report.ts (9)
  • reportTokenBuyFailed (264-266)
  • reportAssetBuyFailed (342-350)
  • reportTokenBuyCancelled (276-278)
  • reportAssetBuyCancelled (360-367)
  • reportTokenBuySuccessful (252-254)
  • reportAssetBuySuccessful (221-228)
  • reportTokenSwapFailed (314-320)
  • reportTokenSwapSuccessful (288-290)
  • reportTokenSwapCancelled (330-332)
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/components/ui/tab-buttons.tsx (1)
  • TabButtons (16-96)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)
  • LeftSection (20-119)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)
  • RightSection (13-113)
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (3)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/lib/code-gen.ts (3)
  • quotes (17-19)
  • stringifyImports (21-29)
  • stringifyProps (1-15)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
  • buildIframeUrl (5-67)
apps/playground-web/src/app/bridge/page.tsx (1)
apps/dashboard/src/@/icons/BridgeIcon.tsx (1)
  • BringToFrontIcon (1-1)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (6)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/app/wallets/sign-in/components/CollapsibleSection.tsx (1)
  • CollapsibleSection (4-33)
packages/thirdweb/src/react/web/ui/components/formElements.tsx (1)
  • Label (20-28)
apps/playground-web/src/app/wallets/sign-in/components/ColorFormGroup.tsx (1)
  • ColorFormGroup (9-111)
packages/thirdweb/src/exports/thirdweb.ts (1)
  • NATIVE_TOKEN_ADDRESS (31-31)
apps/playground-web/src/app/bridge/swap-widget/components/left-section.tsx (1)
  • LeftSection (20-115)
⏰ 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). (8)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Unit Tests
  • GitHub Check: Size
  • GitHub Check: Lint Packages
  • GitHub Check: Build Packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (16)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1)

45-67: Verify the story behavior change is intentional.

Unlike other stories (BasicUsage, CurrencySet, NoThirdwebBranding) that use the Variant helper to display both dark and light themes side by side, CustomTheme now renders only a single light theme variant. Confirm this is the desired behavior for showcasing custom theme colors.

apps/playground-web/src/app/data/pages-metadata.ts (3)

16-16: LGTM!

The SquareIcon import is correctly added and used consistently throughout the bridge feature cards below.


120-152: Well-structured Bridge Widget metadata.

The new Bridge Widget entry and updated descriptions effectively communicate the fiat onramp and cross-chain capabilities. The icon standardization to SquareIcon provides good visual consistency across the widget family.


118-160: No action needed. The Transaction Button's use of RectangleHorizontalIcon while other entries use SquareIcon appears to be an intentional design pattern. In walletsFeatureCards, the "Connect Button" entry similarly uses RectangleHorizontalIcon, establishing a consistent visual distinction between button-type and widget-type components. All widgets use SquareIcon while buttons use RectangleHorizontalIcon.

apps/playground-web/src/app/bridge/page.tsx (1)

1-14: LGTM! Icon replacement aligns with Bridge Widget branding.

The change from PayIcon to BringToFrontIcon is consistent with the broader Bridge Widget playground updates and maintains the proper visual identity for the bridge functionality.

apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1)

1-33: LGTM! Well-structured Next.js page component.

The page follows Next.js App Router conventions with proper metadata, provider wrapping, and clean component composition. The documentation link includes proper UTM tracking.

apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)

7-28: LGTM! Clean and well-structured type definition.

The BridgeWidgetPlaygroundOptions type is well-organized with proper use of optional fields and references to upstream types from thirdweb/react. The structure clearly supports the playground's integration modes and configuration options.

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3)

1-84: LGTM! Component setup is clean.

The client directive, imports, props typing, and mounted-state tracking for analytics are all properly implemented.


221-256: LGTM! Swap callback implementations are clean.

The swap flow callbacks properly extract quote data and invoke analytics reporting without unnecessary duplication.


67-67: LGTM! Branding prop properly wired.

The showThirdwebBranding prop is correctly typed and passed through to BridgeWidget.

Also applies to: 258-258

packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (2)

35-36: LGTM! Styling props properly exposed.

Adding className and style props aligns with the coding guideline to "expose className prop on root element for component overrides." Both are correctly forwarded to EmbedContainer.

Also applies to: 380-384


418-418: LGTM! Props properly propagated to child widgets.

The connectOptions are correctly passed through to both SwapWidget and BuyWidget, ensuring consistent wallet connection configuration across tabs.

Also applies to: 446-446

apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (4)

58-58: LGTM! Clearer, more concise heading.

The simplified heading improves readability while the description below still conveys the functionality clearly.


103-122: Well-structured documentation—verify parameter dependencies.

The Token amounts section is clear and follows the established documentation pattern. However, both examples include currency/chain parameters alongside the amount parameters. Confirm whether inputCurrencyAmount/outputCurrencyAmount require the corresponding inputCurrency/outputCurrency and chain parameters, or if they can function independently.


64-65: Parameter names inputCurrencyAmount and outputCurrencyAmount are correctly implemented and supported by the Bridge widget's query parameter handling.


175-180: Parameter name appears correct but iframe implementation cannot be fully verified.

The showThirdwebBranding parameter name is correctly documented and exists in the Bridge widget SDK (BridgeWidgetScriptProps), following the same naming convention as other documented parameters like currency. However, the actual iframe URL handler at thirdweb.com/bridge/widget could not be located in the codebase to definitively confirm it parses this query parameter. The documentation at lines 175-180 is consistent with the SDK implementation, but endpoint verification requires access to the actual bridge widget route handler.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (1)

35-40: Remove or fix the two skipped tests disabling core MediaRenderer functionality.

Two tests are skipped without explanation at lines 28 and 35, disabling validation of core functionality (image rendering and error handling). Line 28's test also has a code smell—it uses setTimeout instead of proper async/await handling with waitFor.

Either:

  1. Fix the tests and re-enable them
  2. Remove them entirely if permanently broken
  3. If needed temporarily, add a TODO comment explaining why and when to re-enable
♻️ Duplicate comments (2)
apps/playground-web/src/app/transactions/users/page.tsx (1)

32-32: Add explicit return type annotation.

The function still lacks an explicit return type annotation as required by the coding guidelines for TypeScript files.

Apply this diff:

-function UserTransactions() {
+function UserTransactions(): JSX.Element {
apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (1)

119-217: Duplicated quote extraction logic across callbacks.

The chainId and tokenAddress extraction from the quote is repeated identically in onError, onCancel, and onSuccess. This was flagged in a previous review.

🧹 Nitpick comments (6)
apps/playground-web/src/hooks/chains.ts (3)

35-48: Consider adding a comment to clarify sorting intent.

The sorting logic is correct but non-trivial—it places chains with numeric names (e.g., "1inch") after alphabetic names. A brief comment would improve maintainability.

Apply this diff to add a clarifying comment:

  const data = json.data as BridgeChain[];

+ // Sort chains: alphabetic names first, numeric names last; within each group sort alphabetically
  return data.sort((a, b) => {

35-48: Add defensive null checks for chain names in localeCompare.

Line 47 calls a.name.localeCompare(b.name) without verifying that name is defined. While the API contract likely guarantees this, defensive coding would prevent potential runtime errors if the shape ever changes.

Apply this diff to add null safety:

  return data.sort((a, b) => {
    const aStartsWithNumber = a.name[0]?.match(/^\d/);
    const bStartsWithNumber = b.name[0]?.match(/^\d/);

    if (aStartsWithNumber && !bStartsWithNumber) {
      return 1;
    }

    if (!aStartsWithNumber && bStartsWithNumber) {
      return -1;
    }

-   return a.name.localeCompare(b.name);
+   return (a.name ?? "").localeCompare(b.name ?? "");
  });

7-16: Consider reusing the shared BridgeChain type if accessible.

A BridgeChain type is exported from packages/thirdweb/src/bridge/types/Chain.ts. If the playground app has access to that package, importing the shared type would reduce duplication and keep the shape in sync. If the separation is intentional (to avoid package dependencies), the current approach is fine.

Based on coding guidelines: "Re-use shared types from @/types or local types.ts barrels."

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (3)

103-105: Avoid inline styles; use Tailwind classes instead.

Per coding guidelines, dashboard and playground apps should use Tailwind CSS only with no inline styles. The border: "0" can be replaced with Tailwind's border-0 class.

           <iframe
             src={buildIframeUrl(props.options, "preview")}
             height="750px"
             width="100%"
             title="Bridge Widget"
-            className="fade-in-0 duration-500 animate-in rounded-xl"
-            style={{
-              border: "0",
-            }}
+            className="fade-in-0 border-0 duration-500 animate-in rounded-xl"
           />

115-143: Consider reusing or renaming to avoid confusion with shared TabButtons.

This PR also introduces a TabButtons component in @/components/ui/tab-buttons.tsx with different styling and features (underline animation, scroll shadow, icons). Having two components with the same name in the codebase may cause confusion.

Consider either:

  1. Importing and styling the shared TabButtons from @/components/ui/tab-buttons.tsx
  2. Renaming this local component (e.g., SegmentedTabs or PillTabs) to distinguish it

16-22: Simplify state setter usage.

The setPreviewTab wrapper adds no logic beyond calling _setPreviewTab. Consider using the setter directly or destructuring with the desired name.

-  const [previewTab, _setPreviewTab] = useState<Tab>(() => {
-    return "ui";
-  });
-
-  function setPreviewTab(tab: "ui" | "code") {
-    _setPreviewTab(tab);
-  }
+  const [previewTab, setPreviewTab] = useState<Tab>("ui");
📜 Review details

Configuration used: Organization 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.

📥 Commits

Reviewing files that changed from the base of the PR and between d20792c and cf0efe7.

📒 Files selected for processing (25)
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1 hunks)
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/widget/page.tsx (3 hunks)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/page.tsx (2 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (1 hunks)
  • apps/playground-web/src/app/transactions/users/page.tsx (2 hunks)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/FeatureCard.tsx (1 hunks)
  • apps/playground-web/src/components/ui/tab-buttons.tsx (1 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/icons/PayIcon.tsx (0 hunks)
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (8 hunks)
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (2 hunks)
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/playground-web/src/icons/PayIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (13)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Place tests alongside code: foo.tsfoo.test.ts in the same directory
Use real function invocations with stub data in tests; avoid brittle mocks
Use Mock Service Worker (MSW) for fetch/HTTP call interception in tests
Keep tests deterministic and side-effect free
Use predefined test accounts from test/src/test-wallets.ts in tests
Use FORKED_ETHEREUM_CHAIN for mainnet interactions and ANVIL_CHAIN for isolated tests

**/*.test.{ts,tsx}: Co-locate tests with source files using the pattern foo.ts ↔ foo.test.ts
Use real function invocations with stub data in tests; avoid brittle mocks
For network interactions in tests, use Mock Service Worker (MSW) to intercept fetch/HTTP calls, mocking only scenarios that are hard to reproduce
Keep tests deterministic and side-effect free; Vitest is pre-configured

Files:

  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
apps/dashboard/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/dashboard/src/**/*.{ts,tsx}: Use NavLink for internal navigation with automatic active states in dashboard
Start server component files with import "server-only"; in Next.js
Read cookies/headers with next/headers in server components
Access server-only environment variables in server components
Perform heavy data fetching in server components
Implement redirect logic with redirect() from next/navigation in server components
Begin client component files with 'use client'; directive in Next.js
Handle interactive UI with React hooks (useState, useEffect, React Query, wallet hooks) in client components
Access browser APIs (localStorage, window, IntersectionObserver) in client components
Support fast transitions with prefetched data in client components
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header for API calls – never embed tokens in URLs
Return typed results (Project[], User[]) from server-side data fetches – avoid any
Wrap client-side API calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys in React Query for cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components – only use analytics client-side

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

apps/dashboard/**/*.{ts,tsx}: Always import from the central UI library under @/components/ui/* for reusable core UI components like Button, Input, Select, Tabs, Card, Sidebar, Separator, Badge
Use NavLink from @/components/ui/NavLink for internal navigation to ensure active states are handled automatically
For notices and skeletons, rely on AnnouncementBanner, GenericLoadingPage, and EmptyStateCard components
Import icons from lucide-react or the project-specific …/icons exports; never embed raw SVG
Keep components pure; fetch data outside using server components or hooks and pass it down via props
Use Tailwind CSS as the styling system; avoid inline styles or CSS modules
Merge class names with cn from @/lib/utils to keep conditional logic readable
Stick to design tokens: use bg-card, border-border, text-muted-foreground and other Tailwind variables instead of hard-coded colors
Use spacing utilities (px-*, py-*, gap-*) instead of custom margins
Follow mobile-first responsive design with Tailwind helpers (max-sm, md, lg, xl)
Never hard-code colors; always use Tailwind variables
Combine class names via cn, and expose className prop if useful in components
Use React Query (@tanstack/react-query) for all client-side data fetching with typed hooks

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Add className prop to the root element of every component to allow external overrides

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{tsx,ts}: Import UI primitives from @/components/ui/_ (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in Dashboard and Playground apps
Use NavLink for internal navigation so active states are handled automatically
Use Tailwind CSS for styling – no inline styles or CSS modules
Merge class names with cn() from @/lib/utils to keep conditional logic readable
Stick to design tokens for styling: backgrounds (bg-card), borders (border-border), muted text (text-muted-foreground), etc.
Server Components: Read cookies/headers with next/headers, access server-only environment variables or secrets, perform heavy data fetching, implement redirect logic with redirect() from next/navigation, and start files with import 'server-only'; to prevent client bundling
Client Components: Begin files with 'use client'; before imports, handle interactive UI relying on React hooks (useState, useEffect, React Query, wallet hooks), access browser APIs (localStorage, window, IntersectionObserver, etc.), and support fast transitions with client-side data prefetching
For client-side data fetching: Wrap calls in React Query (@tanstack/react-query), use descriptive and stable queryKeys for cache hits, configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s), and keep tokens secret by calling internal API routes or server actions

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/components/**/*.{tsx,ts}: Group feature-specific components under feature/components/_ and expose a barrel index.ts when necessary
Expose a className prop on the root element of every component for styling overrides

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: For server-side data fetching: Always call getAuthToken() to retrieve the JWT from cookies and inject the token as an Authorization: Bearer header – never embed it in the URL. Return typed results (Project[], User[], …) – avoid any
Never import posthog-js in server components; analytics reporting is client-side only

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Add Storybook stories (*.stories.tsx) alongside new UI components for documentation

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

For new UI components, add Storybook stories (*.stories.tsx) alongside the code

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
🧬 Code graph analysis (4)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/components/ui/tab-buttons.tsx (1)
  • TabButtons (16-96)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
  • buildIframeUrl (5-72)
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1)
  • CodeGen (21-33)
apps/playground-web/src/hooks/chains.ts (1)
packages/thirdweb/src/bridge/types/Chain.ts (1)
  • BridgeChain (42-42)
apps/playground-web/src/app/transactions/users/page.tsx (2)
apps/playground-web/src/components/code/code-example.tsx (1)
  • CodeExample (17-56)
apps/playground-web/src/components/account-abstraction/gateway.tsx (1)
  • GatewayPreview (137-292)
apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3)
packages/thirdweb/src/exports/react.ts (1)
  • BridgeWidget (148-148)
apps/dashboard/src/@/constants/connect.ts (1)
  • appMetadata (1-5)
apps/dashboard/src/@/analytics/report.ts (9)
  • reportTokenBuyFailed (264-266)
  • reportAssetBuyFailed (342-350)
  • reportTokenBuyCancelled (276-278)
  • reportAssetBuyCancelled (360-367)
  • reportTokenBuySuccessful (252-254)
  • reportAssetBuySuccessful (221-228)
  • reportTokenSwapFailed (314-320)
  • reportTokenSwapSuccessful (288-290)
  • reportTokenSwapCancelled (330-332)
⏰ 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: Size
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (13)
apps/playground-web/src/app/transactions/users/page.tsx (1)

34-97: LGTM! Clean refactoring.

The conversion from multi-line CodeExample with children to a self-closing tag with props improves readability and aligns with the component's interface.

packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1)

43-69: LGTM! Clear custom theme demonstration.

The refactoring to directly render BridgeWidgetScript (rather than using the Variant helper) is appropriate here since CustomTheme demonstrates a single custom theme configuration, whereas Variant is designed to show dark and light side-by-side comparisons. The custom theme object structure with type and colors clearly showcases the theming API.

apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1)

87-87: LGTM!

Using Number.isNaN() is the correct best practice over the global isNaN() function. Since Number(id) is already applied, the behavior is equivalent, but this is more explicit and idiomatic.

apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3)

58-65: LGTM!

The updated token selections documentation clearly lists all available query parameters including the new amount parameters.


103-122: LGTM!

The new "Token amounts" section provides clear documentation with practical examples for pre-filling token amounts.


175-180: LGTM!

The branding documentation is clear and consistent with the implementation.

apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (1)

28-28: LGTM!

Clean prop threading for the showThirdwebBranding flag, following existing patterns in the component.

Also applies to: 38-38

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3)

104-112: LGTM!

The BridgeWidget configuration with connectOptions is well-structured, properly passing wallets and appMetadata.


67-67: LGTM!

The showThirdwebBranding prop is correctly typed and forwarded to the BridgeWidget component.

Also applies to: 257-258


227-256: Verify swap callback quote parameter is always defined.

The swap callbacks (onError, onSuccess, onCancel) access quote.intent.* properties directly without null checks. In contrast, the buy callbacks guard against undefined quote by checking buyChainId before proceeding.

If quote can be undefined in swap callbacks (similar to buy callbacks), accessing quote.intent.destinationChainId would throw a runtime error.

#!/bin/bash
# Search for BridgeWidget swap callback type definitions to verify if quote is always defined
ast-grep --pattern 'swap: {
  $$$
  onError: $_
  $$$
}'

# Also search for any swap onError type definitions
rg -n --type=ts -C5 'onError.*quote.*swap|swap.*onError'
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)

57-93: LGTM!

The BridgeWidget configuration correctly passes theme, prefill data, currency, and branding options. Using JSON.stringify(props.options.prefill) as a key to force remount on prefill changes is appropriate for this playground use case.

apps/playground-web/src/app/data/pages-metadata.ts (1)

118-160: LGTM!

The new Bridge Widget entry is well-structured with appropriate metadata. The grammar issue previously flagged ("token" → "tokens") on line 137 has been addressed. Icon standardization to SquareIcon for widget entries provides visual consistency.

apps/playground-web/src/components/ui/tab-buttons.tsx (1)

98-148: LGTM with a note on setTimeout usage.

The useUnderline hook correctly manages the underline position with proper cleanup of ResizeObserver and window resize listener. The setTimeout(0) to enable transitions after initial positioning is a standard pattern.

The setTimeout(100) in the ResizeObserver callback acts as debouncing but could potentially show stale positions briefly during rapid resizes. Consider using a more robust debounce if this becomes an issue.

@codecov
Copy link

codecov bot commented Dec 16, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 54.47%. Comparing base (4bab6f1) to head (aad3e64).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8571      +/-   ##
==========================================
- Coverage   54.49%   54.47%   -0.03%     
==========================================
  Files         922      922              
  Lines       61361    61361              
  Branches     4150     4149       -1     
==========================================
- Hits        33441    33425      -16     
- Misses      27818    27835      +17     
+ Partials      102      101       -1     
Flag Coverage Δ
packages 54.47% <ø> (-0.03%) ⬇️
see 5 files with indirect coverage changes
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (2)
packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (1)

19-20: External URL still makes tests non-deterministic.

The previous review comment flagged this external URL as violating the deterministic test principle. Changing it to a different external URL doesn't address the root cause—the test still depends on an external resource that can become unavailable or change.

As per coding guidelines: "Keep tests deterministic and side-effect free" and "Use Mock Service Worker (MSW) for fetch/HTTP call interception in tests."

Apply the previously suggested fix to use an embedded test image:

-const imageLink =
-  "https://i2.seadn.io/ethereum/0xbd3531da5cf5857e7cfaa92426877b022e612cf8/750c4805cd12ea19c8c3909677e61988.png?w=350";
+const imageLink =
+  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";

Alternatively, use MSW to mock the image fetch if you need to test specific image loading behavior.

packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)

202-311: Extract connectOptions type definition to a separate types file.

This inline type definition should be extracted to bridge-widget/types.ts following the established pattern used by SwapWidgetConnectOptions in swap-widget/types.ts, as previously noted in code reviews.

🧹 Nitpick comments (3)
apps/playground-web/src/app/x402/components/X402LeftSection.tsx (2)

69-70: Improve the biome-ignore justification and consider safer alternatives.

The non-null assertion is safe because TokenSelector (which calls handleTokenChange) is only rendered when selectedChain is truthy (line 135). However, the justification "ok" is not descriptive. Consider:

  1. Better justification: Replace "ok" with an explanation like "selectedChain guaranteed by conditional rendering"
  2. Defensive alternative: Add a type guard at the function start:
const handleTokenChange = (token: TokenMetadata) => {
+  if (!selectedChain) return;
+
  setSelectedToken({
    address: token.address,
-    // biome-ignore lint/style/noNonNullAssertion: ok
-    chainId: selectedChain!,
+    chainId: selectedChain,
  });

21-116: Add explicit return types to align with coding guidelines.

As per coding guidelines, "Write idiomatic TypeScript with explicit function declarations and return types." The component function and all handler functions lack explicit return types.

Apply this diff to add return types:

-export function X402LeftSection(props: {
+export function X402LeftSection(props: {
   options: X402PlaygroundOptions;
   setOptions: React.Dispatch<React.SetStateAction<X402PlaygroundOptions>>;
-}) {
+}): React.JSX.Element {

-  const handleChainChange = (chainId: number) => {
+  const handleChainChange = (chainId: number): void => {

-  const handleTokenChange = (token: TokenMetadata) => {
+  const handleTokenChange = (token: TokenMetadata): void => {

-  const handleAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+  const handleAmountChange = (e: React.ChangeEvent<HTMLInputElement>): void => {

-  const handlePayToChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+  const handlePayToChange = (e: React.ChangeEvent<HTMLInputElement>): void => {

-  const handleWaitUntilChange = (
+  const handleWaitUntilChange = (
     value: "simulated" | "submitted" | "confirmed",
-  ) => {
+  ): void => {

-  const handleSchemeChange = (value: "exact" | "upto") => {
+  const handleSchemeChange = (value: "exact" | "upto"): void => {

-  const handleMinAmountChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+  const handleMinAmountChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
apps/playground-web/src/components/ui/tab-buttons.tsx (1)

55-66: Consider adding disabled prop for accessibility.

While the visual styling handles the disabled state via className, using the native disabled prop on the Button would improve accessibility by preventing keyboard activation and communicating the state to assistive technologies.

Apply this diff:

                <Button
                  className={cn(
                    "relative inline-flex h-auto items-center gap-1.5 rounded-lg font-medium hover:bg-accent !px-3",
                    !tab.isActive &&
                      "text-muted-foreground hover:text-foreground",
-                   props.tabClassName,
+                   tab.isDisabled && "cursor-not-allowed opacity-50",
+                   props.tabClassName,
                    tab.isActive && props.activeTabClassName,
                  )}
                  onClick={tab.onClick}
+                 disabled={tab.isDisabled}
                  ref={tab.isActive ? activeTabRef : undefined}
                  variant="ghost"
                >
📜 Review details

Configuration used: Organization 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.

📥 Commits

Reviewing files that changed from the base of the PR and between cf0efe7 and fc1ad2f.

📒 Files selected for processing (25)
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1 hunks)
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/widget/page.tsx (3 hunks)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/page.tsx (2 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (1 hunks)
  • apps/playground-web/src/app/transactions/users/page.tsx (2 hunks)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/FeatureCard.tsx (1 hunks)
  • apps/playground-web/src/components/ui/tab-buttons.tsx (1 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/icons/PayIcon.tsx (0 hunks)
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (8 hunks)
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (2 hunks)
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/playground-web/src/icons/PayIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (10)
  • apps/playground-web/src/hooks/chains.ts
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/playground-web/src/app/navLinks.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
🧰 Additional context used
📓 Path-based instructions (14)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
apps/dashboard/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/dashboard/src/**/*.{ts,tsx}: Use NavLink for internal navigation with automatic active states in dashboard
Start server component files with import "server-only"; in Next.js
Read cookies/headers with next/headers in server components
Access server-only environment variables in server components
Perform heavy data fetching in server components
Implement redirect logic with redirect() from next/navigation in server components
Begin client component files with 'use client'; directive in Next.js
Handle interactive UI with React hooks (useState, useEffect, React Query, wallet hooks) in client components
Access browser APIs (localStorage, window, IntersectionObserver) in client components
Support fast transitions with prefetched data in client components
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header for API calls – never embed tokens in URLs
Return typed results (Project[], User[]) from server-side data fetches – avoid any
Wrap client-side API calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys in React Query for cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components – only use analytics client-side

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

apps/dashboard/**/*.{ts,tsx}: Always import from the central UI library under @/components/ui/* for reusable core UI components like Button, Input, Select, Tabs, Card, Sidebar, Separator, Badge
Use NavLink from @/components/ui/NavLink for internal navigation to ensure active states are handled automatically
For notices and skeletons, rely on AnnouncementBanner, GenericLoadingPage, and EmptyStateCard components
Import icons from lucide-react or the project-specific …/icons exports; never embed raw SVG
Keep components pure; fetch data outside using server components or hooks and pass it down via props
Use Tailwind CSS as the styling system; avoid inline styles or CSS modules
Merge class names with cn from @/lib/utils to keep conditional logic readable
Stick to design tokens: use bg-card, border-border, text-muted-foreground and other Tailwind variables instead of hard-coded colors
Use spacing utilities (px-*, py-*, gap-*) instead of custom margins
Follow mobile-first responsive design with Tailwind helpers (max-sm, md, lg, xl)
Never hard-code colors; always use Tailwind variables
Combine class names via cn, and expose className prop if useful in components
Use React Query (@tanstack/react-query) for all client-side data fetching with typed hooks

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Add className prop to the root element of every component to allow external overrides

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{tsx,ts}: Import UI primitives from @/components/ui/_ (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in Dashboard and Playground apps
Use NavLink for internal navigation so active states are handled automatically
Use Tailwind CSS for styling – no inline styles or CSS modules
Merge class names with cn() from @/lib/utils to keep conditional logic readable
Stick to design tokens for styling: backgrounds (bg-card), borders (border-border), muted text (text-muted-foreground), etc.
Server Components: Read cookies/headers with next/headers, access server-only environment variables or secrets, perform heavy data fetching, implement redirect logic with redirect() from next/navigation, and start files with import 'server-only'; to prevent client bundling
Client Components: Begin files with 'use client'; before imports, handle interactive UI relying on React hooks (useState, useEffect, React Query, wallet hooks), access browser APIs (localStorage, window, IntersectionObserver, etc.), and support fast transitions with client-side data prefetching
For client-side data fetching: Wrap calls in React Query (@tanstack/react-query), use descriptive and stable queryKeys for cache hits, configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s), and keep tokens secret by calling internal API routes or server actions

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/components/**/*.{tsx,ts}: Group feature-specific components under feature/components/_ and expose a barrel index.ts when necessary
Expose a className prop on the root element of every component for styling overrides

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: For server-side data fetching: Always call getAuthToken() to retrieve the JWT from cookies and inject the token as an Authorization: Bearer header – never embed it in the URL. Return typed results (Project[], User[], …) – avoid any
Never import posthog-js in server components; analytics reporting is client-side only

Files:

  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Place tests alongside code: foo.tsfoo.test.ts in the same directory
Use real function invocations with stub data in tests; avoid brittle mocks
Use Mock Service Worker (MSW) for fetch/HTTP call interception in tests
Keep tests deterministic and side-effect free
Use predefined test accounts from test/src/test-wallets.ts in tests
Use FORKED_ETHEREUM_CHAIN for mainnet interactions and ANVIL_CHAIN for isolated tests

**/*.test.{ts,tsx}: Co-locate tests with source files using the pattern foo.ts ↔ foo.test.ts
Use real function invocations with stub data in tests; avoid brittle mocks
For network interactions in tests, use Mock Service Worker (MSW) to intercept fetch/HTTP calls, mocking only scenarios that are hard to reproduce
Keep tests deterministic and side-effect free; Vitest is pre-configured

Files:

  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Add Storybook stories (*.stories.tsx) alongside new UI components for documentation

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

For new UI components, add Storybook stories (*.stories.tsx) alongside the code

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
🧬 Code graph analysis (6)
apps/playground-web/src/app/bridge/page.tsx (1)
apps/dashboard/src/@/icons/BridgeIcon.tsx (1)
  • BringToFrontIcon (1-1)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/components/ui/tab-buttons.tsx (1)
  • TabButtons (9-87)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
  • buildIframeUrl (5-72)
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1)
  • CodeGen (21-33)
apps/playground-web/src/app/transactions/users/page.tsx (1)
apps/playground-web/src/components/code/code-example.tsx (1)
  • CodeExample (17-56)
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)
packages/thirdweb/src/wallets/types.ts (1)
  • AppMetadata (3-20)
apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (3)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/lib/code-gen.ts (3)
  • quotes (17-19)
  • stringifyImports (21-29)
  • stringifyProps (1-15)
apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1)
  • buildIframeUrl (5-72)
apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (4)
apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)
  • BridgeWidgetPlaygroundOptions (7-28)
apps/playground-web/src/components/ui/tab-buttons.tsx (1)
  • TabButtons (9-87)
apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1)
  • LeftSection (20-119)
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)
  • RightSection (13-113)
⏰ 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). (8)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Lint Packages
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Build Packages
  • GitHub Check: Size
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (25)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (6)

1-11: LGTM!

The imports and Storybook meta configuration follow standard patterns and TypeScript best practices.


13-20: LGTM!

BasicUsage story appropriately demonstrates the component with both theme variants.


22-30: LGTM!

CurrencySet story appropriately demonstrates currency configuration.


32-41: LGTM!

NoThirdwebBranding story appropriately demonstrates the new showThirdwebBranding configuration option.


43-69: LGTM – Intentional improvement for custom theme documentation.

The change from Variant-based rendering to direct BridgeWidgetScript usage better demonstrates how to construct custom theme objects. Showing a single instance with the full theme structure is clearer than showing multiple variants when the focus is on the custom theme configuration itself.


71-85: LGTM!

The Variant helper appropriately provides consistent dual-theme rendering for multiple stories.

apps/playground-web/src/app/bridge/page.tsx (1)

1-1: LGTM! Good use of standard icon library.

The replacement of PayIcon with BringToFrontIcon from lucide-react follows the coding guidelines to use standard icon libraries.

Also applies to: 11-11

apps/playground-web/src/app/transactions/users/page.tsx (1)

34-97: LGTM! Cleaner component structure.

The consolidation of CodeExample props into a single self-closing element improves readability without changing functionality.

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (2)

67-67: LGTM! Branding prop correctly added and forwarded.

The showThirdwebBranding prop is properly typed and forwarded to the underlying BridgeWidget component.

Also applies to: 258-258


104-259: Solid refactor to unified BridgeWidget.

The replacement of separate BuyWidget and SwapWidget with a single BridgeWidget entry point simplifies the component architecture and maintains proper analytics reporting. The connectOptions and callback configurations are correct.

Note: The DRY concern regarding duplicated chainId/tokenAddress extraction logic has already been flagged in a previous review.

apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (1)

28-28: LGTM! Clean prop forwarding.

The showThirdwebBranding prop is correctly added to the component interface and properly forwarded to BuyAndSwapEmbed.

Also applies to: 38-38

apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (1)

58-66: LGTM! Clear documentation for new features.

The documentation updates properly document the new inputCurrencyAmount, outputCurrencyAmount, and showThirdwebBranding query parameters with helpful examples.

Also applies to: 103-122, 175-179

apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (2)

10-20: LGTM! Sensible default configuration.

The default options provide a good starting state for the playground with reasonable defaults (iframe mode, USD currency, dark theme, branding enabled).


39-73: Well-structured component layout.

The component structure cleanly separates concerns with TabButtons for integration type selection and distinct Left/Right sections. The responsive layout follows mobile-first principles with appropriate Tailwind utilities.

Note: The theme detection issue (using theme instead of resolvedTheme) has already been flagged in a previous review.

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (2)

56-94: LGTM! BridgeWidget properly configured.

The BridgeWidget configuration correctly forwards all options including theme, prefill data, currency, and branding. The key prop using JSON.stringify ensures the widget remounts when prefill data changes, which is the correct behavior for a playground.


96-107: LGTM! iframe preview properly implemented.

The iframe preview correctly uses buildIframeUrl to construct the source URL, includes proper accessibility attributes, and has appropriate styling for the playground context.

apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (4)

21-33: LGTM! Proper lazy loading pattern.

The CodeGen component correctly uses lazy loading with Suspense for the code client, which follows React best practices for code-splitting optional features.


48-118: LGTM! Script generation logic is sound.

The script code generation correctly handles all configuration options including theme overrides, currency, branding, and token prefills. The conditional logic appropriately omits default values (USD, dark theme without overrides) to keep generated code clean.

Note: The missing return type annotation has already been flagged in a previous review.


120-202: LGTM! React component generation is well-structured.

The React component code generation properly manages imports, conditionally includes theme overrides, and correctly formats props. The use of helper functions (quotes, stringifyImports, stringifyProps) keeps the code clean and maintainable.

Note: The missing return type annotation has already been flagged in a previous review.


204-214: LGTM! iframe code generation is correct.

The iframe code generation correctly delegates URL construction to buildIframeUrl and generates a clean, valid HTML template.

Note: The missing return type annotation has already been flagged in a previous review.

apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1)

1-28: LGTM! Clean type definition.

The type structure is well-organized and follows TypeScript best practices. The optional properties and nested structure appropriately model the playground configuration needs.

packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (4)

3-3: LGTM: Import additions support the new connectOptions prop.

The new imports for Chain, Wallet, SmartWalletOptions, AppMetadata, SiweAuthOptions, and ConnectButton_connectModalOptions are correctly sourced and necessary for the connectOptions type definition.

Also applies to: 8-10, 21-22


35-36: LGTM: className and style props correctly implemented.

The addition of className and style props follows standard React patterns and is correctly forwarded to EmbedContainer. The spread order ensures user-provided styles properly override defaults.

Also applies to: 380-384


471-476: LGTM: TabButton styling refinements align with UI improvements.

The styling updates use design system values correctly (spacing.md, radius.full, fontSize.sm) and improve the visual hierarchy with the updated color logic (primaryText for active vs secondaryText for inactive). The fixed height with zero vertical padding is appropriate for consistent button sizing.


418-418: Both SwapWidget and BuyWidget support the connectOptions prop with compatible type signatures. Verification confirms that SwapWidgetConnectOptions and BuyWidgetConnectOptions include all required properties from the BridgeWidget's connectOptions type definition, ensuring proper type compatibility at lines 418 and 446.

@graphite-app
Copy link
Contributor

graphite-app bot commented Dec 16, 2025

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 enhancing the `BridgeWidget` component within the playground by introducing new features, improving styling, and refining existing functionalities. It also includes updates to metadata and the handling of token selections and amounts.

### Detailed summary
- Removed `PayIcon` and replaced with `BringToFrontIcon`.
- Added `showThirdwebBranding` option to `UniversalBridgeEmbed`.
- Updated `BridgeWidget` to support token amounts and selections.
- Enhanced sorting of bridge chains in `chains.ts`.
- Improved styling for various components, including `FeatureCard` and `ChatPageContent`.
- Updated documentation for widget parameters.
- Refactored code for better readability and maintainability in multiple components.

> ✨ 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**
  * Bridge Widget playground: interactive preview, code generation (script/component/iframe), theme, token/amount prefills, and preview modes.
  * Unified buy/swap experience under a single Bridge widget and a new branding toggle (hide/show branding).
  * Expanded connection options for wallet/connect flows.

* **Documentation**
  * Added docs for token amounts, branding, and iframe examples.

* **Style**
  * Minor typography and spacing refinements.

* **Chores**
  * Navigation and feature card updates; small test adjustment (one image test skipped).

<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 (2)
apps/playground-web/src/hooks/chains.ts (2)

18-18: Add explicit return type annotation.

The function lacks an explicit return type. As per coding guidelines, TypeScript functions should have explicit return types.

Apply this diff:

-async function fetchBridgeSupportedChainsFromApi() {
+async function fetchBridgeSupportedChainsFromApi(): Promise<BridgeChain[]> {

7-16: Import BridgeChain from the library to avoid type duplication.

The BridgeChain type defined locally (lines 7–16) is identical to the BridgeChain export from packages/thirdweb/src/bridge/types/Chain.ts. Remove the local definition and import the library type instead to ensure consistency and prevent drift.

♻️ Duplicate comments (4)
apps/playground-web/src/app/transactions/users/page.tsx (1)

32-32: Add explicit return type annotation (previously flagged).

The UserTransactions function still lacks an explicit return type, which is required by the coding guidelines for TypeScript functions.

Apply this diff:

-function UserTransactions() {
+function UserTransactions(): React.JSX.Element {
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)

202-311: Extract connectOptions type definition to a separate types file.

This 110-line inline type definition should be extracted to bridge-widget/types.ts following the same pattern as SwapWidgetConnectOptions in swap-widget/types.ts. The coding guidelines require reusing shared types from local types.ts barrels, and keeping files focused on single-responsibility.

Based on coding guidelines: "Re-use shared types from @/types or local types.ts barrels" and "Limit each file to one stateless, single-responsibility function for clarity."

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (1)

119-153: Extract helper functions to reduce repetition.

The quote type-checking and chain/token extraction logic is duplicated across onError, onCancel, and onSuccess callbacks, violating the DRY principle stated in the coding guidelines.

Based on coding guidelines: "Choose composition over inheritance" and implicit DRY (Don't Repeat Yourself) principle.

Also applies to: 154-185, 186-217

apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)

115-143: Replace duplicate TabButtons with shared component.

A TabButtons component already exists at apps/playground-web/src/components/ui/tab-buttons.tsx with more features. This internal implementation duplicates that functionality and violates the DRY principle.

Based on coding guidelines: "Choose composition over inheritance" and DRY principle.

🧹 Nitpick comments (3)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1)

13-85: Optional: Consider adding explicit return types to story functions.

Per coding guidelines, exported functions should have explicit return types. While React component return types are often inferred in Storybook stories, you could add : JSX.Element for stricter TypeScript compliance.

Example:

-export function BasicUsage() {
+export function BasicUsage(): JSX.Element {
   return (
apps/playground-web/src/hooks/chains.ts (1)

35-48: Consider simplifying the regex check.

The current approach uses a.name[0]?.match(/^\d/) to check if a name starts with a digit. This can be simplified to /^\d/.test(a.name), which is more direct and idiomatic for boolean checks.

Apply this diff:

   return data.sort((a, b) => {
-    const aStartsWithNumber = a.name[0]?.match(/^\d/);
-    const bStartsWithNumber = b.name[0]?.match(/^\d/);
+    const aStartsWithNumber = /^\d/.test(a.name);
+    const bStartsWithNumber = /^\d/.test(b.name);
 
     if (aStartsWithNumber && !bStartsWithNumber) {
       return 1;
     }
 
     if (!aStartsWithNumber && bStartsWithNumber) {
       return -1;
     }
 
     return a.name.localeCompare(b.name);
   });
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1)

92-92: Avoid JSON.stringify for React keys.

Using JSON.stringify(props.options.prefill) as a React key is a code smell that can cause performance issues. Prefer a more specific key derived from relevant prefill fields, or use useMemo to compute a stable key string.

Apply this diff to compute a more efficient key:

+  const prefillKey = useMemo(() => {
+    const prefill = props.options.prefill;
+    if (!prefill) return 'no-prefill';
+    return [
+      prefill.buyToken?.chainId,
+      prefill.buyToken?.tokenAddress,
+      prefill.buyToken?.amount,
+      prefill.sellToken?.chainId,
+      prefill.sellToken?.tokenAddress,
+      prefill.sellToken?.amount,
+    ].filter(Boolean).join('-');
+  }, [props.options.prefill]);
+
   <BridgeWidget
     className="shadow-xl"
     client={THIRDWEB_CLIENT}
     theme={themeObj}
     // ... props
-    key={JSON.stringify(props.options.prefill)}
+    key={prefillKey}
   />

Don't forget to import useMemo at the top of the file.

📜 Review details

Configuration used: Organization 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.

📥 Commits

Reviewing files that changed from the base of the PR and between fc1ad2f and aad3e64.

📒 Files selected for processing (25)
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3 hunks)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx (1 hunks)
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx (2 hunks)
  • apps/dashboard/src/app/bridge/widget/page.tsx (3 hunks)
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts (1 hunks)
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1 hunks)
  • apps/playground-web/src/app/bridge/page.tsx (2 hunks)
  • apps/playground-web/src/app/data/pages-metadata.ts (2 hunks)
  • apps/playground-web/src/app/navLinks.ts (1 hunks)
  • apps/playground-web/src/app/transactions/users/page.tsx (2 hunks)
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx (1 hunks)
  • apps/playground-web/src/components/blocks/FeatureCard.tsx (1 hunks)
  • apps/playground-web/src/components/ui/tab-buttons.tsx (1 hunks)
  • apps/playground-web/src/hooks/chains.ts (1 hunks)
  • apps/playground-web/src/icons/PayIcon.tsx (0 hunks)
  • apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (3 hunks)
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (8 hunks)
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx (2 hunks)
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/playground-web/src/icons/PayIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (14)
  • apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/public-pages/erc20/erc20.tsx
  • apps/playground-web/src/components/blocks/FeatureCard.tsx
  • apps/dashboard/src/app/bridge/widget/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/left-section.tsx
  • apps/dashboard/src/app/bridge/(general)/components/client/UniversalBridgeEmbed.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/buildIframeUrl.ts
  • apps/playground-web/src/app/bridge/bridge-widget/components/types.ts
  • apps/playground-web/src/app/x402/components/X402LeftSection.tsx
  • packages/thirdweb/src/react/web/ui/MediaRenderer/MediaRenderer.test.tsx
  • apps/playground-web/src/app/bridge/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/code.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/bridge-playground.tsx
  • apps/playground-web/src/components/ui/tab-buttons.tsx
  • apps/playground-web/src/app/data/pages-metadata.ts
🧰 Additional context used
📓 Path-based instructions (13)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each TypeScript file to one stateless, single-responsibility function for clarity
Re-use shared types from @/types or local types.ts barrels
Prefer type aliases over interface except for nominal shapes in TypeScript
Avoid any and unknown in TypeScript unless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.) in TypeScript

**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity and testability
Re-use shared types from @/types or local types.ts barrel exports
Prefer type aliases over interface except for nominal shapes
Avoid any and unknown unless unavoidable; narrow generics whenever possible
Choose composition over inheritance; leverage utility types (Partial, Pick, etc.)
Comment only ambiguous logic in TypeScript files; avoid restating TypeScript types and signatures in prose

Files:

  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/navLinks.ts
apps/{dashboard,playground-web}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{dashboard,playground-web}/src/**/*.{ts,tsx}: Import UI component primitives from @/components/ui/* (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground
Use Tailwind CSS only – no inline styles or CSS modules in dashboard and playground
Use cn() from @/lib/utils for conditional Tailwind class merging
Use design system tokens for styling (backgrounds: bg-card, borders: border-border, muted text: text-muted-foreground)
Expose className prop on root element for component overrides

Files:

  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/navLinks.ts
**/*.{js,jsx,ts,tsx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Biome governs formatting and linting; its rules live in biome.json. Run pnpm fix & pnpm lint before committing, ensure there are no linting errors

Files:

  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/navLinks.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Lazy-import optional features; avoid top-level side-effects

Files:

  • apps/playground-web/src/hooks/chains.ts
  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
  • apps/playground-web/src/app/transactions/users/page.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/page.tsx
  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
  • apps/playground-web/src/app/ai/components/ChatPageContent.tsx
  • apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx
  • apps/playground-web/src/app/navLinks.ts
apps/dashboard/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/dashboard/src/**/*.{ts,tsx}: Use NavLink for internal navigation with automatic active states in dashboard
Start server component files with import "server-only"; in Next.js
Read cookies/headers with next/headers in server components
Access server-only environment variables in server components
Perform heavy data fetching in server components
Implement redirect logic with redirect() from next/navigation in server components
Begin client component files with 'use client'; directive in Next.js
Handle interactive UI with React hooks (useState, useEffect, React Query, wallet hooks) in client components
Access browser APIs (localStorage, window, IntersectionObserver) in client components
Support fast transitions with prefetched data in client components
Always call getAuthToken() to retrieve JWT from cookies on server side
Use Authorization: Bearer header for API calls – never embed tokens in URLs
Return typed results (Project[], User[]) from server-side data fetches – avoid any
Wrap client-side API calls in React Query (@tanstack/react-query)
Use descriptive, stable queryKeys in React Query for cache hits
Configure staleTime/cacheTime in React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never import posthog-js in server components – only use analytics client-side

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

apps/dashboard/**/*.{ts,tsx}: Always import from the central UI library under @/components/ui/* for reusable core UI components like Button, Input, Select, Tabs, Card, Sidebar, Separator, Badge
Use NavLink from @/components/ui/NavLink for internal navigation to ensure active states are handled automatically
For notices and skeletons, rely on AnnouncementBanner, GenericLoadingPage, and EmptyStateCard components
Import icons from lucide-react or the project-specific …/icons exports; never embed raw SVG
Keep components pure; fetch data outside using server components or hooks and pass it down via props
Use Tailwind CSS as the styling system; avoid inline styles or CSS modules
Merge class names with cn from @/lib/utils to keep conditional logic readable
Stick to design tokens: use bg-card, border-border, text-muted-foreground and other Tailwind variables instead of hard-coded colors
Use spacing utilities (px-*, py-*, gap-*) instead of custom margins
Follow mobile-first responsive design with Tailwind helpers (max-sm, md, lg, xl)
Never hard-code colors; always use Tailwind variables
Combine class names via cn, and expose className prop if useful in components
Use React Query (@tanstack/react-query) for all client-side data fetching with typed hooks

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/dashboard/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/dashboard.mdc)

Add className prop to the root element of every component to allow external overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{tsx,ts}: Import UI primitives from @/components/ui/_ (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in Dashboard and Playground apps
Use NavLink for internal navigation so active states are handled automatically
Use Tailwind CSS for styling – no inline styles or CSS modules
Merge class names with cn() from @/lib/utils to keep conditional logic readable
Stick to design tokens for styling: backgrounds (bg-card), borders (border-border), muted text (text-muted-foreground), etc.
Server Components: Read cookies/headers with next/headers, access server-only environment variables or secrets, perform heavy data fetching, implement redirect logic with redirect() from next/navigation, and start files with import 'server-only'; to prevent client bundling
Client Components: Begin files with 'use client'; before imports, handle interactive UI relying on React hooks (useState, useEffect, React Query, wallet hooks), access browser APIs (localStorage, window, IntersectionObserver, etc.), and support fast transitions with client-side data prefetching
For client-side data fetching: Wrap calls in React Query (@tanstack/react-query), use descriptive and stable queryKeys for cache hits, configure staleTime / cacheTime based on freshness requirements (default ≥ 60 s), and keep tokens secret by calling internal API routes or server actions

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/components/**/*.{tsx,ts}: Group feature-specific components under feature/components/_ and expose a barrel index.ts when necessary
Expose a className prop on the root element of every component for styling overrides

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/{dashboard,playground}/**/*.{ts,tsx}: For server-side data fetching: Always call getAuthToken() to retrieve the JWT from cookies and inject the token as an Authorization: Bearer header – never embed it in the URL. Return typed results (Project[], User[], …) – avoid any
Never import posthog-js in server components; analytics reporting is client-side only

Files:

  • apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx
packages/thirdweb/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

packages/thirdweb/src/**/*.{ts,tsx}: Comment only ambiguous logic in SDK code; avoid restating TypeScript in prose
Load heavy dependencies inside async paths to keep initial bundle lean (e.g. const { jsPDF } = await import("jspdf");)

Lazy-load heavy dependencies inside async paths to keep the initial bundle lean (e.g., const { jsPDF } = await import('jspdf');)

Files:

  • packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx
  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Add Storybook stories (*.stories.tsx) alongside new UI components for documentation

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

For new UI components, add Storybook stories (*.stories.tsx) alongside the code

Files:

  • packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx
🧬 Code graph analysis (6)
apps/playground-web/src/hooks/chains.ts (1)
packages/thirdweb/src/bridge/types/Chain.ts (1)
  • BridgeChain (42-42)
apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (3)
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (1)
  • BridgeWidget (363-455)
apps/dashboard/src/@/constants/connect.ts (1)
  • appMetadata (1-5)
apps/dashboard/src/@/analytics/report.ts (9)
  • reportTokenBuyFailed (264-266)
  • reportAssetBuyFailed (342-350)
  • reportTokenBuyCancelled (276-278)
  • reportAssetBuyCancelled (360-367)
  • reportTokenBuySuccessful (252-254)
  • reportAssetBuySuccessful (221-228)
  • reportTokenSwapFailed (314-320)
  • reportTokenSwapSuccessful (288-290)
  • reportTokenSwapCancelled (330-332)
apps/playground-web/src/app/transactions/users/page.tsx (2)
apps/playground-web/src/components/code/code-example.tsx (1)
  • CodeExample (17-56)
apps/playground-web/src/components/account-abstraction/gateway.tsx (1)
  • GatewayPreview (137-292)
apps/playground-web/src/app/bridge/bridge-widget/page.tsx (2)
apps/playground-web/src/components/blocks/APIHeader.tsx (1)
  • PageLayout (49-70)
apps/dashboard/src/@/icons/BridgeIcon.tsx (1)
  • BringToFrontIcon (1-1)
packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (2)
packages/thirdweb/src/exports/react.ts (2)
  • ConnectButton_connectModalOptions (16-16)
  • SiweAuthOptions (12-12)
packages/thirdweb/src/wallets/types.ts (1)
  • AppMetadata (3-20)
packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (2)
packages/thirdweb/src/script-exports/bridge-widget-script.tsx (1)
  • BridgeWidgetScript (69-79)
packages/thirdweb/src/stories/utils.tsx (1)
  • storyClient (14-16)
⏰ 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). (8)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Build Packages
  • GitHub Check: Unit Tests
  • GitHub Check: Size
  • GitHub Check: Lint Packages
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (16)
apps/playground-web/src/app/transactions/users/page.tsx (1)

34-97: LGTM: Fragment removal simplifies the JSX structure.

The refactoring correctly removes the unnecessary fragment wrapper around CodeExample, improving code clarity. All props (code, header, lang, preview) are properly structured and aligned with the component signature.

packages/thirdweb/src/stories/Bridge/BridgeWidget/bridge-widget-script.stories.tsx (1)

43-69: LGTM! Story refactor improves focus.

The change from using the Variant helper to directly rendering BridgeWidgetScript is appropriate. The story now clearly demonstrates a single custom theme rather than showing both dark and light variants, which makes sense since the custom theme object already specifies type: "light".

packages/thirdweb/src/react/web/ui/Bridge/bridge-widget/bridge-widget.tsx (3)

3-3: LGTM! Necessary type imports for connectOptions.

The new type imports support the connectOptions configuration and align with the types used in the inline type definition.

Also applies to: 8-10, 21-22


35-36: LGTM! Proper className and style prop exposure.

The addition of className and style props follows the coding guidelines requiring root element style overrides, and they are correctly forwarded to EmbedContainer.

Also applies to: 380-383


418-418: LGTM! Proper prop forwarding and styling refinements.

The connectOptions are correctly passed to both SwapWidget and BuyWidget, and the TabButton styling updates are reasonable refinements (tighter padding, fixed height, and dynamic text color based on active state).

Also applies to: 446-446, 471-476

apps/dashboard/src/@/components/blocks/BuyAndSwapEmbed.tsx (2)

5-6: LGTM! Proper BridgeWidget import and branding prop addition.

The migration from separate BuyWidget/SwapWidget imports to the unified BridgeWidget is correct, and the showThirdwebBranding prop is properly added to the public API surface.

Also applies to: 67-67


103-112: LGTM! Correct BridgeWidget integration.

The BridgeWidget is properly configured with theme, client, connect options, and branding settings. The swap configuration correctly maps prefill data and callbacks, and the showThirdwebBranding prop is properly forwarded.

Also applies to: 220-258

apps/playground-web/src/app/ai/components/ChatPageContent.tsx (1)

87-87: LGTM! Improved NaN check with explicit method.

Changing from !isNaN(Number(id)) to !Number.isNaN(Number(id)) is a good improvement. Number.isNaN is more explicit and follows modern JavaScript best practices, though the behavior is identical here since Number(id) is already called.

apps/playground-web/src/app/navLinks.ts (1)

190-193: LGTM! Bridge Widget navigation link properly added.

The new navigation entry is correctly structured and positioned within the Bridge submenu, matching the pattern of existing links.

apps/playground-web/src/app/bridge/bridge-widget/page.tsx (1)

1-33: LGTM! Well-structured Bridge Widget playground page.

The new page follows Next.js App Router conventions and coding guidelines:

  • Proper metadata export using createMetadata
  • Correct icon usage (BringToFrontIcon)
  • ThirdwebProvider wrapping for client-side functionality
  • PageLayout configuration with all required props including docs link
apps/playground-web/src/app/bridge/bridge-widget/components/right-section.tsx (2)

24-31: LGTM! Correct theme computation.

The theme object is correctly computed using darkTheme or lightTheme from thirdweb/react with appropriate color overrides based on the options.


56-107: LGTM! Proper BridgeWidget and preview rendering.

The component correctly:

  • Renders BridgeWidget with properly mapped prefill data, theme, currency, and branding settings
  • Conditionally renders an iframe preview using buildIframeUrl for iframe integration type
  • Displays CodeGen in the code tab

The prefill data mapping from options to BridgeWidget props is accurate.

Also applies to: 109-109

apps/portal/src/app/bridge/bridge-widget/iframe/page.mdx (4)

58-58: LGTM: Header simplification improves clarity.

The simplified header "Token selections" is more concise and better encompasses both token selection and amount configuration documented in this section.


103-122: LGTM: Clear documentation with concrete examples.

The new Token amounts section is well-structured and provides clear, concrete examples showing how to use inputCurrencyAmount and outputCurrencyAmount parameters. The Details blocks follow the established pattern used elsewhere in the documentation.


64-65: The parameter names inputCurrencyAmount and outputCurrencyAmount in the documentation are correct and match the implementation. No public aliases exist for these parameters in the iframe widget API.


175-180: Parameter name verified - documentation is accurate.

The showThirdwebBranding query parameter name matches the implementation in apps/dashboard/src/app/bridge/widget/page.tsx, which parses this parameter and defaults to true when not specified. Setting it to false correctly hides the branding as documented. The example URL is properly formed and functional.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Dashboard Involves changes to the Dashboard. packages Playground Changes involving the Playground codebase. Portal Involves changes to the Portal (docs) codebase. SDK Involves changes to the thirdweb SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants