Ripple: TypeScript UI framework analysis
Ripple is a TypeScript UI framework created by Dominic Gannaway. It borrows ideas from React, Solid, and Svelte and is TypeScript-first, with built-in reactivity, component-based architecture, and native control flow in templates.
Architecture overview
The framework introduces a few concepts that set it apart from existing solutions. Rather than function or class components, Ripple uses a component keyword for defining reusable UI elements. The TypeScript integration is thorough: full type checking, VSCode support with diagnostics and IntelliSense.
Reactive state management
The framework implements reactivity through $ prefixed variables and object properties. This approach mirrors Svelte’s reactivity model while extending it to object properties for more complex state scenarios.
let $count = 0;
let $user = { name: 'John', age: 30 };
// Both variables and object properties are reactive
$count++; // triggers re-render
$user.age = 31; // also triggers re-render Component definition and props
Components are defined using the component keyword with TypeScript interfaces for props. The syntax supports both traditional props and shorthand notation:
export component Button(props: { text: string, onClick: () => void }) {
<button onClick={props.onClick}>
{props.text}
</button>
} Native control flow
Ripple allows standard JavaScript control flow (if, for, try) directly in templates without special syntax or helper functions:
export component TodoList({ todos }: Props) {
<ul>
for (const todo of todos) { // no "key" needed!
<li>{todo.text}</li>
}
</ul>
if (todos.length === 0) {
<p>No todos available</p>
}
}
This eliminates the need for array methods like map() or special directives like v-for or #each.
Scoped styling and tooling
Components support scoped CSS through <style> elements. The framework includes Prettier support for .ripple modules and VSCode integration with diagnostics and IntelliSense.
export component Card({ title, content }: Props) {
<div class="card">
<h3>{title}</h3>
<p>{content}</p>
</div>
<style>
.card {
border: 1px solid #ccc;
padding: 1rem;
border-radius: 4px;
}
</style>
} Performance considerations
The framework claims fine-grain rendering with optimized performance and memory usage. The reactive system updates only affected DOM elements rather than re-rendering entire component trees.
Technical assessment
Ripple combines familiar concepts from established frameworks while introducing native JavaScript control flow in templates. The $ prefix reactivity system keeps state management clear. TypeScript integration appears thorough, with full type checking support.
The framework is still in early development. Whether it becomes production-ready, and how its ecosystem matures, remains to be seen.
Full implementation example
import { Button } from './Button.ripple';
export component TodoList({ todos, addTodo }: Props) {
<div class="container">
<h2>{'Todo List'}</h2>
<ul>
for (const todo of todos) {
<li>{todo.text}</li>
}
</ul>
if (todos.length > 0) {
<p>{todos.length} {"items"}</p>
}
<Button onClick={addTodo} label={"Add Todo"} />
</div>
<style>
.container {
text-align: center;
font-family: "Arial", sans-serif;
}
</style>
}
export component Counter() {
let $count = 0;
let $doubled = $count * 2;
<div class='counter'>
<h2>{'Counter'}</h2>
<p>{`Count: ${$count}`}</p>
<p>{`Doubled: ${$doubled}`}</p>
<Button onClick={() => $count++} label={'Increment'} />
<Button onClick={() => $count = 0} label={'Reset'} />
</div>
}