App Shell & MVVM: The DevIndex Architecture
The DevIndex application is built with the Neo.mjs framework, which utilizes a revolutionary architecture: it runs almost entirely outside the browser's main thread. This guide details how the DevIndex frontend is structured to leverage this "Off-Main-Thread" paradigm, along with a hybrid approach to MVC and MVVM patterns.
Explore the Source: You can find the complete source code for the DevIndex application here: GitHub: apps/devindex.
The "Fat Client" Philosophy
Before diving into the components, it's crucial to understand the core philosophy of DevIndex: it is a pure "Fat Client".
Unlike traditional web apps that rely on a server for sorting, filtering, and pagination, DevIndex streams in a single users.jsonl file on startup. From that point forward, the client literally does everything. All 50,000+ developer records are held in memory within the App Worker. Every filter, every sort, and every view transition happens instantly on the client side without ever hitting a backend server again.
(For a deep dive into how this streaming works, see The Backend Twist).
The Minimal Main Thread
If you inspect the source of the DevIndex application's entry point (apps/devindex/index.html), you will notice how remarkably empty it is:
<!DOCTYPE HTML>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<title>DevIndex</title>
</head>
<body>
<script src="../../src/MicroLoader.mjs" type="module"></script>
</body>
</html>
The <body> tag is completely empty. The only script loaded is MicroLoader.mjs.
The main thread of the browser is intentionally kept as idle as possible. It has three primary responsibilities:
- Starting Workers: Bootstrapping the Neo.mjs multi-worker environment.
- Delegating UI Events: Capturing native DOM events (like scroll or click), serializing them as JSON, and delegating them to the App Worker.
- Applying Delta Updates: Receiving minimal Virtual DOM diffs and applying them efficiently via the
domApiRenderer.
By keeping the main thread clear of application logic, data processing, and state management, the DevIndex application ensures the UI remains fluid and responsive, completely immune to "jank" even under heavy computational load.
The App Worker: Where DevIndex Lives
After the MicroLoader boots up the framework, it spawns the application inside a dedicated background thread known as the App Worker.
(Note: For more on how other workers like the Canvas and Data workers fit in, see the OffscreenCanvas & Rendering and The 50k-Row Grid guides.)
The entry point for DevIndex in the App Worker is app.mjs:
import Overwrites from './Overwrites.mjs';
import Viewport from './view/Viewport.mjs';
export const onStart = () => Neo.app({
mainView: Viewport,
name : 'DevIndex'
});
This is where the magic happens. The entire application—every component, every piece of state, every network request—runs within this App Worker.
Viewport and Lazy Loading
The root component of the application is the Viewport (apps/devindex/view/Viewport.mjs). It defines the declarative layout using Neo.mjs's JSON-based Virtual DOM blueprints.
Crucially, DevIndex utilizes lazy loading for its main sections to keep the initial bundle size minimal:
items: [{
module: Header,
flex : 'none'
}, {
ntype : 'container',
flex : 1,
layout : {ntype: 'card', activeIndex: null},
reference: 'main-content',
items : [
{module: () => import('./home/MainContainer.mjs')},
{module: () => import('./learn/MainContainer.mjs')}
]
}]
Notice the module: () => import(...) syntax. The framework will only fetch and execute the code for the Home (Grid) or Learn (Documentation) sections when the routing logic dictates they should be visible.
A Hybrid Approach: MVC meets MVVM
DevIndex organizes its codebase using a blend of classic Model-View-Controller (MVC) for its data layer and Model-View-ViewModel (MVVM) for its UI components.
1. The MVC Foundation
At the top level of the apps/devindex folder, you will find a traditional MVC directory structure:
- Model:
model/Contributor.mjsdefines the schema and virtual fields for the developer data. - Store:
store/Contributors.mjsacts as the collection manager, holding the 50,000+ records in memory. - View: Folders like
view/home/contain the visual components (e.g.,GridContainer.mjs).
2. The MVVM UI Layer (Hierarchical Architecture)
While the data layer leans MVC, the UI components utilize a powerful, instance-based MVVM pattern. Instead of global controllers or single-store monoliths, DevIndex attaches State Providers and View Controllers directly to specific container instances within the component tree.
These providers and controllers form a hierarchical chain:
Hierarchical State Providers (ViewModel): When a component requests data (e.g.,
bind: { text: data => data.title }), the framework searches up the component tree. If the current component's State Provider doesn't have the data, it checks the parent's State Provider, and so on. This creates a deeply nested chain of merged data access. TheViewportStateProviderholds global UI state, while theMainContainerStateProvidermight hold the specificContributorsstore.Hierarchical View Controllers: Event handling works the same way. When a button is configured with a string-based listener (e.g.,
handler: 'onButtonClick'), the framework doesn't look in a global registry. It walks up the component tree to find the closest View Controller that implements theonButtonClickmethod. This elegantly encapsulates logic exactly where it's needed, preventing bloated, monolithic controllers.
For a deeper dive into how these controllers manage filtering and global state, see the State Management & Controls guide.
Summary
By running its entire architecture inside an App Worker, DevIndex achieves true concurrency. While the Main Thread paints, the App Worker handles state changes, lazy loads modules, and coordinates with other workers. Combined with a hierarchical MVVM approach and a "Fat Client" philosophy, this ensures that even when processing 50,000 developer records, the UI remains perfectly fluid and maintainable.