MCPcopy Index your code
hub / github.com/wakujs/waku

github.com/wakujs/waku @v1.0.0-beta.6 sqlite

repository ↗ · DeepWiki ↗ · release v1.0.0-beta.6 ↗
1,237 symbols 3,201 edges 640 files 1 documented · 0%
README

Waku

⛩️ The minimal React framework

visit waku.gg or npm create waku@latest

Build Status Version Downloads Discord Shield

Introduction

Waku (wah-ku) or わく is the minimal React framework. It's lightweight and designed for a fun developer experience, yet supports all the latest React 19 features like server components and actions. Built for marketing sites, headless commerce, and web apps. For large enterprise applications, you may prefer a heavier framework.

Getting started

Start a new Waku project with the create command for your preferred package manager. It will scaffold a new project with our default Waku starter.

npm create waku@latest

Commands

  • waku dev to start the local development server
  • waku build to generate a production build
  • waku start to serve the production build locally

Node.js version requirement: ^26.0.0 or ^24.0.0 or ^22.15.0

Rendering

While there's a bit of a learning curve to modern React rendering, it introduces powerful new patterns of full-stack composability that are only possible with the advent of server components.

So please don't be intimidated by the 'use client' directive! Once you get the hang of it, you'll appreciate how awesome it is to flexibly move server-client boundaries with a single line of code as your full-stack React codebase evolves over time. It's way simpler than maintaining separate codebases for your backend and frontend.

And please don't fret about client components! Even if you only lightly optimize towards server components, your client bundle size will be smaller than traditional React frameworks, which are always 100% client components.

Future versions of Waku may provide additional opt-in APIs to abstract some of the complexity away for an improved developer experience.

Server components

Server components can be made async and can securely perform server-side logic and data fetching. Feel free to access the local file-system and import heavy dependencies since they aren't included in the client bundle. They have no state, interactivity, or access to browser APIs since they run exclusively on the server.

// server component
import db from 'some-db';

import { Gallery } from '../components/gallery';

export const Store = async () => {
  const products = await db.query('SELECT * FROM products');

  return <Gallery products={products} />;
};

Client components

A 'use client' directive placed at the top of a file will create a server-client boundary when imported into a server component. All components imported below the boundary will be hydrated to run in the browser as well. They can use all traditional React features such as state, effects, and event handlers.

// client component
'use client';

import { useState } from 'react';

export const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <>


Count: {count}


      <button onClick={() => setCount((c) => c + 1)}>Increment</button>
    </>
  );
};

Shared components

Simple React components that meet all of the rules of both server and client components can be imported into either server or client components without affecting the server-client boundary.

// shared component
export const Headline = ({ children }) => {
  return <h3>{children}</h3>;
};

Weaving patterns

Server components can import client components and doing so will create a server-client boundary. Client components cannot import server components, but they can accept server components as props such as children. For example, you may want to add global context providers this way.

// ./src/pages/_layout.tsx
import { Providers } from '../components/providers';

export default async function RootLayout({ children }) {
  return (
    <Providers>
      <main>{children}</main>
    </Providers>
  );
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};
// ./src/components/providers.tsx
'use client';

import { Provider } from 'jotai';

export const Providers = ({ children }) => {
  return <Provider>{children}</Provider>;
};

Server-side rendering

Waku provides static prerendering (SSG) and server-side rendering (SSR) options for both layouts and pages including all of their server and client components. Note that SSR is a distinct concept from RSC.

tl;dr:

Each layout and page in Waku is composed of a React component hierarchy.

It begins with a server component at the top of the tree. Then at points down the hierarchy, you'll eventually import a component that needs client component APIs. Mark this file with a 'use client' directive at the top. When imported into a server component, it will create a server-client boundary. Below this point, all imported components are hydrated and will run in the browser as well.

Server components can be rendered below this boundary, but only via composition (e.g., children props). Together they form a new "React server" layer that runs before the traditional "React client" layer with which you're already familiar.

Client components are still server-side rendered as SSR is separate from RSC. Please see the linked diagrams for a helpful visual.

Further reading

To learn more about the modern React architecture, we recommend Making Sense of React Server Components and The Two Reacts.

Routing

Waku provides a minimal file-based "pages router" experience built for the server components era.

Its underlying low-level API is also available for those that prefer programmatic routing. This documentation covers file-based routing since many React developers prefer it, but please feel free to try both and see which you like more!

Overview

The directory for file-based routing in Waku projects is ./src/pages.

