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 an on prop 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.
  • isPending signal – Returns true while 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.
  • action generator – Replaces React’s mutable-state updates. An action(function*…) can optimistically update the UI, yield to 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

  1. Определите источник данных – В React у вас, скорее всего, есть const data = useMyFetch(url). В Solid замените это на memo, которое возвращает промис: const data = createMemo(() => fetch(url).then(r => r.json())).
  2. Оберните компонент верхнего уровня – Если компонент рендерится до того, как промис разрешится, оберните его в <Loading fallback={<Spinner/>}>…</Loading>. Fallback отображается только при первом монтировании.
  3. Замените спиннеры при каждом повторном запросе – Там, где вы раньше переключали флаг загрузки, теперь считывайте isPending(data). Используйте это булево значение для отображения ненавязчивого индикатора, пока текущий интерфейс остается на экране.
  4. Преобразуйте оптимистичные обновления – Если у вас было setState(prev => ({...prev, optimisticValue})), за которым следовал асинхронный вызов, перепишите это как const update = action(function* (newValue) { state = newValue; const server = yield fetch(...); state = reconcile(server); });. Генератор передает управление до ответа сервера, а затем автоматически обновляет реактивный граф.
  5. Обработка нескольких асинхронных частей – Вкладывайте границы <Loading> по мере необходимости, а затем добавьте обертку <Reveal>, чтобы решить, должны ли они появляться вместе или по очереди. Это заменяет паттерны, в которых разработчики React использовали несколько компонентов <Suspense> с разным временем появления через сложные проверки состояния.
  6. Протестируйте процесс – Поскольку Solid не выполняет рендеринг при разрешении промиса, убедитесь, что обновления UI происходят там, где вы ожидаете. Реактивный граф распространяет изменения автоматически; нет необходимости в дополнительных вызовах useEffect.

Что всё еще находится в стадии разработки

Async API в Solid 2.0 сейчас находится в бета-версии. Названия вроде <Loading> и action могут измениться до стабильного релиза, а документация всё еще дополняется. Основная идея — отношение к промисам как к реактивным значениям — остается неизменной, но ранним последователям стоит ожидать небольших ломающих изменений по мере стабилизации библиотеки.

Кому это принесет пользу

  • Команды React с интенсивной загрузкой данных – Снижение потребности во внешних библиотеках управления состоянием и встроенный паттерн stale-while-revalidate могут уменьшить размер бандла и упростить кодовую базу.
  • Приложения, ориентированные на производительность – Избегая полного рендеринга компонентов при каждом запросе, Solid обеспечивает более плавные визуальные обновления, особенно на слабых устройствах.
  • Разработчики, уставшие от «вспышек спиннера» – Поведение границы <Loading> «только при первой загрузке» устраняет распространенную проблему, когда спиннер мелькает поверх уже видимого контента.

Возможные недостатки

  • Статус бета-версии – Пока API не стабилизируется, долгосрочным проектам, возможно, придется закладывать ресурсы на последующую миграцию.

За чем следить дальше

Следите за официальным журналом изменений (changelog) на предмет переименования <Loading> или action.

Итог: SolidJS 2.0 позволяет относиться к промисам как к полноценным реактивным значениям, сохраняя стабильность UI во время обновления данных и избавляя от необходимости использовать набор хуков в стиле React. Для команд, готовых выйти за рамки модели, ориентированной на рендеринг, путь миграции ясен, выигрыш в производительности ощутим, а единственный реальный риск — привычная неопределенность стадии бета-тестирования.