Frontmatter
| tagName | 11.21.0 |
| name | Neo.mjs v11.21.0 Release Notes |
| publishedAt | 1/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-edgeElement.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.Listfrom 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
syncVdomStateengine. 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:
- Unmount Component (State Lost)
- Remove DOM Node (Garbage Collection churn)
- Create New DOM Node
- Mount Component (State Hydration needed)
In v11.21.0, Neo.mjs introduces Atomic Moves:
- Silent Remove: The component instance is detached from its old parent without being destroyed.
- Physical Transfer:
- Best Case: Uses
Element.moveBefore()(Chrome/Edge Canary) to atomically reparent the node. Zero state loss. - Fallback: Uses a
appendChildloop with automatic Focus Restoration to mitigate browser blur events.
- Best Case: Uses
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
insertNodeBatchfor 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 thatDeltaUpdatescan 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.FragmentClass & 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.moveNodeto support Fragments (#8611) - Feat: Update
DeltaUpdates.insertNodeto support Fragments (#8612) - Feat: Update
DeltaUpdates.insertNodeBatchto 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: trueconfig fails to remove Fragment DOM (#8627)
ποΈ Core & VDOM
- Feat: Implement
Element.moveBefore()support for Atomic Moves (#8620) - Refactor:
Neo.container.Basesupport for Atomic Component Moves (#8615) - Enhance: Improve
changeNodeNameto preserve child nodes and state (#8597, #8600, #8598) - Perf: Optimize
updateVtextto be non-destructive and O(1) (#8599) - Fix: Prevent "Chimera" VDOM corruption in
syncVdomState(#8596, #8595) - Fix:
Container.insert()no-op optimization (#8626) - Fix:
moveNodeoff-by-one error for forward moves in same parent (#8614) - Refactor:
StringBasedRendererAPI parity (createNode) (#8617) - Perf: Enable
insertNodeBatchforStringBasedRenderer(#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
moveComponentremote method to App worker (#8608) - Feat: Create Playwright Test Fixtures (
neofixture) (#8619) - Refactor: Migrate tests to use
neofixture (#8623, #8624) - Fix: Outdated ticket index in release workflow (#8581)
All changes delivered in 1 atomic commit: d7e2d44