Core Concepts

Valtio's design is centered around a few simple but powerful concepts. Understanding them will help you use the library effectively.

1. The Proxy State

The foundation of Valtio is the proxy function. You create a state by wrapping a plain JavaScript object with it.

import { proxy } from 'valtio';

const state = proxy({ 
  count: 0,
  user: { name: 'John' },
  items: ['A', 'B']
});

This state object looks and feels just like a regular JavaScript object, but it's now a special proxy that tracks any mutations made to it or its nested properties.

2. Direct Mutation

With Valtio, you don't need setters, reducers, or special update functions. You change the state by directly mutating the proxy object.

// From anywhere in your application
state.count++;
state.user.name = 'Jane';
state.items.push('C');

This is possible because the proxy intercepts these operations and notifies Valtio that a change has occurred.

3. The Snapshot for React

While you can mutate the proxy state from anywhere, you should never use the mutable proxy directly for rendering in React. Doing so can lead to unexpected behavior and break React's rendering model.

Instead, you use the useSnapshot hook to get an immutable, read-only version of the state.

import { useSnapshot } from 'valtio';

function UserDisplay() {
  const snap = useSnapshot(state);

  // `snap` is an immutable snapshot. It's safe to use for rendering.
  return <div>User: {snap.user.name}</div>;
}

This leads to the core rule of thumb in Valtio:

Read from snapshots, mutate the source.

  • In your component's render logic, always read from the snap object returned by useSnapshot.
  • In callbacks and event handlers, always mutate the original state proxy.
function Counter() {
  const snap = useSnapshot(state); // READ from snapshot

  const increment = () => {
    ++state.count; // MUTATE the source proxy
  };

  return (
    <div>
      Count: {snap.count}
      <button onClick={increment}>+1</button>
    </div>
  );
}

4. Automatic Render Optimization

Valtio is finely tuned for performance. When you use useSnapshot, Valtio tracks exactly which properties of the state your component accesses during rendering.

const state = proxy({ a: 1, b: 2 });

function ComponentA() {
  const snap = useSnapshot(state);
  return <div>A: {snap.a}</div>;
}

function ComponentB() {
  const snap = useSnapshot(state);
  return <div>B: {snap.b}</div>;
}

In this example:

  • If state.a changes, only ComponentA will re-render.
  • If state.b changes, only ComponentB will re-render.

This tracking is automatic and works for nested properties as well. It ensures that your components only update when absolutely necessary, without any need for manual memoization like React.memo or useMemo.