How to Migrate to v2 from v1

Valtio v2 introduces some key changes to align with modern React features (like the use hook) and to simplify its core behavior. This guide outlines the breaking changes and how to update your code.

Key Changes in v2

  1. Promise Handling: Valtio v1 handled promises internally, which is now deprecated in favor of React 19's official use hook. In v2, you must use the use hook to resolve promises from your state within React components.
  2. Impure proxy(): In v1, proxy(obj) returned a deep clone. In v2, proxy(obj) modifies obj in-place (making it impure). Reusing the initial object after passing it to proxy is now discouraged.
  3. useSnapshot() Behavior: The internal implementation of useSnapshot has been slightly altered to be more compatible with useMemo and the upcoming React Compiler. This may lead to extra re-renders in some rare edge cases but is generally more robust.
  4. Modernization: Deprecated features have been removed, React 18+ and TypeScript 4.5+ are now required, and the build target is ES2018.

Migration Steps

1. Update Promise Resolution with use Hook

Anywhere you were relying on Valtio to automatically resolve promises for rendering, you now need to wrap the promise-containing snapshot property with the use hook.

Before (v1):

import { proxy, useSnapshot } from 'valtio';

const state = proxy({ data: fetch(...).then(res => res.json()) });

const Component = () => {
  const snap = useSnapshot(state);
  // Valtio v1 resolved this automatically
  return <>{JSON.stringify(snap.data)}</>;
};

After (v2):

import { use } from 'react'; // From React 19 or 'react18-use' shim
import { proxy, useSnapshot } from 'valtio';

const state = proxy({ data: fetch(...).then(res => res.json()) });

const Component = () => {
  const snap = useSnapshot(state);
  // Explicitly use the `use` hook
  return <>{JSON.stringify(use(snap.data))}</>;
};

2. Handle the Impure proxy(obj)

The recommended practice has always been to not reuse the object passed to proxy. If you follow this practice, no changes are needed.

If you were reusing the initial object, you must now explicitly clone it before passing it to proxy or assigning it to a state property to maintain the v1 behavior.

Before (v1):

const initialObj = { text: 'hi' };
const state = proxy(initialObj); // `initialObj` was safe to reuse

After (v2):

import { proxy } from 'valtio';
import { deepClone } from 'valtio/utils';

const initialObj = { text: 'hi' };
const state = proxy(deepClone(initialObj)); // Explicitly clone the object

3. Address useLayoutEffect SSR Warning in React 18

If you use Valtio with Server-Side Rendering (SSR) in React 18, you may see warnings about useLayoutEffect. To prevent this, you can conditionally use the vanilla snapshot function on the server.

import { snapshot, useSnapshot as useSnapshotOrig } from 'valtio';

const isSSR = typeof window === 'undefined';
export const useSnapshot = isSSR ? (p) => snapshot(p) : useSnapshotOrig;

// Then, use this custom `useSnapshot` hook throughout your app.