SolidJSとQwikの違い - Fine-grained ReactivityとResumability

中級 | 8分 で読める | 2026.04.19

公式ドキュメント

SolidJSとQwikは、どちらもcomponentとJSXを使えるfrontend frameworkですが、解決しようとする中心課題が異なります。SolidJSはstateの依存関係を細かく追跡して必要な箇所を更新し、Qwikはserverで作った状態をserializeしてbrowserでresumeする設計を採ります。

比較の前提

記事情報: 2026年4月19日初出。SolidJSとQwikの公式資料を2026年7月25日に再確認しています。

この記事では条件不明のLighthouse値、bundle size、GitHub Stars、npm download数を優劣の根拠にしません。性能はapplication構成、SSR、hosting、network、third-party scriptによって変わります。

まず、2つの考え方を分けます。

  • Fine-grained reactivity: どのstateをどの計算・DOMが読んだかを記録し、変更時に依存先だけを更新する
  • Resumability: serverで得たcomponent境界、listener、stateをHTMLへserializeし、browserで同じ処理を最初から再実行せず再開する

SolidJSは主に更新時のreactivity、QwikはSSRからclientへ渡す起動モデルに特徴があります。「どちらが速いか」ではなく、どの段階のcostと制約を受け入れるかで判断します。

SolidJSは状態に依存する部分を更新し、Qwikはサーバーの情報を引き継いで必要なコードから処理を再開するという、着目する仕組みの違いを示す図

SolidJSのFine-grained Reactivity

SolidJSではcreateSignalがgetterとsetterを返します。tracking scope内でgetterを読むと依存関係が登録され、値が変わったときにsubscriberが再評価されます。

import { createMemo, createSignal } from "solid-js";

function Counter() {
  const [count, setCount] = createSignal(0);
  const doubled = createMemo(() => count() * 2);

  return (
    <section>
      <p>count: {count()}</p>
      <p>doubled: {doubled()}</p>
      <button onClick={() => setCount((value) => value + 1)}>
        increment
      </button>
    </section>
  );
}

count()をJSX内で読む部分とcreateMemoがsubscriberになります。更新時にcomponent function全体を再実行することを基本にせず、依存する式・DOMを更新します。

Reactに似たJSXでも実行モデルが違う

Solid componentは基本的に初期化時に一度実行されます。component function内でsignalを通常の変数として読み出して保存すると、その値は自動では更新されません。

function Example() {
  const [count, setCount] = createSignal(0);
  const snapshot = count();

  return (
    <>
      <p>snapshot: {snapshot}</p>
      <p>reactive: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>update</button>
    </>
  );
}

React Hookの経験だけで同じmental modelを当てはめると、propsのdestructuringやsignalの読み取り位置でreactivityを失うことがあります。Solidのtracking scope、memo、effect、storeを理解する必要があります。

effectの役割

createEffectはsignalの変更を外部処理へ同期するときに使います。派生値を作るだけならcreateMemoや単純なderived functionを優先し、effect内で別stateを書き換える連鎖を増やさないほうが依存を追いやすくなります。

createEffect(() => {
  document.title = `Count: ${count()}`;
});

SSR、routing、data loadingを含むapplicationではSolidStartなど周辺frameworkの仕様も確認します。Solid coreのreactivityだけでdeploymentやserver renderingが完結するわけではありません。

QwikのResumability

一般的なSSR applicationは、serverがHTMLを返した後、browser側でcomponent treeを再構築しevent listenerを結び直すhydrationを行います。Qwikはserver render時にcomponent境界、listener、application stateをHTMLへserializeし、必要になったcodeを後から読み込んで処理をresumeします。

import { component$, useSignal } from "@builder.io/qwik";

export default component$(() => {
  const count = useSignal(0);

  return (
    <button onClick$={() => count.value++}>
      count: {count.value}
    </button>
  );
});

component$onClick$$は、optimizerがlazy-load可能な境界を作るための手がかりです。通常のclosureをどこでも自由に渡すmodelとは異なり、codeとstateを分割・serializeできる形で書く必要があります。

Serializationの制約

Qwikのresumabilityでは、serverからbrowserへ引き継ぐstateをserializeできることが重要です。Date、URL、Map、Setなど対応する型はありますが、任意のclass instanceやstreamはそのままserializeできません。

browser専用libraryのinstanceなどにはnoSerialize()を使えます。ただし、その値はserverからclientへのserializationを越えて保持されず、client側で再初期化する必要があります。

import {
  component$,
  noSerialize,
  useSignal,
  useVisibleTask$,
  type NoSerialize,
} from "@builder.io/qwik";

export default component$(() => {
  const widget = useSignal<NoSerialize<{ destroy(): void }>>();

  useVisibleTask$(() => {
    widget.value = noSerialize(createBrowserWidget());

    return () => widget.value?.destroy();
  });

  return <div id="widget" />;
});

useVisibleTask$はclientで実行されるため、増やしすぎるとresumabilityの利点を弱める可能性があります。browser-only APIが多いapplicationでは、どの処理をserverでserializeし、どれをclientで初期化するかを設計します。

更新モデルと起動モデルの違い

観点SolidJSQwik
中心概念signal依存の細粒度更新server状態からのresume
stategetter/setterとtracking scopesignal/storeとserialize可能性
event通常のhandler$境界でlazy-load可能にする
SSR後選んだrenderer/frameworkのhydration方式serialized情報からresume
主な注意signalを読む場所とtrackingclosure・stateのserialization

両者にはsignalがあり、JSXも似ていますが、codeをそのまま移植できるとは限りません。Solidではreactive graphを正しく作ること、Qwikではlazy境界とserializationを保つことが重要です。

どちらを検討するか

SolidJSは、client側で更新頻度の高いUIを作り、signalによる明示的な依存追跡を採用したい場合に検討できます。表計算的なUI、dashboard、editorなどでも候補になりますが、実際のdata量で測定します。

Qwikは、SSRで初期HTMLを返し、最初に実行するJavaScriptを遅らせたいcontent中心・commerce系のapplicationで設計が合う可能性があります。一方、serializeできないbrowser objectを多数扱う高度なeditorやvisualizationでは、client専用初期化が増える影響を確認します。

既存のReactやVueを置き換える理由がなければ、移行しない判断も妥当です。component library、accessibility、test tool、monitoring、採用・学習cost、hosting adapterまで含めると、runtimeの特性だけで結論は出ません。

検証用の小さな課題

Todo appだけではarchitectureの差が見えにくいため、実際の要件に近い画面を1つ作ります。

  1. SSRした一覧とfilter UIを作る
  2. form送信とvalidation errorを実装する
  3. third-party browser libraryを1つ接続する
  4. route遷移とdata再取得を確認する
  5. slow networkで最初の操作を記録する
  6. build outputとsource map、error monitoringを確認する

Solidでは不要なeffectやreactivityを失った値がないか、Qwikではserialization warningや過剰なclient taskがないかをreviewします。Lighthouseの1回の点数ではなく、実機の複数回測定とuser interactionを確認してください。

Version表記への注意

この記事の旧版は「SolidJS 2.0 / Qwik 2.0」を前提にしていましたが、将来versionの機能を確定事項として扱うのは適切ではありません。導入時にはpackageのstable release、migration guide、使用するmetaframeworkとadapterの対応版を公式情報で確認してください。

architectureの考え方はversionを越えて役立ちますが、API名やrelease状態は変わります。本記事は特定の未公開major versionの予告ではなく、現在の公式documentで確認できる設計差を説明しています。

参考リソース

← 一覧に戻る
PR
PR
PR
PR