ReputationProfileView is marked "use client" but has no client-side behaviour, forcing a static profile page into the client bundle
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 54/100
- Issue type
- Refactor
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- next.js, react, typescript
- Domain
- frontend, performance
Research direction
Start with src/app/reputation/[handle]/ReputationProfileView.tsx and inspect its imports, then read the parent page.tsx and the Avatar component to understand the server and client boundaries. Remove the directive and build the route to confirm the page still renders; the issue also suggests checking two sibling components and adding a regression assertion, which expands the scope. Done means the profile view renders as a server component while Avatar still works.
Written by the indexing model from the issue text.
Description
Location
src/app/reputation/[handle]/ReputationProfileView.tsx:1
Problem
The file opens with "use client", but the component is pure presentation over its single profile prop. Grepping the whole 115-line file for any client-only feature returns nothing:
grep -n 'use[A-Z]\|onClick\|onChange\|onSubmit\|window\|document' \
src/app/reputation/\[handle\]/ReputationProfileView.tsx
# (no matches)
No hooks, no event handlers, no browser globals, no state. It renders an Avatar, a <h1>, Badges, four StatCards, Cards, an EmptyState and some Links — and the only one of those that genuinely needs the client is Avatar (src/components/ui/Avatar.tsx:1 is itself "use client"). StatCard, Card, Badge and EmptyState are all plain server-compatible modules with no "use client" directive of their own.
Because the directive sits at the top of this component, it marks the whole subtree as the client boundary: StatCard, Card, Badge, EmptyState, their lucide-react icon imports and the formatHours/messages modules they pull in are all compiled into the route's client bundle and re-executed during hydration, even though their output never changes after the server render.
The parent, src/app/reputation/[handle]/page.tsx, is already an async Server Component that awaits fetchReputationByUsername and hands the resolved profile down as a plain serialisable prop — so nothing about the data flow requires a client component here.
Why it matters
/reputation/[handle] is a public, indexable, read-only page — closed issue #19 and src/lib/seo-policy.ts treat it as a crawlable profile surface, and it is listed in the sitemap. It is the one route in the app with the strongest case for shipping as pure server-rendered HTML, and it currently ships a client bundle for markup that cannot change. The cost is paid on every profile view: extra JavaScript to download, parse and hydrate, for zero interactivity.
This is the same category of waste as closed issue #223 ("RootLayout wraps every route in client-side AuthProvider and WalletProvider, forcing full client hydration even for fully static, anonymous marketing pages") — a boundary drawn higher than the behaviour that needs it.
Reproduction
npm run buildand read the route summary —/reputation/[handle]'s First Load JS includesStatCard,Card,Badge,EmptyStateand their icon imports.npm start, open a profile page, and in DevTools → Network filter to JS: the route's client chunk contains the stat-card and badge code.- Confirm nothing is lost without it: delete line 1 and rebuild — the page renders identically, because no part of the component depends on the client.
Suggested fix
Remove the "use client" directive from src/app/reputation/[handle]/ReputationProfileView.tsx and let the component render on the server. Avatar already carries its own "use client", so it stays a client component and keeps working as a leaf inside a server parent — that is exactly the boundary this page wants.
While doing it, check the two sibling cases for the same pattern:
src/app/milestones/MilestoneFundingProgress.tsx— if it has no hooks or handlers either, it can be a server component too, with onlyMilestoneActions(which does have handlers) staying on the client.src/app/dashboard/maintainer/PipelineBoard.tsx— currently a client component; if its only client need isLink, the boundary can move down.
Then add an assertion to ReputationProfileView.test.tsx (or a small lint rule) that the file has no "use client" directive, so the boundary does not drift back up the tree when the next prop is added.
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 94
- Avg merge
- 1h 2m
- Merged PRs (30d)
- 25
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from MergeFi/frontend
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
bug testing
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Maintainers usually reply within 1 day
-
accessibility bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
MergeFi/frontend#835 · 1 comment ·
Maintainers usually reply within 1 day
All issues in MergeFi/frontend
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
zapcooking/frontend#946 ·
Maintainers usually reply within 1 day
-
enhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
WiliamRosa/cosmos-semantic-platform#40 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 77/100
liberation-data/drivine#733 · 1 comment ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
makecindy/cindy#10062 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day