Framework
Ripple-TS/ripple avatar
Ripple-TS/ripple

Ripple-TS: a TypeScript UI framework built on .tsrx files

the elegant TypeScript UI framework

7,401 stars294 forksJavaScriptMIT

At a glance

What is it?
Ripple compiles .tsrx components into a small runtime with fine-grained reactivity, template-native control flow and scoped styles. The design is opinionated in ways that will decide whether it fits your project.
Who is it for?
Adopt Ripple if your team already writes TypeScript and wants JSX-like authoring with template control flow, and if you accept that the documentation for a 0.4.x release will not answer every question. Do not adopt it if you need a large ecosystem, a stable API surface, or a framework whose syntax you can look up in a decade of Stack Overflow answers.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Ripple-TS addresses: TypeScript setup that lives next to the markup it feeds

Most component frameworks force a split. You write a function body in TypeScript, then return markup, and any value the markup needs has to be declared before the return statement, in a scope the template compiler treats as opaque. Ripple's answer is the .tsrx file, described in the README as "a standalone language" whose compiler stack can target React, Preact, Solid, Vue and Ripple, with Ripple as "the runtime-focused target".

The audience is narrow and specific. It is TypeScript developers who like JSX authoring but want control flow written as syntax rather than as JavaScript expressions, and who want setup code and UI in the same block without a separate script section. The README's own framing is that Ripple "pairs the authoring feel of JSX with template-native control flow and TypeScript setup that can live right beside the UI it feeds". If you are happy with a script block at the top of a single-file component, this framework is solving a problem you do not have.

How .tsrx components compile: statement containers, track() and reactive collections

The mechanism that matters most is the JSX statement container, written `@{...}`. Plain JSX children are text, elements, comments and `{...}` expression containers. When a scope needs TypeScript before rendering, the container holds the setup first and then finishes with exactly one output node: a JSX element, a fragment, or a control-flow expression. The README is explicit that text such as `x = 123` between tags is JSX text, not JavaScript, unless it sits inside a statement container. That single rule explains most of the syntax surprises a newcomer will hit.

State comes from `track()`, read and written through `.value`. A tracked value can be derived by passing a function, and it can be passed down as a `Tracked<T>` prop so a child component can write it, or wrapped with `trackReadOnly(count)` to give a `Derived<T>` that follows the value without write access. The README lists `RippleArray`, `RippleObject`, `RippleMap` and `RippleSet` as reactive collections, so mutation of a collection is observable without replacing the whole object.

Control flow is directive-prefixed: `@if`, `@for`, `@switch` and `@try`. The `@for` block supports an index binding, a key binding and an `@empty` branch. `@try` carries `@pending` and `@catch (error, reset)` branches, and the catch branch receives a reset function the template can call. One constraint is easy to miss: the README states that direct `return`, `continue` and `break` statements are not valid inside `@if` template branches, so real function exits belong in the TypeScript setup above the markup.

Styles are scoped `<style>` blocks with automatic class hashing, and the README notes that fragments remain useful when a component returns markup plus a style block as siblings. Server rendering is listed as buffered and streaming, with hydration support, though the README does not document the streaming API surface.

Installing Ripple-TS and mounting a first component

The README gives two scaffolding routes and one manual route. The CLI route is the shortest path to a running app:

bash
npx create-ripple
cd my-app
npm install
npm run dev

After `npx create-ripple` the generator creates a project directory, and the remaining commands install dependencies and start the dev server. The README does not state which port the dev server binds to, so check the terminal output rather than assuming one.

If you would rather start from the repository template, the README uses degit:

bash
npx degit Ripple-TS/ripple/templates/basic my-app
cd my-app
npm install
npm run dev

To add Ripple to an existing project, install the runtime and the Vite plugin together. The README notes that npm, pnpm, yarn or bun all work, so match whatever your project already uses:

bash
npm install ripple @ripple-ts/vite-plugin

Mounting is a plain function call. The README's example imports `mount` from `ripple` and the root component from a `.tsrx` file, then passes props and a target element:

ts
// index.ts
import { mount } from 'ripple';
import { App } from './App.tsrx';

mount(App, {
  props: { title: 'Hello world!' },
  target: document.getElementById('root'),
});

The target is resolved by your own call, so the element with id `root` has to exist in the HTML before this module runs. A minimal component with local setup uses the statement container, and the README's counter example shows the shape: declare the tracked value and the handler, then emit one output node.

tsx
import { track } from 'ripple';

export function Counter() @{
  const count = track(0);
  const increment = () => count.value++;

  <button onClick={increment}>Count:{count.value}</button>
}

Note the placement of the brace: `@{` opens the container after the parameter list, and the container closes with a single `}` after the output node. The README's cart example shows the same rule inside a nested scope, where a fragment wraps multiple siblings after the setup statements.

Where Ripple-TS will fight you: container rules, API churn and thin docs

The statement container is the sharpest edge. It must finish with exactly one output node, so as soon as setup produces text, expression containers or several siblings, you wrap them in a fragment. That is a rule you internalise quickly and forget just as quickly, and the failure appears at compile time rather than as a runtime surprise, which is the better outcome but still a friction point.

The second limitation is maturity. The most recent releases listed are `[email protected]`, `[email protected]` and `@ripple-ts/[email protected]`, all published on 2026-09-18. A 0.4 version line means the public API can still move. The repository keeps a `.changeset/` directory and the root `package.json` defines a `changeset:version` script that runs a changeset check before versioning, which tells you the maintainers treat API changes as something to record per release. Read those entries before upgrading rather than assuming a patch bump is inert.

