From cb181391316d746d98ce508185946f8d96d38a75 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 10 Aug 2026 06:26:58 +0000 Subject: [PATCH 1/6] perf: fix react best practices violations Weekly React/Next.js best-practices audit across the four Next.js apps (ui, code, playground, docs), per Vercel's react-best-practices guide: parallelize independent server fetches, dedupe per-request fetches with React.cache, defer heavy client bundles with dynamic imports, and fix render-purity/state-initialization issues. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012AXwZZQXjD9sephujzkJQ9 --- apps/code/next.config.ts | 4 ++ .../src/components/profile/ProfileView.tsx | 12 +++- apps/code/src/components/providers.tsx | 7 ++- apps/code/src/lib/utils/markdown.tsx | 8 ++- apps/docs/app/(home)/[[...slug]]/page.tsx | 6 +- apps/docs/components/ai/search.tsx | 9 ++- apps/docs/components/feedback.tsx | 20 +++---- apps/docs/components/markdown.tsx | 17 +++++- apps/playground/src/app/group/page.tsx | 55 ++++++++---------- apps/playground/src/app/playground-shell.tsx | 56 +++++++++++-------- apps/playground/src/app/posthog.ts | 8 ++- .../src/components/ai-elements/code-block.tsx | 13 +++-- apps/playground/src/components/providers.tsx | 7 ++- apps/playground/src/hooks/useUser.ts | 14 +++-- apps/playground/src/lib/providers.tsx | 7 ++- apps/ui/src/app/blog/page.tsx | 2 +- apps/ui/src/app/changelog/page.tsx | 2 +- .../dashboard/[orgId]/[projectId]/layout.tsx | 14 +---- .../dashboard/[orgId]/[projectId]/page.tsx | 6 +- .../_components/archive-project.tsx | 22 +------- .../_components/caching-settings-rsc.tsx | 19 +------ .../_components/project-mode-settings-rsc.tsx | 19 +------ .../_components/project-name-settings-rsc.tsx | 20 +------ apps/ui/src/app/dashboard/[orgId]/layout.tsx | 24 +++----- .../org/guardrails/guardrails-client.tsx | 19 +++---- .../components/enterprise/calendly-inline.tsx | 2 +- apps/ui/src/components/landing/navbar.tsx | 2 +- apps/ui/src/components/providers.tsx | 16 ++++-- apps/ui/src/lib/server-api.ts | 32 +++++++++++ 29 files changed, 221 insertions(+), 221 deletions(-) diff --git a/apps/code/next.config.ts b/apps/code/next.config.ts index 6496175578..c2f537d83c 100644 --- a/apps/code/next.config.ts +++ b/apps/code/next.config.ts @@ -13,6 +13,10 @@ const nextConfig: NextConfig = { reactCompiler: true, experimental: { serverSourceMaps: true, + // The shared components entry is a barrel fronting the full model + // directory, log cards, and provider icons; rewrite named imports to + // direct module imports so small symbols don't pull the whole barrel. + optimizePackageImports: ["@llmgateway/shared/components"], }, async redirects() { // Truncated pricing-toggle URLs ("/mo", "/yr") picked up by crawlers. diff --git a/apps/code/src/components/profile/ProfileView.tsx b/apps/code/src/components/profile/ProfileView.tsx index 4c240e6ed5..57018fe84e 100644 --- a/apps/code/src/components/profile/ProfileView.tsx +++ b/apps/code/src/components/profile/ProfileView.tsx @@ -1,6 +1,7 @@ "use client"; import { Calendar, Coins, Flame, Github, Hash, Zap } from "lucide-react"; +import dynamic from "next/dynamic"; import Link from "next/link"; import { @@ -14,7 +15,6 @@ import { ProfileShareActions, XIcon, } from "@/components/profile/ProfileShareActions"; -import { ProfileTokensChart } from "@/components/profile/ProfileTokensChart"; import { ProfileViewerCta } from "@/components/profile/ProfileViewerCta"; import { ProfileWrapped } from "@/components/profile/ProfileWrapped"; import { useAppConfig } from "@/lib/config"; @@ -27,6 +27,16 @@ import type { paths } from "@/lib/api/v1"; export type ProfileData = paths["/user/profile"]["get"]["responses"][200]["content"]["application/json"]["profile"]; +// The tokens chart pulls in recharts and renders below the fold, so keep it +// out of the profile page's initial bundle. +const ProfileTokensChart = dynamic( + () => + import("@/components/profile/ProfileTokensChart").then( + (mod) => mod.ProfileTokensChart, + ), + { ssr: false, loading: () =>
}, +); + const AGENT_BY_SOURCE = new Map(); for (const agent of AGENTS) { for (const source of agent.sources) { diff --git a/apps/code/src/components/providers.tsx b/apps/code/src/components/providers.tsx index 5cf3cb1397..004689319f 100644 --- a/apps/code/src/components/providers.tsx +++ b/apps/code/src/components/providers.tsx @@ -5,7 +5,7 @@ import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { ThemeProvider } from "next-themes"; import posthog from "posthog-js"; import { PostHogProvider } from "posthog-js/react"; -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { Toaster } from "@/components/ui/sonner"; import { AppConfigProvider } from "@/lib/config"; @@ -20,7 +20,9 @@ interface ProvidersProps { } export function Providers({ children, config }: ProvidersProps) { - const queryClient = useMemo( + // useState, not useMemo: React may discard a useMemo cache, which would + // silently swap in a fresh QueryClient and drop the whole query cache. + const [queryClient] = useState( () => new QueryClient({ defaultOptions: { @@ -31,7 +33,6 @@ export function Providers({ children, config }: ProvidersProps) { }, }, }), - [], ); const posthogOptions = useMemo>( diff --git a/apps/code/src/lib/utils/markdown.tsx b/apps/code/src/lib/utils/markdown.tsx index 9f32ec1c2d..1ddcd680bb 100644 --- a/apps/code/src/lib/utils/markdown.tsx +++ b/apps/code/src/lib/utils/markdown.tsx @@ -1,4 +1,10 @@ -import { SyntaxHighlightedPre } from "./markdown-code-block"; +import dynamic from "next/dynamic"; + +// Defer the prism-based highlighter chunk until a markdown document actually +// renders a fenced code block. +const SyntaxHighlightedPre = dynamic(() => + import("./markdown-code-block").then((mod) => mod.SyntaxHighlightedPre), +); // Get markdown component options with custom styling export function getMarkdownOptions() { diff --git a/apps/docs/app/(home)/[[...slug]]/page.tsx b/apps/docs/app/(home)/[[...slug]]/page.tsx index cf0bb14804..a325aaa6dd 100644 --- a/apps/docs/app/(home)/[[...slug]]/page.tsx +++ b/apps/docs/app/(home)/[[...slug]]/page.tsx @@ -7,7 +7,6 @@ import { DocsTitle, } from "fumadocs-ui/page"; import { notFound } from "next/navigation"; -import posthog from "posthog-js"; import { LLMCopyButton, ViewOptions } from "@/components/ai/page-actions"; import { EnterpriseCTA } from "@/components/enterprise-cta"; @@ -120,7 +119,7 @@ export default async function Page(props: { style: "clerk", footer: , }} - lastUpdate={time ? new Date(time) : new Date()} + lastUpdate={time ? new Date(time) : undefined} >