- React・Solid・Svelteの長所をひとつにまとめ、JS/TS優先設計と独自の
.rippleモジュール言語でコンポーネントを構築する型安全なUIフレームワーク
$接頭辞によるリアクティブ変数/プロパティ、JSXライクなテンプレート、きめ細かなレンダリングによって高い性能とメモリ効率を目指す
- VS Code拡張、Prettier、TypeScript統合を提供し、リアクティブな配列・マップ・セット、
effectベースの副作用、デコレータ(@use)などモダンなDXを含む
- 現在は初期アルファで、SSR未対応や型定義の不足といった制約があり、アイデアの実験とフィードバック共有を目的として公開されている
- Svelte 5のシグナルベースのランタイムと似た思想を土台に、JS/TS-firstアプローチとLLMフレンドリーな文法を実験している点から、フロントエンドフレームワーク進化の流れを読み取れる
What is Ripple?
- RippleはReact・Solid・Svelteに着想を得たTypeScript優先UIフレームワークで、独自の
.ripple拡張子を使うスーパーセット言語の上に構築されている
- JSXと相性のよい文法でコンポーネント本体の中でのみテンプレートを使い、return式(JSX return)の代わりに文(statement)スタイルのテンプレートを採用している
- 作者はInferno・React Hooks・Lexical・Svelte 5などでの経験をもとに、アイデアを実験的な形で実装した
- 現在は初期開発段階でバグやTODOが多く、本番利用は非推奨
- 目的はアイデアの共有とインキュベーション、そして他のフレームワークへの還流可能性の模索である
- JS/TS-first設計が人間とLLMの双方にとって優れたDXを提供するという仮説を検証する試みでもある
Features
- Reactive State Management:
$接頭辞を使った変数・オブジェクトプロパティのリアクティビティを提供し、派生値も$変数として自然に記述できる
- Component-Based Architecture:
componentキーワードによる明示的なコンポーネント宣言、props/childrenのサポート、そしてJSXライクなテンプレートを組み合わせる
- Performance: きめ細かな(fine-grained)レンダリングにより、業界最高水準の性能とメモリ効率を目指す
- Tooling: TypeScript統合、VS CodeのIntelliSense/診断/ハイライト、Prettierによるフォーマットをサポート
- DX強化: JS/TS-first思想、LLMフレンドリーな文法、SPA前提の構造を提供
Missing Features
- SSR不在: 現時点ではSPA専用で、SSRにはまだ取り組んでいない
- 型の不足: コードベースの型定義が限定的で、今後改善予定
Getting Started / Try Ripple
- 基本テンプレートをStackBlitzで開くか、
degitで複製してVite開発サーバーで実行できる
- コマンド例:
npx degit trueadm/ripple/templates/basic my-app → npm i → npm run dev の流れ
- オンラインプレイグラウンドは今後追加予定で、リポジトリの
playgroundフォルダを使ってローカル実験が可能
VSCode Extension
- Ripple for VS Code拡張を提供し、
.rippleファイルに対して構文ハイライト、リアルタイム診断、TS統合、**オートコンプリート(IntelliSense)**をサポート
- マーケットプレイス配布版と手動インストール用**
.vsixパッケージ**へのリンクも提供
Mounting your app
- エントリーポイントで
mount(App, { props, target })の形によるルートコンポーネントのマウントをサポート
targetにDOMノードを指定し、propsで初期属性を注入する簡潔なAPIを提供する
Key Concepts
- Components
componentキーワードで関数ライクなシグネチャの宣言的コンポーネントを定義し、本体内でJSXライクなテンプレートを直接記述する
- プロパティの短縮記法(
{onClick})、スプレッド({...props})など馴染みある文法をサポート
- Reactive Variables
$接頭辞の変数・プロパティは自動的なリアクティビティを持ち、インクリメントや代入だけで再レンダリングを引き起こす
- 派生状態は
$double = $count * 2のような直感的な記法で宣言でき、連鎖的な派生も可能
untrackにより初期化時など特定区間でリアクティビティ伝播を遮断できる
- グローバル/モジュールトップレベルではリアクティブ変数は禁止で、必ずアクティブなコンポーネントコンテキスト内で生成しなければならない
- Transporting Reactivity
- 配列・オブジェクトのラッピングによってリアクティビティを境界の外へ運び、合成と共存を可能にする
- ファクトリ関数が
$値を受け取り、内部で派生/effectを作成し、再び$値を返すパターンが推奨される
- Reactive Arrays / Set / Map
RippleArray/Set/Mapは標準コレクションを拡張したリアクティブコレクションで、長さ/サイズには**$length/$size**でアクセスしないとリアクティブにならない
- メソッドの結果を直接使うか、
$hasのようにリアクティブ変数へ代入してテンプレートで利用できる
- Effects
effect(() => { ... })で依存する$値が変化したときに実行される副作用を宣言し、トラッキングの自動化を提供する
- Control flow
- テンプレートはコンポーネント本体の内部でのみ許可され、通常の関数・変数代入式としてJSX自体を返さない
- テンプレート内では**変数宣言/関数呼び出し/
debugger**が利用でき、文(statement)中心の制御フローを提供する
- **文字列リテラルは
{ "…" }**で明示してコードと区別するルールを採用
- If / For / Try statements
if/elseをテンプレートブロック内で使い、可読性の高い制御フローを構成する
for...ofでコレクションをレンダリングし、key prop不要の簡潔な反復をサポート
try/catchブロックでエラーバウンダリ(fallback UI)を構成し、catchでロギング・レポート後に代替UIをレンダリングできる
- Props
- リアクティブなpropには**
$接頭辞を使い、DOM属性も$class、$idのように接頭辞を付けないとリアクティブ更新**されない
- Children
- 暗黙的な**
$childrenコンポーネントスロットを提供し、<$children />でコンポジション**を実装できる
- 明示的なスロットコンポーネント宣言も可能で、可読性と制御性を確保する
- Events
- ReactライクなイベントAPI(
onClick, onKeyDown, …Capture)をサポートし、一部イベントはデリゲート最適化される
- Decorators
- デコレータ構文
{@use fn}で実DOMノード参照を取得し、マウント/アンマウントフックを返せる
- インライン関数/ファクトリを通じたリアクティブ値の受け渡しパターンや、合成コンポーネントへの受け渡しをサポートする
- Styling
- コンポーネントローカルスコープのCSSを
<style>最上位ブロックに記述し、カプセル化されたスタイリングを提供
- Context
createContextで上下ツリー共有値を設定し、コンポーネント内部でのみget/setを許可して予測可能性を確保する
Playground
- リポジトリをクローンした後、
pnpm i && cd playground && pnpm devでローカルプレイグラウンドを実行できる
- Viteプラグインを使い、
playground/srcで**.ripple文法の実験**を手軽に行える
Why it matters
- JS/TS-first・statement-styleテンプレート・$接頭辞リアクティビティなどは、LLMフレンドリーなコード構造と可読性/静的解析のしやすさの両立を模索する実験である
- きめ細かなレンダリング・コレクションのリアクティビティ・デコレータによるDOMフックの組み合わせは、微細な最適化とDXを両立させようとする試みである
- まだアルファ段階ではあるが、Svelte 5のシグナルベースの流れとReactエコシステムの開発体験を交差検証しながら、次世代フレームワーク設計への示唆を与える
3件のコメント
良さそうでもあり、そうでもない気もする
なんとなく Svelte っぽい感じがかなり強いですね。
巡り巡って、結局そのうち ReactLike に行くんじゃないかと(笑)
AI に学習されている分量が少ないせいで、みんな新しいフレームワークを敬遠する傾向が出てきたように思います。
Svelte が最後のチャンスに乗れたと思います。Svelte ですら、プロンプトをうまく書かないと rune を使わずに
$構文で来てしまうので困ります。