React Server Components best practices, Next.js App Router performance, RSC server actions

React Server Components + Next.js: Real-World Patterns That Actually Move INP

RSC is here to stay—see how real teams are using it to boost Core Web Vitals.

Modern web performance is increasingly measured by user-centric metrics. One of the newest and most critical is Interaction to Next Paint (INP) – a Core Web Vital that replaced First Input Delay in early 2024. INP observes all user interactions (clicks, taps, key presses) on a page and reports essentially the worst delay among them. A low INP means the site consistently responds fast to user input, providing a snappy user experience. This matters not only for users but also for SEO: Google now treats INP as a ranking factor. In other words, if your site lags when users try to interact, your search rankings and conversions could suffer. So how can we build interfaces that feel instantaneous? Enter React Server Components (RSC) and Next.js’s App Router, a powerful combo that can dramatically improve INP by cutting down JavaScript and leveraging streaming server rendering.

Teams adopting React Server Components in Next.js have reported striking gains. For example, one experiment by Frigade saw a 62% reduction in JS bundle size and the site rendered almost 3× faster with RSC compared to a traditional React SPA. In real-world case studies, companies saw Core Web Vitals skyrocket: DoorDash slashed its Largest Contentful Paint by ~65% by moving to server rendering, and Preply brought INP down from ~250 ms to ~175 ms on key pages after performance optimizations. Another team, GeekyAnts, upgraded their site to Next.js 13 with RSC and saw Lighthouse performance scores leap from ~50 to 90+, with markedly less main-thread work and near-instant page loads. These improvements translated to real business outcomes too – faster sites enjoyed more user engagement and even SEO boosts (one site gained +35k monthly Google impressions after fixing Web Vitals). The takeaway is clear: investing in performance (especially responsiveness) pays off, and React Server Components are a cutting-edge tool to achieve that. In this article, we’ll explore production-ready patterns for using RSC and Next.js’s App Router to reduce the JS payload, improve INP, and enhance UX – along with tips on migrating to this new paradigm.

RSC and Next.js App Router: A Paradigm Shift for Performance

React Server Components allow us to do something revolutionary: render React on the server, component by component, and send the pre-processed UI to the browser. In Next.js’s new App Router (available since v13), RSC is the default mode for all components (unless you opt out with "use client" at the top of a file). This means by default your React components do not ship any JavaScript to the client – they render to static HTML on the server, and the browser just receives that HTML. Only components that truly need interactivity or state (like a form or a dropdown) are marked as Client Components and hydrated on the client. This architecture has huge performance implications.

Most importantly, large libraries and heavy logic can stay on the server. If you use a big charting library or complex data processing in a Server Component, the client never downloads that code – it only gets the rendered result (HTML). As Smashing Magazine notes, with RSC “you’re only taking the static HTML output and avoiding having to ship any JavaScript to the browser,” effectively removing the entire hydration step for those components. By eliminating client-side hydration for non-interactive parts of the UI, we free up the browser’s main thread. The page can appear fully rendered without the usual boot-up of a huge JS bundle, which is a big win for time to interactive and input responsiveness. Additionally, Server Components run in a Node.js environment where they can securely access databases or APIs (no API keys leak to the browser) and leverage the server’s compute power. The server can also cache RSC outputs and reuse them across users to save work on subsequent requests. In short, RSC shifts much of the work to the server side – closer to your data and away from the user’s device – resulting in less code shipped and often faster execution.

Another key part of the Next.js App Router paradigm is streaming. Traditionally, even with server rendering, the user had to wait for the entire HTML to be ready before seeing anything. Next.js 13+ with RSC uses HTML streaming with React Suspense to avoid that delay. What does this mean in practice? You can designate parts of the UI as low-priority (wrapped in <Suspense> with a fallback). The server will immediately send the portions of the page that are ready (like the shell – header, nav, basic layout), along with lightweight placeholders for the suspended parts. This results in an instant page shell being rendered on the client, so users aren’t staring at a blank screen. As data for the slower sections resolves, the server streams those chunks of HTML down and React fills them in without a full refresh. In a real use-case, the team at Inngest found that serving a page shell instantly and streaming in dashboard data as it loaded “allowed users to start interacting with the page sooner”, greatly improving perceived performance. In other words, the page is interactive and partially usable almost immediately, instead of making users wait for all content. Streaming and partial hydration also ensure that long data-fetching operations don’t block the main thread – the browser can respond to clicks on the already-loaded parts while the rest of the content loads in. This is a boon for INP: interactions can be handled promptly because the JavaScript workload is broken into smaller async chunks, not one giant blocking task.

