How to Split and Compose States

Valtio's proxy-based nature makes it incredibly flexible for organizing state. You can easily split a large state into smaller, manageable pieces or compose multiple smaller states into a larger one.

Splitting a Large State

When you create a state with nested objects, each nested object is also a proxy. You can extract these nested proxies and use or subscribe to them independently.

import { proxy, useSnapshot } from 'valtio';

const state = proxy({
  user: { name: 'John', email: 'john@example.com' },
  settings: { theme: 'dark', notifications: true },
});

// Extract nested proxies
const userState = state.user;
const settingsState = state.settings;

// You can now use these smaller proxies directly
function UserProfile() {
  // This component will only re-render when userState changes
  const userSnap = useSnapshot(userState);
  return <div>{userSnap.name}</div>;
}

function ThemeToggle() {
  // This component only re-renders when settingsState changes
  const settingsSnap = useSnapshot(settingsState);
  // ...
}

Any mutation to userState will also be reflected in the main state object, and vice versa.

Combining Multiple States

You can also do the reverse: create multiple small, independent state proxies and combine them into a single, larger state object.

import { proxy } from 'valtio';

const userState = proxy({ name: 'John', email: 'john@example.com' });
const settingsState = proxy({ theme: 'dark', notifications: true });

const state = proxy({
  user: userState,
  settings: settingsState,
});

This composed state behaves identically to the one in the first example. This pattern is powerful for organizing your application's state into logical domains or modules.

Circular State Structures

While less common, Valtio can handle circular references within its state, as proxies can refer to themselves or each other.

const state = proxy({
  obj: { foo: 3 },
});

// Create a circular reference
state.obj.bar = state.obj; // 🤯

// Mutations still work as expected
state.obj.bar.foo = 4;
console.log(state.obj.foo); // 4