---
tagName: 11.21.0
name: Neo.mjs v11.21.0 Release Notes
publishedAt: '2026-01-14T02:29:58Z'
isPrerelease: false
isDraft: false
---
# Neo.mjs v11.21.0 Release Notes

**Release Type:** Core Architecture & Standards Adoption  
**Stability:** Production-Ready  
**Upgrade Path:** Drop-in replacement for v11.20.0  

> **TL;DR:** **v11.21.0 introduces a new principle: "Conservation of Identity".** Like conservation of mass in physics, DOM nodes in Neo.mjs are now never destroyed during moves—only transferred. This release breaks the 30-year dichotomy of "wrapper pollution" vs "compile-time tricks" by introducing **`Neo.container.Fragment`**—true runtime phantom containers. We refactored the entire VDOM engine to support **Atomic Moves**, leveraging the cutting-edge `Element.moveBefore()` API to preserve state (focus, video, iframes) where other frameworks force a reload. **47 tickets** resolved in **2 days**.

---

## ⚡ Velocity Case Study: Concurrency Stress Test

To prove the "Human + AI" velocity, let's look at **Ticket #8595**.

*   **The Context:** We shifted `Neo.tree.List` from CSS hiding to **Real DOM Removal** for collapsed nodes. This optimization enables trees with 100k+ items to render instantly, but it introduces complex state synchronization challenges.
*   **The Challenge:** During internal stress testing, we identified a theoretical race condition. If a store update arrived exactly while a folder was expanding (and mounting new DOM), the VDOM engine could momentarily misalign dynamic containers.
*   **The Resolution:** Instead of patching the component, the Agent implemented a **Recursive Tag Safeguard** in the core `syncVdomState` engine. It intelligently prevents ID corruption on mismatched nodes while **continuing to parse children**, ensuring that valid sub-trees preserve their state even if their container changes.

**Actual Timeline (Jan 13, 2026):**
*   **12:47:22 UTC** - Issue identified during high-load stress testing.
*   **13:06:34 UTC** - Fix Committed (Core Engine Hardening).
*   **13:08:00 UTC** - Verified and Closed.
*   **Total Duration:** **20 Minutes, 38 Seconds**.

In 20 minutes, we diagnosed a complex multi-threaded race condition and implemented a permanent engine-level safeguard, ensuring Enterprise-grade stability before the feature ever reached production.

---

## 👻 The Phantom Architecture

> **"The best DOM node is the one you don't render."**

For years, "Fragments" (rendering children without a parent `div`) have been a compile-time trick in other frameworks. In Neo.mjs, we made them **First-Class Runtime Citizens**.

### 1. True Wrapperless Rendering
`Neo.container.Fragment` renders as a "Logical Container" backed by physical **Comment Anchors**.
*   **The Output:** `<!-- fragment-id-start -->` ... children ... `<!-- fragment-id-end -->`
*   **The Layout:** Children participate directly in the parent's layout context (CSS Grid/Flexbox).
*   **The Intelligence:** The **VDOM Worker** knows the fragment exists (for component lifecycle), but the **Main Thread** flattens it into the physical DOM.

### 2. Smart Runtime / Lean IPC
When you move a Fragment (e.g., reordering it in a list), the Worker does **not** serialize N child operations. It sends a single high-level delta: `{action: 'moveNode', id: 'fragment-id'}`. The Main Thread resolves this by locating the anchors and moving the entire physical range atomically.

---

## 💻 Code in Action

Here is the "Impossible Move": transferring a live Input Field into a Fragment without losing focus or value.

```javascript
import Fragment from '../container/Fragment.mjs';

class MainContainer extends FormContainer {
    static config = {
        layout: {ntype: 'vbox', align: 'start'},
        items : [{
            module   : TextField,
            reference: 'myField',
            placeholder: 'Type here then move me!'
        }, {
            // A Fragment has no DOM wrapper
            module   : Fragment,
            reference: 'myFragment',
            items    : [/*...*/]
        }, {
            module : Button,
            text   : 'Atomic Move',
            handler: 'up.onMoveIntoFragment'
        }]
    }

    onMoveIntoFragment() {
        const
            field    = this.getReference('myField'),
            fragment = this.getReference('myFragment');

        // Atomic Move: Physically transfers the node.
        // Input value and focus are preserved!
        fragment.insert(0, field);
    }
}
```

---

## ⚡ Performance: Conservation of Identity

We optimized specific critical paths in the `DeltaUpdates` engine to be surgical, replacing destructive operations with precise DOM manipulations where state preservation matters most.

### The "Atomic Move" Advantage
Most frameworks handle component moves by **Destruction and Recreation**:
1.  Unmount Component (State Lost)
2.  Remove DOM Node (Garbage Collection churn)
3.  Create New DOM Node
4.  Mount Component (State Hydration needed)

