` objects in any standard JavaScript object, like arrays:
```ripple
let first = track(0);
let second = track(0);
const arr = [first, second];
const total = track(() => arr.reduce((a, b) => a + @b, 0));
console.log(@total);
```
Like shown in the above example, you can compose normal arrays with reactivity and pass them through props or boundaries.
However, if you need the entire array to be fully reactive, including when new elements get added, you should use the reactive array that Ripple provides.
#### Fully Reactive Array
`TrackedArray` class from Ripple extends the standard JS `Array` class, and supports all of its methods and properties. Import it from the `'ripple'` namespace or use the provided syntactic sugar for a quick creation via the bracketed notation. All elements existing or new of the `TrackedArray` are reactive and respond to the various array operations such as push, pop, shift, unshift, etc. Even if you reference a non-existent element, once it added, the original reference will react to the change. You do NOT need to use the unboxing `@` with the elements of the array.
```ripple
import { TrackedArray } from 'ripple';
// using syntactic sugar `#`
const arr = #[1, 2, 3];
// using the new constructor
const arr = new TrackedArray(1, 2, 3);
// using static from method
const arr = TrackedArray.from([1, 2, 3]);
// using static of method
const arr = TrackedArray.of(1, 2, 3);
```
Usage Example:
```ripple
export component App() {
const items = new #[1, 2, 3];
{"Length: "}{items.length}
// Reactive length
for (const item of items) {
{item}
}
items.push(items.length + 1)}>{"Add"}
}
```
#### Reactive Object
`TrackedObject` class extends the standard JS `Object` class, and supports all of its methods and properties. Import it from the `'ripple'` namespace or use the provided syntactic sugar for a quick creation via the curly brace notation. `TrackedObject` fully supports shallow reactivity and any property on the root level is reactive. You can even reference non-existent properties and once added the original reference reacts to the change. You do NOT need to use the unboxing `@` with the properties of the `TrackedObject`.
```ripple
import { TrackedObject } from 'ripple';
// using syntactic sugar `#`
const arr = #{a: 1, b: 2, c: 3};
// using the new constructor
const arr = new TrackedObject({a: 1, b: 2, c: 3});
```
Usage Example:
```ripple
export component App() {
const obj = #{a: 0}
obj.a = 0;
{'obj.a is: '}{obj.a}
{'obj.b is: '}{obj.b}
{ obj.a++; obj.b = obj.b ?? 5; obj.b++; }}>{'Increment'}
}
```
#### Reactive Set
```ripple
import { TrackedSet } from 'ripple';
component SetExample() {
const mySet = new TrackedSet([1, 2, 3]);
{"Size: "}{mySet.size}
// Reactive size
{"Has 2: "}{mySet.has(2)}
mySet.add(4)}>{"Add 4"}
}
```
#### Reactive Map
The `TrackedMap` extends the standard JS `Map` class, and supports all of its methods and properties.
```ripple
import { TrackedMap, track } from 'ripple';
const map = new TrackedMap([[1,1], [2,2], [3,3], [4,4]]);
```
TrackedMap's reactive methods or properties can be used directly or assigned to reactive variables.
```ripple
import { TrackedMap, track } from 'ripple';
export component App() {
const map = new TrackedMap([[1,1], [2,2], [3,3], [4,4]]);
// direct usage
{"Direct usage: map has an item with key 2: "}{map.has(2)}
// reactive assignment
let has = track(() => map.has(2));
{"Assigned usage: map has an item with key 2: "}{@has}
map.delete(2)}>{"Delete item with key 2"}
map.set(2, 2)}>{"Add key 2 with value 2"}
}
```
#### Reactive Date
The `TrackedDate` extends the standard JS `Date` class, and supports all of its methods and properties.
```ripple
import { TrackedDate } from 'ripple';
const date = new TrackedDate(2026, 0, 1); // January 1, 2026
```
TrackedDate's reactive methods or properties can be used directly or assigned to reactive variables. All getter methods (`getFullYear()`, `getMonth()`, `getDate()`, etc.) and formatting methods (`toISOString()`, `toDateString()`, etc.) are reactive and will update when the date is modified.
```ripple
import { TrackedDate, track } from 'ripple';
export component App() {
const date = new TrackedDate(2025, 0, 1, 12, 0, 0);
// direct usage
{"Direct usage: Current year is "}{date.getFullYear()}
{"ISO String: "}{date.toISOString()}
// reactive assignment
let year = track(() => date.getFullYear());
let month = track(() => date.getMonth());
{"Assigned usage: Year "}{@year}{", Month "}{@month}
date.setFullYear(2027)}>{"Change to 2026"}
date.setMonth(11)}>{"Change to December"}
}
```
## Advanced Features
### Portal Component
The `Portal` component allows you to render (teleport) content anywhere in the DOM tree, breaking out of the normal component hierarchy. This is particularly useful for modals, tooltips, and notifications.
```ripple
import { Portal } from 'ripple';
export component App() {
{'My App'}
{/* This will render inside document.body, not inside the .app div */}
{'I am rendered in document.body!'}
{'This content escapes the normal component tree.'}
}
```
### Untracking Reactivity
```ripple
import { untrack, track, effect } from 'ripple';
let count = track(0);
let double = track(() => @count * 2);
let quadruple = track(() => @double * 2);
effect(() => {
// This effect will never fire again, as we've untracked the only dependency it has
console.log(untrack(() => @quadruple));
})
```
### Prop Shortcuts
```ripple
// Object spread
{"Content"}
// Shorthand props (when variable name matches prop name)
{"Content"}
// Equivalent to:
{"Content"}
```
### Raw HTML
All text nodes are escaped by default in Ripple. To render trusted raw HTML
strings, use the `{html}` directive.
```ripple
export component App() {
let source = `
My Blog Post
Hi! I like JS and Ripple.
`
{html source}
}
```
## TypeScript Integration
### Component Types
```typescript
import type { Component } from 'ripple';
interface Props {
value: string;
label: string;
children?: Component;
}
component MyComponent(props: Props) {
// Component implementation
}
```
### Context Types
```typescript
type Theme = 'light' | 'dark';
const ThemeContext = new Context('light');
```
## File Structure
```
src/
App.ripple # Main app component
components/
Button.ripple # Reusable components
Card.ripple
index.ts # Entry point with mount()
```
## Development Tools
### VSCode Extension
- **Name**: "Ripple for VS Code"
- **ID**: `ripplejs.ripple-vscode-plugin`
- **Features**: Syntax highlighting, diagnostics, TypeScript integration, IntelliSense
### Vite Plugin
```typescript
// vite.config.js
import { defineConfig } from 'vite';
import ripple from 'vite-plugin-ripple';
export default defineConfig({
plugins: [ripple()]
});
```
### Prettier Plugin
```javascript
// .prettierrc
{
"plugins": ["prettier-plugin-ripple"]
}
```
## Key Differences from Other Frameworks
### vs React
- No JSX functions/returns - components use statement-based templates
- Built-in reactivity with `track` and `@` syntax instead of useState/useEffect
- Scoped CSS without CSS-in-JS libraries
- No virtual DOM - fine-grained reactivity
### vs Svelte
- TypeScript-first approach
- JSX-like syntax instead of HTML templates
- `.ripple` extension instead of `.svelte`
- Similar reactivity concepts but different syntax
### vs Solid
- Component definition with `component` keyword
- Built-in collections (TrackedArray, TrackedSet)
- Different templating approach within component bodies
## Best Practices
1. **Reactivity**: Use `track()` to create reactive variables and `@` to access them
2. **Strings**: Wrap string literals in `{"string"}` within templates
3. **Effects**: Use `effect()` for side effects, not direct reactive variable access
4. **Components**: Keep components focused and use TypeScript interfaces for props
5. **Styling**: Use scoped `