Trimming the Fat: Less JavaScript = Better INP

Why does shipping less JavaScript improve INP? The INP metric is heavily influenced by how quickly the browser can respond to input, which in turn depends on the main thread being free of long tasks. Huge client-side bundles and heavy hydration processes tie up the main thread, delaying event handlers and paints. By using RSC to cut down the JS payload, we minimize those delays. Google’s performance experts emphasize loading only what is necessary, when necessary. In fact, a best-practice emerging from many projects is to “reduce code on the client to a minimum”. React Server Components are basically built around that principle – move as much code as possible off the client.

The results can be dramatic. In the earlier Frigade experiment, the RSC version of the site delivered over 60% less JavaScript to the browser and as a result achieved a significantly faster Speed Index (visual load speed). Users saw content sooner and could interact faster. GeekyAnts, after migrating to RSC, noted that far fewer bytes of JS were shipped and main-thread execution time plummeted, allowing pages to become interactive much sooner. Their before/after charts showed JavaScript parse/execute time sharply reduced, which directly led to a more responsive feel. When the browser isn’t busy parsing a megabyte of framework code, it can focus on responding to user input promptly. This is exactly what INP measures – and indeed GeekyAnts saw all their Core Web Vitals, including INP, meet optimal thresholds after the move to Next 13 and RSC.

Another hidden cost of client-heavy apps is the hydration step. With traditional SSR, even after HTML is delivered, the browser must download the React bundle and hydrate the UI – essentially re-rendering on top of the server-rendered HTML to attach event listeners. This can delay interactivity (FID/INP) especially if the app is large. RSC eliminates hydration for server-rendered parts. Those components are truly static from the client’s perspective – there’s no re-rendering needed, because they never had client-side state to reconcile. As a result, the time between page load and the page being fully interactive is much shorter. Preply’s team, working with the older Pages Router (no RSC), had to implement “selective hydration” manually – wrapping parts of the UI in Suspense so that less critical parts could hydrate later, allowing important interactive components to hydrate first. This effort “drastically cut down input delay” in their app. The App Router with RSC provides this benefit by default – non-interactive layout and content come pre-hydrated (as static HTML), and only the truly interactive widgets need hydration. In effect, RSC gives you selective, priority hydration out-of-the-box, boosting responsiveness.

Finally, less JavaScript means fewer chances for long JS tasks during the page’s life. A high INP often stems from a slow interaction somewhere – maybe a heavy onclick handler or a large chunk of app logic executing. If we’ve offloaded much of the logic to the server, the client-side event handlers tend to be lighter. And with React 18’s concurrent rendering features (which Next’s App Router fully utilizes), even when the client does have heavy state updates, it can schedule them more intelligently. We’ll discuss that next.

Leveraging React 18 Concurrent Features for UX

One reason Next.js 13’s App Router was such a leap in performance is that it rides on React 18’s concurrent rendering capabilities. This includes features like transitions (startTransition), which let us mark state updates as non-urgent, and improved Suspense for data fetching. These are directly aimed at keeping apps responsive even under load. For example, using startTransition to wrap a large state update will tell React “this update can be delayed if the user triggers something high-priority” – so the UI remains responsive to new input. Preply’s engineers did exactly this: they identified expensive event handlers (e.g. a filter that triggers heavy computations) and wrapped those updates in transitions, effectively demoting them to low priority. The result was that if a user quickly clicked something else, React could pause the heavy update to respond to the new interaction, keeping the interface snappy. Combined with techniques like debouncing rapid-fire events, this ensured the main thread wasn’t overwhelmed by JavaScript during critical moments.