The third issue is documentation coverage. The README documents `track()`, `Tracked<T>`, `trackReadOnly()`, the reactive collections, the control-flow directives and the mount call. It does not document rollback, it does not give the dev server port, and it does not spell out the streaming SSR API even though streaming is listed as a feature. The repository has a `website/` and a `website-new/` directory and the docs live at ripple-ts.com, so the README is a summary rather than the reference.

Finally, the toolchain is strict. The root `package.json` sets `engineStrict: true` with Node `>=22.0.0` and pnpm `11.15.1`, and the monorepo is pinned with `packageManager`. If your CI runs an older Node line, Ripple is the wrong tool until that changes. The `typecheck` script also invokes `tsgo` across ten package configs, which is a heavier build surface than a single-package framework.

Ripple-TS compared with Solid and Svelte 5: same reactivity family, different authoring contract

The honest comparison is with Solid, because both use fine-grained reactivity and both avoid a virtual DOM diff. In Solid you write components as functions returning JSX, and reactivity comes from signals and a compiler that tracks which expressions depend on which signals. Ripple keeps the function-returning-JSX shape but adds the `@{...}` container so TypeScript setup can sit inside the component body, and it exposes reactivity through `track()` plus `.value` rather than a signal call. The practical difference is where setup lives: Solid puts it above the return; Ripple lets it sit inside the markup scope, including nested scopes, at the cost of the one-output-node rule.

Svelte 5 is the other close relative, and the README names it directly: the project's author, @trueadm, is credited with contributions to Svelte 5, alongside React, Inferno and Lexical. Svelte uses runes such as `$state` in `.svelte` files with a separate script block. Ripple uses `.tsrx` files with no separate script block, and its control flow is directive-prefixed (`@if`, `@for`) rather than block-based. The README also notes that the shared TSRX compiler can target Solid and Svelte among others, so the compiler and the runtime are separable pieces rather than one indivisible framework.

If you want the largest hiring pool and the deepest set of answered questions, React remains the alternative, and the trade-off is a different rendering model and a much larger runtime. Ripple's README describes its runtime as "small" without quantifying it, and the repository carries a `benchmarks/` directory with a `bench` script, but no numbers are published in the README, so do not take size or speed on faith.

Licence and the cost of keeping Ripple-TS current

Ripple is MIT licensed, both in the repository metadata and in the root `package.json` (`"license": "MIT"`). That permits commercial use and modification under the usual MIT terms, and the copyright notice has to be preserved. This is a description of the licence identifier, not legal advice; if your organisation has specific obligations around attribution or redistribution, have counsel read the LICENSE file.

Upgrade cost is dominated by the changeset workflow. The root scripts include `changeset`, `changeset:check`, `changeset:version` and `changeset:publish`, and `changeset:version` runs the check before `changeset version`. That means published versions are gated on a changeset existing, which is a good sign for changelog quality but also means the changelog is the place where breaking changes are announced. Budget time to read it at each bump.

The dependency surface is small on the runtime side: adding Ripple to an existing project means `ripple` plus `@ripple-ts/vite-plugin`. The plugin ties you to Vite, though the repository also contains a `packages/rollup-plugin` directory, so a Rollup path exists in the monorepo layout even though the README does not document it. If your build is webpack-based, neither the README nor the visible package list offers a documented integration.

Editorial conclusion

Adopt Ripple if your team already writes TypeScript and wants JSX-like authoring with template control flow, and if you accept that the documentation for a 0.4.x release will not answer every question. Do not adopt it if you need a large ecosystem, a stable API surface, or a framework whose syntax you can look up in a decade of Stack Overflow answers. Before committing, verify the .tsrx statement-container rules yourself, check that the mount target and hydration path match your deployment, and read the changesets in the repository to see how often the public API has moved between releases.

Frequently asked questions

What exactly is Ripple-TS?

It is a TypeScript-first UI framework built around .tsrx files, with fine-grained reactivity through track() and .value, template-native control flow, scoped styles and a small runtime. The README describes it as the runtime-focused target of a shared TSRX compiler stack that can also target React, Preact, Solid and Vue.

How do I install Ripple-TS?

The README gives three routes: run npx create-ripple and then npm install and npm run dev, scaffold from the repository template with npx degit Ripple-TS/ripple/templates/basic my-app, or add it to an existing project with npm install ripple @ripple-ts/vite-plugin. npm, pnpm, yarn and bun are all listed as supported, so match your project.

How do I use track() in a Ripple-TS component?

Call track() with an initial value and read or write it through .value. The README's counter example declares const count = track(0), increments it with count.value++, and renders count.value in the template; derived values come from passing a function to track(), and trackReadOnly(count) produces a Derived<T> that follows the value without allowing writes.

What is the @{...} statement container in Ripple-TS?

It is a JSX statement container that holds TypeScript setup before the rendered output. Setup comes first and the container must finish with exactly one output node, a JSX element, fragment or control-flow expression; if the output needs text or multiple siblings after setup, the README says to wrap them in a fragment.

Which Node version does Ripple-TS require?

The root package.json sets engineStrict to true with node >=22.0.0 and pnpm 11.15.1, and pins the package manager. An older Node line will not satisfy the declared engine range.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. Ripple-TS/ripple on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ripple-ts-ripple.svg)](https://hysenlabs.com/projects/ripple-ts-ripple)