rari Documentation

repository·main·Indexed 22 days ago

https://github.com/rari-build/rari

A high-performance React Server Components (RSC) framework that replaces the Node.js runtime with a custom Rust-based runtime. rari features an App Router, streaming SSR, and a build toolchain powered by Rolldown-powered Vite and TypeScript 7. The ecosystem includes the rari CLI for server and image management, a specialized error handling system via RariError, and a caching transformation system using the 'use cache' directive.

Tokens
56.2K
Snippets
192
Records
247
Agent score
77%

What's inside rari

  1. Key features of rari

    main

    rari provides a modern React development experience with several core features:

    • App Router: File-based routing supporting layouts, loading states, and error boundaries.
    • React Server Components: Server components are the default; use client components only when necessary.
    • Streaming SSR: Progressive rendering using Suspense boundaries.
    • Rust-powered Runtime: High-performance HTTP server and RSC renderer.
    • Zero-config Setup: Works immediately with pre-built binaries.
    • Standard Compatibility: Supports standard node_modules resolution without requiring the npm: specifier.
    • Developer Experience: Includes Hot Module Reloading (HMR) and full TypeScript type safety across the server/client boundary.
  2. Overview of rari architecture

    main

    rari is a React Server Components (RSC) framework built on a high-performance Rust runtime. It is composed of three distinct layers:

    1. Rust Runtime: Handles the HTTP server, RSC rendering, and routing (using an embedded V8 engine).
    2. React Framework: Provides the developer experience for the App Router, including server actions, streaming, and Suspense.
    3. Build Toolchain: Uses Rolldown-powered Vite for bundling and TypeScript 7 for type checking.

    This architecture allows you to write standard React code while benefiting from a Rust-based execution environment instead of Node.js.

  3. Implement dynamic routes

    main

    Use square brackets in filenames to create dynamic segments.

    • Single Dynamic Segment: [slug] matches one segment (e.g., /blog/my-post).
    • Catch-All Segments: [...slug] matches one or more segments (e.g., /docs/api/ref matches slug = ['api', 'ref']). Requires at least one segment.
    • Optional Catch-All Segments: [[...slug]] matches zero or more segments, including the base path (e.g., /docs matches slug = undefined).

    Route Priority

    1. Static routes (/about)
    2. Dynamic routes (/[id])
    3. Catch-all routes (/[...slug])
    4. Optional catch-all routes (/[[...slug]])
    import type { PageProps } from 'rari'
    
    export default async function BlogPost({ params }: PageProps<{ slug: string }>) {
      const post = await fetch(`https://api.example.com/posts/${params.slug}`).then(r => r.json())
    
      return (
        <article>
          <h1>{post.title}</h1>
          <p>{post.content}</p>
        </article>
      )
    }
  4. Understand the rari Project Structure

    main

    A standard rari project follows this directory convention:

    • src/app/: Contains the App Router files (layouts, pages, dynamic routes like [id]).
    • src/components/: Reusable UI components (Server or Client).
    • src/actions/: Server Actions defined with 'use server'.
    • public/: Static assets.
    • vite.config.ts: Vite configuration including the rari() plugin.
    • package.json: Project dependencies and scripts.
    my-rari-app/
    ├── src/
    │   ├── app/
    │   │   ├── layout.tsx
    │   │   ├── page.tsx
    │   │   └── users/
    │   │       └── [id]/
    │   │           └── page.tsx
    │   ├── components/
    │   └── actions/
    ├── public/
    ├── vite.config.ts
    └── package.json
  5. How database integration works in rari

    main

    rari enables direct database access through its server-side architecture, eliminating the need for intermediate API routes.

    Key architectural features include:

    • Server Components by default: Query databases directly within your components.
    • Server Actions: Use the 'use server' directive to mutate data securely from client components without exposing credentials.
    • Streaming SSR: Progressively stream database results using Suspense boundaries.
    • React.cache() support: Deduplicate database queries across your component tree to optimize performance.
    • High-performance Runtime: The Rust-powered runtime handles database queries efficiently.
  6. Use Client Components with 'use client'

    main

    By default, all components in the src/app/ directory are React Server Components. To use browser APIs, hooks (like useState), or event handlers, you must explicitly mark the file with the 'use client' directive.

    'use client'
    
    import { useState } from 'react'
    
    export default function ClientComponent() {
      const [count, setCount] = useState(0)
      return (
        <button onClick={() => setCount(count + 1)} type="button">
          Clicked {count} times
        </button>
      )
    }
  7. Differences between rari fetch and native fetch

    main

    rari's fetch() is a drop-in replacement for the native Web API fetch() but includes several enhanced features:

    1. Automatic caching: GET requests are cached by default using an LRU (Least Recently Used) eviction policy.
    2. Request deduplication: Identical requests made during the same render pass are deduplicated.
    3. Time-based revalidation: Supports the rari.revalidate option to set cache TTL.
    4. Built-in timeout: Supports the rari.timeout option (defaults to 5 seconds).
    5. Rust-powered performance: Cache operations are executed in Rust for high speed.

    The core API remains compatible with the standard fetch() specification.

  8. How Streaming SSR works in rari

    main

    When a page contains Suspense boundaries (e.g., via a loading.tsx file), rari uses Streaming SSR to send HTML progressively to the browser.

    The Streaming Process:

    1. Initial Render: The StreamingRenderer executes the composition script in V8. It produces the initial synchronous RSC tree and a list of pending promises for async components.
    2. First Chunk: rari converts the initial tree to HTML and sends it immediately. The browser begins painting the shell and synchronous content.
    3. Background Resolution: A background task resolves pending promises in V8. When a promise resolves, a boundary update is sent through a channel.
    4. Stream Updates: A listener task receives updates, attaches DOM position hints, and forwards them as stream chunks. The server emits a hidden <div> containing the rendered content and an inline <script> to swap the content into the correct DOM position (using the $RC pattern).
    5. Hydration: Once all components resolve, the RSC payload is embedded in a <script> tag at the end of the stream for client-side hydration.
  9. Optimize image quality

    main

    You can control the compression level using the quality prop.

    Important: Only values present in your configured qualityAllowlist are accepted. The default allowlist is [25, 50, 75, 100]. Any other value will result in an error.

    • quality={100}: Best for hero images.
    • quality={75}: Default/Balanced.
    • quality={50}: Good for thumbnails.
    • quality={25}: Best for decorative backgrounds.
  10. Understand rari's two-tier caching system

    main

    rari uses a two-tier caching architecture to optimize performance:

    1. Request Deduplication: An in-memory layer that prevents identical requests from being sent multiple times during a single render pass.
    2. LRU Cache: A global, high-performance cache powered by Rust that stores successful responses. It supports a maximum of 1000 entries and respects Time-To-Live (TTL) settings via rari.revalidate.
  11. How rari's pre-compressed response cache works

    main

    rari uses an in-memory cache to serve pre-compressed response bytes (zstd, brotli, or gzip) without re-rendering the page on subsequent requests. This is highly efficient for synchronous server components.

    Key Behaviors:

    • Automatic Caching: After the first request, the rendered and compressed bytes are stored in an in-memory DashMap.
    • Cached Path: Routes that do not have a loading.tsx file use this pre-compressed response cache path. This results in sub-millisecond server-side processing.
    • Streaming Path: Routes that do include a loading.tsx file use streaming SSR instead. These pages perform real rendering on each request and do not benefit from the pre-compressed response cache, resulting in higher (though still fast) processing times (typically 2-3ms).
    /* Note: Caching behavior is determined by the presence of loading.tsx */
    
    // Path A: No loading.tsx -> Uses pre-compressed response cache (Fastest)
    // Path B: Has loading.tsx -> Uses streaming SSR (Real rendering per request)
  12. Project structure and file conventions

    main

    Rari uses a file-system based router within the src/app/ directory. Different file names within a directory determine the behavior of that route:

    • page.tsx: Defines the UI for a specific route.
    • layout.tsx: Defines a layout shared by all child routes (nested layouts).
    • loading.tsx: Defines a loading state for the route and its children.
    • error.tsx: Defines an error boundary for the route and its children.
    • not-found.tsx: Defines the UI for 404 errors.
    • route.ts: Defines API endpoints (Route Handlers).
    • [[...slug]]: Represents an optional catch-all segment.
    • [slug]: Represents a single dynamic segment.
    • opengraph-image.tsx: Used for generating dynamic Open Graph images.
    src/app/
    ├── layout.tsx              # Root layout
    ├── page.tsx                # /
    ├── loading.tsx             # Global loading state
    ├── error.tsx               # Global error boundary
    ├── not-found.tsx           # Global 404
    ├── about/
    │   └── page.tsx            # /about
    ├── blog/
    │   ├── layout.tsx          # Blog layout
    │   ├── page.tsx           # /blog
    │   └── [slug]/
    │       ├── page.tsx        # /blog/:slug
    │       ├── loading.tsx     # Loading state for blog posts
    │       └── opengraph-image.tsx  # Dynamic OG image
    ├── dashboard/
    │   ├── layout.tsx          # Dashboard layout (sidebar nav)
    │   ├── page.tsx            # /dashboard
    │   ├── error.tsx           # Error boundary for dashboard
    │   └── analytics/
    │       └── page.tsx        # /dashboard/analytics
    ├── docs/
    │   └── [[...slug]]/
    │       └── page.tsx        # /docs, /docs/*, /docs/*/*
    └── api/
        └── users/
            ├── route.ts        # GET/POST /api/users
            └── [id]/
                └── route.ts    # GET/PUT/DELETE /api/users/:id