The App Router encourages these patterns. In fact, Next.js automatically batches state updates and uses an asynchronous rendering model by default, which means your app can handle a flurry of updates more gracefully. Features like Suspense for data fetching allow the UI to wait for data without blocking the whole app. And the new Transition API lets you implement the kind of split updates mentioned above with relatively little code. Google’s guidance for optimizing INP aligns perfectly here: break up long tasks, yield to the browser, do work in background threads when possible. React 18’s concurrency does a lot of that heavy lifting for you. Next.js 13’s architecture is designed to keep the UI responsive by default – it’s no coincidence that one case study found a 46–54% performance boost just by enabling concurrent rendering in a React app. The bottom line is that RSC + App Router not only reduces the amount of JS but also uses smarter scheduling for what does run on the client, which together drive down input latency (better INP).

Server Actions: Fewer Client-Side Duties

React Server Components focus on the rendering path, but what about interactions that normally require client-side logic? This is where Server Actions come in – an emerging Next.js feature (introduced experimentally in v13.4) that lets you define form submissions or other actions that run directly on the server. It’s like having your React onClick handler execute on the server without an API call, thanks to a special "use server" directive in the function. If used wisely, Server Actions can further shrink the client bundle and even allow basic interactivity with zero client JavaScript.

For instance, consider a classic counter button. Normally you’d need React state and onClick logic in the browser to update it. With Server Actions, you can make the button submit to a server function that increments the counter on the server and re-renders the component. In Next 13, you’d write an async function with "use server" and use it as the form action. The framework handles the rest. In a demo by This Dot Labs, they built a counter that runs with JavaScript turned off – the increment button makes a request to the server, updates server state, and Next.js revalidates the UI. After a click, the new count comes through via server-rendered HTML. This is essentially a return to classic web forms (full page reloads), but turbocharged with partial rendering and without leaving the SPA context. The advantage is you don’t ship any React code for that interaction – no useState, no reducer, nothing. The user still sees the update (with a tiny delay for the round-trip), but from an INP perspective, the initial click is handled almost instantly in the UI because the browser just needs to paint a loading state (which could even be optimistic). The heavy lifting happens server-side, off the main thread.

Server Actions are still new, and not every interaction should be handled this way (a round-trip to increment a counter is overkill in production). But for operations like form submissions, database writes, or complex logic that you’d rather not bundle into the client, they are a game changer. They let you keep your app interactive and thin on the client. Think of actions like form processing, authentication, or multi-step workflows – these can often be handled with Server Actions, meaning the client-side code only needs to handle the UX around it (show a success message, etc.), not the data processing itself. Less client JavaScript, again, means less chance of blocking the main thread. And as a bonus, actions work even if the user has disabled JS, providing progressive enhancement for critical paths. In summary, RSC + Server Actions enable an architecture where the server handles both rendering and many interactions, leaving the client UI lightweight and focused on instantaneous feedback – exactly what we want for great INP and UX.

Real-World Results: Faster Sites, Happier Users (and Marketers)

It’s worth recapping how these patterns translate into tangible improvements. We’ve mentioned a few throughout this article:

  • Frigade’s RSC experiment: By switching a product website to React Server Components and Next.js App Router, they cut the JS bundle size by 62% and improved Speed Index by 63%. The RSC version rendered almost three times faster, and the team noted it “made for a better user experience since it rendered almost 3× faster” than the all-client version. This showcases the impact of sending less JavaScript down the pipe – the site felt much snappier.
  • Preply’s INP overhaul: Preply formed a performance strike team to fix poor INP on critical pages (their INP was ~250 ms, above Google’s 200 ms “good” threshold). Without using RSC, they still achieved huge gains by applying many of the principles we’ve discussed: trimming unused JS, splitting big tasks, deferring non-urgent work, and leveraging React 18 features. In a few months, they brought INP down to ~175 ms – under the threshold – and as a result, users felt the pages were “almost instant” after the fixes. Impressively, this yielded an estimated $200k/year benefit (from better conversion and lower bounce rate). It underlines that responsiveness isn’t just a tech metric; it affects the bottom line. Preply’s case also proves you can improve performance even on older architectures – but frameworks like Next 13 make it much easier.
  • GeekyAnts website migration: This tech consultancy upgraded their marketing site from an older Next.js version to Next 13 with RSC to tackle poor performance scores (Lighthouse ~50). By converting a large portion of their pages to Server Components and moving data fetching to the server, they drastically reduced client-side processing. The outcome: Lighthouse “health scores reached 90+” post-migration. All Core Web Vitals hit good levels, and side-by-side comparisons showed JavaScript execution time dropped significantly thanks to RSC. Users experienced near-instant loads and smoother interactions, even on media-heavy pages. SEO also improved alongside the performance gains. This real-world case demonstrates that adopting RSC in production can yield dramatic performance boosts. As their team put it, by “shipping less JavaScript and doing more work on the server, apps can achieve dramatic performance gains” – a perfect summary of why RSC matters.

