BACKGROUND
Most modern web applications are built to be served from capable servers with fast networks. Assets get split into chunks, lazy-loaded, cached separately, and optimized for that environment. When a client came to us with an industrial sensor project, none of that applied. The sensor shipped with a low-power embedded server, the kind of hardware designed to do one job well, not to host web applications. The constraint was hard: one file, everything inside it, no exceptions. The application needed to pull live data from the sensor and give technicians a clean interface for configuration, all served from hardware that was never meant to run a modern SPA.
HYPOTHESIS
We believed existing build tooling could be pushed into producing a true single-file output if we were willing to work against its defaults. The tools were built for the opposite use case, but the primitives were probably there.
APPROACH
The embedded server changed every assumption we normally make about web delivery. No CDN, no chunked loading, no expectation that the client would make multiple requests and reassemble a page. The device had limited memory and processing power, and the application had to initialize from a single payload without leaning on anything external.
We started with standard bundler configurations. Vite is fast and well-supported, so it was the right starting point, but it is designed around code splitting as a feature. Every default it ships with pushes output toward multiple optimized files. Getting it to do the opposite meant going against the grain of how it was built.
The first real breakthrough was finding a plugin capable of inlining non-code assets. JavaScript and CSS are easy enough to consolidate, but images and icons are a different problem. Normally they get referenced as separate files and fetched on demand. To meet the single-file constraint, every image and icon had to be base64-encoded and embedded directly in the HTML. We wrote custom build overrides to handle that encoding step and wired it into the Vite pipeline so the final output was genuinely self-contained.
The other complication came from Quasar, the UI framework we were using. A version-specific bug prevented the favicon from being inlined correctly. It was a small asset but it was enough to break the single-file requirement. We tested across versions, identified where the issue was absent, and switched. That kind of debugging is tedious because the surface area is narrow and the error messages do not point you in the right direction. It took methodical version isolation to track it down.
The final file loaded and ran without performance issues on the embedded hardware. For a configuration and data interface used by technicians in the field, the experience was indistinguishable from a normally served web app.
TAKEAWAYS
01
The environment your application runs in should drive your build strategy, not the other way around. Embedded systems have real constraints: limited memory, no network assumptions, no external dependencies. When those constraints are hard, the entire approach to bundling and delivery has to change. Teams that start from tooling defaults and try to adapt them after the fact spend a lot of time fighting configuration. Starting from the deployment environment and working backward is faster.
02
Build tools have assumptions baked in, and understanding them is part of the job. Standard bundlers treat code splitting as a best practice because for most projects it is. When your environment is genuinely unusual, you have to understand what the tool is optimizing for before you can redirect it. Teams that treat tooling as a black box get stuck the moment a project steps outside the common case.
03
Version-specific bugs are real and worth tracking methodically. We lost time to a Quasar bug that only appeared in certain versions and only mattered because of our inlining requirement. The lesson is not to avoid frameworks. It is to isolate version as a variable when something breaks unexpectedly and test deliberately rather than randomly upgrading or downgrading until something works.