In v11.21.0, Neo.mjs introduces **Atomic Moves**:
1.  **Silent Remove:** The component instance is detached from its old parent *without* being destroyed.
2.  **Physical Transfer:**
    *   **Best Case:** Uses `Element.moveBefore()` (Chrome/Edge Canary) to atomically reparent the node. **Zero state loss.**
    *   **Fallback:** Uses a `appendChild` loop with automatic **Focus Restoration** to mitigate browser blur events.

**Result:** A massive reduction in Main Thread work and memory churn, with the unique ability to preserve the state of "heavy" elements like `<video>`, `<iframe>`, and complex form inputs during layout changes.

### String-Based Renderer Parity
We didn't just optimize the default DOM API path. We invested significantly to ensure the **`StringBasedRenderer`** (used for SSR and legacy environments) maintains full feature parity.
*   **Fragment Support:** It now correctly generates HTML strings for `tag: 'fragment'` (Comment Anchors).
*   **Batching Enabled:** We enabled `insertNodeBatch` for the string renderer, allowing it to perform bulk insertions as efficiently as the DOM renderer.
*   **Unified Architecture:** We refactored the creation logic (`createNode`) to align both renderers, ensuring that `DeltaUpdates` can orchestrate complex moves regardless of how the nodes were born.

---

## 📰 Portal Blog 2.0

We finalized the content engine separation initiated in v11.19.0.
*   **Blog vs. Medium:** The "Blog" tab now hosts our internal engineering deep-dives (Markdown), while "Medium" aggregates external posts.
*   **Chronological Engine:** Added build-time logic to sort posts by date and group them by year.
*   **Deep Linking Architecture:** To support the new **Real DOM Removal** strategy in `TreeList` (where collapsed items physically don't exist in the DOM), we implemented a robust **"Expand -> Mount -> Scroll"** pattern. The application now intelligently expands the tree path, waits for the VDOM/DOM mount cycle to complete, and then precisely scrolls the target item into view.

---

## 📦 Full Changelog

### 👻 Epic: Neo.container.Fragment (#8601)
*   **Feat:** Implement `Neo.container.Fragment` Class & VNode Support (#8602)
*   **Feat:** VDOM Helper Support for Fragment Indexing & Deltas (#8603)
*   **Feat:** Fragment Rendering Support (DomApi & StringBased) (#8604)
*   **Feat:** Fragment Range Operations in DeltaUpdates (#8605)
*   **Feat:** Add Fragment Helper Methods to DeltaUpdates (#8609)
*   **Feat:** Update `DeltaUpdates.moveNode` to support Fragments (#8611)
*   **Feat:** Update `DeltaUpdates.insertNode` to support Fragments (#8612)
*   **Feat:** Update `DeltaUpdates.insertNodeBatch` to support Fragments (#8613)
*   **Test:** Comprehensive Unit & E2E Testing for Fragments (#8606, #8607)
*   **Docs:** Create 'Fragments' Guide in UI Building Blocks (#8629)
*   **Docs:** Knowledge Base Enhancement for Fragment Implementation (#8628)
*   **Fix:** `hidden: true` config fails to remove Fragment DOM (#8627)

### 🏗️ Core & VDOM
*   **Feat:** Implement `Element.moveBefore()` support for Atomic Moves (#8620)
*   **Refactor:** `Neo.container.Base` support for Atomic Component Moves (#8615)
*   **Enhance:** Improve `changeNodeName` to preserve child nodes and state (#8597, #8600, #8598)
*   **Perf:** Optimize `updateVtext` to be non-destructive and O(1) (#8599)
*   **Fix:** Prevent "Chimera" VDOM corruption in `syncVdomState` (#8596, #8595)
*   **Fix:** `Container.insert()` no-op optimization (#8626)
*   **Fix:** `moveNode` off-by-one error for forward moves in same parent (#8614)
*   **Refactor:** `StringBasedRenderer` API parity (`createNode`) (#8617)
*   **Perf:** Enable `insertNodeBatch` for `StringBasedRenderer` (#8618)

### 🚀 Portal & Blog
*   **Feat:** Add Blog Tab to Portal News Section (#8585)
*   **Feat:** Group Blog posts by Year (#8586)
*   **Feat:** Reorder Portal Blog Index Chronologically (#8588)
*   **Feat:** Add Dates to Portal Blog Tree List (#8589)
*   **Feat:** Centralize TreeList expand and scroll logic (#8591)
*   **Refactor:** Rename Blog Models/Stores to resolve ambiguity (#8590)
*   **Fix:** TimelineCanvas sync drift via debounced alignment (#8582)
*   **Fix:** TimelineCanvas animation reset logic (#8583)

### 🛠️ Infrastructure & Testing
*   **Feat:** Add `moveComponent` remote method to App worker (#8608)
*   **Feat:** Create Playwright Test Fixtures (`neo` fixture) (#8619)
*   **Refactor:** Migrate tests to use `neo` fixture (#8623, #8624)
*   **Fix:** Outdated ticket index in release workflow (#8581)

All changes delivered in 1 atomic commit: [d7e2d44](https://github.com/neomjs/neo/commit/d7e2d446a16507c9da6136f71464c28104590859)
