lunx.docs
DocsFrameworksWaku

Waku Adapter

Minimal React Server Components framework by the creator of Zustand and Jotai. Verified: 'use server'/'use client' boundaries work correctly with RSC streaming.

Waku 0.21 · RSC · Minimal
React Server Components

True RSC support with streaming — server components fetch data without client-side waterfalls.

'use server' / 'use client'

Explicit boundary markers for server/client code splitting — verified working end-to-end.

Minimal footprint

Waku focuses on RSC fundamentals without meta-framework complexity. Great for learning RSC deeply.

Streaming SSR

Components stream to the client as they resolve. Users see content immediately, not a loading spinner.

TypeScript native

Full TypeScript support with typed server actions and RSC props.

Server actions

Form-based server mutations using the 'use server' directive — no API routes needed.

Setup

bash
npm create lunx-dev@latest my-waku-app -- --framework waku --ts
cd my-waku-app && npm install && npm run dev

Server and client components

typescriptsrc/pages/index.tsx
// Server component — no 'use client' = runs on server
import { db }         from '../lib/db'
import { LikeButton } from '../components/LikeButton'  // client component

async function PostList() {
  // Direct async data fetching — no useEffect, no loading states
  const posts = await db.post.findMany({ take: 10 })

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          <h2>{post.title}</h2>
          {/* Client component nested inside server component */}
          <LikeButton postId={post.id} initialLikes={post.likes} />
        </li>
      ))}
    </ul>
  )
}

export default function HomePage() {
  return (
    <main>
      <h1>Latest Posts</h1>
      <PostList />
    </main>
  )
}
typescriptsrc/components/LikeButton.tsx
'use client'  // This component runs in the browser

import { useState } from 'react'

export function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: number }) {
  const [likes, setLikes]   = useState(initialLikes)
  const [liked, setLiked]   = useState(false)

  async function handleLike() {
    if (liked) return
    setLikes(l => l + 1)
    setLiked(true)
    await fetch(`/api/posts/${postId}/like`, { method: 'POST' })
  }

  return (
    <button onClick={handleLike} disabled={liked}>
      ❤️ {likes} {liked ? '(liked!)' : 'Like'}
    </button>
  )
}