solid-js-best-practices について
solid-js-best-practicesは、Solid.jsアプリのAI支援による実装、レビュー、リファクタリング向けのルール集です。テーマや作業別に整理され、例を適用する前にプロジェクトのSolid.jsバージョンを確認します。
主な機能
- 01バージョンに応じたルール選択
- 02リアクティビティとコンポーネントの指針
- 03状態、DOM、パフォーマンスのルール
- 04アクセシビリティとテストの指針
- 05Web Component統合ルール
活用シーン
- 01Solid.jsコンポーネントの作成
- 02Solid.jsコードのレビュー
- 03アプリ状態とリアクティビティのリファクタリング
- 04Solid.jsへのカスタム要素の統合
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ richardcarls/ solid-js-best-practices --skill solid-js-best-practicesインストール後、Claude Code で/solid-js-best-practicesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSE.gitignoreAGENTS.mdREADME.mdCHANGELOG.mddocs/version-support-plan.mdrules/1-1-use-signals-correctly.md1-4-avoid-signal-in-effect.md1-2-use-memo-for-derived.md1-3-effects-for-side-effects.md1-6-batch-signal-updates.md2-2-use-merge-props.md1-5-use-untrack-when-needed.md2-1-never-destructure-props.md1-7-no-primitives-in-reactive-contexts.md2-10-custom-element-typescript-declarations.md2-3-use-split-props.md2-5-component-composition.md2-4-use-children-helper.md2-6-components-return-once.md3-1-use-show-for-conditionals.md2-8-style-prop-conventions.md2-9-never-call-components-as-functions.md2-7-no-react-specific-props.md3-3-use-index-for-primitives.md3-6-stable-component-mount.md3-5-provide-fallbacks.md3-7-use-keyed-for-stateful-children.md3-2-use-for-for-lists.md3-4-use-switch-match.md詳細
Solid.js のベストプラクティス
AI 支援によるコード生成、レビュー、リファクタリングに最適化された、Solid.js アプリケーションとコンポーネント構築のための包括的なベストプラクティスです。
バージョンに応じたルール
コード例を適用する前に、プロジェクトのマニフェストとロックファイルで solid-js のバージョンを確認してください。メジャーバージョンが不明な場合は、プロジェクトで使用しているメジャーバージョンを確認してください。既存のルールファイルは Solid 1.x に基づいています。version_basis にはルールの基になったバージョンが記録されていますが、その原則をそのメジャーバージョンに限定するものではありません。現在の対応する API を確認せずに、1.x の API 例を 2.x のプロジェクトにコピーしないでください。Solid 2.x への対応は、公式リリースまで保留されています。プレリリース版の 2.x を使うプロジェクトでは、公式の 1.x 移行ガイドとインストール済みのリリース候補版のドキュメントを参照してください。これらの 1.x 向けルールを、検証済みの 2.x 向けガイダンスとして扱わないでください。
カテゴリ別のルール
1. リアクティビティ(7 ルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 1-1 | シグナルを正しく使う | 最重要 | シグナルは count ではなく、必ず関数として count() のように呼び出します |
| 1-2 | 派生値にはメモを使う | 高 | 計算値には createEffect ではなく createMemo を使います |
| 1-3 | エフェクトは副作用にのみ使う | 高 | 派生処理には使わず、副作用にのみ createEffect を使います |
| 1-7 | リアクティブなコンテキスト内でプリミティブを使わない | 高 | エフェクトやメモの内部でフックを呼び出したり、リアクティブプリミティブを作成したりしないでください |
| 1-4 | エフェクト内でのシグナル設定を避ける | 中 | エフェクト内でシグナルを設定すると、無限ループが発生する可能性があります |
| 1-5 | 必要に応じて Untrack を使う | 中 | 意図しないリアクティブな購読を防ぐには untrack() を使います |
| 1-6 | シグナルの更新をまとめる | 低 | 複数の同期的なシグナル更新には batch() を使います |
2. コンポーネント(10 ルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 2-1 | props を分割代入しない | 最重要 | props を分割代入するとリアクティビティが失われます |
| 2-6 | コンポーネントは一度だけ return する | 最重要 | 早期 return は使わず、JSX 内で <Show> や <Switch> などを使います |
| 2-9 | コンポーネントを関数として呼び出さない | 最重要 | 必ず JSX または createComponent() を使います。直接呼び出すとリアクティブスコープが漏れます |
| 2-2 | mergeProps を使う | 高 | props のデフォルト値には mergeProps を使います |
| 2-3 | splitProps を使う | 高 | props のグループを安全に分けるには splitProps を使います |
| 2-7 | React 固有の props を使わない | 高 | className ではなく class を、htmlFor ではなく for を使います |
| 2-10 | カスタム要素の TypeScript 宣言 | 高 | JSX 名前空間でカスタム要素のタグを宣言し、新しい属性については DOM 型を拡張します |
| 2-4 | children ヘルパーを使う | 中 | children に安全にアクセスするには children() ヘルパーを使います |
| 2-5 | コンポジションを優先する | 中 | props のバケツリレーより、コンポジションとコンテキストを優先します |
| 2-8 | style prop の規約 | 中 | style にはケバブケースのプロパティを使ったオブジェクト構文を使用します |
3. 制御フロー(7 ルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 3-1 | 条件分岐には Show を使用する | HIGH | 条件付きレンダリングには <Show> を使用し、呼び出し元で絞り込まれた値が必要な場合は関数の子を使用します |
| 3-2 | リストには For を使用する | HIGH | 参照キーに基づくリストのレンダリングには <For> を使用します |
| 3-7 | 状態を持つ子には keyed を使用する | HIGH | 子が内部状態を持ち、真偽値だけでなく値の同一性が重要な場合は、keyed を追加します |
| 3-3 | プリミティブには Index を使用する | MEDIUM | 同一性より配列のインデックスが重要な場合は、<Index> を使用します |
| 3-4 | Switch/Match を使用する | MEDIUM | 複数の条件には <Switch>/<Match> を使用します。絞り込まれたアクセサーは、対応する分岐が無効になると例外をスローします |
| 3-6 | コンポーネントのマウントを安定させる | MEDIUM | 同じコンポーネントを複数の Switch/Show 分岐でレンダリングすることは避けます |
| 3-5 | フォールバックを指定する | LOW | ローディング状態には、必ず fallback プロパティを指定します |
4. 状態管理(7 ルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 4-1 | Signals と Stores | HIGH | プリミティブにはシグナルを、ネストされたオブジェクトにはストアを使用します |
| 4-2 | Store のパス構文を使用する | HIGH | 細かく効率的なストアの更新にはパス構文を使用します |
| 4-3 | ミューテーションには produce を使用する | MEDIUM | 複雑な、ミュータブルな形式のストア更新には produce を使用します |
| 4-4 | サーバーデータには reconcile を使用する | MEDIUM | サーバーまたは外部のデータを統合する場合は reconcile を使用します |
| 4-5 | グローバル状態には Context を使用する | MEDIUM | コンポーネント間で共有する状態には Context API を使用します |
| 4-6 | ラッパーを使って関数をストアに保存する | HIGH | setStore が関数値をアップデーター関数として呼び出さないよう、関数値をラップします |
| 4-7 | ページの所有境界でクリーンアップする | HIGH | 複数のルーティングされたペインがマウントされたままの場合は、ページごとにクリーンアップします。共有コンテキストを分割する際は、すべてのコンテキストフィールドを確認します |
5. 参照と DOM(10 ルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 5-1 | 参照を正しく使う | 高 | 条件付き要素にはコールバック参照を使います |
| 5-2 | onMount で DOM にアクセスする | 高 | レンダリング中ではなく、onMount で DOM 要素にアクセスします |
| 5-3 | onCleanup でクリーンアップする | 高 | サブスクリプションとタイマーは必ずクリーンアップします |
| 5-5 | innerHTML を避ける | 高 | XSS を防ぐため innerHTML を避け、JSX または textContent を使います |
| 5-7 | Web コンポーネントの制御状態 | 高 | モダンなカスタム要素では prop:* プロパティと on:wc-* イベントを使い、ネイティブまたはレガシー API には参照/エフェクトを使います |
| 5-8 | シグナルの変更を View Transition でラップする | 高 | ブラウザーが新しいリアクティブ状態を適用する前に古い DOM をキャプチャできるよう、startViewTransition() の内側で Solid のセッターを呼び出します |
| 5-9 | 双方向スクロール同期 | 高 | 確定したスクロール位置のみを通知し、自身のエコーによる再スクロールはスキップします |
| 5-10 | progress/meter の値がない場合への対応 | 高 | value は常にプロパティであり、undefined ではエラーになるため、attr:value を使います |
| 5-4 | ディレクティブを使う | 中 | 再利用可能な要素の動作には use: ディレクティブを使います |
| 5-6 | イベントハンドラーのパターン | 中 | on:/oncapture: 名前空間と配列ハンドラー構文を正しく使います |
6. パフォーマンス(6 ルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 6-1 | 不要なトラッキングを避ける | 高 | リアクティブコンテキストの外でシグナルにアクセスしないでください |
| 6-2 | 遅延コンポーネントを使う | 中 | 大きなコンポーネントのコード分割には lazy() を使います |
| 6-3 | Suspense を使う | 中 | 非同期読み込みの境界には <Suspense> を使います |
| 6-6 | Web コンポーネントの CSS とバンドル戦略 | 中 | コンポーネントを個別にインポートし、::part() のオーバーライドはグローバルスタイルシートに配置します |
| 6-4 | ストアへのアクセスを最適化する | 低 | 必要なストアのプロパティのみを参照します |
| 6-5 | classList を優先する | 低 | 条件付きのクラス切り替えには classList プロパティを使います |
7. アクセシビリティ(4 ルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 7-1 | セマンティック HTML を使う | 高 | 適切なセマンティック HTML 要素を使います |
| 7-2 | ARIA 属性を使う | 中 | カスタムコントロールには適切な ARIA 属性を適用します |
| 7-3 | キーボード操作に対応する | 中 | すべてのインタラクティブ要素をキーボードで操作できるようにします |
| 7-4 | ルータールートリンクに end マッチを設定する | 高 | ルートリンクがすべてのルートで現在のリンクとして扱われないよう、end マッチを追加します |
8. テスト(12 件のルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 8-1 | Solid 用に Vitest を設定する | CRITICAL | Solid 固有の解決条件とプラグインを使って Vitest を設定します |
| 8-2 | render をアロー関数でラップする | CRITICAL | 常に render(() => <C />) を使用し、render(<C />) は使用しません |
| 8-3 | ルート内でプリミティブをテストする | HIGH | シグナル/エフェクト/メモのテストを createRoot または renderHook でラップします |
| 8-4 | テストで非同期処理を扱う | HIGH | 非同期の動作には findBy クエリと適切なタイマー設定を使用します |
| 8-5 | アクセシブルなクエリを使用する | MEDIUM | テスト ID よりもロールやラベルによるクエリを優先します |
| 8-6 | ロジックのテストと UI のテストを分離する | MEDIUM | プリミティブ/フックは、コンポーネントのレンダリングとは独立してテストします |
| 8-7 | ブラウザー依存の動作にはブラウザーモードを使用する | HIGH | jsdom では忠実に再現できないレイアウト、フォーカス、ネイティブ API、カスタム要素の動作には、実際のブラウザーを使用します |
| 8-8 | 標準的でない ARIA を使うヘッドレス UI ライブラリのテスト | MEDIUM | ヘッドレス UI ライブラリは分かりにくい ARIA 構造やポータルを使用するため、クエリを実行する前に実際のツリーを確認します |
| 8-9 | ブラウザーのネイティブ API テストを分離する | HIGH | テスト間で IndexedDB と localStorage をクリアし、deleteDatabase の前に接続を閉じます |
| 8-10 | ルーターの統合テスト | HIGH | MemoryRouter の root プロパティを使って、レイアウトプロバイダーにルーターコンテキストを提供します |
| 8-11 | TanStack Query のテストを設定する | HIGH | テストごとに新しい QueryClient を作成し、リトライとキャッシュを無効にします |
| 8-12 | Structured Clone の前にプロキシを解除する | HIGH | IndexedDB にデータを書き込む前に、すべてのリアクティブプロキシを取り除きます |
9. Web コンポーネントの統合(8 件のルール)
| # | ルール | 優先度 | 説明 |
|---|---|---|---|
| 9-1 | アプリのエントリーポイントでカスタム要素を登録する | HIGH | SolidJS のリアクティブコンテキストが始まる前に /define の副作用をインポートします |
| 9-2 | スロットの割り当て後に slotchange を処理する | HIGH | slotchange はマイクロタスクで配信されます。後続のライフサイクルまたはレンダリングフェーズが必要な処理だけを遅延させます |
| 9-3 | カスタム要素と SolidJS のリアクティビティを切り離す | MEDIUM | 一方向のデータフロー(SolidJS -> 属性/プロパティ -> イベント -> SolidJS)を使用し、SolidJS のリアクティブコンテキストからカスタム要素の内部状態を読み取らないでください |
| 9-4 | Web コンポーネントのラッパーを薄く保つ | HIGH | ラッパーはラベル、レイアウト、型の変換、フォームとの連携を担当し、カスタム要素はタイミングとネイティブ同期を担当します |
| 9-5 | プロパティと属性のバインディング | HIGH | カスタム要素の動的な props はすでにプロパティです。意図を明示するには prop:* を、data-*、ID 参照 ARIA、CSS から見える属性には attr:* を使用します |
| 9-6 | カスタムフィールドをフォームライブラリに登録する | HIGH | プロパティバインドされたカスタムフィールドが、遅延読み込みされるフォームライブラリのレジストリに登録されるようにします |
| 9-7 | Web コンポーネントを多用するフォームの状態を Store に保持する | MEDIUM | カスタム要素がすでにフィールド操作を担っている場合は、Solid の Store を優先します |
| 9-8 | controlled のみの WC にラッパーシグナルを使用する | HIGH | WC に defaultValue がない場合は、ラッパーで createSignal を使い、非制御から制御への橋渡しを担います |
タスクベースのルール選択
新しいコンポーネントの作成
Solid.js コンポーネントを新しく作成する際は、以下のルールを読み込んでください。
| ルール | 理由 |
|---|---|
| 1-1 | シグナルを関数として呼び出すようにします |
| 2-1 | リアクティビティの破損を防ぎます |
| 2-6 | 早期リターンを避け、JSX 内で制御フローを使用します |
| 2-9 | コンポーネントを通常の関数として呼び出しません |
| 2-2 | デフォルトの props を正しく処理します |
| 2-3 | ローカルの props と転送する props を分けます |
| 3-1 | 条件付きレンダリングを適切に行います |
| 3-7 | フォームや状態を持つ子には keyed を使用します |
| 3-2 | リストを効率的にレンダリングします |
| 5-3 | メモリーリークを防ぎます |
Web コンポーネントの統合
Lit やその他のカスタム要素を SolidJS と統合する際は、以下のルールを読み込んでください。
| ルール | 理由 |
|---|---|
| 9-1 | SolidJS のコンテキストがマウントされる前に登録します |
| 9-2 | ブラウザーが slotchange を通知した後に割り当て済みノードを処理します |
| 9-3 | 一方向のデータフローを設計します |
| 9-4 | ラッパーはアプリケーションの関心事に絞ります |
| 9-5 | プロパティには prop:*、CSS や ARIA が参照する属性には attr:* を使用します |
| 9-6 | プロパティにバインドされたフィールドをフォームライブラリのレジストリーに登録します |
| 9-7 | カスタム要素を多用するフォームの状態をストアに保持します |
| 5-6 | カスタム要素のイベントには on: 名前空間を使用します |
コードレビュー
コードレビューでは、以下のルールを重点的に確認してください。
| 優先度 | ルール |
|---|---|
| 重大 | 1-1, 2-1, 2-6, 2-9 |
| 高 | 1-2, 1-3, 1-7, 2-7, 5-2, 5-3, 5-5 |
パフォーマンスの最適化
パフォーマンスを最適化する際は、以下のルールを読み込んでください。
| ルール | 焦点 |
|---|---|
| 1-2 | 不要な再計算を防ぎます |
| 1-6 | 更新サイクルを減らします |
| 4-2 | ストアを細かく更新します |
| 6-1 | 不要なサブスクリプションを防ぎます |
| 6-2 | コード分割 |
| 6-4 | ストアへのアクセスを効率化します |
状態管理
アプリケーションの状態を扱う際は、以下のルールを読み込んでください。
| ルール | 焦点 |
|---|---|
| 4-1 | 適切なプリミティブを選択します |
| 4-2 | 効率的な更新 |
| 4-3 | 複雑な変更 |
| 4-4 | 外部データの統合 |
| 4-5 | コンポーネント間の状態 |
| 4-6 | ストア内の関数値 |
| 4-7 | コンテキストを共有する複数のマウント済みペイン |
アクセシビリティ監査
アクセシビリティを監査する際は、以下のルールを読み込んでください。
| ルール | 焦点 |
|---|---|
| 7-1 | セマンティックな構造 |
| 7-2 | スクリーンリーダーのサポート |
| 7-3 | キーボードユーザー |
| 7-4 | ルーターリンクの現在のページ状態 |
テストの作成
テストを作成またはレビューする際は、以下のルールを読み込んでください。
| ルール | 焦点 |
|---|---|
| 8-1 | 正しい Vitest 設定 |
| 8-2 | リアクティブなレンダリングスコープ |
| 8-3 | プリミティブのリアクティブな所有権 |
| 8-4 | 非同期クエリとタイマー |
| 8-5 | アクセシブルなクエリの選択 |
| 8-6 | テストアーキテクチャ |
| 8-7 | ブラウザモードと jsdom の使い分け |
| 8-8 | ポータルと標準的でない ARIA 構造 |
| 8-9 | IDB と localStorage のクリーンアップパターン |
| 8-10 | 統合テスト用の MemoryRouter セットアップ |
| 8-11 | テスト用の QueryClient 設定 |
| 8-12 | IndexedDB に書き込まれるストアプロキシ |
Web コンポーネント/カスタム要素の統合
カスタム要素ライブラリ(Shoelace、FAST、Lion、Material Web Components など)や、<dialog>、Popover API などのネイティブブラウザー API を使用する際は、以下のルールを読み込んでください。
| ルール | 理由 |
|---|---|
| 2-10 | JSX 名前空間でカスタム要素のタグを宣言し、新しい HTML 属性や実験的な CSS プロパティに型を付けます |
| 5-6 | すべてのカスタム要素イベントに on: を使用し、CustomEvent のペイロードに正しく型を付けます |
| 5-7 | 宣言的な prop:*/on:wc-* を優先し、ネイティブまたはレガシー API に限り ref を使用します |
| 6-6 | ツリーシェイキングのためにコンポーネントごとにインポートし、::part() のオーバーライドはグローバル CSS のみに記述します |
| 9-8 | WC に defaultValue がない場合は、createSignal で非制御から制御への橋渡しを管理します |
| 5-8 | Solid の DOM 更新を View Transition のスナップショットコールバック内に保ちます |
| 5-10 | ネイティブの <progress>/<meter> で value が undefined の場合、例外が発生します |
カルーセルとスクロール同期コンポーネント
インデックスがスクロールコンテナーに反映され、そこから再び通知される場合は、5-9 を読み込んでください。
React からの移行
シグナル、 コンポーネントの実行、 リアクティブな props、 HTML の prop 名、 スタイル構文、 制御フロー、 リストのレンダリング、および イベントハンドラーを読み込んでください。
ツール
これらのベストプラクティスに加えて自動リンティングを行うには、 eslint-plugin-solid を使用してください。このプラグインは、このスキルで扱う問題(分割代入された props、早期リターン、React 固有の props、innerHTML の使用、style prop の形式など)の多くを検出し、自動修正可能なルールも提供します。