SolidJS 2.0 ships with a native async-data model that lets a component treat a promise like any other reactive value, and it does so without the cascade of re-renders that React developers see with Suspense and hooks. The change matters because it keeps UI on screen while data refreshes, cuts boilerplate, and offers a concrete path for React teams to move their data-fetching code over to Solid.
Why Solid’s async model feels different
In React, a component that needs data usually calls a hook (often a custom one) that returns a promise-like object, then wraps the UI in <Suspense> to show a fallback while the promise resolves. Every time the promise settles, React schedules a re-render; if the same component later refetches, the fallback can flash over content that is already visible.
Solid flips the script. A promise is simply a value that the reactive graph watches. When a computation reads that value, the graph pauses that read until the promise resolves, but the rest of the UI stays rendered. The first time a component mounts, a <Loading> boundary can display a fallback; after that, a refetch leaves the existing DOM untouched until the new data arrives. No explicit “await” inside the component, no createResource, no manual state toggles.
The core primitives
<Loading>boundary – Replaces React’s<Suspense>. It shows a fallback only on the initial load. After the first paint, a pending read does not replace the UI; the old content remains until the fresh value resolves. Pass anonprop to force a spinner on a specific refetch.<Reveal>component – Controls how multiple<Loading>boundaries appear together. Choose:- Sequential: boundaries reveal one after another in DOM order.
- Together: all reveal at once when every piece of data is ready.
- Natural: each reveals as soon as its own data lands.
isPendingsignal – Returnstruewhile a particular read is in flight. Use it to overlay a thin loading bar or subtle animation while the main content stays visible, giving you “stale-while-revalidate” for free.actiongenerator – Replaces React’s mutable-state updates. Anaction(function*…)can optimistically update the UI,yieldto wait for a server response, then reconcile the result when the promise resolves. The UI feels instant, and the reconciliation step is baked into the primitive.
How the pieces map to React concepts
| Feature | React approach | Solid 2.0 approach |
|---|---|---|
| Data fetching | use() (experimental) or third-party hooks; result wrapped in <Suspense> |
createMemo (or similar) that returns a promise; read directly in JSX |
| Loading UI | <Suspense> may re-trigger fallback on every refetch |
<Loading> only on first load; refetch keeps old UI |
| Refreshing | useTransition to defer UI updates |
isPending signals pending reads without UI swap |
| Mutations | useState/useReducer + async calls, often wrapped in custom actions |
action(function*…) with built-in optimistic handling |
The practical upshot is that Solid builds what React treats as separate hooks into the core reactivity engine.
A step-by-step migration guide
- Identify the data source – In React you likely have
const data = useMyFetch(url). In Solid replace that with a memo that returns the promise:const data = createMemo(() => fetch(url).then(r => r.json())). - Wrap the top-level component – If the component renders before the promise resolves, surround it with
<Loading fallback={<Spinner/>}>…</Loading>. The fallback appears only on the first mount. - Replace per-refetch spinners – Where you previously toggled a loading flag, now read
isPending(data). Use that boolean to render a subtle indicator while the existing UI stays on screen. - Convert optimistic updates – If you had
setState(prev => ({...prev, optimisticValue}))followed by an async call, rewrite asconst update = action(function* (newValue) { state = newValue; const server = yield fetch(...); state = reconcile(server); });. The generator yields control until the server responds, then automatically updates the reactive graph. - Handle multiple async pieces – Nest
<Loading>boundaries as needed, then add a<Reveal>wrapper to decide whether they appear together or one after another. This replaces patterns where React developers staggered multiple<Suspense>components with complex state checks. - Test the flow – Because Solid does not re-render on promise resolution, verify that UI updates still occur where you expect them. The reactive graph propagates changes automatically; there is no need for extra
useEffectcalls.
What’s still in flux
Solid 2.0’s async API is currently in beta. Names like <Loading> and action may shift before the stable release, and the documentation still evolves. The core idea—treating promises as reactive values—remains unchanged, but early adopters should expect minor breaking changes as the library settles.
Who stands to gain
- React teams with heavy data fetching – The reduced need for external state libraries and the built-in stale-while-revalidate pattern can shrink bundle size and simplify codebases.
- Performance-focused apps – By avoiding full component re-renders on every fetch, Solid delivers smoother visual updates, especially on low-end devices.
- Developers tired of “flash of spinner” – The
<Loading>boundary’s “first-load-only” behavior eliminates the common annoyance of a spinner flashing over content that is already visible.
Possible drawbacks
- Beta status – Until the API stabilizes, long-term projects may need to budget for migration effort later.
What to watch next
Keep an eye on the official changelog for any renaming of <Loading> or action.
Bottom line: SolidJS 2.0 lets you treat promises as first-class reactive values, keeping the UI steady while data refreshes and removing the need for a suite of React-style hooks. For teams ready to move beyond the re-render-centric model, the migration path is clear, the performance upside is tangible, and the only real risk is the usual beta-stage uncertainty.
