We tried full RSC at work on a new dashboard project. Looked perfect on paper. Data fetching on the server, less JS shipped, built-in caching. First month was smooth.
Then we hit real product requirements. Filters, sorting, pagination state that needs to live somewhere. We kept reaching for useState and it started feeling backwards. You're in a server component, want to add a simple toggle, now you're wrapping it in a client boundary. The component tree gets weird fast.
Switched to hybrid approach halfway through. Server components for the chrome, layout, data that doesn't change. Client components for anything with interactivity. It's boring but it actually works.
The RSC evangelist stuff undersells how much your app is actually stateful. Most dashboards are. Most forms are. Unless you're building a blog or a docs site, you're probably reaching for plain client components anyway. RSC is good for the 20% of your app that's truly static.
Next project I'd start with client components and add server components only where it matters. Not the other way around.
Hybrid is the right call. RSCs solve a real problem (data fetching overhead), but they're not a silver bullet for state management.
The friction you hit is fundamental. Server components can't hold interactive state. So you either accept client boundaries everywhere (defeating the purpose) or you artificially flatten your component tree to avoid them. Both suck.
What worked for us: RSCs for top-level data fetching and layout, client components for anything with interaction. Keep the boundary high in the tree. Treat it like an API contract, not a seamless abstraction.
The marketing made it sound like RSCs replace client state. They don't. They're a different tool for a different problem.
Ravi Menon
Cloud architect. AWS and serverless.
RSCs are great for the read path, terrible for stateful UX. Your experience matches what I've seen. The toggle problem is real: you end up with this awkward layering where server components handle data, client components handle literally everything interactive, and the boundary between them becomes a pain to reason about.
Hybrid works better in practice. Server for initial load and refetching. Client for form state, filters, toggles. Keep them loosely coupled so you're not constantly context-drilling through server boundaries.
The "less JS shipped" argument falls apart once you account for the framework overhead and serialization costs anyway.