Multiple other companies and open-source projects have similarly reported that using Next.js’s App Router (with RSC, streaming, etc.) led to faster apps and better Core Web Vitals. Faster sites don’t just score higher on Google’s tests – they feel better to users. A responsive interface is part of great UI/UX Design because it builds trust: users feel in control and aren’t frustrated by lag. And from an SEO perspective, Google rewards these improvements with potentially higher rankings, which means more traffic and opportunities for conversion. It’s a virtuous cycle of better performance leading to better engagement.

Best Practices for RSC Adoption (and Migration Tips)

If you’re ready to embrace React Server Components and Next.js for better performance, here are some best practices and tips to ensure a smooth transition:

  • Identify Server vs Client Components: Go through your app and mark components as client-only ("use client") only when truly needed – i.e. those handling interactivity, local state, or browser APIs. Everything else (static content, layout, data-display components) can be Server Components. This maximizes the JavaScript savings. Often, you’ll find a large percentage of your UI doesn’t need to be interactive. By keeping those as Server Components, you avoid bundling their code to the client altogether.
  • Offload Data Fetching to the Server: Embrace the Next.js App Router way of fetching data in Server Components (using fetch() in RSC, which supports caching and revalidation). This replaces functions like getServerSideProps. It ensures data is loaded on the server side and merged into the HTML, so the client isn’t waiting on additional API calls to render content. Server-side data fetching not only improves performance (thanks to caching and avoiding waterfall requests) but also keeps sensitive data and logic on the server.
  • Use Streaming and Suspense: Structure your routes to take advantage of streaming. Provide sensible <Suspense> fallbacks for parts of the UI that might take longer (for example, a chart or personalized feed). This way the initial shell renders immediately and secondary content streams in. The goal is to always show something meaningful within the first few hundred milliseconds. Next.js will handle streaming those suspended parts when ready. Users get faster feedback, and your INP benefits because the page can handle input even if some data is still loading.
  • Adopt React 18 Concurrent Patterns: Use features like startTransition for any expensive state updates, so they don’t block user interactions. Wrap non-critical updates in startTransition and use useDeferredValue if needed for filtering/search use-cases to avoid locking up the UI. Also utilize React’s automatic batching – avoid artificial await or setTimeout patterns that break batching. Let the framework batch re-renders to keep things efficient. Essentially, lean into React’s tools for scheduling updates; they’re there to help your app stay responsive.
  • Optimize Event Handlers: Audit your event handlers (onClick, onChange, etc.). Remove unnecessary work from them – e.g. heavy computations should be moved out (debounced or shifted to web workers or server calls). Keep handlers light and quick. If an interaction triggers a large data load, consider using a Server Action or deferring the work with a loading state. The faster your handler returns, the quicker the browser can paint the next frame for the user.
  • Code-Split Remaining Client Bundles: Even after adopting RSC, you will have some client-side code (for interactive components). Make sure to lazy load and split these as much as possible. Next.js by default splits on a per-component basis, but be mindful of not importing large libraries in a client component’s module scope (that would pull them into the bundle). If you have a heavy client component that isn’t needed immediately, dynamically import it so it doesn’t block initial load. The principle “load only what is necessary, when it is necessary” still applies. Every kilobyte of JS saved helps lower main-thread time and improves INP.
  • Utilize Server Actions for Mutations: Where appropriate, try out Server Actions (with the "use server" directive) for form submissions and other state mutations that can be handled on the backend. This can simplify your client-side code (no need to write an API call and Redux dispatch – you just call the server function). For example, a contact form can directly action={sendContactEmail} where sendContactEmail is a server function – the user submits and sees a success message without your frontend bundling a bunch of logic. This pattern keeps your client bundle lean and often provides more secure, robust handling. Just be cautious to provide quick UX feedback (e.g. disable the button and show a spinner instantly on form submit) so the user isn’t left hanging during the round-trip.
  • Monitor and Iterate: After implementing RSC and new patterns, test your site’s Core Web Vitals using tools like PageSpeed Insights or Lighthouse. Verify that INP (and other vitals like LCP, CLS) are hitting the “good” range. Often, you’ll spot additional areas to optimize (e.g. a third-party script blocking the main thread). Performance work is iterative. The good news is that the App Router provides a strong foundation – many optimizations are built-in, so you might find you’re already in great shape. But keep an eye on real-user metrics (in Chrome UX Report or your analytics) to catch any regressions.
  • Plan an Incremental Migration: If you have an existing Next.js (Pages Router) app or even a Create React App, you don’t have to rebuild everything at once. It’s often best to migrate one route or section at a time. Next.js allows running the old pages directory and new app directory concurrently, so you can move page by page. Focus on a high-impact page (perhaps your homepage or an important landing page) and convert it to the App Router with RSC. Measure the improvement, iron out any issues, then proceed to the next page. An incremental rollout like this reduces risk – much like how DoorDash handled their migration by running SSR pages alongside CSR until confident. Also ensure your team is up to speed on the new conventions (file-based routing, etc.), perhaps starting with a smaller feature before adopting framework-wide.

