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:
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:
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:
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.
{count()} in JSX
Reading the signal in JSX subscribes that text node. When the value changes, only that node updates, not the whole component.
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:
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 (
countin this case) - Caches its value until dependencies change
- Updates automatically when
countchanges
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():
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: