Skill لـ Claude CodeA Claude Code Skill

"use client"
مش زرار سحري
"use client"
is not a magic switch

بتلاقي كل "use client" ملهاش لازمة في مشروع Next.js، وتقيس كل واحدة بتسحب كام module للمتصفح، وتديك حل آمن مكتوب من الكود بتاعك نفسه. Finds every unnecessary "use client" in a Next.js App Router project, measures how much code each one drags into the browser, and gives you a safe fix written from your own code.

01<Boundary />

الـ directive دي مش بتخص الملف ده بسThe directive is not about one file

أي حاجة الملف بيعملها import بتنزل للمتصفح معاه. جرّب بنفسك: حط الـ "use client" فوق، وبعدين انزل بيها لأصغر حتة.Everything the file imports ships to the browser with it. Try it: put "use client" at the top, then push it down to the smallest piece.

🌐 Browser
JavaScript

02<Fixes />

4 أنواع حلول، كل واحد ليه إمتىFour fixes, each with a clear "when"

الـ Skill بتختار الحل حسب الكود نفسه: مين بيستخدم الـ state، ومين بيعمل import لمين، وإيه اللي موجود في الـ client bundle أصلًا.The skill picks the fix from the code itself: who uses which state, who imports what, and what is already in the client bundle anyway.

REMOVE

شيلها خالصRemove it

الملف مفيهوش hooks ولا events ولا browser APIs. بيعمل render لـ components بس.No hooks, no events, no browser APIs. It only renders other components.

Before
"use client";
import { TeamCard } from "@/components/TeamCard";

export default function AboutPage() {
  return <TeamCard … />;
}
After
import { TeamCard } from "@/components/TeamCard";

export default function AboutPage() {
  return <TeamCard … />;
}
PUSH_DOWN

انزل بيها لأصغر حتةPush it down

الـ hook بيغذّي component واحد بس؟ انقله جواه، والـ layout يرجع Server.A hook feeds only one component? Move it inside, and the layout becomes a Server Component again.

Before · layout.tsx
"use client";
const pathname = usePathname();
…
<Breadcrumbs path={pathname} />
After · Breadcrumbs.tsx
"use client";
export function Breadcrumbs() {
  const path = usePathname();
  …
CHILDREN_PATTERN

الـ Modal Client، والمحتوى ServerClient wrapper, server content

الـ Modal محتاج state، بس المحتوى التقيل جواه ممكن ييجي من الـ Server كـ children.The Modal needs state, but the heavy content inside can arrive from the server as children.

Before · ProductModal.tsx
"use client";
import { ProductSpecs } …   // + marked
…
<ProductSpecs product={product} />
After · page.tsx (Server)
<ProductModal>
  <ProductSpecs product={product} />
</ProductModal>
// Modal renders {children}
MOVE_FETCH

هات الـ Data من الـ ServerFetch on the server

لو الـ component بقى Client عشان useEffect + fetch بس، خليه async Server Component.If it is client only for useEffect + fetch, make it an async Server Component.

Before
"use client";
const [items, setItems] = useState([]);
useEffect(() => { fetch(…) … }, []);
After
export default async function
  RelatedProducts({ category }) {
  const items = await getRelated(category);
  return <ul>…</ul>;
}
03<Report />

ده تقرير حقيقي، مش mockupA real report, not a mock-up

ده الـ output الحقيقي للـ audit على مشروع الاختبار رقم 3: dashboard الـ layout بتاعته عليها "use client". وبعده Before/After كامل لأهم 3 مشاكل.This is the actual audit output for test project 3: a dashboard whose layout is marked "use client". A full Before/After for the top 3 findings follows it.

## "use client" audit — 03-layout-boundaryparser: typescript · read-only
6ملفات عليها "use client"files with "use client"
2أولوية HighHigh priority
≈13module بيسحبهم الـ layoutmodules pulled by the layout
1محتاج مراجعة يدويةneeds a manual check
FileWhy it is flaggedPriorityFix≈ Modules
src/app/layout.tsxEverything this layout imports is sent to the browser on every page under it. Client features used: useState(), usePathname(), onClick on <button>.HighPUSH_DOWN≈13
src/app/dashboard/settings/page.tsxA page marked "use client" makes the whole page client code. Uses useForm() (react-hook-form), onSubmit.HighPUSH_DOWN≈2 + react-hook-form
src/components/LocaleSwitch.tsxNeeds manual check · useFeatureFlag() from an unknown package: may or may not work on the Server.LowREMOVE≈1
After · src/app/layout.tsx (Server Component)
export const metadata = { title: "Acme Dashboard" };

export default function DashboardLayout({ children }) {
  return (
    <html lang="en"><body>
      <Header />
      <SidebarShell sidebar={<Sidebar />}>
        <main>
          <Breadcrumbs />
          {children}
        </main>
      </SidebarShell>
      <ThemeBadge />
      <Footer />
    </body></html>
  );
}
New · src/components/SidebarShell.tsx
"use client";
import { useState } from "react";

export function SidebarShell({ sidebar, children }) {
  const [menuOpen, setMenuOpen] = useState(false);
  return (
    <>
      <button className="menu-toggle"
        aria-expanded={menuOpen}
        onClick={() => setMenuOpen(!menuOpen)}>
        Menu
      </button>
      <div className="shell" data-open={menuOpen}>
        {sidebar}
        {children}
      </div>
    </>
  );
}

⚠️ وفي نفس التقرير: SearchBox.tsx بيستخدم useState من غير "use client"، وكان شغال بس عشان كان جوه الـ boundary بتاعة الـ layout، فالحل بيضيفله الـ directive. والـ usePathname بيتنقل جوه Breadcrumbs. وبيقولك إن الـ layout فيها build error النهارده: metadata جوه Client Component، وimport "server-only" جوه الـ boundary. الحل ده بيصلّح الاتنين. وClaude بيراجع ترتيب الـ DOM، فالـ shell بيرندر نفس الـ div.shell اللي كان موجود.⚠️ The same report also catches that SearchBox.tsx uses useState with no "use client" of its own. It only worked inside the layout's boundary, so the fix gives it the directive. usePathname moves into Breadcrumbs. The report also flags that the layout is a build error today (metadata in a Client Component, and import "server-only" inside the boundary), and this fix repairs both. Claude checks the DOM order too, so the shell renders the same div.shell the layout had.

04<Trust />

مصممة إنها متكدبش عليكBuilt not to lie to you

أي audit بيقول "شيل الـ directive" غلط ممكن يكسر الـ build. عشان كده كل قرار عليه دليل، وأي حاجة مش مؤكدة بتتقال بوضوح.An audit that wrongly says "remove the directive" breaks your build. So every verdict comes with evidence, and anything uncertain is said out loud.

🔒

Read-only

مش بتعدّل ولا ملف. Claude بيطبّق حل واحد في المرة، وبعد ما تقوله "اه" بس.Never edits a file. Claude applies one fix at a time, only after you say yes.

❓

Needs manual check

Hook من package مش معروف؟ import مش لاقياه؟ بتقولك إنها مش متأكدة وتتأكد من إيه.Unknown package hook? An import it can't resolve? It says it isn't sure, and what to check.

📦

بتفتح الـ packagesOpens your packages

قبل ما تقولك شيل الـ directive من wrapper بتاع Radix، بتتأكد إن الـ package نفسه فيه "use client".Before saying a Radix/shadcn wrapper can drop its directive, it checks the package itself ships "use client".

🎯

مكسب حقيقي بسOnly real wins

مش بتقترح تنقل component كـ children لو هو أصلًا في الـ client bundle، أو لو بيستخدم الـ state.Won't suggest moving a component to children if it's in the client bundle anyway, or uses the wrapper's state.

📐

تقدير، مش bytesEstimates, not bytes

بتعدّ الـ modules من الـ import graph وبتقول إنه تقدير. الأرقام الحقيقية من next build.Counts modules from the import graph and labels it an estimate. Real numbers come from next build.

⚠️

بتقولك المخاطرةNames the risk

MOVE_FETCH بيغيّر وقت تحميل الـ Data، والـ fetch بـ URL relative مش بيشتغل على الـ Server. بتقولك الكلام ده جنب الحل.MOVE_FETCH changes when data loads, and a relative fetch URL fails on the server. You hear it next to the fix.

05<How />

بتشتغل إزايHow it works

DiscoveryDiscovery

بتلف على app/ وcomponents/ (وsrc/)، وبتتجاهل node_modules والـ build والـ tests. ولو مفيش App Router بتقولك ده وتقف.Scans app/ and components/ (and src/), skipping node_modules, builds and tests. If there is no App Router, it says so and stops.

Directive حقيقية بسReal directives only

بتقرا أول الملف زي الـ bundler بالظبط. "use client" اللي في comment أو string مش بتتحسب، واللي بعد الـ imports بتنبّهك عليها.Reads the file prologue exactly like the bundler does. "use client" in a comment or string doesn't count; one after the imports gets flagged.

محتاجاها ولا لأ؟Needed or not?

بتدوّر على Hooks وEvents وBrowser APIs ومكتبات client، وبتفتح الـ custom hooks وتشوفها. بتستخدم الـ TypeScript compiler API لو موجود في مشروعك.Looks for hooks, events, browser APIs and client libraries, and opens custom hooks to check them. Uses the TypeScript compiler API from your project when available.

الـ Import graphImport graph

بتبني الـ graph بالـ aliases بتاعة tsconfig، وتعدّ كل boundary بتسحب كام module، ومين من تحت معتمد عليها.Builds the graph with your tsconfig aliases, counts what each boundary pulls in, and finds children that silently rely on it.

الحل + Before/AfterFix + Before/After

بتشوف مين بيستخدم أنهي state فين، وتختار الحل، وتكتب الكود من ملفاتك. وClaude بيراجعه على الملفات الحقيقية قبل ما يعرضه.Traces which JSX uses which state, picks the fix, and drafts code from your files. Claude reviews it against the real files before showing you.

06<Tested />

متجرّبة على مشاريع حقيقية الشكلTested on realistic projects

3 مشاريع Next.js صغيرة فيها الحالات المهمة، وفيها فخاخ عشان أي false positive أو false negative يبان.Three small Next.js projects with the core cases, plus traps that expose false positives and false negatives.

01-page-no-need

صفحة عليها "use client" على الفاضيA page marked "use client" for nothing

REMOVE، أو PUSH_DOWN لو فيه form تحتها معتمدة على الـ boundary في السر.REMOVE, or PUSH_DOWN when a form below silently relies on the boundary.

02-modal-children

Modal محتاج Client بجدA Modal that really needs Client

CHILDREN_PATTERN للمحتوى التقيل، وMOVE_FETCH لـ widget بيعمل fetch في useEffect.CHILDREN_PATTERN for the heavy content, MOVE_FETCH for a widget fetching in useEffect.

03-layout-boundary

Layout بتسحب الدنيا كلهاA layout dragging everything in

High priority PUSH_DOWN: shell للـ menu، والـ usePathname بيتنقل جوه Breadcrumbs.High priority PUSH_DOWN: a shell for the menu, usePathname moved into Breadcrumbs.

كل check بيتجرّب بـ TypeScript وBabel والـ regex fallback:Every check runs with TypeScript, Babel and the regex fallback: node tests/run-tests.js → 276 passed, 0 failed
07<Install />

سطّبها في دقيقةInstall in a minute

Node.js 18 أو أحدث، ومن غير dependencies.Node.js 18+, zero dependencies.

git clone https://github.com/radyelkasas/nextjs-client-audit.git
mkdir -p ~/.claude/skills
cp -R nextjs-client-audit/nextjs-client-audit ~/.claude/skills/

ولو عايزها لمشروع واحد بس، حطها في .claude/skills/ جوه المشروع.For one project only, put it in that project's .claude/skills/.

curl -LO https://github.com/radyelkasas/nextjs-client-audit/releases/latest/download/nextjs-client-audit.skill
unzip nextjs-client-audit.skill -d ~/.claude/skills/

ملف الـ .skill ده zip. تقدر تنزّله من هنا: nextjs-client-audit.skillA .skill file is a zip. Direct download: nextjs-client-audit.skill

Settings → Capabilities → Skills → Upload skill
→ nextjs-client-audit.skill

في Claude.ai الـ Skill بتشتغل على الكود اللي ترفعه في المحادثة.On Claude.ai the skill works on code you upload to the conversation.

وبعدين جرّب:Then try: audit use client in this project why is my dashboard bundle so big? ليه الصفحة دي تقيلة؟

أسئلةFAQ

الـ layout اللي عليها "use client" بتخلي كل الصفحات Client؟Does a "use client" layout make all my pages client?

لأ. الصفحات بتيجي للـ layout كـ children فبتفضل Server Components. اللي بيتأثر هو الـ imports بتاعة الـ layout نفسها (Header وSidebar وFooter…)، ودي بتتبعت مع كل صفحة تحتها. والـ Skill بتقول الكلام ده بدقة.No. Pages reach the layout as children, so they stay Server Components. What ships is the layout's own imports (Header, Sidebar, Footer…), on every page under it. The skill says exactly that.

ليه بتقول "≈13 module" مش KB؟Why "≈13 modules" and not KB?

لأن الحجم الحقيقي بيتحدد وقت الـ build. الـ Skill بتعدّ من الـ import graph وبتقول إنه تقدير، وبتقولك تقارن الـ First Load JS بـ next build قبل وبعد.Real size is decided at build time. The skill counts from the import graph, labels it an estimate, and tells you to compare First Load JS with next build before and after.

لو مشروعي مفيهوش TypeScript؟What if my project has no TypeScript installed?

بتستخدم @babel/parser لو موجود. ولو مش موجود، بتشتغل بـ regex بحذر وبتقولك إن النتايج أقل دقة، وبتلجأ لـ "Needs manual check" أكتر.It uses @babel/parser if present; otherwise it falls back to a careful regex scan, tells you results are less precise, and uses "Needs manual check" more often.

هتعدّل الكود بتاعي لوحدها؟Will it change my code by itself?

لأ. الـ audit read-only. لو عايز Claude يطبّق حل، قوله، وهيطبّق حل واحد ويشغّل الـ type check ويعيد الـ audit.No. The audit is read-only. Ask Claude to apply a fix and it applies one, runs the type check, and re-audits.