LearnNewsExamplesServices
Frontmatter
tagName11.21.0
nameNeo.mjs v11.21.0 Release Notes
publishedAt1/14/2026, 2:29:58 AM
isPrerelease
isDraft

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.

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