Fitz: A Compiled Language with a Frontend Story
Fitz is taking a novel approach to web development by combining a compiled language backend with a frontend component model that offers a development experience akin to modern JavaScript frameworks. This series, Fitz, explores the intricacies of this system, and this installment focuses on the frontend's developer loop. Fitz compiles its single-file components, denoted by the .fitzv extension, directly into WebAssembly (WASM). This allows for the creation of native binary components that can be executed in the browser. The core of this frontend development experience is the .fitzv format, which encapsulates component state, event handling, and a template structure, drawing parallels to popular frameworks like Vue and Svelte. The compilation process itself is straightforward, targeting WASM for client-side execution:
fitz build --bin web --target wasm-client # → target/wasm/web/{web.js, web_bg.wasm}
This command transforms the .fitzv components into JavaScript bindings and the WASM binary, forming the foundation for instant feedback during development.
The Vite-Inspired Dev Loop
The primary innovation Fitz introduces is a development loop that directly mirrors the instantaneous feedback developers expect from tools like Vite. For a compiled language, achieving this level of responsiveness, especially with features like hot module replacement (HMR) and state preservation, is a significant challenge. Traditional compiled languages often involve lengthy build cycles that interrupt the creative flow. Fitz tackles this by architecting its build system to facilitate rapid iteration.
When a .fitzv file is modified, Fitz doesn't recompile the entire application or even the entire WASM module. Instead, it performs a targeted recompilation of only the changed component. This compiled WASM is then efficiently swapped into the running application. Crucially, the state of the existing component is preserved across these updates. This means that if a developer is interacting with a form, or has a component in a specific UI state, that state is maintained even after the component's code has been updated. This is analogous to how Vite updates modules in the browser without a full page reload, keeping the application's current state intact.
The system also provides a live manifest. This likely refers to an up-to-date index or registry of available components and their states within the application. As components are added, removed, or updated, this manifest is dynamically refreshed, ensuring that the running application always reflects the latest code structure. This dynamic manifest is key to the hot-reloading mechanism, allowing the runtime to correctly identify and swap in the updated components.

Technical Underpinnings: WASM and JavaScript Bindings
The compilation of .fitzv components to WebAssembly is central to Fitz's performance and its ability to achieve a Vite-like development experience. WASM provides a near-native execution environment in the browser, offering significant performance advantages over traditional JavaScript for computationally intensive tasks. However, direct interaction between WASM modules and the JavaScript environment requires a bridge: JavaScript bindings.
The fitz build --target wasm-client command not only produces the web_bg.wasm file but also generates web.js. This JavaScript file acts as an intermediary, exposing functions and data structures from the WASM module to the JavaScript runtime and vice-versa. This allows the Fitz frontend to seamlessly integrate WASM components into a standard web page, manage their lifecycle, and handle events.
The hot-reloading mechanism relies heavily on this binding layer. When a component is recompiled, the new WASM module is loaded, and the JavaScript bindings are updated to point to the new module. State preservation is achieved by serializing the component's state before the old WASM module is unloaded and deserializing it when the new module is initialized. This process needs to be highly optimized to be imperceptible to the developer.
State Preservation: The Holy Grail for Compiled Frontends
State preservation is often the most challenging aspect of implementing hot-reloading for compiled languages or systems that don't inherently support incremental updates. In Fitz's case, the .fitzv components are designed with state management as a first-class concern. When a developer modifies a component, the Fitz tooling intercepts the change. Before the old WASM code is discarded, the current state of the component is captured. This might involve serializing internal variables, component props, or any other relevant data that defines the component's current condition.
Once the new WASM module for the updated component is compiled and loaded, the captured state is then deserialized and passed back into the new instance of the component. This ensures that the user's interaction flow is not disrupted. Imagine editing a complex form with multiple fields filled in; with effective state preservation, the user wouldn't have to re-enter any data after a code update. This feature is critical for maintaining developer productivity and a smooth workflow, especially as applications grow in complexity.
The Live Manifest: Dynamic Component Management
The concept of a "live manifest" suggests a dynamic registry that the Fitz runtime uses to manage its components. In a typical web application, components are loaded and managed by the JavaScript framework or bundler. Fitz's approach seems to integrate this management directly into its compiled WASM output and its development server.
This live manifest likely plays several roles:
- Component Discovery: It allows the runtime to know which components are available and how to instantiate them.
- Hot Reload Coordination: It facilitates the process of identifying which specific component needs to be updated and orchestrating its replacement.
- State Mapping: It might also be responsible for mapping the preserved state to the correct component instance after an update.
The "live" aspect implies that this manifest is not static but is updated in real-time as code changes are detected. This continuous synchronization between the development environment and the running application is what provides the seamless, Vite-like experience. It’s the glue that holds the rapid compilation, state preservation, and dynamic loading together.
Implications for Compiled Languages and WebAssembly
Fitz's approach has significant implications for the broader adoption of compiled languages and WebAssembly in frontend development. Historically, the primary barrier to entry for languages like Rust, Go, or Zig in web frontend has been the development experience. The slow compile times and lack of robust hot-reloading capabilities made them less appealing compared to interpreted languages with mature tooling.
By abstracting away the complexities of WASM compilation and providing a Vite-level developer experience, Fitz lowers this barrier considerably. It demonstrates that it's possible to build performant, compiled applications for the web without sacrificing developer velocity. This could encourage more developers to explore compiled languages for frontend projects, potentially leading to more robust and performant web applications.
The success of Fitz's dev loop also highlights the growing maturity of the WebAssembly ecosystem. As WASM becomes more capable, with features like component model proposals and improved tooling, it opens up new possibilities for how web applications are built. Fitz is at the forefront of this evolution, showing a practical and compelling use case for WASM beyond just performance-critical modules.
The Unanswered Question: Scalability of State Preservation
While Fitz's hot reload and state preservation are impressive, a key question remains unaddressed: how well does this system scale with application complexity? Preserving the state of a single component is one thing, but managing and preserving the state of a large, interconnected application with hundreds of components, each potentially holding complex internal data, presents a much greater challenge. The serialization and deserialization process for deeply nested or large state objects can become a performance bottleneck. Furthermore, ensuring that the preserved state remains compatible with potentially breaking changes in component APIs across multiple updates will require sophisticated tooling and developer discipline. The effectiveness of this system in large-scale, production environments will be the true test of its viability.