Migrating to a new architecture can be challenging, but with these patterns, you’ll be on your way to a faster, more resilient application. The process is made easier with the right expertise – consider involving a React/Next.js Development specialist if needed to guide the refactor. The payoff is worth it: you’re building a foundation for long-term Frontend Performance and excellent user experience.

Conclusion

React Server Components, especially when combined with Next.js’s App Router and streaming, represent a significant step forward in web performance. By rendering more on the server and sending less code to the client, you can dramatically improve not just page load times but the responsiveness of your site. A low INP means users never feel a lag – their clicks and taps are acknowledged almost immediately. Achieving this level of performance used to require painstaking micro-optimizations and trade-offs. Now, the React/Next.js ecosystem bakes these optimizations into the framework, allowing developers to deliver speedy, app-like experiences on the web without reinventing the wheel.

From real-world examples, we’ve seen that adopting RSC and the App Router can lead to faster sites, higher Core Web Vitals, better SEO outcomes, and ultimately happier users. It aligns technical excellence with business goals: faster interactions lead to higher engagement and conversion, which every marketer and product owner can get behind. For developers, these patterns are also a joy to work with – you can write simple React components and let the framework figure out the most efficient way to deliver them. There are still nuances to learn (like the caching behavior and the boundary between client/server), but as the community gains experience, best practices are emerging quickly.

In 2025 and beyond, building performant web apps will increasingly mean embracing hybrid rendering approaches like RSC. If you haven’t tried Next.js’s new routing system yet, it might be time to explore it. Even a partial migration could yield immediate improvements in your site’s vitality. And since RSC is still evolving, we can expect even more tooling and community support to make it easier. One thing is certain: users won’t complain that your site feels too fast! By proactively optimizing for metrics like INP now, you’re not only staying ahead of Google’s algorithm changes but also delivering a superior UI/UX Design to your audience. React Server Components are here to stay – and they just might be the secret sauce to a snappier, more successful web application.

Newsletter Updates

Enter your email address below and subscribe to our newsletter

Leave a Reply

Your email address will not be published. Required fields are marked *