Layouts and pages can be created by making a new file with two exports: a default function for the React component and a named getConfig function that returns a configuration object to specify the render method and other options.

Waku currently supports two rendering options:

  • 'static' for static prerendering (SSG)

  • 'dynamic' for server-side rendering (SSR)

Layouts, pages, and slices are all static by default, while api handlers default to dynamic.

For example, you can statically prerender a global header and footer in the root layout at build time, but dynamically render the rest of a home page at request time for personalized user experiences.

// ./src/pages/_layout.tsx
import '../styles.css';

import { Providers } from '../components/providers';
import { Header } from '../components/header';
import { Footer } from '../components/footer';

// Create root layout
export default async function RootLayout({ children }) {
  return (
    <Providers>
      <Header />
      <main>{children}</main>
      <Footer />
    </Providers>
  );
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};
// ./src/pages/index.tsx

// Create home page
export default async function HomePage() {
  const data = await getData();

  return (
    <>
      <h1>{data.title}</h1>


{data.content}


    </>
  );
}

const getData = async () => {
  /* ... */
};

export const getConfig = async () => {
  return {
    render: 'dynamic',
  } as const;
};

Pages

Pages render a single route, segment route, or catch-all route based on the file system path (conventions below). All page components automatically receive two props related to the rendered route: path (string) and query (string).

Single routes

Pages can be rendered as a single route (e.g., about.tsx or blog/index.tsx).

// ./src/pages/about.tsx

// Create about page
export default async function AboutPage() {
  return <>{/* ...*/}</>;
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};
// ./src/pages/blog/index.tsx

// Create blog index page
export default async function BlogIndexPage() {
  return <>{/* ...*/}</>;
}

export const getConfig = async () => {
  return {
    render: 'static',
  } as const;
};

Segment routes

Segment routes (e.g., [slug].tsx or [slug]/index.tsx) are marked with brackets.

The rendered React component automatically receives a prop named by the segment (e.g., slug) with the value of the rendered segment (e.g., 'introducing-waku').

If statically prerendering a segment route at build time, a staticPaths array must also be provided.

// ./src/pages/blog/[slug].tsx
import type { PageProps } from 'waku/router';

// Create blog article pages
export default async function BlogArticlePage({
  slug,
}: PageProps<'/blog/[slug]'>) {
  const data = await getData(slug);

  return <>{/* ...*/}</>;
}

const getData = async (slug) => {
  /* ... */
};

export const getConfig = async () => {
  return {
    render: 'static',
    staticPaths: ['introducing-waku', 'introducing-pages-router'],
  } as const;
};
// ./src/pages/shop/[category].tsx
import type { PageProps } from 'waku/router';

// Create product category pages
export default async function ProductCategoryPage({
  category,
}: PageProps<'/shop/[category]'>) {
  const data = await getData(category);

  return <>{/* ...*/}</>;
}

const getData = async (category) => {
  /* ... */
};

export const getConfig = async () => {
  return {
    render: 'dynamic',
  } as const;
};

Static paths (or other config values) can also be generated programmatically.

// ./src/pages/blog/[slug].tsx
import type { PageProps } from 'waku/router';

// Create blog article pages
export default async function BlogArticlePage({
  slug,
}: PageProps<'/blog/[slug]'>) {
  const data = await getData(slug);

  return <>{/* ...*/}</>;
}

const getData = async (slug) => {
  /* ... */
};

export const getConfig = async () => {
  const staticPaths = await getStaticPaths();

  return {
    render: 'static',
    staticPaths,
  } as const;
};

const getStaticPaths = async () => {
  /* ... */
};

Nested segment routes

Routes can contain multiple segments (e.g., /shop/[category]/[product]) by creating folders with brackets as well.

// ./src/pages/shop/[category]/[product].tsx
import type { PageProps } from 'waku/router';

// Create product category pages
export default async function ProductDetailPage({
  category,
  product,
}: PageProps<'/shop/[category]/[product]'>) {
  return <>{/* ...*/}</>;
}

export const getConfig = async () => {
  return {
    render: 'dynamic',
  } as const;
};

For static prerendering of nested segment routes, the staticPaths array is instead composed of ordered arrays.

// ./src/pages/shop/[category]/[product].tsx
import type { PageProps } from 'waku/router';

// Create product detail pages
export default async function ProductDetailPage({
  category,
  product,
}: PageProps<'/shop/[category]/[product]'>) {
  return <>{/* ...*/}</>;
}

export const getConfig = async () => {
  return {
    render: 'static',
    staticPaths: [
      ['same-category', 'some-product'],
      ['same-category', 'another-product'],
    ],
  } as const;
};

