Quick Start

Build your first What app in under 5 minutes. You'll learn the core concepts by building a simple counter.

Create a New Project

The fastest way to get started is with our CLI:

Terminal
npm create what@latest my-app
cd my-app
npm install
npm run dev

Open http://localhost:5173 and you'll see your app running.

Project Structure

Your new project looks like this:

my-app/
├── src/
│   ├── main.jsx        # App entry
│   └── styles.css      # App styles
├── public/             # Optional static assets
├── index.html
├── vite.config.js
└── package.json

Your First Component

Let's build a counter. Open src/main.jsx:

src/main.jsx
import { mount, useSignal } from 'what-framework';

function App() {
  const count = useSignal(0);

  return (
    <div>
      <h1>Counter: {count()}</h1>
      <button onClick={() => count.set(c => c + 1)}>
        Increment
      </button>
    </div>
  );
}

mount(<App />, '#app');

Save the file. The counter now works. Let's break down what's happening:

1

useSignal(0)

Creates a reactive value starting at 0. Unlike React's useState tuple, this returns a signal getter/setter pair in one primitive.

2

{count()} in JSX

Reading the signal in JSX subscribes that text node. When the value changes, only that node updates, not the whole component.

3

count.set(c => c + 1)

Updates the signal. The function receives the current value and returns the new one. The DOM updates immediately.

No Re-renders

Unlike React, What doesn't re-run your component function when state changes. It updates only the specific DOM nodes that depend on changed signals. This is called fine-grained reactivity.

Adding Computed Values

Let's add a derived value that updates automatically:

src/main.jsx
import { mount, useSignal, useComputed } from 'what-framework';

function App() {
  const count = useSignal(0);
  const doubled = useComputed(() => count() * 2);

  return (
    <div>
      <h1>Count: {count()}</h1>
      <p>Doubled: {doubled()}</p>
      <button onClick={() => count.set(c => c + 1)}>
        Increment
      </button>
    </div>
  );
}

mount(<App />, '#app');

useComputed() creates a derived signal. It:

  • Tracks which signals it reads (count in this case)
  • Caches its value until dependencies change
  • Updates automatically when count changes

Why not just const doubled = count() * 2?

In React, that works because the entire component function re-runs on every state change. In What, the component function runs once — so count() * 2 evaluates to a plain number at creation time and never updates. useComputed creates a reactive derivation that stays in sync.

For simple one-off expressions in JSX, the compiler handles it: <p>{count() * 2}</p> works directly. Use useComputed when you need the derived value in multiple places or want to pass it to child components.

Running Side Effects

Need to do something when a signal changes? Use effect():

src/main.jsx
import { mount, useSignal, effect } from 'what-framework';

function App() {
  const count = useSignal(0);

  // Runs immediately, then re-runs when count changes
  effect(() => {
    document.title = `Count: ${count()}`;
  });

  return (
    <div>
      <p>{count()}</p>
      <button onClick={() => count.set(c => c + 1)}>+1</button>
    </div>
  );
}

Effects auto-track their dependencies — no dependency array needed. Any signal read inside the effect becomes a dependency automatically.

Next Steps

You've learned the basics. Here's where to go next: