neo.mjs v2.2: Main thread MicroLoaders for your Apps
I am still moved by the neo.mjs nomination:
This article covers the enhancements of the version v2.2.0 Release.
Content
- Introduction
- The new index file structure
- What is a MicroLoader?
- npx neo-app v2.2.1
- The examples collection is now available inside workspaces
- How to migrate a workspace to v2.2?
- Final thoughts
1. Introduction
Let us take a look at the format of an index.html file prior to the v2.2 release:
You were able to define all available configs of DefaultConfig.mjs directly inside the index file. While this was not too bad regarding the developer experience, it did not feel clean to mix JS and html in one file.
More importantly, the values of the configs change for build versions (dist/development and dist/production). While the html-webpack-plugin could to some degree handle this, we needed to store the values for all demo apps and examples inside separate config files.
It even got worse when we wanted to move apps into different folder hierarchy levels (e.g. into a neo.mjs workspace). This happens for the docs app and is now possible for the entire examples collection as well.
2. The new index file structure
We now have a sharp separation of the html and the JS config variables:
Every app now has a top level neo-config.json file:
Obviously both files do get minified for the dist/production
output.
While it was painful to have build programs parse the index.html files as a string, it is trivial to do the same with JSON. The build programs got a lot cleaner and smaller.
The html-webpack-plugin
dependency got removed. You can now manually change your index.html files and the changes will get kept inside the dist environments.
3. What is a MicroLoader?
If you look at the old version of the index file again, you will notice that the Neo.config
got defined before the Main Thread file got loaded. Keeping this timing is key, since Main.mjs will import the Neo.mjs file first.
This one merges the custom framework config with the DefaultConfig at the very bottom:
Neo.assignDefaults(Neo.config, DefaultConfig);
The MicroLoader looks as follows:
We are fetching the config file, transforming the JSON into a JS Object and spreading its content directly into Neo.config
.
Minified it looks like this:
Well, we could remove the 2 “;” chars at the end, but the word “micro” really fits, doesn’t it?
We can use a variable as the path inside the import()
call, since the MicroLoader file will never get touched by webpack.
The App, Data and Vdom workers do not need to use a MicroLoader of manually fetch the Neo.config
JSON files. As soon as a worker does get created, the config will get sent using a postMessage.
4. npx neo-app v2.2.1
The new neo.mjs is coupled with a new version of the create-app repo:
You do not need to install or clone this repo, to generate a new workspace just enter:
npx neo-app
into your terminal (or CMD on Windows).
The npm CLI has some really bad “hickups” at the moment. For me, npm update no longer works. Manually changing a dependency and running npm install results in “success”, but just installs the old version again.
The only way for me to get around it (except for downgrading the CLI) is to manually delete the package-lock.json file as well as the node_modules folder every time. Details here:
In case the npx call does not give you v2.2+, please use:
npx neo-app@latest
The generator programs got adjusted.
In case anything does not work with the npx script locally:
This repo contains the default output, so you can clone or fork it as a starting point.
5. The examples collection is now available inside workspaces
Workspaces now have a new script inside their package.json:
npm run copy-examples
This script only takes 0.2s on my machine and adjusts all neo-config.json files on the fly:

You can take a look into the code here:
neomjs/create-app/blob/master/resources/copyExamples.js
(or inside your workspace buildScripts folder)
In case you are wondering why we do not just copy the examples directly inside the npx call: there is a good reason.
There are no issues at all inside the neo development
environment, since this mode is using the real JavaScript code and does not need any builds or transpilations.
However, the dist/development
and dist/production
environments rely on webpack based builds. You are most likely familiar with the splitchunks plugin. You can use dynamic imports inside your app(s) and webpack will create separate bundles for the files you want to lazy load.
neo.mjs goes one step further: there is just one entry point for the Application worker: the Application worker itself. This one will fetch all possible app entry points (app.mjs) files as dynamic imports.
The result is that we are not only getting split chunks for bundles we lazy load inside one app, but we will also get cross app split chunks. This is very powerful in case you e.g. want to put multiple apps on one page, since there is close to no overhead.
The catch is that in case you copy the examples apps into your workspaces, these apps will increase the file size of the dist/*
App worker.

Without the examples inside your workspace, the map would just contain the first two lines.
My recommendation: I would clone or fork the neo.mjs repository and dive into the examples or demo apps code or browser output there.
If you want to get the examples into your workspace, you should delete the examples folder again before a deployment build to keep the App worker file size small. Afterwards you can copy the examples back in (as mentioned this only takes 0.2s).
6. How to migrate a workspace to v2.2?
The API on how to use the framework is still exactly the same. Only app shells did change. There are two strategies:
The first one is to just use npx neo-app
and copy the content of your app(s) over. Strongly recommended.
The second approach would be to also use the new default npx output:
neomjs/workspace and manually adjust the files inside your “old” workspace as needed. This means:
- Adding the neo-config.json files to your app(s)
- Adjust the index.html files of your app(s)
- Delete the docs app folder and copy over the version from the demo workspace
- [Optional] Adding the copy-examples script into your package.json
- buildScripts/myApps.json: “apps” is no longer an object containing configs, but an array of names. E.g.
"apps": ["Docs", "MyApp"]
- delete the buildScripts/webpack folder
- Copy the MicroLoader.mjs file into the workspace src folder
- run the
buildAll
program
The framework is in a very good shape now, no plan on my end to change the app structures again any time soon.
7. Final thoughts
While this update might not look like a big deal at a first glance, it resulted in several 100 commits on my end. neo.mjs has a huge collection of examples already and adjusting the build programs on all levels was a challenge.
Feel free to take a look into the commit logs of the neo and create-app repos.
In case you are fluent in German: I got invited into the Working Draft Podcast on Monday (May 31). The topic is workers, shared workers and multi window apps.
The next big item on my roadmap is to make the “getting up to speed” part easier for you. I will write more guides on how to create components as well as apps. I also would love to work more on the component side myself, since this is the fun part.
I will also enhance the theming engine a bit more to include child neo workspaces. You can expect a new article on mirco frontends soon!
You can find the repo here:
In case you are just starting with using neo, you are welcome to join the Slack Channel:
Feedback appreciated!
Best regards & happy coding,
Tobias