Ripple-TS / ripple
the elegant TypeScript UI framework
AI Architecture Analysis
This repository is indexed by RepoMind. By analyzing Ripple-TS/ripple in our AI interface, you can instantly generate complete architecture diagrams, visualize control flows, and perform automated security audits across the entire codebase.
Our Agentic Context Augmented Generation (Agentic CAG) engine loads full source files into context, avoiding the fragmentation of traditional RAG systems. Ask questions about the architecture, dependencies, or specific features to see it in action.
Repository Summary (README)
PreviewRipple TS
Ripple is a TypeScript UI framework that combines the best parts of React, Solid, and Svelte. Created by @trueadm, who has contributed to Inferno, React, Lexical, and Svelte 5.
Key Philosophy: Ripple is TS-first with its own .ripple file extension,
allowing seamless TypeScript integration and a unique syntax that enhances both
human and LLM developer experience.
📚 Full Documentation | 🎮 Interactive Playground
Features
- ⚡ Fine-grained Reactivity:
trackand@syntax with a unique reactivity system - 🔥 Performance: Industry-leading rendering speed, bundle size, and memory usage
- 📦 Reactive Collections:
#[]arrays and#{}objects with full reactivity - 🎯 TypeScript First: Complete type safety with
.ripplefile extension - 🛠️ Developer Tools: VSCode extension, Prettier, and ESLint support
- 🎨 Scoped Styling: Component-level CSS with automatic scoping
Note: SSR support is coming soon! Currently SPA-only.
🚀 Quick Start
Using CLI (Recommended)
npx create-ripple
cd my-app
npm install && npm run dev
Using Template
npx degit Ripple-TS/ripple/templates/basic my-app
cd my-app
npm install && npm run dev
Add to Existing Project
npm install ripple @ripple-ts/vite-plugin
Note: You can use
npm,pnpm,yarn, orbunpackage managers.
Mounting Your App
// index.ts
import { mount } from 'ripple';
import { App } from './App.ripple';
mount(App, {
props: { title: 'Hello world!' },
target: document.getElementById('root'),
});
🔧 VSCode Extension
Install the Ripple VSCode extension for:
- Syntax highlighting
- TypeScript integration
- Real-time diagnostics
- IntelliSense autocomplete
Core Concepts
Components
Define components with the component keyword. Unlike React, you don't return
JSX—you write it directly:
component Button(props: { text: string, onClick: () => void }) {
<button onClick={props.onClick}>
{props.text}
</button>
}
export component App() {
<Button text="Click me" onClick={() => console.log("Clicked!")} />
}
Reactivity
Create reactive state with track and access it with the @ operator:
import { track } from 'ripple';
export component App() {
let count = track(0);
<div>
<p>{"Count: "}{@count}</p>
<button onClick={() => @count++}>{"Increment"}</button>
</div>
}
Derived values automatically update:
import { track } from 'ripple';
export component App() {
let count = track(0);
let double = track(() => @count * 2);
let quadruple = track(() => @double * 2);
<div>
<p>{"Count: "}{@count}</p>
<p>{"Double: "}{@double}</p>
<p>{"Quadruple: "}{@quadruple}</p>
<button onClick={() => @count++}>{"Increment"}</button>
</div>
}
Reactive collections with shorthand syntax:
export component App() {
const items = #[1, 2, 3]; // TrackedArray
const obj = #{a: 1, b: 2}; // TrackedObject
<div>
<p>{"Items: "}{items.join(', ')}</p>
<p>{"Object: a="}{obj.a}{", b="}{obj.b}{", c="}{obj.c}</p>
<button onClick={() => items.push(items.length + 1)}>{"Add Item"}</button>
<button onClick={() => obj.c = (obj.c ?? 0) + 1}>{"Increment c"}</button>
</div>
}
Transporting Reactivity
Pass reactive state across function boundaries:
import { track } from 'ripple';
function createDouble(count) {
return track(() => @count * 2);
}
export component App() {
let count = track(0);
const double = createDouble(count);
<div>
<p>{"Double: "}{@double}</p>
<button onClick={() => @count++}>{"Increment"}</button>
</div>
}
→ Transporting Reactivity Guide
Effects & Side Effects
import { effect, track } from 'ripple';
export component App() {
let count = track(0);
effect(() => {
console.log('Count changed:', @count);
});
<button onClick={() => @count++}>{'Increment'}</button>
}
Control Flow
Conditionals:
import { track } from 'ripple';
export component App() {
let condition = track(true);
<div>
if (@condition) {
<div>{'True'}</div>
} else {
<div>{'False'}</div>
}
<button onClick={() => @condition = !@condition}>{"Toggle"}</button>
</div>
}
Loops:
export component App() {
const items = #[
{id: 1, name: 'Item 1'},
{id: 2, name: 'Item 2'},
{id: 3, name: 'Item 3'}
];
<div>
for (const item of items; index i; key item.id) {
<div>{item.name}{" (index: "}{i}{")"}</div>
}
<button onClick={() => items.push({id: items.length + 1, name: `Item ${items.length + 1}`})}>{"Add Item"}</button>
</div>
}
Error Boundaries:
import { track } from 'ripple';
component ComponentThatMayFail(props: { shouldFail: boolean }) {
if (props.shouldFail) {
throw new Error('Component failed!');
{'This will never render'}
}
<div>{"Component working fine"}</div>
}
export component App() {
let shouldFail = track(false);
<div>
try {
<ComponentThatMayFail shouldFail={@shouldFail} />
} catch (e) {
<div>{'Error: ' + e.message}</div>
}
<button onClick={() => @shouldFail = !@shouldFail}>{"Toggle Error"}</button>
</div>
}
DOM Refs
Capture DOM elements with the {ref fn} syntax:
export component App() {
<div {ref (node) => console.log(node)}>{"Hello"}</div>
}
Events
Use React-style event handlers:
import { track } from 'ripple';
export component App() {
let value = track('');
<div>
<button onClick={() => console.log('Clicked')}>{'Click'}</button>
<input onInput={(e) => @value = e.target.value} />
<p>{"You typed: "}{@value}</p>
</div>
}
Styling
Scoped CSS:
export component App() {
<div class="container">{"Content"}</div>
<style>
.container {
padding: 1rem;
background: lightblue;
border-radius: 8px;
}
</style>
}
Dynamic styles:
import { track } from 'ripple';
export component App() {
let color = track('red');
<div>
<div style={{ color: @color, fontWeight: 'bold' }}>{"Styled text"}</div>
<button onClick={() => @color = @color === 'red' ? 'blue' : 'red'}>{"Toggle Color"}</button>
</div>
}
Advanced Features
Context API
Share state across the component tree:
import { Context, track } from 'ripple';
const ThemeContext = new Context();
component Child() {
const theme = ThemeContext.get();
<div>{"Theme: " + @theme}</div>
}
export component App() {
let theme = track('light');
ThemeContext.set(theme);
<div>
<Child />
<button onClick={() => @theme = @theme === 'light' ? 'dark' : 'light'}>{"Toggle Theme"}</button>
</div>
}
Portals
Render content outside the component hierarchy:
import { Portal, track } from 'ripple';
export component App() {
let showModal = track(false);
<div>
<button onClick={() => @showModal = !@showModal}>{"Toggle Modal"}</button>
if (@showModal) {
<Portal target={document.body}>
<div class="modal">
<p>{'Modal content'}</p>
<button onClick={() => @showModal = false}>{"Close"}</button>
</div>
</Portal>
}
</div>
}
Resources
- 📚 Full Documentation - Complete guide and API reference
- 🎮 Interactive Playground - Try Ripple in your browser
- 🐛 GitHub Issues - Report bugs or request features
- 💬 Discord Community - Get help and discuss Ripple
- 📦 npm Package - Install from npm
Contributing
Contributions are welcome! Please see our contributing guidelines.
License
MIT License - see LICENSE for details.