Quick Start

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

Try it online

Want to see What Framework in action? Try the live playground to write and run code in your browser — no install required.

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, signal } from 'what-framework';

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

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

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

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

1

signal(0)

Creates a reactive value starting at 0. Unlike React's useState tuple, this returns one function: call it to read, call it with a value to write. signal() works anywhere — module scope, components, or stores.

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(c => c + 1)

Updates the signal — call it with the new value, or a function that receives the current value and returns the next. count.set(c => c + 1) is an equivalent explicit form. 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, signal, computed } from 'what-framework';

function App() {
  const count = signal(0);
  const doubled = computed(() => count() * 2);

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

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

computed() 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. computed 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 computed 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, signal, effect } from 'what-framework';

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

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

  return (
    <div>
      <p>{count()}</p>
      <button onClick={() => count(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: