Releases: neomjs/neo
Neo.mjs v10.5.4 Release Notes
- Added the new blog post to the Portal App (neo website)
- Friends link: https://tobiasuhlig.medium.com/benchmarking-frontends-in-2025-f6bbf43b7721?source=friends_link&sk=af0f2c6745a7ca4993bc0ae60ad0ebb4
Neo.mjs v10.5.3 Release Notes
Bug Fixes
- grid.Container: Corrected the calculation for the
aria-rowcount
attribute. The count was previouslystore.count + 2
, and has been adjusted tostore.count + 1
to accurately reflect the number of data rows plus the single header row, improving accessibility for screen readers.
Neo.mjs v10.5.2 Release Notes
Features
- grid.Container: Added the
aria-colcount
attribute to the grid's wrapper element to enhance accessibility. - This attribute dynamically updates with the number of columns, ensuring screen readers can accurately announce the grid's structure.
Enhancements
- grid.Container: The
updateColCount()
method now includes asilent
parameter. This allows for updating the column count without triggering an immediate DOM update, optimizing performance in scenarios where multiple changes occur in sequence. - examples.grid.bigData.ControlsContainer: To improve user feedback, a loading mask is now displayed during the initial data filtering process in the Big Data grid example.
Bug Fixes
- data.Store: Fixed an issue where the
isLoaded
flag was not set totrue
when records were added to the store via
theadd()
method. This ensures that theload
event is fired correctly, even when the store is populated manually
instead of through assigningdata
orautoLoad
.
Neo.mjs v10.5.1 Release Notes
Bug Fixes
table.Body
- Fixed a JavaScript error that occurred when filtering a table's store. A previous change modified the method signature of
onStoreLoad()
to expect a destructured object containing the store's items. However, theonStoreFilter()
method was not updated to reflect this change and continued to callonStoreLoad()
without the required arguments. onStoreFilter()
now correctly passes the store's data toonStoreLoad()
, resolving the error and ensuring that table filtering works as expected.
Neo.mjs v10.5.0 Release Notes
Major Performance Enhancements for Grids and Data Handling
This release introduces a groundbreaking set of performance optimizations for Neo.data.Store
and Neo.grid.Container
, fundamentally transforming how large datasets are handled within Neo.mjs applications. These enhancements dramatically reduce initial load times, improve UI responsiveness, and resolve critical VDom reconciliation issues, making it feasible to work with millions of data points with unprecedented fluidity.
Key Highlights:
-
Lazy Record Instantiation (GET-driven approach):
Neo.data.Store
now defers the creation ofNeo.data.Record
instances. Raw data objects are stored directly, andRecord
instances are only created on-demand when an item is explicitly accessed (e.g., viastore.get()
,store.getAt()
, or during VDom rendering of visible rows).- Impact: This eliminates the massive upfront cost of instantiating millions of
Record
objects, leading to up to 97% reduction in initial data processing time for large datasets.
-
Configurable Data Chunking for UI Responsiveness:
- Introduced an
initialChunkSize
config inNeo.data.Store
(default0
). When enabled,Store.add()
processes data in chunks, significantly mitigating UI freezes during synchronous loading of extremely large datasets (e.g., 1,000,000+ rows). - Impact: Provides a smoother perceived user experience during initial data loads, even for datasets that would otherwise cause multi-second UI blocks.
- Introduced an
-
Robust Component ID Management for VDom Stability:
- Resolved critical VDom reconciliation errors (e.g.,
RangeError
, infinite loops) that could occur with component columns when chunking was active.Neo.grid.column.Component
now intelligently generates unique IDs based on the store's chunking state, ensuring VDom stability. - Impact: Eliminates a major source of instability and crashes when using component columns with large, dynamically loaded datasets.
- Resolved critical VDom reconciliation errors (e.g.,
-
Automated Component Instance Cleanup:
- Enhanced
Neo.grid.Body
with sophisticated component instance management. Components are now automatically destroyed when the grid body is destroyed, the store is cleared, the store changes, or when they scroll out of view after a chunked load. - Impact: Reduces memory overhead and improves long-term performance by preventing the accumulation of unused component instances.
- Enhanced
-
GPU-Accelerated Vertical Scrolling (
translate3d
):- Grid rows now utilize
transform: translate3d(0px, Ypx, 0px)
for vertical positioning. This hints to the browser to promote rows to their own composite layers, offloading rendering to the GPU. - Impact: Leads to noticeably smoother and more fluid vertical scrolling, especially during rapid movements through large grids.
- Grid rows now utilize
Other Enhancements:
- ComboBox Initial Display Fix: Resolved an issue where
Neo.form.field.ComboBox
instances would appear blank on initial load when backed by lazily instantiated stores. TheupdateInputValueFromValue
method now correctly displays values from raw data orRecord
instances. - Enhanced BigData Grid Example: The
examples/grid/bigData
demo has been updated to include control options for up to 200 columns, allowing for testing with up to 20,000,000 cells. This provides a more extreme and comprehensive benchmark for grid performance.
These collective improvements mark a significant leap forward in Neo.mjs's capability to handle and display massive amounts of data with high performance and a superior user experience.
Performance Benchmarks (from examples/grid/bigData
):
Scenario | Before (Eager Instantiation) | After (Lazy Instantiation, with chunking) | After (Lazy Instantiation, no chunking) |
---|---|---|---|
1,000 rows, 50 columns | 49ms (Record creation) | 2ms (Data gen + add) | 2ms (Data gen + add) |
50,000 rows, 50 columns | 2391ms (Record creation) | 1252ms (Data gen + add) | 93ms (Data gen + add) |
50,000 rows, 100 columns | 4377ms (Record creation) | 1289ms (Data gen + add) | 95ms (Data gen + add) |
100,000 rows, 50 columns | N/A (too slow) | 1299ms (Data gen + add) | 156ms (Data gen + add) |
100,000 rows, 200 columns | N/A (too slow) | 1427ms (Data gen + add) | 174ms (Data gen + add) |
Note: "Record creation" refers to the time taken for Neo.data.Record
instantiation. "Data gen + add" refers to the time taken to generate raw data and add it to the store's collection.
Important Note on Chunking:
The introduction of lazy record instantiation significantly reduces the need for chunking in most common use cases, as the initial data loading is now extremely fast even for large datasets. However, Neo.mjs still optionally supports data chunking via the initialChunkSize
config for scenarios involving truly massive synchronous data additions where mitigating UI freezes is paramount.
Try it out by yourself:
https://neomjs.com/examples/grid/bigData/index.html
https://neomjs.com/dist/production/examples/grid/bigData/index.html
All changes combined into 1 Commit: 5d4760b
Neo.mjs v10.4.1 Release Notes
Highlights
This patch release polishes the "instant preview" feature for grids introduced in v10.4.0, ensuring the grid's scrollbar and accessibility properties are correctly sized from the very first paint when loading large datasets. The non-buffered table.Body
also receives a major performance boost for clearing data.
Enhancements & Bug Fixes
1. Polished "Instant Preview" for Large Datasets in Grids
The chunked loading mechanism for stores and grids has been refined. When adding a large dataset, the initial load
event now carries the final total record count. The grid.Container
uses this information immediately to set the correct aria-rowcount
and to size the vertical scrollbar accurately, preventing layout shifts and providing a more stable and accessible user experience from the very first render.
2. Standardized Internal Store load
Event
To support the "instant preview" refinements, the data.Store
load
event payload has been standardized internally. It now consistently uses an object containing an items
property (e.g., {items: [...]}
). Framework components have been updated to use this new signature.
3. Performance Fast Path for table.Body
The non-buffered table.Body
is now significantly faster when clearing its data. It uses the same "fast path" optimization as grid.Body
, directly clearing the DOM instead of processing a large and unnecessary VDOM diff when an empty dataset is loaded. This aligns the performance of the two components.
Neo.mjs v10.4.0 Release Notes
Highlights
This release focuses heavily on performance and robustness, especially when dealing with very large datasets in collections, stores, and grids. Key improvements include non-blocking chunked data loading for stores, a fix for stack overflow errors when adding large arrays to collections, and significant optimizations for grid scrolling and data clearing.
Enhancements
1. High-Performance Chunking for Store.add()
To prevent the UI from freezing when adding massive datasets (e.g., 1M+ records), Store.add()
now uses a two-chunk loading strategy. It first adds a small "interactive" batch of records to make the UI responsive almost instantly. It then silently adds the remaining data in the background by leveraging the suspendEvents
flag, and fires a final load
event to update the UI (e.g., grid scrollbars) once complete. This ensures a non-blocking, highly responsive user experience.
2. Refactored Grid ScrollManager for Smoother Scrolling
The grid.ScrollManager
has been refactored to use the framework's built-in delayable
system instead of manual setTimeout
logic. Both vertical and horizontal scroll event handlers are now throttled to align with a 60fps refresh rate, and a debounced onBodyScrollEnd
handler efficiently manages the isScrolling
state. This results in smoother scrolling, more consistent behavior, and cleaner, more maintainable code.
3. Grid onStoreLoad
Fast Path for Instant Clearing
A performance "fast path" has been added to the grid.Body
for scenarios where a store is cleared. Instead of performing a full VDOM diff between the existing rows and an empty dataset, the grid now detects this specific case and directly clears the VDOM and the real DOM. This reduces the time to clear a large grid from ~13ms to a near-instantaneous operation.
Bug Fixes
1. Fixed collection.Base#splice
Stack Overflow
A RangeError: Maximum call stack size exceeded
would occur when adding a very large number of items (e.g., >100k) to an already populated collection at runtime. This was caused by using the spread operator (...
) on a massive array. The splice
method now intelligently switches to a safer concat()
-based approach for large arrays, preventing the crash while retaining the high performance of native splice
for smaller arrays.
2. Fixed collection.Base#construct
Race Condition
A race condition was fixed where methods on a collection could be called during instantiation before the collection's internal properties were initialized. The construct
method's logic was reordered to ensure all internal properties are defined before the parent constructor is called, making the class more robust and preventing subtle bugs during component initialization.
Neo.mjs v10.3.4 Release Notes
This patch release enhances the build-all script to only copy generated docs output into dist envs, in case the
docs app does exist (might not be the case inside custom workspaces).
Neo.mjs v10.3.4 Release Notes
This patch release addresses a critical bug in the grid component that caused stale data to be displayed, along with several build process enhancements and minor documentation corrections.
Bug Fixes
-
Grid Cells Displayed Stale Data After Record Updates
- Fixed a significant issue in
Neo.grid.Body
where surgically updating a record's data (e.g., viarecord.set()
) would not visually update the corresponding grid cells if the grid was scrolled. The component was attempting to update VDOM nodes for rows outside the currently rendered (mounted) range, leading to a visual desynchronization between the store and the UI. - The
onStoreRecordChange
method now ensures that cell updates are only performed for rows that are currently visible or within the buffered render range, guaranteeing that the UI always reflects the latest data state. - See: GitHub Issue #7070
- Fixed a significant issue in
-
Functional Component Event Handling
- Corrected an event propagation issue in the functional nested template component example (
examples/functional/nestedTemplateComponent/Component.mjs
). Clicks on a button within the component were not being handled correctly, and this has now been resolved.
- Corrected an event propagation issue in the functional nested template component example (
Enhancements & Build System
-
Smarter Docs App Detection
- The webpack configuration (
buildScripts/webpack/development/webpack.config.appworker.mjs
,buildScripts/webpack/production/webpack.config.appworker.mjs
) now uses a more precise check (docs/app
) to detect the presence of the documentation application. This prevents unnecessary build steps if only the output folder (docs/output
) exists. - See: GitHub Issue #7171
- The webpack configuration (
-
Optimized Docs Generation
- The main build script (
buildScripts/buildAll.mjs
) will no longer attempt to generate the docs JSON output if the docs application itself is not present in the workspace. This streamlines the build process for projects that do not include the docs app. - See: GitHub Issue #7172
- The main build script (
-
Documentation Corrections
- Fixed a typo in a blog post title within the portal application's data.
- Corrected a method name in the "The Surgical Update: From JSON Blueprints to Flawless UI" blog post from
render()
tocreateVdom()
for accuracy.
Neo.mjs v10.3.3 Release Notes
This patch release introduces a major new deep-dive article on our rendering architecture and enhances our framework comparison documentation.
New Content & Documentation
-
New Blog Post: The Surgical Update: From JSON Blueprints to Flawless UI
- A comprehensive deep dive into the Neo.mjs off-thread rendering engine.
- Explores key concepts like the secure
DomApiRenderer
, asymmetric update batching, and how our architecture preserves DOM state (e.g., for a playing<video>
). - This is the fourth article in the v10 blog post series.
-
Enhanced Framework Comparisons
- The comparison documents for React, Vue, and Angular have been updated with a new section: "Component Mobility: Portals vs. True Persistence."
- This section details the architectural advantages of Neo.mjs's persistent component model for moving stateful DOM elements, a common challenge for single-threaded frameworks.