Skip to content

Commit a6c7c73

Browse files
committed
rephrase
1 parent 43ccc00 commit a6c7c73

File tree

1 file changed

+16
-16
lines changed

1 file changed

+16
-16
lines changed

src/content/posts/013-shades-vnode-refactor.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ excerpt: Shades v12 replaces the rendering engine with a VNode-based reconciler
1010

1111
## Why rewrite the renderer
1212

13-
Shades has always had a simple rendering model: your `render()` function returns real DOM elements, the framework diffs them against the current tree, and patches the differences. It works — but it has a cost. Every render cycle creates a full shadow DOM tree just to compare it with the one on screen, even if nothing meaningful changed. That's a lot of allocation pressure and GC work for what often amounts to updating a single text node.
13+
Shades had a beautifully dumb rendering model: your `render()` function spits out real DOM elements, the framework diffs them against what's already on screen, and patches the differences. Simple, honest, easy to reason about... and wasteful. Every single render cycle spun up a full shadow DOM tree _just to throw it away after comparison_. That's a lot of garbage collection for what often boils down to changing one text node.
1414

15-
The v12 release replaces this with a **VNode-based reconciler**. Instead of creating real DOM elements during render, the JSX factory produces lightweight descriptor objects. A reconciler diffs the previous VNode tree against the new one and applies surgical DOM updates using tracked element references. No throwaway DOM trees, no redundant element creation.
15+
v12 rips that out and replaces it with a **VNode-based reconciler**. Now the JSX factory produces lightweight descriptor objects — plain JS, no DOM involved. The reconciler diffs the old VNode tree against the new one and pokes the real DOM only where something actually changed. No throwaway trees. No phantom elements. Just surgical updates.
1616

17-
## New hooks, fewer lifecycle callbacks
17+
## Hooks in, lifecycle callbacks out
1818

19-
The old API surface had three separate lifecycle entry points: `constructed`, `onAttach`, and `onDetach`. Each had its own timing semantics and cleanup patterns. In v12, all three are gone — replaced by a single, composable primitive: **`useDisposable`**.
19+
The old API had three separate lifecycle hooks: `constructed`, `onAttach`, and `onDetach`. Three places to scatter your setup and teardown logic, three sets of timing semantics to keep in your head. In v12, they're all gone — consolidated into one composable primitive: **`useDisposable`**.
2020

2121
```typescript
22-
// Before — scattered lifecycle management
22+
// Before — lifecycle spaghetti
2323
Shade({
2424
shadowDomName: 'my-component',
2525
constructed: ({ element }) => {
@@ -30,7 +30,7 @@ Shade({
3030
render: () => <div>Hello</div>,
3131
})
3232

33-
// After — setup and teardown live together in render
33+
// After — setup and cleanup live together, right where you use them
3434
Shade({
3535
shadowDomName: 'my-component',
3636
render: ({ useDisposable }) => {
@@ -44,7 +44,7 @@ Shade({
4444
})
4545
```
4646

47-
The `element` parameter — direct access to the host custom element — is also gone. Imperatively mutating the host was always a bit at odds with a declarative component model. The replacement is **`useHostProps`**, which lets you set attributes, styles (including CSS custom properties), ARIA attributes, and event handlers declaratively:
47+
The `element` parameter is also gone. Reaching into the host element and mutating it imperatively was always a bit... rebellious for a declarative framework. Say hello to **`useHostProps`** instead — it lets you declare attributes, styles, CSS custom properties, ARIA attrs, and event handlers without ever touching the DOM yourself:
4848

4949
```typescript
5050
render: ({ useHostProps, props }) => {
@@ -56,7 +56,7 @@ render: ({ useHostProps, props }) => {
5656
}
5757
```
5858

59-
There's also a new **`useRef`** hook for capturing child element references — no more querying the shadow DOM manually:
59+
And for those moments when you _do_ need a handle on a child element (focusing an input, measuring a bounding rect), there's **`useRef`** — no more `querySelector` treasure hunts through the shadow DOM:
6060

6161
```typescript
6262
render: ({ useRef }) => {
@@ -66,17 +66,17 @@ render: ({ useRef }) => {
6666
}
6767
```
6868

69-
## Batched updates
69+
## Batched updates (a.k.a. stop re-rendering so much)
7070

71-
`updateComponent()` used to render synchronously. Call it three times in a row and you'd get three render passes. In v12, updates are scheduled via `queueMicrotask` and coalesced — multiple observable changes within the same synchronous block produce a single render pass. The new `flushUpdates()` utility gives tests a reliable way to wait for pending renders without arbitrary `sleepAsync` calls.
71+
`updateComponent()` used to be synchronous. Fire three observable changes in a row? Enjoy your three render passes. In v12, updates go through `queueMicrotask` and get coalesced — hammer as many observables as you want within a synchronous block and the component renders _once_. The new `flushUpdates()` utility lets tests await pending renders properly, so you can finally delete those sketchy `sleepAsync(50)` calls.
7272

73-
## SVG support
73+
## SVG — for real this time
7474

75-
Shades now handles SVG elements natively. Elements are created with `createElementNS` under the correct namespace, and attributes are applied via `setAttribute` instead of property assignment. A full set of typed SVG attribute interfaces covers shapes, gradients, filters, and animations — so you get proper autocompletion in your editor.
75+
Shades now handles SVG elements as first-class citizens. Elements are created with `createElementNS` under the correct namespace, attributes go through `setAttribute` instead of property assignment (because SVG is picky like that), and there's a full set of typed interfaces covering shapes, gradients, filters, and animations. Your editor's autocomplete will thank you.
7676

77-
## Migration at a glance
77+
## Migration cheat sheet
7878

79-
| Removed | Replacement |
79+
| Gone | Use this instead |
8080
| ------------------------------- | ---------------------------------------- |
8181
| `constructed` callback | `useDisposable` in `render` |
8282
| `element` in render options | `useHostProps` hook |
@@ -85,6 +85,6 @@ Shades now handles SVG elements natively. Elements are created with `createEleme
8585

8686
## What's next
8787

88-
The subsequent v12.x releases have already landed dependency tracking for `useDisposable`, a `css` property for component-level styling with pseudo-selectors, and a brand new routing system. The framework is moving fast — stay tuned for a dedicated post on the new `NestedRouter`.
88+
The v12.x train keeps rolling — we've already shipped dependency tracking for `useDisposable`, a `css` property for component-level styling with pseudo-selectors, and a brand new routing system. Stay tuned for a dedicated post on the `NestedRouter`.
8989

90-
If you want to try it out: `npm install @furystack/shades@latest` and check the [changelog](https://github.com/furystack/furystack/blob/develop/packages/shades/CHANGELOG.md#1220---2026-02-22) for the full details.
90+
Want to take it for a spin? `npm install @furystack/shades@latest` and check the [changelog](https://github.com/furystack/furystack/blob/develop/packages/shades/CHANGELOG.md#1220---2026-02-22) for all the gory details.

0 commit comments

Comments
 (0)