6 min read

How I Built a Full-Stack React Framework 4x Faster Than Next.js With 4x More Throughput

  • Open Source
  • React
  • Rust
  • Software Development

After nearly 25 years of building for the web, from Perl to PHP, through the jQuery years, and into the modern JavaScript era, I've watched tooling come and go. Bundlers got faster, server-side rendering improved, and React evolved from a simple view library into a full-stack platform. But for a while now, I've felt like I could do better.

Today I'm introducing Runtime Accelerated Rendering Infrastructure (rari), a full-stack React framework that delivers performance numbers I didn't think were possible with React:

  • 4.04x faster component rendering under load (4.23ms vs 17.11ms average response time)
  • 3.74x higher throughput under load (10,586 req/sec vs 2,832 req/sec)
  • 5.80x faster build times (1.59 seconds vs 9.22 seconds)
  • 100% React Server Component compatibility with true streaming support

rari isn't another Node.js framework with performance optimizations bolted on. It's a fundamental rethinking of how React applications should run, built on a custom Rust runtime with V8 at its core.

The Performance Story

Response Time Comparison

I built identical applications in both Next.js and rari, each rendering the same complex component tree with server-side data fetching, client components, and real-world patterns.

FrameworkAvg Response TimeMinMaxP95
rari2.15ms1.34ms3.09ms3.09ms
Next.js4.88ms2.57ms6.93ms6.93ms

rari renders complex React Server Components 2.27x faster than Next.js across identical workloads.

Concurrent Load Performance

I stress-tested both frameworks with 50 concurrent connections for 30 seconds:

FrameworkTotal RequestsReq/SecAvg LatencyP99 Latency
rari317,56910,5864.23ms46ms
Next.js84,9552,83217.11ms33ms

rari handled 3.74x more requests with 4.04x lower latency. Under extreme load, rari consistently delivered sub-50ms response times while maintaining high throughput.

Build Performance

FrameworkBuild TimeBundle Size
rari1.59 seconds400 KB
Next.js9.22 seconds742 KB

rari builds 5.80x faster while producing 46% smaller bundles all thanks to tsgo and Rolldown.

How It Works: The Architecture

rari's performance comes from a unique three-layer architecture that combines the best of Rust's performance with JavaScript's ecosystem compatibility.

Layer 1: Rust Core Runtime

At the heart of rari is a custom Rust runtime built on Deno Core and V8. The performance gains aren't just "Rust is fast" as a hand-wave; they come from specific architectural decisions:

// React Server Components execute directly in the Rust runtime
// JSX is transformed and components are registered automatically
let mut renderer = RscRenderer::new(runtime);
renderer.initialize().await?;

// Transform and register the component
renderer.register_component("UserProfile", jsx_source).await?;

// Render to RSC serialized format for streaming to client
let rsc_payload = renderer.render_to_rsc_format("UserProfile", Some(props_json)).await?;

Why Rust? Performance bottlenecks often come from the runtime itself. Node.js, despite years of improvements, still carries overhead from its event loop design and garbage collection patterns. Rust gives us:

  • Zero-cost abstractions, so performance optimizations don't compromise developer experience
  • Memory safety without garbage collection, which means predictable performance under load
  • True concurrency to handle thousands of requests without blocking
  • Direct V8 integration for JavaScript execution without Node.js layers

Layer 2: Intelligent Vite Integration

Rather than reinventing build tooling, rari extends Vite with RSC-aware transformations:

// Automatic client/server component detection and transformation
function transformServerModule(code: string, id: string): string {
  if (!code.includes('use server'))
    return code

  // Transform server components for RSC serialization
  let newCode = `\\n\\nimport {registerServerReference} from "react-server-dom-rari/server";\\n`

  // Register server functions for RPC calls
  for (const name of exportedNames) {
    newCode += `registerServerReference(, , );\\n`
  }

  return newCode
}

This intelligent transformation layer:

  • Automatically detects component boundaries between client and server
  • Handles RSC serialization without manual configuration
  • Enables hot module replacement for server components
  • Manages dependency graphs across the client/server boundary

Layer 3: React Server Components Done Right

rari implements React Server Components as they were meant to be, with true server-side rendering and streaming support:

// RSC streaming implementation in Rust
async fn stream_component(
    State(state): State<ServerState>,
    Json(request): Json<RenderRequest>,
) -> Result<Response, StatusCode> {
    let props_str = request.props.as_ref().map(|p| serde_json::to_string(p).unwrap_or_default());

    // Create RSC stream directly from renderer
    let stream_result = {
        let mut renderer = state.renderer.lock().await;
        renderer.render_with_readable_stream(&request.component_id, props_str.as_deref())
    };

    match stream_result {
        Ok(mut rsc_stream) => {
            // Convert RSC stream to HTTP response stream
            let byte_stream = async_stream::stream! {
                while let Some(chunk_result) = rsc_stream.next().await {
                    match chunk_result {
                        Ok(chunk_bytes) => yield Ok(chunk_bytes),
                        Err(e) => yield Err(std::io::Error::other(e.to_string())),
                    }
                }
            };

            Ok(Response::builder()
                .header("content-type", "text/x-component")
                .header("transfer-encoding", "chunked")
                .body(Body::from_stream(byte_stream))
                .expect("Valid streaming response"))
        }
        Err(e) => Err(StatusCode::INTERNAL_SERVER_ERROR)
    }
}

Intelligent Routing and Code Splitting

rari's file-based routing automatically discovers and optimizes routes during build, with smart code splitting and prefetching built in. Routes are pre-analyzed for RSC and client component boundaries, enabling optimal bundle sizes without any configuration.

Developer Experience

rari maintains all the conveniences you'd expect from a modern React framework:

Server and Client Components

// Server component - runs in Rust runtime
'use server'

export default async function UserProfile({ userId }: { userId: string }) {
  const response = await fetch(`https://api.example.com/users/$\{userId}`)
  const user = await response.json()

  return (
    <div>
      <h1>{user.name}</h1>
      <ClientCounter initialCount={user.loginCount} />
    </div>
  )
}
// Client component - hydrates in browser
'use client'

export default function ClientCounter({ initialCount }: { initialCount: number }) {
  const [count, setCount] = useState(initialCount)

  return (
    <button onClick={() => setCount(count + 1)}>
      Clicked
      {' '}
      {count}
      {' '}
      times
    </button>
  )
}

Hot Module Replacement for Everything

rari's HMR works for both client and server components. Change a server component and see it update instantly, without a full page reload or lost state.

TypeScript by Default

Full TypeScript support with intelligent type inference across the client/server boundary. Props flow from server components to client components with complete type safety.

Why I Built This

Current React frameworks make architectural compromises that limit their performance ceiling. They prioritize developer experience first, then spend years optimizing. By building on Rust while maintaining JavaScript compatibility, I wanted to see if those two goals could coexist from the start. rari is MIT licensed and represents the framework I wish I'd had for the past decade.

Getting Started

npm create rari-app@latest my-app
cd my-app
npm run dev

rari automatically downloads the appropriate binary for your platform and starts both the Vite development server and Rust runtime, with no Docker or complex setup required.

The Benchmark Code

All benchmarks in this post are reproducible. The test applications and benchmarking scripts are available at github.com/rari-build/benchmarks. I encourage you to run them yourself and share your results.

Key testing details:

  • Hardware: MacBook Pro M2, 8GB RAM
  • Test duration: 30 seconds per test with 5-second warmup
  • Concurrent users: 50 simultaneous connections
  • Component complexity: Real-world patterns including data fetching, nested components, and client interactivity

Get Involved