Catch-all routes

Catch-all or "wildcard" segment routes (e.g., /app/[...catchAll]) are marked with an ellipsis before the name and have indefinite segments.

Wildcard routes receive a prop with segment values as an ordered array. For example, the /app/profile/settings route would receive a catchAll prop with the value ['profile', 'settings']. These values can then be used to determine what to render in the component.

// ./src/pages/app/[...catchAll].tsx
import type { PageProps } from 'waku/router';

// Create dashboard page
export default async function DashboardPage({
  catchAll,
}: PageProps<'/app/[...catchAll]'>) {
  return <>{/* ...*/}</>;
}

export const getConfig = async () => {
  return {
    render: 'dynamic',
  } as const;
};

Group routes

Group routes allow you to organize routes into logical groups without affecting the URL structure. They're created by wrapping directory names in parentheses (e.g., (group)). This is particularly useful for sharing layouts across multiple routes while keeping the URL clean.

For example, you might want a home page at / that doesn't use a shared layout, but all other routes should share a common layout. This can be achieved by grouping those routes:

├── (main)
│ ├── _layout.tsx
│ ├── about.tsx
│ └── contact.tsx
└── index.tsx

In this structure, /about and /contact will use the layout from (main)/_layout.tsx, but / (from index.tsx) will not.

```tsx // ./src/pages/(main)/_layout.tsx import { Header } from '../../components/header'; import { Footer } from '../../componen

Extension points exported contracts — how you extend this code

GetRepoInfo (Interface)
* this is a part of the response type for github "Get a repository" API * @see {@link https://docs.github.com/en/rest/r
packages/create-waku/src/helpers/example-option.ts
ImportMeta (Interface)
(no doc)
packages/waku/src/config.ts
SmartQuotesInstance (Interface)
(no doc)
packages/website/src/smartquotes.d.ts
RouteConfig (Interface)
(no doc)
e2e/fixtures/create-pages/src/waku.server.tsx
Config (Interface)
(no doc)
packages/waku/src/config.ts
CreatePagesConfig (Interface)
(no doc)
e2e/fixtures/create-pages/src/waku.server.tsx
Reference (Interface)
(no doc)
packages/waku/src/lib/react-types.d.ts
SearchCodecsConfig (Interface)
(no doc)
e2e/fixtures/create-pages/src/lib/search.ts

Core symbols most depended-on inside this repo

createPage
called by 154
packages/waku/src/router/create-pages.tsx
waitForHydration
called by 137
e2e/utils.ts
createPages
called by 76
packages/waku/src/main.ts
getPathMapping
called by 58
packages/waku/src/lib/utils/path.ts
startApp
called by 54
e2e/utils.ts
stopApp
called by 52
e2e/utils.ts
parsePathWithSlug
called by 40
packages/waku/src/lib/utils/path.ts
prepareNormalSetup
called by 39
e2e/utils.ts

Shape

Function 1,204
Interface 21
Method 8
Class 4

Languages

TypeScript100%

Modules by API surface

packages/waku/src/router/client.tsx64 symbols
packages/waku/src/router/create-pages.tsx52 symbols
packages/waku/src/router/define-router.tsx50 symbols
packages/waku/tests/router-client.test.tsx31 symbols
packages/waku/src/minimal/client.tsx27 symbols
packages/waku/src/lib/utils/stream.ts17 symbols
packages/waku/src/lib/utils/path.ts16 symbols
e2e/utils.ts16 symbols
packages/waku/src/lib/vite-plugins/rsc-devtools.ts15 symbols
packages/waku/src/lib/vite-plugins/allow-server.ts14 symbols
packages/waku/src/lib/vite-plugins/fs-router-typegen.ts13 symbols
packages/waku/src/lib/utils/react-debug-channel.ts9 symbols

Dependencies from manifests, versioned

@actions/core3.0.1 · 1×
@ai-sdk/rsc3.0.0 · 1×
@clack/prompts1.6.0 · 1×
@cloudflare/vite-plugin1.42.3 · 1×
@eslint/js9.39.4 · 1×
@fontsource/inter5.2.8 · 1×
@hono/node-server2.0.6 · 1×
@playwright/test1.59.1 · 1×
@rolldown/plugin-babel0.2.3 · 1×
@sindresorhus/slugify3.0.0 · 1×
@stylexjs/stylex0.19.0 · 1×
@stylexjs/unplugin0.19.0 · 1×

For agents

$ claude mcp add waku \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact