Repository: layerswap/layerswapapp Branch: dev Commit: c251cec71dc1 Files: 728 Total size: 2.7 MB Directory structure: gitextract_wrqmm7zr/ ├── .claude/ │ ├── agents/ │ │ ├── pr-review-coordinator.md │ │ ├── pr-reviewer-architecture.md │ │ ├── pr-reviewer-bugs.md │ │ ├── pr-reviewer-performance.md │ │ ├── pr-reviewer-quality.md │ │ ├── pr-reviewer-react.md │ │ ├── pr-reviewer-security.md │ │ └── pr-reviewer.md │ ├── commands/ │ │ └── reviewchanges.md │ └── skills/ │ └── vercel-react-best-practices/ │ ├── SKILL.md │ └── rules/ │ ├── advanced-event-handler-refs.md │ ├── advanced-use-latest.md │ ├── async-api-routes.md │ ├── async-defer-await.md │ ├── async-dependencies.md │ ├── async-parallel.md │ ├── async-suspense-boundaries.md │ ├── bundle-barrel-imports.md │ ├── bundle-conditional.md │ ├── bundle-defer-third-party.md │ ├── bundle-dynamic-imports.md │ ├── bundle-preload.md │ ├── client-event-listeners.md │ ├── client-localstorage-schema.md │ ├── client-passive-event-listeners.md │ ├── client-swr-dedup.md │ ├── js-batch-dom-css.md │ ├── js-cache-function-results.md │ ├── js-cache-property-access.md │ ├── js-cache-storage.md │ ├── js-combine-iterations.md │ ├── js-early-exit.md │ ├── js-hoist-regexp.md │ ├── js-index-maps.md │ ├── js-length-check-first.md │ ├── js-min-max-loop.md │ ├── js-set-map-lookups.md │ ├── js-tosorted-immutable.md │ ├── rendering-activity.md │ ├── rendering-animate-svg-wrapper.md │ ├── rendering-conditional-render.md │ ├── rendering-content-visibility.md │ ├── rendering-hoist-jsx.md │ ├── rendering-hydration-no-flicker.md │ ├── rendering-svg-precision.md │ ├── rerender-defer-reads.md │ ├── rerender-dependencies.md │ ├── rerender-derived-state.md │ ├── rerender-functional-setstate.md │ ├── rerender-lazy-state-init.md │ ├── rerender-memo.md │ ├── rerender-simple-expression-in-memo.md │ ├── rerender-transitions.md │ ├── server-after-nonblocking.md │ ├── server-auth-actions.md │ ├── server-cache-lru.md │ ├── server-cache-react.md │ ├── server-dedup-props.md │ ├── server-parallel-fetching.md │ ├── server-serialization.md │ └── unused-detection.md ├── .eslintrc.json ├── .github/ │ └── workflows/ │ ├── chromatic.yml │ ├── deploy.yml │ └── rebase-main-sandbox.yml ├── .gitignore ├── .storybook/ │ ├── main.ts │ ├── manager.js │ └── preview.ts ├── .vscode/ │ ├── launch.json │ └── settings.json ├── AGENTS.md ├── CLAUDE.md ├── Models/ │ ├── ApiError.ts │ ├── ApiResponse.ts │ ├── Balance.ts │ ├── BalanceProvider.ts │ ├── Exchange.ts │ ├── LayerSwapAppSettings.ts │ ├── LayerSwapAuth.ts │ ├── LayerSwapSettings.ts │ ├── Network.ts │ ├── Partner.ts │ ├── QueryParams.ts │ ├── RangeError.ts │ ├── Route.ts │ ├── SwapStatus.ts │ ├── Theme.ts │ ├── WalletConnectWallet.ts │ ├── WalletProvider.ts │ └── Wizard.ts ├── README.md ├── components/ │ ├── AddressIcon/ │ │ ├── colors.mjs │ │ ├── index.tsx │ │ ├── jazzicon/ │ │ │ ├── colors.js │ │ │ ├── index.js │ │ │ ├── mersenne-twister.js │ │ │ └── paper.js │ │ ├── jazzicon.mjs │ │ ├── mersenne-twister.mjs │ │ └── paper.mjs │ ├── AvatarGroup.tsx │ ├── Campaigns/ │ │ ├── Details/ │ │ │ ├── Leaderboard.tsx │ │ │ ├── Rewards.tsx │ │ │ └── index.tsx │ │ └── index.tsx │ ├── Carousel.tsx │ ├── ColorSchema.tsx │ ├── Common/ │ │ ├── AnimatedValue.tsx │ │ ├── AverageCompletionTime.tsx │ │ ├── ConnectWalletButton.tsx │ │ ├── CountDownTimer.tsx │ │ ├── FormattedAverageCompletionTime.tsx │ │ ├── FormattedDate.tsx │ │ ├── ImageWithFallback.tsx │ │ ├── LinkWithIcon.tsx │ │ ├── NumFlowWithFallback.tsx │ │ ├── ReactPortal.tsx │ │ └── TypingEffect.tsx │ ├── ConnectNetwork.tsx │ ├── ContactSupport.tsx │ ├── DTOs/ │ │ ├── SwapConfirmationFormValues.ts │ │ └── SwapFormValues.ts │ ├── ErrorFallback.tsx │ ├── FeeDetails/ │ │ ├── DepositMethod/ │ │ │ └── index.tsx │ │ ├── Rate.tsx │ │ ├── ReceiveAmounts.tsx │ │ ├── Refuel.tsx │ │ ├── RefuelModal.tsx │ │ ├── Slippage.tsx │ │ ├── SwapQuote/ │ │ │ ├── DetailedEstimates.tsx │ │ │ ├── SummaryRow.tsx │ │ │ └── index.tsx │ │ └── index.tsx │ ├── HeaderWithMenu/ │ │ └── index.tsx │ ├── Input/ │ │ ├── Address/ │ │ │ ├── AddressPicker/ │ │ │ │ ├── AddressBook.tsx │ │ │ │ ├── AddressButton.tsx │ │ │ │ ├── AddressWithIcon.tsx │ │ │ │ ├── ConnectedWallets.tsx │ │ │ │ ├── ManualAddressInput.tsx │ │ │ │ └── index.tsx │ │ │ ├── ContractAddressNote.tsx │ │ │ ├── UrlAddressNote.tsx │ │ │ └── index.tsx │ │ ├── Amount/ │ │ │ ├── Balance.tsx │ │ │ ├── ExchangeAmountField.tsx │ │ │ ├── ExchangeReceiveAmount.tsx │ │ │ ├── MinMax.tsx │ │ │ ├── PriceImpact.tsx │ │ │ ├── ReceiveAmount.tsx │ │ │ ├── helpers.ts │ │ │ └── index.tsx │ │ ├── CexPicker.tsx │ │ ├── DestinationPicker.tsx │ │ ├── DestinationWalletPicker.tsx │ │ ├── NumericInput.tsx │ │ ├── RoutePicker/ │ │ │ ├── Content.tsx │ │ │ ├── RouteSearch.tsx │ │ │ ├── RouteSortingMenu.tsx │ │ │ ├── RouteTokenSwitch.tsx │ │ │ ├── RouterPickerWalletConnect.tsx │ │ │ ├── Routes.tsx │ │ │ ├── Rows/ │ │ │ │ ├── CollapsableHeader.tsx │ │ │ │ ├── CollapsibleRow.tsx │ │ │ │ ├── StickyHeader.tsx │ │ │ │ ├── SuggestionsHeader.tsx │ │ │ │ ├── TitleRow.tsx │ │ │ │ └── index.tsx │ │ │ ├── TokenTitleDetails.tsx │ │ │ └── index.tsx │ │ ├── Search.tsx │ │ ├── SourcePicker.tsx │ │ ├── SourceWalletPicker.tsx │ │ └── TransferCEX.tsx │ ├── LayerswapMenu/ │ │ ├── Menu.tsx │ │ ├── MenuList.tsx │ │ └── index.tsx │ ├── LinkWraapper.tsx │ ├── LoadingCard.tsx │ ├── MessageComponent.tsx │ ├── NavigatableList/ │ │ ├── NavigatableItem.tsx │ │ ├── NavigatableList.tsx │ │ ├── context.ts │ │ ├── hooks.ts │ │ └── index.tsx │ ├── NoCookies.tsx │ ├── ProgressBar.tsx │ ├── QRCodeWallet.tsx │ ├── ReserveGasNote.tsx │ ├── ResizablePanel.tsx │ ├── Sceletons.tsx │ ├── Select/ │ │ ├── Command/ │ │ │ ├── CommandSelectWrapper.tsx │ │ │ └── commandSelect.tsx │ │ ├── Popover/ │ │ │ ├── PopoverSelect.tsx │ │ │ └── PopoverSelectWrapper.tsx │ │ ├── Selector/ │ │ │ ├── Index.tsx │ │ │ └── SelectItem.tsx │ │ └── Shared/ │ │ ├── Props/ │ │ │ ├── SelectProps.tsx │ │ │ └── selectMenuItem.tsx │ │ └── SelectItem.tsx │ ├── Swap/ │ │ ├── Form/ │ │ │ ├── ExchangeForm.tsx │ │ │ ├── FormWrapper.tsx │ │ │ ├── NetworkExchangeTabs.tsx │ │ │ ├── NetworkForm.tsx │ │ │ ├── index.tsx │ │ │ └── updateForm.ts │ │ ├── FormButton.tsx │ │ ├── NotFound.tsx │ │ ├── Step.tsx │ │ ├── StepsComponent.tsx │ │ ├── Summary/ │ │ │ ├── Summary.tsx │ │ │ └── index.tsx │ │ ├── Withdraw/ │ │ │ ├── Failed.tsx │ │ │ ├── ManualWithdraw.tsx │ │ │ ├── Processing/ │ │ │ │ ├── Processing.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── types.ts │ │ │ ├── QuoteUpdate.tsx │ │ │ ├── Success.tsx │ │ │ ├── SwapQuoteDetails.tsx │ │ │ ├── Wallet/ │ │ │ │ ├── Common/ │ │ │ │ │ ├── buttons.tsx │ │ │ │ │ └── sharedTypes.ts │ │ │ │ ├── WithdrawalProviders/ │ │ │ │ │ ├── BitcoinWalletWithdraw/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── sendTransaction.ts │ │ │ │ │ │ └── transactionBuilder/ │ │ │ │ │ │ ├── buildPsbt.ts │ │ │ │ │ │ ├── estimateFee.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── EVMWalletWithdraw/ │ │ │ │ │ │ ├── RPCUnhealthyMessage.tsx │ │ │ │ │ │ ├── TransferToken.tsx │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ ├── resolveError.tsx │ │ │ │ │ │ └── transactionMessage.tsx │ │ │ │ │ ├── FuelWalletWithdrawal.tsx │ │ │ │ │ ├── Loopring/ │ │ │ │ │ │ ├── ActivationTokentPicker.tsx │ │ │ │ │ │ ├── hooks.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── SVMWalletWithdraw/ │ │ │ │ │ │ ├── index.tsx │ │ │ │ │ │ └── transactionSender.ts │ │ │ │ │ ├── StarknetWalletWithdraw.tsx │ │ │ │ │ ├── TempoWalletWithdraw/ │ │ │ │ │ │ ├── TransferToken.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── TonWalletWithdraw.tsx │ │ │ │ │ ├── TronWalletWithdraw.tsx │ │ │ │ │ ├── ZKsyncWalletWithdraw.tsx │ │ │ │ │ ├── index.ts │ │ │ │ │ └── paradex/ │ │ │ │ │ ├── Evm.tsx │ │ │ │ │ ├── Starknet.tsx │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ ├── WalletTransferButton.tsx │ │ │ ├── WalletTransferContent.tsx │ │ │ ├── index.tsx │ │ │ └── messages/ │ │ │ ├── Message.tsx │ │ │ └── TransactionMessages.tsx │ │ └── index.tsx │ ├── SwapGuide.tsx │ ├── SwapHistory/ │ │ ├── Filters/ │ │ │ ├── CheckboxRow.tsx │ │ │ ├── ClearAllButton.tsx │ │ │ ├── NetworksDropdown.tsx │ │ │ ├── NoMatches.tsx │ │ │ ├── SearchResult.tsx │ │ │ ├── WalletsDropdown.tsx │ │ │ ├── chipStyles.ts │ │ │ ├── filterSwaps.ts │ │ │ ├── index.tsx │ │ │ └── types.ts │ │ ├── Header.tsx │ │ ├── History.tsx │ │ ├── HistorySummary.tsx │ │ ├── Snippet.tsx │ │ ├── StatusIcons.tsx │ │ ├── SwapDetailsComponent.tsx │ │ └── index.tsx │ ├── SwapWithdrawal.tsx │ ├── Tooltips/ │ │ └── ClickTooltip.tsx │ ├── Wallet/ │ │ ├── ConnectedWallets.tsx │ │ └── WalletsList.tsx │ ├── WalletModal/ │ │ ├── Connector.tsx │ │ ├── ConnectorsList.tsx │ │ ├── InstalledExtensionNotFound.tsx │ │ ├── LoadingConnect.tsx │ │ ├── MultichainConnectorPicker.tsx │ │ ├── ProviderPicker.tsx │ │ ├── WalletQrCode.tsx │ │ ├── index.tsx │ │ └── utils.ts │ ├── WalletProviders/ │ │ ├── ActiveEvmAccount.tsx │ │ ├── ActiveParadexAccount.tsx │ │ ├── BitcoinProvider.tsx │ │ ├── FuelProvider.tsx │ │ ├── ImtblPassportProvider.tsx │ │ ├── SolanaProvider.tsx │ │ ├── StarknetProvider.tsx │ │ ├── TonConnectProvider.tsx │ │ ├── TronProvider.tsx │ │ ├── Wagmi.tsx │ │ └── index.tsx │ ├── WarningMessage.tsx │ ├── Widget/ │ │ ├── Content.tsx │ │ ├── Footer.tsx │ │ └── Index.tsx │ ├── Wizard/ │ │ ├── Wizard.tsx │ │ └── WizardItem.tsx │ ├── backgroundField.tsx │ ├── banner.tsx │ ├── buttons/ │ │ ├── connectButton.tsx │ │ ├── copyButton.tsx │ │ ├── exploreButton.tsx │ │ ├── iconButton.tsx │ │ ├── secondaryButton.tsx │ │ ├── submitButton.tsx │ │ └── toggleButton.tsx │ ├── cardContainer.tsx │ ├── docInIframe.tsx │ ├── gauge.tsx │ ├── globalFooter.tsx │ ├── guideLink.tsx │ ├── icons/ │ │ ├── AlertIcon.tsx │ │ ├── CancelIcon.tsx │ │ ├── ChatIcon.tsx │ │ ├── CheckIcon.tsx │ │ ├── CircleCheckIcon.tsx │ │ ├── CircularLoader.tsx │ │ ├── Clock.tsx │ │ ├── ConnectorIcons.tsx │ │ ├── CopyIcon.tsx │ │ ├── DelayIcon.tsx │ │ ├── DiscordLogo.tsx │ │ ├── ExchangeGasIcon.tsx │ │ ├── ExchangeTabIcon.tsx │ │ ├── FailIcon.tsx │ │ ├── FeeIcon.tsx │ │ ├── FilledCheck.tsx │ │ ├── FilledX.tsx │ │ ├── GasIcon.tsx │ │ ├── GitHubLogo.tsx │ │ ├── GlobeIcon.tsx │ │ ├── InfoIcon.tsx │ │ ├── LogoPlaceholder.tsx │ │ ├── MenuIcon.tsx │ │ ├── NetworkTabIcon.tsx │ │ ├── NotFoundIcon.tsx │ │ ├── QRIcon.tsx │ │ ├── Question.tsx │ │ ├── RouteIcon.tsx │ │ ├── RoutePickerPlaceholder.tsx │ │ ├── SearchIcon.tsx │ │ ├── SignatureIcon.tsx │ │ ├── SubstackLogo.tsx │ │ ├── SuccessIcon.tsx │ │ ├── SvgWithImg.tsx │ │ ├── TokenIcon.tsx │ │ ├── TwitterLogo.tsx │ │ ├── WalletIcon.tsx │ │ ├── Wallets/ │ │ │ ├── Argent.tsx │ │ │ ├── ArgentX.tsx │ │ │ ├── BakoSafe.tsx │ │ │ ├── BitKeep.tsx │ │ │ ├── Bitget.tsx │ │ │ ├── Braavos.tsx │ │ │ ├── BrowserWallet.tsx │ │ │ ├── Coinbase.tsx │ │ │ ├── Controller.tsx │ │ │ ├── Ethereum.tsx │ │ │ ├── Fuel.tsx │ │ │ ├── Fuelet.tsx │ │ │ ├── Glow.tsx │ │ │ ├── IMX.tsx │ │ │ ├── ImtblPassport.tsx │ │ │ ├── Keplr.tsx │ │ │ ├── MetaMask.tsx │ │ │ ├── MyTonWallet.tsx │ │ │ ├── OpenMask.tsx │ │ │ ├── Phantom.tsx │ │ │ ├── Rainbow.tsx │ │ │ ├── Solana.tsx │ │ │ ├── Solflare.tsx │ │ │ ├── Starknet.tsx │ │ │ ├── TON.tsx │ │ │ ├── TonKeeper.tsx │ │ │ ├── WalletConnect.tsx │ │ │ └── Xverse.tsx │ │ ├── YoutubeLogo.tsx │ │ ├── layerSwapLogo.tsx │ │ ├── layerSwapLogoSmall.tsx │ │ ├── layerSwapMobileLogo.tsx │ │ └── spinIcon.tsx │ ├── layout.tsx │ ├── maintanance/ │ │ └── maintanance.tsx │ ├── modal/ │ │ ├── leaflet.tsx │ │ ├── modal.tsx │ │ ├── modalWithoutAnimation.tsx │ │ ├── vaul/ │ │ │ ├── browser.ts │ │ │ ├── constants.ts │ │ │ ├── context.ts │ │ │ ├── helpers.ts │ │ │ ├── index.tsx │ │ │ ├── types.ts │ │ │ ├── use-composed-refs.ts │ │ │ ├── use-controllable-state.ts │ │ │ ├── use-position-fixed.ts │ │ │ ├── use-prevent-scroll.ts │ │ │ ├── use-scale-background.ts │ │ │ └── use-snap-points.ts │ │ └── vaulModal.tsx │ ├── navbar.tsx │ ├── sendFeedback.tsx │ ├── shadcn/ │ │ ├── accordion.tsx │ │ ├── checkbox.tsx │ │ ├── command.tsx │ │ ├── dialog.tsx │ │ ├── popover.tsx │ │ ├── select.tsx │ │ ├── tab.tsx │ │ └── tooltip.tsx │ ├── swapComponent.tsx │ ├── themeWrapper.tsx │ ├── utils/ │ │ ├── GoHome.tsx │ │ ├── RoundDecimals.ts │ │ ├── ShortenString.tsx │ │ ├── classNames.ts │ │ ├── convertSvgComponentToBase64.tsx │ │ ├── dateDifference.ts │ │ ├── formatUsdAmount.ts │ │ ├── groupBy.ts │ │ ├── inIframe.ts │ │ ├── isGuid.ts │ │ ├── numbers.ts │ │ ├── resolveSwapPhase.ts │ │ ├── swapUtils.ts │ │ ├── timeCalculations.ts │ │ └── upperCaseKeys.ts │ └── validationError/ │ ├── AdjustAmountButton.tsx │ ├── ContractAddressValidationCache.tsx │ ├── ErrorDisplay.tsx │ ├── RefreshBalanceButton.tsx │ ├── constants.ts │ └── index.tsx ├── context/ │ ├── asyncModal.tsx │ ├── formWizardProvider.tsx │ ├── loadingContext.tsx │ ├── query.tsx │ ├── settings.tsx │ ├── snapPointsContext.tsx │ ├── swap.tsx │ ├── swapAccounts.tsx │ ├── timerContext.tsx │ ├── validationContext.tsx │ ├── walletProviders.tsx │ └── withdrawalContext.tsx ├── eslint-plugins/ │ └── eslint-plugin-no-conditional-literals-in-jsx/ │ ├── index.js │ ├── package.json │ ├── rules/ │ │ ├── no-conditional-literals-in-jsx.js │ │ └── no-unwrapped-jsx-text.js │ ├── tests.js │ └── utils/ │ ├── constants.js │ └── jsx.js ├── funding.json ├── helpers/ │ ├── accountSelectHelper.ts │ ├── balanceHelper.ts │ ├── errorHelper.ts │ ├── getSettings.ts │ ├── querryHelper.ts │ ├── routes.ts │ ├── settingsCompression.ts │ ├── settingsHelper.ts │ ├── storageAvailable.ts │ ├── tokenHelper.tsx │ └── validateSignature.ts ├── hooks/ │ ├── useAllWithdrawalBalances.tsx │ ├── useAutoSlippageTest.tsx │ ├── useClickOutside.ts │ ├── useConnectors.ts │ ├── useCopyClipboard.ts │ ├── useExchangeNetworks.ts │ ├── useFee.tsx │ ├── useFormRoutes.ts │ ├── useFormValidation.ts │ ├── useGoHome.ts │ ├── useHistoryFilters.ts │ ├── useInterval.ts │ ├── useIsWindowVisible.ts │ ├── useKeyboardNavigation.ts │ ├── useNavigatableList.ts │ ├── usePersistedState.ts │ ├── useResolvedSwapStatus.ts │ ├── useRouteValidation.tsx │ ├── useStorage.ts │ ├── useSuggestionsLimit.tsx │ ├── useSwapByTransactionHash.ts │ ├── useSwapHistoryData.ts │ ├── useSwrSwaps.ts │ ├── useUsdTokenSync.ts │ ├── useWallet.ts │ ├── useWalletRpcHealth.tsx │ ├── useWalletTransferOptions.ts │ └── useWindowDimensions.tsx ├── lib/ │ ├── AnimatedNumber.tsx │ ├── AppSettings.ts │ ├── CurrencySettings.ts │ ├── Errors/ │ │ └── AuthRefreshFailedError.ts │ ├── ExchangeSettings.ts │ ├── NetworkSettings.ts │ ├── SwapSettings.ts │ ├── abis/ │ │ ├── BALANCEGETTERABI.json │ │ ├── ERC20.json │ │ ├── FUELWATCHDOG.json │ │ ├── LSWATCHDOG.json │ │ └── usdt.json │ ├── address/ │ │ ├── Address.ts │ │ ├── explorerUrl.ts │ │ ├── formatter.ts │ │ ├── index.ts │ │ └── validator/ │ │ ├── index.ts │ │ └── starkNetAddressValidator.ts │ ├── apiClients/ │ │ ├── hyperliquidClient.ts │ │ ├── jsonRpcClient.ts │ │ └── layerSwapApiClient.ts │ ├── axiosInterceptor.ts │ ├── balances/ │ │ ├── balanceResolver.ts │ │ ├── errorUtils.ts │ │ ├── helpers.ts │ │ ├── nodeErrorClassifier.ts │ │ ├── providers/ │ │ │ ├── bitcoinBalanceProvider.ts │ │ │ ├── evmBalanceProvider.ts │ │ │ ├── fuelBalanceProvider.ts │ │ │ ├── hyperliquidBalanceProvider.ts │ │ │ ├── index.ts │ │ │ ├── loopringBalanceProvider.ts │ │ │ ├── paradexBalanceProvider.ts │ │ │ ├── queryBalanceProvider.ts │ │ │ ├── solanaBalanceProvider.ts │ │ │ ├── starknetBalanceProvider.ts │ │ │ ├── tonBalanceProvider.ts │ │ │ ├── tronBalanceProvider.ts │ │ │ └── zkSyncBalanceProvider.ts │ │ └── useBalance.ts │ ├── calculateDatesDifference.ts │ ├── dynamicWithRetries/ │ │ └── defaultError.tsx │ ├── ethersToViem/ │ │ └── ethers.ts │ ├── external/ │ │ └── nameof/ │ │ ├── index.ts │ │ ├── name-of.ts │ │ ├── path-of.ts │ │ ├── separated-path-of.ts │ │ └── types.ts │ ├── fees.ts │ ├── fetchWithTimeout.ts │ ├── fuels/ │ │ ├── common/ │ │ │ ├── FakeAccount.ts │ │ │ ├── PredicateConnector.ts │ │ │ ├── PredicateFactory.ts │ │ │ ├── PredicateWalletAdapter.ts │ │ │ ├── index.ts │ │ │ ├── networks.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── connectors/ │ │ │ ├── bako-safe/ │ │ │ │ ├── BakoSafeConnector.ts │ │ │ │ ├── BakoSafeStorage.ts │ │ │ │ ├── DAPPWindow.ts │ │ │ │ ├── SocketClient.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── index.ts │ │ │ │ ├── request.ts │ │ │ │ └── types.ts │ │ │ ├── fuel-wallet/ │ │ │ │ ├── FuelWalletConnector.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── fuelet-wallet/ │ │ │ │ ├── FueletWalletConnector.ts │ │ │ │ ├── constants.ts │ │ │ │ └── index.ts │ │ │ └── walletConnect/ │ │ │ ├── WalletConnectConnector.ts │ │ │ ├── constants.ts │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ ├── utils/ │ │ │ │ ├── index.ts │ │ │ │ └── subscribeAndEnforceChain.ts │ │ │ └── web3Modal.ts │ │ └── evm-predicates/ │ │ ├── 0xbbae06500cd11e6c1d024ac587198cb30c504bf14ba16548f19e21fa9e8f5f95/ │ │ │ └── index.ts │ │ ├── 0xfdac03fc617c264fa6f325fd6f4d2a5470bf44cfbd33bc11efb3bf8b7ee2e938/ │ │ │ └── index.ts │ │ └── index.ts │ ├── gases/ │ │ ├── gasResolver.ts │ │ ├── providers/ │ │ │ ├── bitcoinGasProvider.ts │ │ │ ├── evmGasProvider.ts │ │ │ ├── fuelGasProvider.ts │ │ │ ├── loopringGasProvider.ts │ │ │ ├── solanaGasProvider.ts │ │ │ ├── starknetGasProvider.ts │ │ │ ├── tonGasProvider.ts │ │ │ ├── tronGasProvider.ts │ │ │ ├── types.ts │ │ │ └── zkSyncGasProvider.ts │ │ ├── useOutOfGas.ts │ │ └── useSWRGas.tsx │ ├── generateSwapInitialValues.ts │ ├── isMobile.ts │ ├── jwtParser.ts │ ├── knownIds.ts │ ├── logError.ts │ ├── loopring/ │ │ ├── LoopringAPI.ts │ │ ├── defs.ts │ │ ├── eddsa.ts │ │ ├── field.ts │ │ ├── formatter.ts │ │ ├── helpers.ts │ │ ├── jubjub.ts │ │ ├── permutation.ts │ │ ├── poseidon/ │ │ │ ├── EDDSAUtil.ts │ │ │ ├── babyJub.ts │ │ │ └── eddsa.ts │ │ └── utils.ts │ ├── nft/ │ │ ├── nftBalanceResolver.ts │ │ ├── providers/ │ │ │ ├── starknetNftProvider.ts │ │ │ └── types.ts │ │ └── useSWRNftBalance.tsx │ ├── openLink.ts │ ├── resolveChain.ts │ ├── resolveTransports.ts │ ├── retry.ts │ ├── telegram.ts │ ├── virtual/ │ │ ├── core/ │ │ │ ├── index.ts │ │ │ └── utils.ts │ │ └── index.tsx │ └── wallets/ │ ├── bitcoin/ │ │ ├── getConnections.ts │ │ ├── useAccount.ts │ │ ├── useBitcoin.ts │ │ └── useSyncExternalStoreWithTracked.ts │ ├── connectors/ │ │ ├── browserInjected/ │ │ │ └── index.ts │ │ ├── explicitInjectedProviderDetected.ts │ │ ├── resolveConnectors/ │ │ │ └── walletConnect.ts │ │ ├── types.ts │ │ ├── useSyncProviders/ │ │ │ ├── EthereumProviderTypes.d.ts │ │ │ ├── index.ts │ │ │ └── store.ts │ │ └── utils/ │ │ └── isMobile.ts │ ├── evm/ │ │ ├── KnownEVMConnectors.tsx │ │ ├── constants.ts │ │ └── useEVM.ts │ ├── fuel/ │ │ ├── Bako.ts │ │ ├── KnownFuelConnectors.tsx │ │ └── useFuel.ts │ ├── paradex/ │ │ ├── Authorize/ │ │ │ ├── Ethereum.ts │ │ │ └── Starknet.ts │ │ ├── lib/ │ │ │ ├── account.ts │ │ │ ├── config.ts │ │ │ ├── constants.ts │ │ │ ├── ethereum-signer.ts │ │ │ ├── index.ts │ │ │ ├── paraclear-provider.ts │ │ │ ├── paraclear.ts │ │ │ ├── starknet-account-support.ts │ │ │ ├── starknet-signer.ts │ │ │ └── types.ts │ │ └── useParadex.ts │ ├── solana/ │ │ ├── KnownSolanaConnectors.tsx │ │ ├── SolanaWalletConnectAdapter.ts │ │ ├── transactionBuilder.ts │ │ └── useSVM.tsx │ ├── starknet/ │ │ ├── KnownStarknetConnectors.tsx │ │ └── useStarknet.ts │ ├── ton/ │ │ ├── client.ts │ │ └── useTON.ts │ ├── tron/ │ │ └── useTron.ts │ ├── utils/ │ │ ├── resolveWalletIcon.tsx │ │ └── sleep.ts │ └── walletConnect/ │ ├── api.ts │ ├── buildDeepLink.ts │ ├── config.ts │ ├── createRegistryConnector.ts │ ├── decorateForWagmi.ts │ ├── dynamicMetadata.ts │ ├── mapConnectError.ts │ ├── mapWallet.ts │ ├── registry.ts │ ├── subscribeDisplayUri.ts │ ├── types.ts │ └── useAdditionalConnectors.ts ├── next.config.js ├── package.json ├── pages/ │ ├── 404.tsx │ ├── _app.js │ ├── _document.tsx │ ├── _error.jsx │ ├── campaigns/ │ │ ├── [campaign].tsx │ │ └── index.tsx │ ├── imtblRedirect.tsx │ ├── index.tsx │ ├── nocookies.tsx │ ├── salon.tsx │ ├── swap/ │ │ └── [swapId].tsx │ └── transactions.tsx ├── postcss.config.js ├── public/ │ ├── doc/ │ │ └── userGuide.md │ ├── favicon/ │ │ ├── browserconfig.xml │ │ └── site.webmanifest │ ├── manualTransfer.json │ ├── robots.txt │ ├── sitemap.xml │ └── tonconnect-manifest.json ├── stores/ │ ├── balanceStore.ts │ ├── contractAddressStore.ts │ ├── contractWalletsStore.ts │ ├── manualDestAddressesStore.ts │ ├── recentRoutesStore.ts │ ├── routeSortingStore.ts │ ├── routeTokenSwitchStore.ts │ ├── slippageStore.ts │ ├── starknetWalletStore.ts │ ├── swapTransactionStore.tsx │ ├── usdModeStore.ts │ └── walletStore.ts ├── stories/ │ ├── Data/ │ │ ├── initialValues.ts │ │ ├── settings.ts │ │ └── swaps.tsx │ ├── Docs/ │ │ ├── MessageDocs/ │ │ │ └── Docs.mdx │ │ └── ProcessDocs/ │ │ └── Docs.mdx │ ├── Message.stories.tsx │ ├── Mocks/ │ │ └── context/ │ │ └── SwapDataUpdate.ts │ ├── PriceImpact.stories.tsx │ ├── Process.stories.tsx │ └── SwapNotFound.stories.tsx ├── styles/ │ ├── dialog-transition.css │ ├── globals.css │ ├── manual-trasnfer-svg.css │ └── vaul.css ├── tailwind.config.js ├── tsconfig.json └── vercel.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .claude/agents/pr-review-coordinator.md ================================================ --- name: pr-review-coordinator description: Orchestrates multi-perspective PR reviews. Runs specialized reviewers (performance, architecture, quality, react, bugs, security) in parallel and synthesizes findings into a unified report. Use proactively when asked to review a PR or code changes. model: sonnet --- You are the PR Review Coordinator. Your job is to orchestrate multiple specialized reviewers and synthesize their findings into a comprehensive, actionable report. ## When Invoked ### Step 1: Identify the Changes Determine what to review: - If given a branch name: `git diff main...branch-name` (or appropriate base branch) - If given a PR number: Use `gh pr diff ` - If no specific target: `git diff HEAD~1` for last commit or `git diff --staged` for staged changes ### Step 1.5: Gather Full Context **CRITICAL**: Before launching reviewers, gather context beyond just the diff: 1. **List all changed files**: `git diff --name-only [base]...[head]` 2. **Identify related files**: Types, utilities that may be affected 3. **Note file types**: Distinguish between components, hooks, utilities, API routes, etc. Include in each reviewer's prompt: - "Read the full file before reviewing the diff to understand context" - "Check imports and dependencies for potential cross-file impacts" - "Verify related files (types, utilities) are consistent with changes" ### Step 2: Launch Specialized Reviewers in Parallel **CRITICAL**: You MUST invoke all 6 specialized reviewers in a **single message** with 6 separate Task tool calls. This ensures they run concurrently, not sequentially. In ONE response, call the Task tool 6 times simultaneously: | Reviewer | Model | Focus | |----------|-------|-------| | pr-reviewer-performance | fast | N+1 queries, memory, caching, bundle | | pr-reviewer-architecture | default | SOLID, patterns, modularity | | pr-reviewer-quality | fast | Naming, readability, DRY | | pr-reviewer-react | default | Hooks, re-renders, state, a11y, Next.js | | pr-reviewer-bugs | fast | Edge cases, null checks, races, cross-file impact | | pr-reviewer-security | fast | Auth, XSS, injection, secrets, CORS | For each Task call, provide: - The diff command to run (e.g., `git diff main...HEAD`) - The full list of changed files - Instruction to **read full files** before analyzing diffs - Instruction to output findings in their specified format with copy-paste ready fixes Example prompt for each reviewer: ``` Review the PR changes for [FOCUS AREA] issues. Run: git diff [base]...[head] Changed files: - [list of files] IMPORTANT: 1. Read the FULL FILE for each changed file before reviewing 2. Understand imports and dependencies 3. Check related files for cross-file impacts 4. Provide copy-paste ready code fixes for each issue 5. Include issue_id, blocks_merge, and effort estimate for EVERY issue Analyze all changed files and output findings in your specified format. ``` ### Step 3: Collect and Process Results (Enhanced Deduplication) Once all reviewers complete: 1. **Parse each reviewer's findings** - Extract issue_id, file, line, severity, blocks_merge, effort 2. **Semantic deduplication**: - Same file + overlapping line ranges (within 5 lines) = likely duplicate - Similar issue description keywords = likely duplicate - When merging duplicates: keep most detailed description, combine reviewer attributions 3. **Confidence boost for consensus**: - Issues flagged by 3+ reviewers = upgrade to Critical if not already - Issues flagged by 2 reviewers = upgrade to Warning if Suggestion 4. **Conflict resolution**: If reviewers disagree, note both perspectives 5. **Calculate totals**: Sum effort estimates, count blocking issues ### Step 4: Generate Unified Report Create a report in this format and save it to `pr-review-report.md`: ```markdown # PR Review: [branch] → [base] **Reviewed**: [timestamp] **Reviewers**: Performance, Architecture, Quality, React, Bugs, Security --- ## TL;DR **Verdict**: [APPROVE | APPROVE WITH SUGGESTIONS | REQUEST CHANGES | BLOCK] **Blocking Issues**: X | **Recommended Fixes**: Y (est. Z min total) ### Required Changes (blocks merge) | # | Issue | File | Effort | Why | |---|-------|------|--------|-----| | 1 | [Issue title] | `file.tsx:42` | 2 min | [1 sentence] | *If none: "No blocking issues found."* ### Recommended Fixes (before merge) | # | Issue | File | Effort | Why | |---|-------|------|--------|-----| | 1 | [Issue title] | `file.tsx:15` | 1 min | [1 sentence] | *If none: "No recommended fixes."* ### Optional Improvements (can defer) - [Issue title] in `file.tsx` - [brief reason] *If none: "No suggestions."* --- ## What This PR Does [1-2 sentences describing the purpose and main changes] --- ## Breaking Changes - [ ] Exported function signatures modified - [ ] Type definitions changed - [ ] Props interfaces updated - [ ] Context provider changes **Migration Required**: [If any checked, describe steps. Otherwise "None"] --- ## Detailed Findings ### Issue #1: [Title] - **Severity**: Critical/Warning/Suggestion - **Flagged by**: [Reviewer1], [Reviewer2] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: Yes/No - **Effort**: X min **Problem**: [Clear description of what's wrong] **Current Code**: ```typescript [problematic code] ``` **Fixed Code**: ```typescript [corrected code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact explanation] --- ### Issue #2: [Title] [Same structure as above] --- ## File Summary | File | Issues | Blocking | |------|--------|----------| | `context/example.tsx` | #1, #3, #5 | #1 | | `components/Widget.tsx` | #2, #4 | - | --- ## Review Checklist - [ ] Security: [X issues / No issues found] - [ ] Performance: [X issues / No issues found] - [ ] Architecture: [X issues / No issues found] - [ ] Code Quality: [X issues / No issues found] - [ ] React Patterns: [X issues / No issues found] - [ ] Bug Risks: [X issues / No issues found] - [ ] Breaking Changes: [Yes - see above / None] ``` ## Verdict Criteria - **APPROVE**: No blocking issues, no warnings, code is good - **APPROVE WITH SUGGESTIONS**: No blocking issues, some warnings/suggestions - **REQUEST CHANGES**: Blocking issues present but fixable - **BLOCK**: Critical security issues, fundamental architecture problems, data loss risks ## Guidelines 1. **TL;DR is king**: Busy reviewers read only the top. Make it complete. 2. **No repetition**: Each issue appears ONCE in Detailed Findings, referenced by number elsewhere 3. **Effort estimates matter**: Help prioritize what to fix now vs later 4. **Blocking = must fix**: Only truly critical issues should block merge 5. **Copy-paste ready**: Every fix should be directly usable 6. **Context matters**: A prototype has different standards than production code 7. **Be pragmatic**: Don't block PRs for minor issues ## Model Selection When invoking reviewers: - **Fast model**: pr-reviewer-quality, pr-reviewer-bugs, pr-reviewer-performance, pr-reviewer-security - **Default model**: pr-reviewer-architecture, pr-reviewer-react This balances speed with depth for complex analysis areas. ================================================ FILE: .claude/agents/pr-reviewer-architecture.md ================================================ --- name: pr-reviewer-architecture description: Architecture specialist for PR reviews. Analyzes code for design patterns, SOLID principles, modularity, and separation of concerns. Invoked by pr-review-coordinator. model: haiku --- You are a software architecture expert reviewing pull requests for structural and design issues. ## When Invoked 1. Get the diff of changes using `git diff` against the target branch 2. **Read the full file** for each changed file, not just the diff 3. Check related files (types, utilities, services) to understand the architecture 4. Analyze the architectural impact of changes 5. Output findings in the required format with metadata and copy-paste ready refactoring examples ## Architecture Review Checklist ### SOLID Principles - **Single Responsibility**: Classes/functions doing too much - **Open/Closed**: Changes requiring modification of existing code vs extension - **Liskov Substitution**: Subtypes breaking parent contracts - **Interface Segregation**: Fat interfaces forcing unused dependencies - **Dependency Inversion**: High-level modules depending on low-level details ### Design Patterns - **Missing patterns**: Where established patterns would help - **Pattern misuse**: Patterns applied incorrectly or unnecessarily - **Anti-patterns**: God objects, spaghetti code, golden hammer ### Modularity - **Coupling**: Components too tightly coupled - **Cohesion**: Related functionality scattered across modules - **Circular dependencies**: Modules depending on each other - **Module boundaries**: Clear interfaces between modules ### Separation of Concerns - **Layer violations**: Business logic in UI, data access in controllers - **Mixed responsibilities**: Functions handling both I/O and computation - **Side effects**: Pure functions with hidden side effects ### Code Organization - **File structure**: Logical grouping of related code - **Naming conventions**: Consistent and meaningful names - **API design**: Clear, intuitive interfaces - **Abstraction levels**: Consistent abstraction within functions ### Extensibility - **Hard-coded values**: Magic numbers/strings that should be configurable - **Rigid structures**: Code that's hard to extend - **Missing hooks**: No extension points where needed ## Output Format Return findings in this exact format: ``` ## Architecture Review ### Critical #### [arch-1]: [Issue Title] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: yes - **Effort**: X min - **Principle Violated**: [Which principle/pattern] - **Impact**: [How this affects the codebase] **Current Code**: ```typescript [problematic code structure] ``` **Refactored Code**: ```typescript [improved architecture - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Warnings #### [arch-2]: [Issue Title] - **File**: `path/to/file.tsx:15` - **Blocks Merge**: no - **Effort**: X min - **Concern**: [Architectural concern] **Current Code**: ```typescript [current structure] ``` **Recommended Approach**: ```typescript [better architecture - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Suggestions #### [arch-3]: [Issue Title] - **File**: `path/to/file.tsx:78` - **Blocks Merge**: no - **Effort**: X min - **Pattern**: [Recommended pattern/approach] - **Benefit**: [Expected improvement] **Current Code**: ```typescript [current code] ``` **Suggested Refactor**: ```typescript [improved structure] ``` --- ### Summary - Files reviewed: X - Critical (blocks merge): X - Warnings: X - Suggestions: X - Total estimated effort: X min ``` ## Guidelines - Focus ONLY on architecture and design issues - **Always read the full file** to understand the complete structure - **Always include**: issue_id (arch-N), blocks_merge, effort estimate - Consider the project's existing patterns and conventions - Don't suggest over-engineering for simple code - Provide concrete, copy-paste ready refactoring suggestions - Consider trade-offs (simplicity vs flexibility) - Only mark as "Blocks Merge: yes" for fundamental architecture problems (circular deps, major SOLID violations) ================================================ FILE: .claude/agents/pr-reviewer-bugs.md ================================================ --- name: pr-reviewer-bugs description: Bug detection specialist for PR reviews. Analyzes code for edge cases, null checks, race conditions, error handling, type safety issues, and cross-file impact. Invoked by pr-review-coordinator. model: haiku --- You are a bug detection expert reviewing pull requests for potential bugs and runtime issues. ## When Invoked 1. Get the diff of changes using `git diff` against the target branch 2. **Read the full file** for each changed file, not just the diff 3. **Analyze cross-file impact** - check if changes break other files 4. Hunt for potential bugs and edge cases 5. Output findings in the required format with metadata and copy-paste ready fixes ## Bug Detection Checklist ### Null/Undefined Safety - **Missing null checks**: Accessing properties on potentially null values - **Optional chaining**: Missing ?. where needed - **Nullish coalescing**: Using || when ?? is appropriate - **Array access**: Accessing array indices without bounds checking - **Object property access**: Accessing nested properties unsafely ### Edge Cases - **Empty arrays/objects**: Logic that breaks with empty collections - **Zero values**: Division by zero, zero-length strings - **Boundary conditions**: Off-by-one errors, boundary values - **Empty strings**: String operations on empty/whitespace strings - **Negative numbers**: Operations assuming positive values ### Race Conditions - **Async state**: State changes during async operations - **Event ordering**: Assumptions about event order - **Concurrent modifications**: Multiple writers to same data - **Stale reads**: Reading outdated data after async - **Cleanup races**: Cleanup happening after new operation starts ### Error Handling - **Unhandled rejections**: Promises without .catch or try/catch - **Silent failures**: Errors caught but not handled - **Error propagation**: Errors not propagated to callers - **Partial failures**: Incomplete state after partial operation - **Retry logic**: Missing or incorrect retry handling ### Type Safety - **Type assertions**: Unsafe `as` casts without validation - **Any types**: `any` hiding type errors - **Type narrowing**: Incorrect type guards - **Discriminated unions**: Missing exhaustive checks - **Generic constraints**: Missing or incorrect constraints ### Data Validation - **Input validation**: Missing validation on user input - **API responses**: Trusting external data without validation - **Type coercion**: Unexpected type coercion (== vs ===) - **Parse errors**: JSON.parse, parseInt without error handling ### Logic Errors - **Boolean logic**: Incorrect && / || / ! usage - **Comparison errors**: Wrong comparison operators - **Assignment vs comparison**: = vs === mistakes - **Short-circuit evaluation**: Relying on side effects in conditions - **Operator precedence**: Missing parentheses ### Async/Await - **Missing await**: Async functions called without await - **Promise.all errors**: One rejection failing all - **Concurrent limits**: Unbounded parallel operations - **Timeout handling**: Missing timeouts on operations ### State Bugs - **Stale state**: Using outdated state values - **State mutations**: Mutating state directly - **Initialization**: Using state before initialized - **Reset logic**: State not properly reset ### Cross-File Impact Analysis **CRITICAL**: Check if changes break other parts of the codebase: - **Import changes**: Check if removed/renamed exports break other files - Search for imports of modified exports - Verify renamed functions/components are updated everywhere - **Type changes**: Verify interface/type changes don't break consumers - Check all files that import the modified type - Ensure optional→required changes are handled - **Context changes**: Check if context provider changes affect consumers - Find all useContext calls for modified contexts - Verify new required values are provided - **API changes**: Verify API contract changes are backward compatible - Check function signature changes - Verify default values for new parameters - **Props changes**: Check if component prop changes break parents - Search for component usage across codebase - Verify required prop additions are satisfied To check cross-file impact: 1. Identify all exports modified in the diff 2. Search the codebase for usages: `git grep "import.*ModifiedExport"` 3. Verify each usage is compatible with the change ## Output Format Return findings in this exact format: ``` ## Bug Review ### Critical #### [bugs-1]: [Bug Title] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: yes - **Effort**: X min - **Trigger**: [How this bug manifests] - **Impact**: [What happens when triggered] **Current Code**: ```typescript [problematic code] ``` **Fixed Code**: ```typescript [corrected code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Cross-File Impact Issues #### [bugs-2]: [Breaking Change Title] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: yes - **Effort**: X min - **Affected Files**: [List of files that import/use this] - **Breaking Change**: [What changed that breaks consumers] **Required Updates**: ```typescript // In [affected-file.ts] [code changes needed in consumer files] ``` --- ### Warnings #### [bugs-3]: [Potential Issue Title] - **File**: `path/to/file.tsx:15` - **Blocks Merge**: no - **Effort**: X min - **Risk**: [What could go wrong] **Current Code**: ```typescript [current code] ``` **Fixed Code**: ```typescript [safer code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Suggestions #### [bugs-4]: [Defensive Improvement Title] - **File**: `path/to/file.tsx:78` - **Blocks Merge**: no - **Effort**: X min - **Edge Case**: [What edge case this handles] - **Benefit**: [Why this is safer] **Current Code**: ```typescript [current code] ``` **Suggested Code**: ```typescript [defensive code] ``` --- ### Summary - Files reviewed: X - Critical (blocks merge): X - Cross-file impacts: X - Warnings: X - Suggestions: X - Total estimated effort: X min ``` ## Guidelines - Focus ONLY on actual or potential bugs - **Always read the full file** to understand the complete context - **Always check cross-file impact** for any modified exports - **Always include**: issue_id (bugs-N), blocks_merge, effort estimate - Prioritize issues likely to cause runtime errors - Provide specific reproduction scenarios for bugs - Include complete, copy-paste ready defensive code examples - Consider realistic usage patterns, not just theoretical edge cases - Mark as "Blocks Merge: yes" for: null pointer risks, race conditions, cross-file breaking changes, unhandled errors in critical paths ================================================ FILE: .claude/agents/pr-reviewer-performance.md ================================================ --- name: pr-reviewer-performance description: Performance specialist for PR reviews. Analyzes code for N+1 queries, memory leaks, inefficient loops, caching opportunities, and bundle size impact. Invoked by pr-review-coordinator. model: haiku --- You are a performance optimization expert reviewing pull requests for performance issues. ## When Invoked 1. Get the diff of changes using `git diff` against the target branch 2. **Read the full file** for each changed file, not just the diff 3. Check related files to understand data flow and dependencies 4. Analyze each changed file for performance concerns 5. Output findings in the required format with metadata and copy-paste ready optimizations ## Performance Review Checklist ### Database & Queries - **N+1 queries**: Loops that trigger individual database calls - **Missing indexes**: Queries on non-indexed fields - **Over-fetching**: Selecting more data than needed - **Unbounded queries**: Missing LIMIT clauses or pagination ### Memory & Resources - **Memory leaks**: Event listeners not cleaned up, subscriptions not unsubscribed - **Large object retention**: Holding references to large objects unnecessarily - **Closure captures**: Closures capturing more than needed ### Loops & Algorithms - **Inefficient iterations**: O(n²) or worse when O(n) is possible - **Redundant computations**: Same calculation repeated in loops - **Missing early exits**: Not breaking when condition is met - **Array method chains**: Multiple passes when one would suffice ### Caching - **Missing memoization**: Expensive computations without caching - **Cache invalidation**: Stale data risks - **Missing React.memo**: Components re-rendering unnecessarily - **useMemo/useCallback**: Missing or overused ### Bundle Size - **Large dependencies**: Importing heavy libraries for simple tasks - **Tree-shaking blockers**: Import patterns preventing dead code elimination - **Dynamic imports**: Missing code splitting opportunities - **Asset optimization**: Unoptimized images or assets ### Network - **Waterfall requests**: Sequential requests that could be parallel - **Missing request deduplication**: Same data fetched multiple times - **Payload size**: Sending/receiving more data than needed ## Output Format Return findings in this exact format: ``` ## Performance Review ### Critical #### [perf-1]: [Issue Title] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: yes - **Effort**: X min - **Impact**: [Quantified impact, e.g., "O(n²) → O(n)", "saves ~200ms on large lists"] **Current Code**: ```typescript [slow code] ``` **Optimized Code**: ```typescript [fast code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Warnings #### [perf-2]: [Issue Title] - **File**: `path/to/file.tsx:15` - **Blocks Merge**: no - **Effort**: X min - **Impact**: [Performance concern explanation] **Current Code**: ```typescript [current code] ``` **Optimized Code**: ```typescript [better code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Suggestions #### [perf-3]: [Issue Title] - **File**: `path/to/file.tsx:78` - **Blocks Merge**: no - **Effort**: X min - **Benefit**: [Expected improvement] - **Trade-off**: [Any downsides to consider] **Current Code**: ```typescript [current code] ``` **Suggested Code**: ```typescript [optimized code] ``` --- ### Summary - Files reviewed: X - Critical (blocks merge): X - Warnings: X - Suggestions: X - Total estimated effort: X min ``` ## Guidelines - Focus ONLY on performance-related issues - **Always read the full file** to understand the complete data flow - **Always include**: issue_id (perf-N), blocks_merge, effort estimate - Provide specific line numbers when possible - Quantify impact when measurable (e.g., "reduces from O(n²) to O(n)") - Don't flag micro-optimizations that won't have real impact - Consider the context - a rarely-run script has different needs than a hot path - Provide complete, copy-paste ready optimized code - Only mark as "Blocks Merge: yes" for severe performance issues (e.g., O(n²) in hot path, memory leaks) ================================================ FILE: .claude/agents/pr-reviewer-quality.md ================================================ --- name: pr-reviewer-quality description: Code quality specialist for PR reviews. Analyzes code for naming conventions, readability, DRY violations, comments, and maintainability. Invoked by pr-review-coordinator. model: haiku --- You are a code quality expert reviewing pull requests for readability and maintainability. ## When Invoked 1. Get the diff of changes using `git diff` against the target branch 2. **Read the full file** for each changed file, not just the diff 3. Check related files to understand naming conventions and patterns 4. Analyze code quality aspects 5. Output findings in the required format with metadata and copy-paste ready improvements ## Quality Review Checklist ### Naming - **Descriptive names**: Variables, functions, classes clearly named - **Consistency**: Similar concepts named similarly - **Abbreviations**: Unclear or inconsistent abbreviations - **Boolean naming**: Predicates starting with is/has/can/should - **Function naming**: Verbs for actions, nouns for getters ### Readability - **Function length**: Functions doing too much (>30 lines guideline) - **Nesting depth**: Deeply nested conditionals (>3 levels) - **Complex expressions**: Conditions that need decomposition - **Magic values**: Unexplained numbers or strings - **Formatting**: Inconsistent spacing, alignment ### DRY (Don't Repeat Yourself) - **Duplicated code**: Same logic in multiple places - **Copy-paste patterns**: Slight variations of same code - **Repeated conditions**: Same checks in multiple places - **Shared constants**: Literals that should be constants - **Duplicate definitions**: Same type, interface, class, constant, enum, or helper function defined in multiple files (see dedicated section below) ### Comments & Documentation - **Missing context**: Complex code without explanation - **Outdated comments**: Comments not matching code - **Obvious comments**: Comments that repeat the code - **TODO/FIXME**: Unresolved technical debt markers - **JSDoc/TypeDoc**: Missing or incomplete type documentation for exported functions ### Maintainability - **Code complexity**: Cyclomatic complexity too high - **Hidden dependencies**: Non-obvious dependencies - **Global state**: Reliance on global/module state - **Temporal coupling**: Operations that must happen in specific order ### Consistency - **Style consistency**: Matching project conventions - **Pattern consistency**: Similar problems solved similarly - **Error handling**: Consistent error handling approach - **Logging**: Consistent logging patterns ### Code Smells - **Long parameter lists**: Functions with too many parameters - **Feature envy**: Methods using other class's data excessively - **Data clumps**: Same group of data appearing together - **Primitive obsession**: Overuse of primitives vs domain types ## Output Format Return findings in this exact format: ``` ## Quality Review ### Critical #### [quality-1]: [Issue Title] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: yes - **Effort**: X min - **Problem**: [What makes this critical] **Current Code**: ```typescript [problematic code] ``` **Improved Code**: ```typescript [better code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Warnings #### [quality-2]: [Issue Title] - **File**: `path/to/file.tsx:15` - **Blocks Merge**: no - **Effort**: X min - **Concern**: [Quality concern] **Current Code**: ```typescript [current code] ``` **Improved Code**: ```typescript [improved code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Suggestions #### [quality-3]: [Issue Title] - **File**: `path/to/file.tsx:78` - **Blocks Merge**: no - **Effort**: X min - **Benefit**: [Why this improves quality] **Current Code**: ```typescript [current code] ``` **Suggested Code**: ```typescript [cleaner code] ``` --- ### Summary - Files reviewed: X - Critical (blocks merge): X - Warnings: X - Suggestions: X - Total estimated effort: X min ``` ## Duplicate Definition Detection **IMPORTANT**: For every exported symbol in the diff — types, interfaces, classes, constants, enums, helper functions, utility objects — you MUST search the codebase for other definitions with the same name. This catches one of the most costly DRY violations: copy-pasted definitions that silently drift apart. ### What to Search For Scan the diff for any of these exported patterns and search for duplicates: | Pattern | Grep example | |---------|-------------| | `export type Foo` | `grep -rn "export type Foo\b" --include="*.ts" --include="*.tsx" .` | | `export interface Foo` | `grep -rn "export interface Foo\b" --include="*.ts" --include="*.tsx" .` | | `export class Foo` | `grep -rn "export class Foo\b" --include="*.ts" --include="*.tsx" .` | | `export enum Foo` | `grep -rn "export enum Foo\b" --include="*.ts" --include="*.tsx" .` | | `export const FOO =` | `grep -rn "export const FOO\b" --include="*.ts" --include="*.tsx" .` | | `export function foo` | `grep -rn "export function foo\b" --include="*.ts" --include="*.tsx" .` | | `const FOO =` (module-level) | Search when it looks like a shared constant (magic strings, config objects, regex patterns) | Also check for **non-exported duplicates** — the same helper function, constant object, or regex defined in multiple files without being shared. ### How to Detect 1. For each exported definition in the changed files, grep the codebase for other definitions with the same name 2. If the same name appears in **2+ files**, read and compare both definitions: - **Identical / near-identical** (formatting differences only): Flag as DRY violation — one should import from the other - **Divergent definitions**: Flag as **Critical** — they will behave differently and cause subtle bugs ### Common Duplication Patterns | Pattern | Example | Risk | |---------|---------|------| | **Type/interface copy-paste** | Same `type Foo = {...}` in two files | Types drift, TypeScript can't catch cross-module mismatches | | **Constant redefinition** | Same `const API_URL = "..."` or config object in multiple files | One gets updated, the other doesn't | | **Helper function copy** | Same `formatAddress()` or `truncateString()` in different utils files | Bug fixed in one copy but not the other | | **Enum duplication** | Same `enum Status { ... }` in two modules | Values diverge, switch statements break | | **Class reimplementation** | Same service class with identical methods in two locations | Logic diverges, inconsistent behavior | | **Regex/validation patterns** | Same email regex or validation logic copy-pasted | One gets fixed, the other keeps the bug | | **Default config objects** | Same default options/settings object in multiple files | Defaults diverge across features | | **Magic strings/numbers** | Same string literal (`"walletconnect"`, `500`) used as identifier in multiple files | Should be a shared constant | ### Where to Put the Canonical Definition The definition should live in the **lowest-level module** that owns the concept: ``` Models/types <- lib/ <- helpers/ <- context/ <- components/ <- pages/ (lowest) (highest) ``` - **Types/interfaces**: `Models/` or a `types.ts` next to the domain code - **Constants/enums**: `lib/` or `helpers/` — near the business logic that uses them - **Utility functions**: `lib/` or a shared `utils.ts` in the relevant domain folder - **Classes**: `lib/` — never define a service class inside a React context/component file Higher-level modules (context, components, pages) should **import**, never redefine. ### Severity - **Warning** if both definitions are currently identical — they will drift over time - **Critical** if definitions have already diverged — consumers may get inconsistent behavior ### Example Finding ``` #### [quality-N]: Duplicate type `WalletConnectWallet` defined in two files - **File**: `context/evmConnectorsContext.tsx:9` and `lib/wallets/connectors/resolveConnectors/index.ts:9` - **Blocks Merge**: no - **Effort**: 5 min - **Concern**: Same type defined in two files. They are identical today but will silently drift when one is updated without the other. **Fix**: Delete the definition from the higher-level file (`context/evmConnectorsContext.tsx`) and import from the canonical source: ```typescript import type { WalletConnectWallet } from '../lib/wallets/connectors/resolveConnectors'; ``` **Why This Matters**: Duplicate definitions create a maintenance trap — a future change to one copy won't update the other, causing silent inconsistencies that are hard to debug. ``` ## Guidelines - Focus ONLY on code quality and readability - **Always read the full file** to understand context and conventions - **Always include**: issue_id (quality-N), blocks_merge, effort estimate - Be pragmatic - don't be pedantic about minor style issues - Consider the team's existing conventions - Provide specific, actionable feedback with copy-paste ready code - Include complete code examples for complex suggestions - Only mark as "Blocks Merge: yes" for severe quality issues (completely unreadable code, major DRY violations) - **Always run the duplicate definition detection step** for any new or modified exported symbol (type, interface, class, const, enum, function) in the diff ================================================ FILE: .claude/agents/pr-reviewer-react.md ================================================ --- name: pr-reviewer-react description: React specialist for PR reviews. Analyzes code for hooks usage, re-renders, state management, component patterns, accessibility, and Next.js patterns. Invoked by pr-review-coordinator. model: gpt-5.2-codex --- You are a React expert reviewing pull requests for React-specific issues and best practices. ## When Invoked 1. Get the diff of changes using `git diff` against the target branch 2. **Read the full file** for each changed file, not just the diff, to understand context 3. Check related files (types, utilities, context providers) that may be affected 4. Analyze React-specific code patterns 5. Output findings in the required format with metadata and copy-paste ready fixes ## Project-Specific Patterns Before reviewing, read the project skill at: `.cursor/skills/vercel-react-best-practices/SKILL.md` Apply these patterns during review: - Check bundle optimization rules - Verify rendering patterns match best practices - Validate async patterns - Check server/client component usage (Next.js App Router) ## React Review Checklist ### Hooks - **Rules of hooks**: Called conditionally or in loops - **Dependency arrays**: Missing or incorrect dependencies in useEffect/useMemo/useCallback - **Stale closures**: Callbacks capturing stale state - **useEffect cleanup**: Missing cleanup for subscriptions/timers - **Custom hooks**: Logic that should be extracted to custom hooks ### Re-renders - **Unnecessary renders**: Components re-rendering without prop/state changes - **Inline functions**: Creating functions in render causing child re-renders - **Inline objects**: Creating objects/arrays in render as props - **Missing React.memo**: Components that should be memoized - **Context splits**: Large contexts causing unnecessary re-renders ### State Management - **State location**: State lifted too high or kept too low - **Derived state**: State that should be computed from other state - **State synchronization**: Duplicated state that can get out of sync - **Unnecessary state**: Values that don't need to be in state - **State batching**: Multiple setState calls that should be batched ### Component Patterns - **Component size**: Components doing too much - **Prop drilling**: Props passed through many levels - **Render props vs hooks**: Outdated patterns that could use hooks - **Composition**: Missing composition opportunities - **Controlled vs uncontrolled**: Inconsistent form patterns ### Effects - **Effect timing**: Effects that should be event handlers - **Effect dependencies**: Effects running more than needed - **Race conditions**: Async effects without cleanup/cancellation - **Effect cascades**: Effects triggering other effects ### Accessibility (a11y) - **Semantic HTML**: Non-semantic elements where semantic would work - **ARIA labels**: Missing labels for interactive elements - **Keyboard navigation**: Non-focusable interactive elements - **Focus management**: Missing focus handling on modals/navigation - **Color contrast**: Insufficient contrast (if visible in code) ### TypeScript/Props - **Prop types**: Missing or incorrect TypeScript types - **Children typing**: Incorrect children prop types - **Event handler types**: Missing event types - **Generic components**: Missing generics where helpful ### Performance Patterns - **Lazy loading**: Missing React.lazy for route components - **Virtualization**: Long lists without virtualization - **Image optimization**: Missing next/image or lazy loading - **Suspense boundaries**: Missing or poorly placed boundaries ### Next.js Patterns **Server vs Client Components**: - Correct "use client" / "use server" directives - Client components not importing server-only code - Server components not using hooks or browser APIs - Proper data fetching at server component level **App Router**: - Proper use of page.tsx, layout.tsx, loading.tsx, error.tsx - Route groups and parallel routes used appropriately - Dynamic routes with proper generateStaticParams - Metadata properly configured for SEO **Server Actions**: - Secure server action implementation - Input validation in server actions - Proper error handling and revalidation - Not exposing sensitive logic to client **Data Fetching**: - Appropriate use of fetch with cache/revalidate options - Avoiding client-side fetches when server fetch works - Proper use of React cache() for deduplication - SWR/React Query patterns for client data **Route Handlers** (API routes): - Proper request/response handling - Authentication checks present - Rate limiting considerations - Error responses with appropriate status codes **Performance**: - Using next/image for images - Using next/font for fonts - Proper code splitting with dynamic imports - Avoiding large client bundles ## Output Format Return findings in this exact format: ``` ## React Review ### Critical #### [react-1]: [Issue Title] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: yes - **Effort**: X min - **Problem**: [What's wrong and why it matters] **Current Code**: ```tsx [current problematic code] ``` **Fixed Code**: ```tsx [corrected code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Warnings #### [react-2]: [Issue Title] - **File**: `path/to/file.tsx:15` - **Blocks Merge**: no - **Effort**: X min - **Concern**: [React-specific concern] **Current Code**: ```tsx [current code] ``` **Fixed Code**: ```tsx [improved code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Suggestions #### [react-3]: [Issue Title] - **File**: `path/to/file.tsx:78` - **Blocks Merge**: no - **Effort**: X min - **Pattern**: [Recommended React pattern] - **Benefit**: [Expected improvement] **Current Code**: ```tsx [current code] ``` **Suggested Code**: ```tsx [suggested improvement] ``` --- ### Summary - Files reviewed: X - Critical (blocks merge): X - Warnings: X - Suggestions: X - Total estimated effort: X min ``` ## Guidelines - Focus ONLY on React-specific issues - **Always read the full file** to understand component context - **Always include**: issue_id (react-N), blocks_merge, effort estimate - Apply Next.js patterns if it's a Next.js project (check for next.config.js or app/ directory) - Provide complete, copy-paste ready code examples for all fixes - Don't over-optimize with memo/useMemo/useCallback without clear benefit - Consider the component's actual usage context - Check the project skill file for project-specific patterns - Only mark as "Blocks Merge: yes" for rules of hooks violations, critical a11y issues, or severe re-render problems ================================================ FILE: .claude/agents/pr-reviewer-security.md ================================================ --- name: pr-reviewer-security description: Security specialist for PR reviews. Analyzes code for authentication/authorization issues, input validation, XSS/injection vulnerabilities, sensitive data exposure, and dependency security. Invoked by pr-review-coordinator. model: haiku --- You are a security expert reviewing pull requests for security vulnerabilities and best practices. ## When Invoked 1. Get the diff of changes using `git diff` against the target branch 2. **Read the full file** for each changed file, not just the diff, to understand context 3. Check related files (types, utilities, API handlers) that may be affected 4. Hunt for security vulnerabilities and weaknesses 5. Output findings in the required format with metadata ## Security Review Checklist ### Authentication & Authorization - **Missing auth checks**: Endpoints or pages without proper authentication - **Broken access control**: Users able to access resources they shouldn't - **Session management**: Insecure session handling, missing expiration - **Token security**: JWT vulnerabilities, token exposure in URLs/logs - **Auth bypass**: Logic that could allow skipping authentication - **Privilege escalation**: Users gaining higher privileges than intended ### Input Validation & Sanitization - **Missing validation**: User input used without validation - **Insufficient validation**: Weak validation that can be bypassed - **Type coercion attacks**: Exploiting JavaScript type coercion - **Format string issues**: Unvalidated format strings - **Path traversal**: User input used in file paths without sanitization - **URL validation**: Open redirects, SSRF vulnerabilities ### Injection Vulnerabilities - **XSS (Cross-Site Scripting)**: - `dangerouslySetInnerHTML` without sanitization - Unescaped user content in DOM - Template injection - **SQL/NoSQL injection**: String concatenation in queries - **Command injection**: User input in shell commands - **Code injection**: `eval()`, `Function()`, `innerHTML` with user data - **Header injection**: User input in HTTP headers ### Sensitive Data Exposure - **Secrets in code**: API keys, passwords, tokens hardcoded - **Environment variables**: Sensitive config exposed to client - **Logging sensitive data**: PII, passwords, tokens in logs - **Error messages**: Stack traces or internal details exposed to users - **Data in URLs**: Sensitive data in query parameters - **Insecure storage**: Sensitive data in localStorage/sessionStorage ### API Security - **CORS misconfiguration**: Overly permissive CORS policies - **Missing rate limiting**: Endpoints vulnerable to brute force - **Mass assignment**: Accepting unexpected fields in requests - **Insecure deserialization**: Trusting serialized data - **GraphQL vulnerabilities**: Introspection enabled, query depth attacks - **API versioning**: Breaking changes without deprecation ### Client-Side Security - **CSP violations**: Content Security Policy issues - **Clickjacking**: Missing X-Frame-Options or CSP frame-ancestors - **Insecure cookies**: Missing HttpOnly, Secure, SameSite flags - **Postmessage vulnerabilities**: Insecure origin checking - **DOM-based vulnerabilities**: Client-side only attacks ### Cryptography - **Weak algorithms**: MD5, SHA1 for security purposes - **Hardcoded secrets**: Encryption keys in source code - **Insecure random**: Math.random() for security purposes - **Missing encryption**: Sensitive data transmitted/stored unencrypted ### Third-Party Dependencies - **Known vulnerabilities**: Dependencies with CVEs - **Outdated packages**: Security patches not applied - **Untrusted sources**: Dependencies from unknown sources - **Excessive permissions**: Packages requesting unnecessary access ### Next.js Specific Security - **Server Actions**: Unvalidated input in server actions - **API Routes**: Missing authentication/authorization - **Middleware bypass**: Security middleware that can be circumvented - **Environment exposure**: NEXT_PUBLIC_ exposing sensitive data - **SSR data leaks**: Sensitive data serialized to client ## Output Format Return findings in this exact format: ``` ## Security Review ### Critical #### [sec-1]: [Vulnerability Title] - **File**: `path/to/file.tsx:42` - **Blocks Merge**: yes - **Effort**: X min - **Vulnerability Type**: [e.g., XSS, SQL Injection, Auth Bypass] - **Attack Vector**: [How an attacker could exploit this] - **Impact**: [What damage could result] **Current Code**: ```typescript [problematic code snippet] ``` **Fixed Code**: ```typescript [secure code snippet - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Warnings #### [sec-2]: [Security Concern Title] - **File**: `path/to/file.tsx:15` - **Blocks Merge**: no - **Effort**: X min - **Risk**: [What could go wrong] **Current Code**: ```typescript [current code] ``` **Fixed Code**: ```typescript [improved code - copy-paste ready] ``` **Why This Matters**: [1 sentence impact] --- ### Suggestions #### [sec-3]: [Hardening Opportunity Title] - **File**: `path/to/file.tsx:78` - **Blocks Merge**: no - **Effort**: X min - **Improvement**: [What could be more secure] - **Benefit**: [Security benefit gained] **Approach**: [How to implement] --- ### Summary - Files reviewed: X - Critical (blocks merge): X - Warnings: X - Suggestions: X - Total estimated effort: X min ``` ## Guidelines - Focus ONLY on security issues - **Always read the full file** to understand security context - **Always include**: issue_id (sec-N), blocks_merge, effort estimate - Prioritize issues by exploitability and impact - Provide working, copy-paste ready fixes - Consider realistic attack scenarios - Don't flag theoretical issues with no practical exploit path - Check for defense in depth - multiple layers of security - Consider the application's threat model - Mark as "Blocks Merge: yes" for: XSS, injection, auth bypass, exposed secrets, missing auth on sensitive endpoints ================================================ FILE: .claude/agents/pr-reviewer.md ================================================ --- name: pr-reviewer description: PR and branch review specialist. Reviews pull requests or local branches for code quality, security, and best practices. Use proactively when the user wants to review a PR, compare branches, or check changes before merging. model: sonnet --- You are an expert PR reviewer specializing in thorough code reviews for pull requests and branch comparisons. ## When Invoked 1. First, understand what the user wants to review: - A specific PR (by number or URL) - A local branch compared to another branch (e.g., feature branch vs main) - Recent commits on the current branch 2. Gather the changes using appropriate git commands: - For PR review: `gh pr diff ` or `gh pr view --web` to see the PR - For branch comparison: `git diff ...` - For recent changes: `git log --oneline -10` then `git diff HEAD~N` 3. Review the changes systematically ## Review Process ### Step 1: Get Context ```bash # Check current branch and status git status git branch -a # For PR review (if gh CLI available) gh pr list gh pr view gh pr diff # For branch comparison git log --oneline .. # See commits git diff ... --stat # See files changed git diff ... # See full diff ``` ### Step 2: Analyze Changes For each modified file, evaluate: **Code Quality** - Is the code clear and readable? - Are functions/variables well-named? - Is there duplicated code that should be refactored? - Are there any code smells? **Logic & Correctness** - Does the logic make sense? - Are edge cases handled? - Are there potential bugs or race conditions? **Security** - Are there exposed secrets, API keys, or credentials? - Is user input properly validated and sanitized? - Are there SQL injection or XSS vulnerabilities? - Are authentication/authorization checks in place? **Performance** - Are there N+1 queries or inefficient loops? - Could any operations be optimized? - Are there memory leaks or resource management issues? **Testing** - Are there tests for new functionality? - Do existing tests still pass? - Is test coverage adequate? **Best Practices** - Does it follow the project's coding standards? - Are there proper error handling patterns? - Is documentation updated if needed? ### Step 3: Provide Feedback Organize feedback by priority: #### Critical (Must Fix) Issues that would cause bugs, security vulnerabilities, or data loss. #### Warnings (Should Fix) Code smells, potential issues, or violations of best practices. #### Suggestions (Consider) Improvements for readability, performance, or maintainability. #### Praise (What's Good) Highlight well-written code and good decisions. ## Output Format ```markdown # PR Review: [Title/Branch Name] ## Summary Brief overview of what the PR does and overall assessment. ## Files Reviewed - `path/to/file1.ts` - [brief description of changes] - `path/to/file2.ts` - [brief description of changes] ## Critical Issues - [ ] **file.ts:42** - Description of critical issue ```suggestion // Suggested fix ``` ## Warnings - [ ] **file.ts:15** - Description of warning ## Suggestions - [ ] **file.ts:78** - Description of suggestion ## What's Good - Good use of [pattern/practice] in `file.ts` - Clear naming conventions throughout ## Verdict - [ ] Approve - [ ] Request Changes - [ ] Needs Discussion ``` ## Tips - Always read the full context of changes, not just the diff - Check if related files need updates (tests, docs, types) - Look for breaking changes that might affect other parts of the codebase - Consider the PR in the context of the broader project architecture - Be constructive and specific - explain why something is an issue and how to fix it ================================================ FILE: .claude/commands/reviewchanges.md ================================================ # reviewchanges Review all changes in the current branch compared to the dev branch using specialized subagents. ## What You Get A comprehensive `pr-review-report.md` with: - **TL;DR section** with verdict and action items upfront - **Effort estimates** for each fix (helps prioritize) - **Blocking vs non-blocking** classification - **Copy-paste ready code fixes** - **Single-location findings** (no duplicate issues across sections) ## Available Review Options ### Option 1: Comprehensive Multi-Perspective Review (Recommended) Uses the **PR Review Coordinator** that runs 6 specialized subagents in parallel: - **Architecture Review**: Design patterns, SOLID principles, modularity, separation of concerns - **Bug Detection**: Edge cases, null checks, race conditions, error handling, cross-file impact - **Performance Analysis**: N+1 queries, memory leaks, inefficient loops, caching opportunities - **Code Quality**: Naming, readability, DRY violations, comments, maintainability - **React Patterns**: Hooks usage, re-renders, state management, component patterns, a11y - **Security Review**: Auth issues, XSS, injection, secrets exposure, CORS Best for: Thorough, multi-dimensional code reviews before merging ### Option 2: Single Specialist Review Uses the **PR Reviewer** for focused, faster reviews Best for: Quick reviews or when you need a specific perspective ### Option 3: Codebase Exploration Uses the **Explore Agent** to understand code structure and patterns Best for: Onboarding, understanding existing patterns, or architectural questions ### Option 4: General Purpose Uses the **General Purpose Agent** for complex multi-step analysis Best for: Custom analysis or research questions about the codebase ## How to Use Ask for a review using the command, specifying which type you want: - `/reviewchanges comprehensive` - Full multi-perspective review (default) - `/reviewchanges quick` - Single specialist review - `/reviewchanges explore` - Codebase exploration - `/reviewchanges general` - General purpose analysis All reviews automatically compare your current branch against the dev branch. ## Report Format The comprehensive review produces a report structured for actionability: ``` # PR Review: [branch] → [base] ## TL;DR Verdict + action items table with effort estimates ## What This PR Does Brief description ## Breaking Changes Checklist ## Detailed Findings Each issue once, numbered for reference ## File Summary Quick navigation table ## Review Checklist Single scannable checklist ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/SKILL.md ================================================ --- name: vercel-react-best-practices description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements. license: MIT metadata: author: vercel version: "1.0.0" --- # Vercel React Best Practices Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 45 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation. ## When to Apply Reference these guidelines when: - Writing new React components or Next.js pages - Implementing data fetching (client or server-side) - Reviewing code for performance issues - Refactoring existing React/Next.js code - Optimizing bundle size or load times ## Rule Categories by Priority | Priority | Category | Impact | Prefix | |----------|----------|--------|--------| | 1 | Eliminating Waterfalls | CRITICAL | `async-` | | 2 | Bundle Size Optimization | CRITICAL | `bundle-` | | 3 | Server-Side Performance | HIGH | `server-` | | 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` | | 5 | Re-render Optimization | MEDIUM | `rerender-` | | 6 | Rendering Performance | MEDIUM | `rendering-` | | 7 | Unused Code Detection | MEDIUM | `unused-` | | 8 | JavaScript Performance | LOW-MEDIUM | `js-` | | 9 | Advanced Patterns | LOW | `advanced-` | ## Quick Reference ### 1. Eliminating Waterfalls (CRITICAL) - `async-defer-await` - Move await into branches where actually used - `async-parallel` - Use Promise.all() for independent operations - `async-dependencies` - Use better-all for partial dependencies - `async-api-routes` - Start promises early, await late in API routes - `async-suspense-boundaries` - Use Suspense to stream content ### 2. Bundle Size Optimization (CRITICAL) - `bundle-barrel-imports` - Import directly, avoid barrel files - `bundle-dynamic-imports` - Use next/dynamic for heavy components - `bundle-defer-third-party` - Load analytics/logging after hydration - `bundle-conditional` - Load modules only when feature is activated - `bundle-preload` - Preload on hover/focus for perceived speed ### 3. Server-Side Performance (HIGH) - `server-cache-react` - Use React.cache() for per-request deduplication - `server-cache-lru` - Use LRU cache for cross-request caching - `server-serialization` - Minimize data passed to client components - `server-parallel-fetching` - Restructure components to parallelize fetches - `server-after-nonblocking` - Use after() for non-blocking operations ### 4. Client-Side Data Fetching (MEDIUM-HIGH) - `client-swr-dedup` - Use SWR for automatic request deduplication - `client-event-listeners` - Deduplicate global event listeners ### 5. Re-render Optimization (MEDIUM) - `rerender-defer-reads` - Don't subscribe to state only used in callbacks - `rerender-memo` - Extract expensive work into memoized components - `rerender-dependencies` - Use primitive dependencies in effects - `rerender-derived-state` - Subscribe to derived booleans, not raw values - `rerender-functional-setstate` - Use functional setState for stable callbacks - `rerender-lazy-state-init` - Pass function to useState for expensive values - `rerender-transitions` - Use startTransition for non-urgent updates ### 6. Rendering Performance (MEDIUM) - `rendering-animate-svg-wrapper` - Animate div wrapper, not SVG element - `rendering-content-visibility` - Use content-visibility for long lists - `rendering-hoist-jsx` - Extract static JSX outside components - `rendering-svg-precision` - Reduce SVG coordinate precision - `rendering-hydration-no-flicker` - Use inline script for client-only data - `rendering-activity` - Use Activity component for show/hide - `rendering-conditional-render` - Use ternary, not && for conditionals ### 7. Unused Code Detection (MEDIUM) - `unused-dead-exports` - Identify exports with no import references - `unused-unreachable-code` - Find code after return/throw statements - `unused-variables` - Spot variables assigned but never read - `unused-imports` - Remove imports never referenced in file - `unused-components` - Find components not rendered anywhere - `unused-props` - Detect props defined but never passed - `unused-state` - Find useState values never used in render - `unused-effects` - Identify useEffect with no observable side effects - `unused-commented-code` - Flag commented-out code for removal ### 8. JavaScript Performance (LOW-MEDIUM) - `js-batch-dom-css` - Group CSS changes via classes or cssText - `js-index-maps` - Build Map for repeated lookups - `js-cache-property-access` - Cache object properties in loops - `js-cache-function-results` - Cache function results in module-level Map - `js-cache-storage` - Cache localStorage/sessionStorage reads - `js-combine-iterations` - Combine multiple filter/map into one loop - `js-length-check-first` - Check array length before expensive comparison - `js-early-exit` - Return early from functions - `js-hoist-regexp` - Hoist RegExp creation outside loops - `js-min-max-loop` - Use loop for min/max instead of sort - `js-set-map-lookups` - Use Set/Map for O(1) lookups - `js-tosorted-immutable` - Use toSorted() for immutability ### 9. Advanced Patterns (LOW) - `advanced-event-handler-refs` - Store event handlers in refs - `advanced-use-latest` - useLatest for stable callback refs ## How to Use Read individual rule files for detailed explanations and code examples: ``` rules/async-parallel.md rules/bundle-barrel-imports.md rules/unused-detection.md rules/_sections.md ``` Each rule file contains: - Brief explanation of why it matters - Incorrect code example with explanation - Correct code example with explanation - Additional context and references ## Full Compiled Document For the complete guide with all rules expanded: `AGENTS.md` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md ================================================ --- title: Store Event Handlers in Refs impact: LOW impactDescription: stable subscriptions tags: advanced, hooks, refs, event-handlers, optimization --- ## Store Event Handlers in Refs Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes. **Incorrect (re-subscribes on every render):** ```tsx function useWindowEvent(event: string, handler: (e) => void) { useEffect(() => { window.addEventListener(event, handler) return () => window.removeEventListener(event, handler) }, [event, handler]) } ``` **Correct (stable subscription):** ```tsx function useWindowEvent(event: string, handler: (e) => void) { const handlerRef = useRef(handler) useEffect(() => { handlerRef.current = handler }, [handler]) useEffect(() => { const listener = (e) => handlerRef.current(e) window.addEventListener(event, listener) return () => window.removeEventListener(event, listener) }, [event]) } ``` **Alternative: use `useEffectEvent` if you're on latest React:** ```tsx import { useEffectEvent } from 'react' function useWindowEvent(event: string, handler: (e) => void) { const onEvent = useEffectEvent(handler) useEffect(() => { window.addEventListener(event, onEvent) return () => window.removeEventListener(event, onEvent) }, [event]) } ``` `useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/advanced-use-latest.md ================================================ --- title: useLatest for Stable Callback Refs impact: LOW impactDescription: prevents effect re-runs tags: advanced, hooks, useLatest, refs, optimization --- ## useLatest for Stable Callback Refs Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures. **Implementation:** ```typescript function useLatest(value: T) { const ref = useRef(value) useLayoutEffect(() => { ref.current = value }, [value]) return ref } ``` **Incorrect (effect re-runs on every callback change):** ```tsx function SearchInput({ onSearch }: { onSearch: (q: string) => void }) { const [query, setQuery] = useState('') useEffect(() => { const timeout = setTimeout(() => onSearch(query), 300) return () => clearTimeout(timeout) }, [query, onSearch]) } ``` **Correct (stable effect, fresh callback):** ```tsx function SearchInput({ onSearch }: { onSearch: (q: string) => void }) { const [query, setQuery] = useState('') const onSearchRef = useLatest(onSearch) useEffect(() => { const timeout = setTimeout(() => onSearchRef.current(query), 300) return () => clearTimeout(timeout) }, [query]) } ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/async-api-routes.md ================================================ --- title: Prevent Waterfall Chains in API Routes impact: CRITICAL impactDescription: 2-10× improvement tags: api-routes, server-actions, waterfalls, parallelization --- ## Prevent Waterfall Chains in API Routes In API routes and Server Actions, start independent operations immediately, even if you don't await them yet. **Incorrect (config waits for auth, data waits for both):** ```typescript export async function GET(request: Request) { const session = await auth() const config = await fetchConfig() const data = await fetchData(session.user.id) return Response.json({ data, config }) } ``` **Correct (auth and config start immediately):** ```typescript export async function GET(request: Request) { const sessionPromise = auth() const configPromise = fetchConfig() const session = await sessionPromise const [config, data] = await Promise.all([ configPromise, fetchData(session.user.id) ]) return Response.json({ data, config }) } ``` For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization). ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/async-defer-await.md ================================================ --- title: Defer Await Until Needed impact: HIGH impactDescription: avoids blocking unused code paths tags: async, await, conditional, optimization --- ## Defer Await Until Needed Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them. **Incorrect (blocks both branches):** ```typescript async function handleRequest(userId: string, skipProcessing: boolean) { const userData = await fetchUserData(userId) if (skipProcessing) { // Returns immediately but still waited for userData return { skipped: true } } // Only this branch uses userData return processUserData(userData) } ``` **Correct (only blocks when needed):** ```typescript async function handleRequest(userId: string, skipProcessing: boolean) { if (skipProcessing) { // Returns immediately without waiting return { skipped: true } } // Fetch only when needed const userData = await fetchUserData(userId) return processUserData(userData) } ``` **Another example (early return optimization):** ```typescript // Incorrect: always fetches permissions async function updateResource(resourceId: string, userId: string) { const permissions = await fetchPermissions(userId) const resource = await getResource(resourceId) if (!resource) { return { error: 'Not found' } } if (!permissions.canEdit) { return { error: 'Forbidden' } } return await updateResourceData(resource, permissions) } // Correct: fetches only when needed async function updateResource(resourceId: string, userId: string) { const resource = await getResource(resourceId) if (!resource) { return { error: 'Not found' } } const permissions = await fetchPermissions(userId) if (!permissions.canEdit) { return { error: 'Forbidden' } } return await updateResourceData(resource, permissions) } ``` This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/async-dependencies.md ================================================ --- title: Dependency-Based Parallelization impact: CRITICAL impactDescription: 2-10× improvement tags: async, parallelization, dependencies, better-all --- ## Dependency-Based Parallelization For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment. **Incorrect (profile waits for config unnecessarily):** ```typescript const [user, config] = await Promise.all([ fetchUser(), fetchConfig() ]) const profile = await fetchProfile(user.id) ``` **Correct (config and profile run in parallel):** ```typescript import { all } from 'better-all' const { user, config, profile } = await all({ async user() { return fetchUser() }, async config() { return fetchConfig() }, async profile() { return fetchProfile((await this.$.user).id) } }) ``` Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all) ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/async-parallel.md ================================================ --- title: Promise.all() for Independent Operations impact: CRITICAL impactDescription: 2-10× improvement tags: async, parallelization, promises, waterfalls --- ## Promise.all() for Independent Operations When async operations have no interdependencies, execute them concurrently using `Promise.all()`. **Incorrect (sequential execution, 3 round trips):** ```typescript const user = await fetchUser() const posts = await fetchPosts() const comments = await fetchComments() ``` **Correct (parallel execution, 1 round trip):** ```typescript const [user, posts, comments] = await Promise.all([ fetchUser(), fetchPosts(), fetchComments() ]) ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md ================================================ --- title: Strategic Suspense Boundaries impact: HIGH impactDescription: faster initial paint tags: async, suspense, streaming, layout-shift --- ## Strategic Suspense Boundaries Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads. **Incorrect (wrapper blocked by data fetching):** ```tsx async function Page() { const data = await fetchData() // Blocks entire page return (
Sidebar
Header
Footer
) } ``` The entire layout waits for data even though only the middle section needs it. **Correct (wrapper shows immediately, data streams in):** ```tsx function Page() { return (
Sidebar
Header
}>
Footer
) } async function DataDisplay() { const data = await fetchData() // Only blocks this component return
{data.content}
} ``` Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data. **Alternative (share promise across components):** ```tsx function Page() { // Start fetch immediately, but don't await const dataPromise = fetchData() return (
Sidebar
Header
}>
Footer
) } function DataDisplay({ dataPromise }: { dataPromise: Promise }) { const data = use(dataPromise) // Unwraps the promise return
{data.content}
} function DataSummary({ dataPromise }: { dataPromise: Promise }) { const data = use(dataPromise) // Reuses the same promise return
{data.summary}
} ``` Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together. **When NOT to use this pattern:** - Critical data needed for layout decisions (affects positioning) - SEO-critical content above the fold - Small, fast queries where suspense overhead isn't worth it - When you want to avoid layout shift (loading → content jump) **Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md ================================================ --- title: Avoid Barrel File Imports impact: CRITICAL impactDescription: 200-800ms import cost, slow builds tags: bundle, imports, tree-shaking, barrel-files, performance --- ## Avoid Barrel File Imports Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`). Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts. **Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph. **Incorrect (imports entire library):** ```tsx import { Check, X, Menu } from 'lucide-react' // Loads 1,583 modules, takes ~2.8s extra in dev // Runtime cost: 200-800ms on every cold start import { Button, TextField } from '@mui/material' // Loads 2,225 modules, takes ~4.2s extra in dev ``` **Correct (imports only what you need):** ```tsx import Check from 'lucide-react/dist/esm/icons/check' import X from 'lucide-react/dist/esm/icons/x' import Menu from 'lucide-react/dist/esm/icons/menu' // Loads only 3 modules (~2KB vs ~1MB) import Button from '@mui/material/Button' import TextField from '@mui/material/TextField' // Loads only what you use ``` **Alternative (Next.js 13.5+):** ```js // next.config.js - use optimizePackageImports module.exports = { experimental: { optimizePackageImports: ['lucide-react', '@mui/material'] } } // Then you can keep the ergonomic barrel imports: import { Check, X, Menu } from 'lucide-react' // Automatically transformed to direct imports at build time ``` Direct imports provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR. Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`. Reference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js) ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/bundle-conditional.md ================================================ --- title: Conditional Module Loading impact: HIGH impactDescription: loads large data only when needed tags: bundle, conditional-loading, lazy-loading --- ## Conditional Module Loading Load large data or modules only when a feature is activated. **Example (lazy-load animation frames):** ```tsx function AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch> }) { const [frames, setFrames] = useState(null) useEffect(() => { if (enabled && !frames && typeof window !== 'undefined') { import('./animation-frames.js') .then(mod => setFrames(mod.frames)) .catch(() => setEnabled(false)) } }, [enabled, frames, setEnabled]) if (!frames) return return } ``` The `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md ================================================ --- title: Defer Non-Critical Third-Party Libraries impact: MEDIUM impactDescription: loads after hydration tags: bundle, third-party, analytics, defer --- ## Defer Non-Critical Third-Party Libraries Analytics, logging, and error tracking don't block user interaction. Load them after hydration. **Incorrect (blocks initial bundle):** ```tsx import { Analytics } from '@vercel/analytics/react' export default function RootLayout({ children }) { return ( {children} ) } ``` **Correct (loads after hydration):** ```tsx import dynamic from 'next/dynamic' const Analytics = dynamic( () => import('@vercel/analytics/react').then(m => m.Analytics), { ssr: false } ) export default function RootLayout({ children }) { return ( {children} ) } ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md ================================================ --- title: Dynamic Imports for Heavy Components impact: CRITICAL impactDescription: directly affects TTI and LCP tags: bundle, dynamic-import, code-splitting, next-dynamic --- ## Dynamic Imports for Heavy Components Use `next/dynamic` to lazy-load large components not needed on initial render. **Incorrect (Monaco bundles with main chunk ~300KB):** ```tsx import { MonacoEditor } from './monaco-editor' function CodePanel({ code }: { code: string }) { return } ``` **Correct (Monaco loads on demand):** ```tsx import dynamic from 'next/dynamic' const MonacoEditor = dynamic( () => import('./monaco-editor').then(m => m.MonacoEditor), { ssr: false } ) function CodePanel({ code }: { code: string }) { return } ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/bundle-preload.md ================================================ --- title: Preload Based on User Intent impact: MEDIUM impactDescription: reduces perceived latency tags: bundle, preload, user-intent, hover --- ## Preload Based on User Intent Preload heavy bundles before they're needed to reduce perceived latency. **Example (preload on hover/focus):** ```tsx function EditorButton({ onClick }: { onClick: () => void }) { const preload = () => { if (typeof window !== 'undefined') { void import('./monaco-editor') } } return ( ) } ``` **Example (preload when feature flag is enabled):** ```tsx function FlagsProvider({ children, flags }: Props) { useEffect(() => { if (flags.editorEnabled && typeof window !== 'undefined') { void import('./monaco-editor').then(mod => mod.init()) } }, [flags.editorEnabled]) return {children} } ``` The `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/client-event-listeners.md ================================================ --- title: Deduplicate Global Event Listeners impact: LOW impactDescription: single listener for N components tags: client, swr, event-listeners, subscription --- ## Deduplicate Global Event Listeners Use `useSWRSubscription()` to share global event listeners across component instances. **Incorrect (N instances = N listeners):** ```tsx function useKeyboardShortcut(key: string, callback: () => void) { useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.metaKey && e.key === key) { callback() } } window.addEventListener('keydown', handler) return () => window.removeEventListener('keydown', handler) }, [key, callback]) } ``` When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener. **Correct (N instances = 1 listener):** ```tsx import useSWRSubscription from 'swr/subscription' // Module-level Map to track callbacks per key const keyCallbacks = new Map void>>() function useKeyboardShortcut(key: string, callback: () => void) { // Register this callback in the Map useEffect(() => { if (!keyCallbacks.has(key)) { keyCallbacks.set(key, new Set()) } keyCallbacks.get(key)!.add(callback) return () => { const set = keyCallbacks.get(key) if (set) { set.delete(callback) if (set.size === 0) { keyCallbacks.delete(key) } } } }, [key, callback]) useSWRSubscription('global-keydown', () => { const handler = (e: KeyboardEvent) => { if (e.metaKey && keyCallbacks.has(e.key)) { keyCallbacks.get(e.key)!.forEach(cb => cb()) } } window.addEventListener('keydown', handler) return () => window.removeEventListener('keydown', handler) }) } function Profile() { // Multiple shortcuts will share the same listener useKeyboardShortcut('p', () => { /* ... */ }) useKeyboardShortcut('k', () => { /* ... */ }) // ... } ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/client-localstorage-schema.md ================================================ --- title: Version and Minimize localStorage Data impact: MEDIUM impactDescription: prevents schema conflicts, reduces storage size tags: client, localStorage, storage, versioning, data-minimization --- ## Version and Minimize localStorage Data Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data. **Incorrect:** ```typescript // No version, stores everything, no error handling localStorage.setItem('userConfig', JSON.stringify(fullUserObject)) const data = localStorage.getItem('userConfig') ``` **Correct:** ```typescript const VERSION = 'v2' function saveConfig(config: { theme: string; language: string }) { try { localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config)) } catch { // Throws in incognito/private browsing, quota exceeded, or disabled } } function loadConfig() { try { const data = localStorage.getItem(`userConfig:${VERSION}`) return data ? JSON.parse(data) : null } catch { return null } } // Migration from v1 to v2 function migrate() { try { const v1 = localStorage.getItem('userConfig:v1') if (v1) { const old = JSON.parse(v1) saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang }) localStorage.removeItem('userConfig:v1') } } catch {} } ``` **Store minimal fields from server responses:** ```typescript // User object has 20+ fields, only store what UI needs function cachePrefs(user: FullUser) { try { localStorage.setItem('prefs:v1', JSON.stringify({ theme: user.preferences.theme, notifications: user.preferences.notifications })) } catch {} } ``` **Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled. **Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md ================================================ --- title: Use Passive Event Listeners for Scrolling Performance impact: MEDIUM impactDescription: eliminates scroll delay caused by event listeners tags: client, event-listeners, scrolling, performance, touch, wheel --- ## Use Passive Event Listeners for Scrolling Performance Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay. **Incorrect:** ```typescript useEffect(() => { const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX) const handleWheel = (e: WheelEvent) => console.log(e.deltaY) document.addEventListener('touchstart', handleTouch) document.addEventListener('wheel', handleWheel) return () => { document.removeEventListener('touchstart', handleTouch) document.removeEventListener('wheel', handleWheel) } }, []) ``` **Correct:** ```typescript useEffect(() => { const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX) const handleWheel = (e: WheelEvent) => console.log(e.deltaY) document.addEventListener('touchstart', handleTouch, { passive: true }) document.addEventListener('wheel', handleWheel, { passive: true }) return () => { document.removeEventListener('touchstart', handleTouch) document.removeEventListener('wheel', handleWheel) } }, []) ``` **Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`. **Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/client-swr-dedup.md ================================================ --- title: Use SWR for Automatic Deduplication impact: MEDIUM-HIGH impactDescription: automatic deduplication tags: client, swr, deduplication, data-fetching --- ## Use SWR for Automatic Deduplication SWR enables request deduplication, caching, and revalidation across component instances. **Incorrect (no deduplication, each instance fetches):** ```tsx function UserList() { const [users, setUsers] = useState([]) useEffect(() => { fetch('/api/users') .then(r => r.json()) .then(setUsers) }, []) } ``` **Correct (multiple instances share one request):** ```tsx import useSWR from 'swr' function UserList() { const { data: users } = useSWR('/api/users', fetcher) } ``` **For immutable data:** ```tsx import { useImmutableSWR } from '@/lib/swr' function StaticContent() { const { data } = useImmutableSWR('/api/config', fetcher) } ``` **For mutations:** ```tsx import { useSWRMutation } from 'swr/mutation' function UpdateButton() { const { trigger } = useSWRMutation('/api/user', updateUser) return } ``` Reference: [https://swr.vercel.app](https://swr.vercel.app) ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-batch-dom-css.md ================================================ --- title: Batch DOM CSS Changes impact: MEDIUM impactDescription: reduces reflows/repaints tags: javascript, dom, css, performance, reflow --- ## Batch DOM CSS Changes Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow. **Incorrect (interleaved reads and writes force reflows):** ```typescript function updateElementStyles(element: HTMLElement) { element.style.width = '100px' const width = element.offsetWidth // Forces reflow element.style.height = '200px' const height = element.offsetHeight // Forces another reflow } ``` **Correct (batch writes, then read once):** ```typescript function updateElementStyles(element: HTMLElement) { // Batch all writes together element.style.width = '100px' element.style.height = '200px' element.style.backgroundColor = 'blue' element.style.border = '1px solid black' // Read after all writes are done (single reflow) const { width, height } = element.getBoundingClientRect() } ``` **Better: use CSS classes** ```css .highlighted-box { width: 100px; height: 200px; background-color: blue; border: 1px solid black; } ``` ```typescript function updateElementStyles(element: HTMLElement) { element.classList.add('highlighted-box') const { width, height } = element.getBoundingClientRect() } ``` Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-cache-function-results.md ================================================ --- title: Cache Repeated Function Calls impact: MEDIUM impactDescription: avoid redundant computation tags: javascript, cache, memoization, performance --- ## Cache Repeated Function Calls Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render. **Incorrect (redundant computation):** ```typescript function ProjectList({ projects }: { projects: Project[] }) { return (
{projects.map(project => { // slugify() called 100+ times for same project names const slug = slugify(project.name) return })}
) } ``` **Correct (cached results):** ```typescript // Module-level cache const slugifyCache = new Map() function cachedSlugify(text: string): string { if (slugifyCache.has(text)) { return slugifyCache.get(text)! } const result = slugify(text) slugifyCache.set(text, result) return result } function ProjectList({ projects }: { projects: Project[] }) { return (
{projects.map(project => { // Computed only once per unique project name const slug = cachedSlugify(project.name) return })}
) } ``` **Simpler pattern for single-value functions:** ```typescript let isLoggedInCache: boolean | null = null function isLoggedIn(): boolean { if (isLoggedInCache !== null) { return isLoggedInCache } isLoggedInCache = document.cookie.includes('auth=') return isLoggedInCache } // Clear cache when auth changes function onAuthChange() { isLoggedInCache = null } ``` Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components. Reference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast) ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-cache-property-access.md ================================================ --- title: Cache Property Access in Loops impact: LOW-MEDIUM impactDescription: reduces lookups tags: javascript, loops, optimization, caching --- ## Cache Property Access in Loops Cache object property lookups in hot paths. **Incorrect (3 lookups × N iterations):** ```typescript for (let i = 0; i < arr.length; i++) { process(obj.config.settings.value) } ``` **Correct (1 lookup total):** ```typescript const value = obj.config.settings.value const len = arr.length for (let i = 0; i < len; i++) { process(value) } ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-cache-storage.md ================================================ --- title: Cache Storage API Calls impact: LOW-MEDIUM impactDescription: reduces expensive I/O tags: javascript, localStorage, storage, caching, performance --- ## Cache Storage API Calls `localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory. **Incorrect (reads storage on every call):** ```typescript function getTheme() { return localStorage.getItem('theme') ?? 'light' } // Called 10 times = 10 storage reads ``` **Correct (Map cache):** ```typescript const storageCache = new Map() function getLocalStorage(key: string) { if (!storageCache.has(key)) { storageCache.set(key, localStorage.getItem(key)) } return storageCache.get(key) } function setLocalStorage(key: string, value: string) { localStorage.setItem(key, value) storageCache.set(key, value) // keep cache in sync } ``` Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components. **Cookie caching:** ```typescript let cookieCache: Record | null = null function getCookie(name: string) { if (!cookieCache) { cookieCache = Object.fromEntries( document.cookie.split('; ').map(c => c.split('=')) ) } return cookieCache[name] } ``` **Important (invalidate on external changes):** If storage can change externally (another tab, server-set cookies), invalidate cache: ```typescript window.addEventListener('storage', (e) => { if (e.key) storageCache.delete(e.key) }) document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') { storageCache.clear() } }) ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-combine-iterations.md ================================================ --- title: Combine Multiple Array Iterations impact: LOW-MEDIUM impactDescription: reduces iterations tags: javascript, arrays, loops, performance --- ## Combine Multiple Array Iterations Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop. **Incorrect (3 iterations):** ```typescript const admins = users.filter(u => u.isAdmin) const testers = users.filter(u => u.isTester) const inactive = users.filter(u => !u.isActive) ``` **Correct (1 iteration):** ```typescript const admins: User[] = [] const testers: User[] = [] const inactive: User[] = [] for (const user of users) { if (user.isAdmin) admins.push(user) if (user.isTester) testers.push(user) if (!user.isActive) inactive.push(user) } ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-early-exit.md ================================================ --- title: Early Return from Functions impact: LOW-MEDIUM impactDescription: avoids unnecessary computation tags: javascript, functions, optimization, early-return --- ## Early Return from Functions Return early when result is determined to skip unnecessary processing. **Incorrect (processes all items even after finding answer):** ```typescript function validateUsers(users: User[]) { let hasError = false let errorMessage = '' for (const user of users) { if (!user.email) { hasError = true errorMessage = 'Email required' } if (!user.name) { hasError = true errorMessage = 'Name required' } // Continues checking all users even after error found } return hasError ? { valid: false, error: errorMessage } : { valid: true } } ``` **Correct (returns immediately on first error):** ```typescript function validateUsers(users: User[]) { for (const user of users) { if (!user.email) { return { valid: false, error: 'Email required' } } if (!user.name) { return { valid: false, error: 'Name required' } } } return { valid: true } } ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-hoist-regexp.md ================================================ --- title: Hoist RegExp Creation impact: LOW-MEDIUM impactDescription: avoids recreation tags: javascript, regexp, optimization, memoization --- ## Hoist RegExp Creation Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`. **Incorrect (new RegExp every render):** ```tsx function Highlighter({ text, query }: Props) { const regex = new RegExp(`(${query})`, 'gi') const parts = text.split(regex) return <>{parts.map((part, i) => ...)} } ``` **Correct (memoize or hoist):** ```tsx const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ function Highlighter({ text, query }: Props) { const regex = useMemo( () => new RegExp(`(${escapeRegex(query)})`, 'gi'), [query] ) const parts = text.split(regex) return <>{parts.map((part, i) => ...)} } ``` **Warning (global regex has mutable state):** Global regex (`/g`) has mutable `lastIndex` state: ```typescript const regex = /foo/g regex.test('foo') // true, lastIndex = 3 regex.test('foo') // false, lastIndex = 0 ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-index-maps.md ================================================ --- title: Build Index Maps for Repeated Lookups impact: LOW-MEDIUM impactDescription: 1M ops to 2K ops tags: javascript, map, indexing, optimization, performance --- ## Build Index Maps for Repeated Lookups Multiple `.find()` calls by the same key should use a Map. **Incorrect (O(n) per lookup):** ```typescript function processOrders(orders: Order[], users: User[]) { return orders.map(order => ({ ...order, user: users.find(u => u.id === order.userId) })) } ``` **Correct (O(1) per lookup):** ```typescript function processOrders(orders: Order[], users: User[]) { const userById = new Map(users.map(u => [u.id, u])) return orders.map(order => ({ ...order, user: userById.get(order.userId) })) } ``` Build map once (O(n)), then all lookups are O(1). For 1000 orders × 1000 users: 1M ops → 2K ops. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-length-check-first.md ================================================ --- title: Early Length Check for Array Comparisons impact: MEDIUM-HIGH impactDescription: avoids expensive operations when lengths differ tags: javascript, arrays, performance, optimization, comparison --- ## Early Length Check for Array Comparisons When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal. In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops). **Incorrect (always runs expensive comparison):** ```typescript function hasChanges(current: string[], original: string[]) { // Always sorts and joins, even when lengths differ return current.sort().join() !== original.sort().join() } ``` Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings. **Correct (O(1) length check first):** ```typescript function hasChanges(current: string[], original: string[]) { // Early return if lengths differ if (current.length !== original.length) { return true } // Only sort when lengths match const currentSorted = current.toSorted() const originalSorted = original.toSorted() for (let i = 0; i < currentSorted.length; i++) { if (currentSorted[i] !== originalSorted[i]) { return true } } return false } ``` This new approach is more efficient because: - It avoids the overhead of sorting and joining the arrays when lengths differ - It avoids consuming memory for the joined strings (especially important for large arrays) - It avoids mutating the original arrays - It returns early when a difference is found ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-min-max-loop.md ================================================ --- title: Use Loop for Min/Max Instead of Sort impact: LOW impactDescription: O(n) instead of O(n log n) tags: javascript, arrays, performance, sorting, algorithms --- ## Use Loop for Min/Max Instead of Sort Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower. **Incorrect (O(n log n) - sort to find latest):** ```typescript interface Project { id: string name: string updatedAt: number } function getLatestProject(projects: Project[]) { const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt) return sorted[0] } ``` Sorts the entire array just to find the maximum value. **Incorrect (O(n log n) - sort for oldest and newest):** ```typescript function getOldestAndNewest(projects: Project[]) { const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt) return { oldest: sorted[0], newest: sorted[sorted.length - 1] } } ``` Still sorts unnecessarily when only min/max are needed. **Correct (O(n) - single loop):** ```typescript function getLatestProject(projects: Project[]) { if (projects.length === 0) return null let latest = projects[0] for (let i = 1; i < projects.length; i++) { if (projects[i].updatedAt > latest.updatedAt) { latest = projects[i] } } return latest } function getOldestAndNewest(projects: Project[]) { if (projects.length === 0) return { oldest: null, newest: null } let oldest = projects[0] let newest = projects[0] for (let i = 1; i < projects.length; i++) { if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i] if (projects[i].updatedAt > newest.updatedAt) newest = projects[i] } return { oldest, newest } } ``` Single pass through the array, no copying, no sorting. **Alternative (Math.min/Math.max for small arrays):** ```typescript const numbers = [5, 2, 8, 1, 9] const min = Math.min(...numbers) const max = Math.max(...numbers) ``` This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-set-map-lookups.md ================================================ --- title: Use Set/Map for O(1) Lookups impact: LOW-MEDIUM impactDescription: O(n) to O(1) tags: javascript, set, map, data-structures, performance --- ## Use Set/Map for O(1) Lookups Convert arrays to Set/Map for repeated membership checks. **Incorrect (O(n) per check):** ```typescript const allowedIds = ['a', 'b', 'c', ...] items.filter(item => allowedIds.includes(item.id)) ``` **Correct (O(1) per check):** ```typescript const allowedIds = new Set(['a', 'b', 'c', ...]) items.filter(item => allowedIds.has(item.id)) ``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md ================================================ --- title: Use toSorted() Instead of sort() for Immutability impact: MEDIUM-HIGH impactDescription: prevents mutation bugs in React state tags: javascript, arrays, immutability, react, state, mutation --- ## Use toSorted() Instead of sort() for Immutability `.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation. **Incorrect (mutates original array):** ```typescript function UserList({ users }: { users: User[] }) { // Mutates the users prop array! const sorted = useMemo( () => users.sort((a, b) => a.name.localeCompare(b.name)), [users] ) return
{sorted.map(renderUser)}
} ``` **Correct (creates new array):** ```typescript function UserList({ users }: { users: User[] }) { // Creates new sorted array, original unchanged const sorted = useMemo( () => users.toSorted((a, b) => a.name.localeCompare(b.name)), [users] ) return
{sorted.map(renderUser)}
} ``` **Why this matters in React:** 1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only 2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior **Browser support (fallback for older browsers):** `.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator: ```typescript // Fallback for older browsers const sorted = [...items].sort((a, b) => a.value - b.value) ``` **Other immutable array methods:** - `.toSorted()` - immutable sort - `.toReversed()` - immutable reverse - `.toSpliced()` - immutable splice - `.with()` - immutable element replacement ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/rendering-activity.md ================================================ --- title: Use Activity Component for Show/Hide impact: MEDIUM impactDescription: preserves state/DOM tags: rendering, activity, visibility, state-preservation --- ## Use Activity Component for Show/Hide Use React's `` to preserve state/DOM for expensive components that frequently toggle visibility. **Usage:** ```tsx import { Activity } from 'react' function Dropdown({ isOpen }: Props) { return ( ) } ``` Avoids expensive re-renders and state loss. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md ================================================ --- title: Animate SVG Wrapper Instead of SVG Element impact: LOW impactDescription: enables hardware acceleration tags: rendering, svg, css, animation, performance --- ## Animate SVG Wrapper Instead of SVG Element Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `
` and animate the wrapper instead. **Incorrect (animating SVG directly - no hardware acceleration):** ```tsx function LoadingSpinner() { return ( ) } ``` **Correct (animating wrapper div - hardware accelerated):** ```tsx function LoadingSpinner() { return (
) } ``` This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/rendering-conditional-render.md ================================================ --- title: Use Explicit Conditional Rendering impact: LOW impactDescription: prevents rendering 0 or NaN tags: rendering, conditional, jsx, falsy-values --- ## Use Explicit Conditional Rendering Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render. **Incorrect (renders "0" when count is 0):** ```tsx function Badge({ count }: { count: number }) { return (
{count && {count}}
) } // When count = 0, renders:
0
// When count = 5, renders:
5
``` **Correct (renders nothing when count is 0):** ```tsx function Badge({ count }: { count: number }) { return (
{count > 0 ? {count} : null}
) } // When count = 0, renders:
// When count = 5, renders:
5
``` ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/rendering-content-visibility.md ================================================ --- title: CSS content-visibility for Long Lists impact: HIGH impactDescription: faster initial render tags: rendering, css, content-visibility, long-lists --- ## CSS content-visibility for Long Lists Apply `content-visibility: auto` to defer off-screen rendering. **CSS:** ```css .message-item { content-visibility: auto; contain-intrinsic-size: 0 80px; } ``` **Example:** ```tsx function MessageList({ messages }: { messages: Message[] }) { return (
{messages.map(msg => (
{msg.content}
))}
) } ``` For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render). ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md ================================================ --- title: Hoist Static JSX Elements impact: LOW impactDescription: avoids re-creation tags: rendering, jsx, static, optimization --- ## Hoist Static JSX Elements Extract static JSX outside components to avoid re-creation. **Incorrect (recreates element every render):** ```tsx function LoadingSkeleton() { return
} function Container() { return (
{loading && }
) } ``` **Correct (reuses same element):** ```tsx const loadingSkeleton = (
) function Container() { return (
{loading && loadingSkeleton}
) } ``` This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render. **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary. ================================================ FILE: .claude/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md ================================================ --- title: Prevent Hydration Mismatch Without Flickering impact: MEDIUM impactDescription: avoids visual flicker and hydration errors tags: rendering, ssr, hydration, localStorage, flicker --- ## Prevent Hydration Mismatch Without Flickering When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates. **Incorrect (breaks SSR):** ```tsx function ThemeWrapper({ children }: { children: ReactNode }) { // localStorage is not available on server - throws error const theme = localStorage.getItem('theme') || 'light' return (
{children}
) } ``` Server-side rendering will fail because `localStorage` is undefined. **Incorrect (visual flickering):** ```tsx function ThemeWrapper({ children }: { children: ReactNode }) { const [theme, setTheme] = useState('light') useEffect(() => { // Runs after hydration - causes visible flash const stored = localStorage.getItem('theme') if (stored) { setTheme(stored) } }, []) return (
{children}
) } ``` Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content. **Correct (no flicker, no hydration mismatch):** ```tsx function ThemeWrapper({ children }: { children: ReactNode }) { return ( <>
{children}