Mental Model

Qwik is very similar to other web frameworks on a high level. Qwik is a framework that renders a tree of components resulting in an interactive application.

The unique part of Qwik is not in what it does but in how it achieves its goals. Qwik's goal is to have instant-on applications, even on mobile devices. Qwik achieves this through two main strategies:

  1. Delay execution and download of Javascript for as long as possible.
  2. Serialize the execution state of the application and the framework on the server and resume it on the client.

The goal of Qwik is having only to download and execute the bare minimum of the application. The remainder of the document lists the problems the framework will encounter to minimize the amount of code that needs to be downloaded and executed.

1. Qwikloader and event handler serialization

The most expensive part for the startup for the current generation of frameworks is the cost of hydration. Hydration refers to attaching event listeners to the DOM elements to make the application interactive. Qwik does not require hydration because Qwik serializes the event handlers into HTML in the form of on:event attributes. Together with Qwikloader, Qwik can delay executing Javascript until user interaction. (See preloading for discussion on how to minimize the cost of loading code on interaction.)

Without serializing event handlers into the HTML, the framework would be forced to resume on application startup. Hydration would require downloading all components currently in HTML and executing their templates to determine where the event listeners need to be attached.

2. Code splitting

Qwikloader and event handler serialization from above can delay the execution of javascript until later. However, serializing event handlers will not have benefits if the first interaction will have to download the whole application. Qwik puts a lot of emphasis on breaking up the application into many small lazy loadable chunks through Optimizer so that only a tiny portion of the application needs to be downloaded on the first interaction. The amount of code downloaded should be proportional to the complexity of the interaction rather than just forcing the download of all components currently in HTML.

Without Optimizer, a typical application would end up a single chunk because all of the application parts are interconnected. Breaking up the application codebase requires explicit dynamic imports. Most current-generation frameworks make it best to place these lazy-loaded boundaries on routing boundaries only. Routing level lazy loading still results in chunks that are too big for the Qwik approach.

3. Bootstrap

All applications have a main entry point to get the application going. We call this the bootstrap code. Typically this involves importing the application root component and calling a framework method to render it. Rendering a root component, in turn, causes more components to be pulled in and rendered until the whole application renders.

On hydration, the bootstrap again takes a root component and calls a framework method to resume the current HTML (meaning re-attach the listeners as well as rebuild the framework internal state about the application component tree.)

Qwik does not need to do hydration. Qwik still has a bootstrap method, but that typically executes on the server to render the initial application. There is no rebootstrap on the client. Instead, Qwik serializes not only the application state into HTML but also the framework state as well. The serialized state allows the Qwik framework to continue execution where the server left off. Not requiring a rebootstrap is what makes Qwik resumable.

If the Qwik application required a rebootstrap in the browser, they would require pulling the root component and, with it, all child components. This would require an early download of the application code, the exact thing that Qwik aims to avoid.

4. State

A user interaction causes the browser to fire an event processed by the Qwikloader, which in turn downloads the specific event handler to process the user interaction. When the event handler starts execution, it needs the application state; otherwise, it will not perform useful work.

The current generation of frameworks rebuild application (and framework) state as part of the rehydration. Qwik explicitly avoids rehydration because doing so would require the download and execution of code. Instead, Qwik relies on serializing the application (and framework) state from the server into HTML and then restoring the state on the client. By deserializing (restoring) the state, the Qwik application can continue execution in the browser from where the server left off.

Without restoring the state, the application would have to rebuild the state before the event handler runs. The rebuild of the state would require downloading and executing application initialization code that would not fit into a reasonable handler response.

5. Reactivity

At this point, Qwik was able to download a minimal amount of code to process the event. The event most likely modified the application state, and therefore UI needs to be updated. To do this efficiently, Qwik must determine which components are invalidated due to the state change.

Most current-generation frameworks are not reactive, and they solve the above problem by simply rerendering all of the components from the root. (The frameworks can use tricks to limit the DOM updates and prune some tree branches to stay fast, but they still need to execute too many templates.) The consequence of this is that component templates are downloaded and executed even if no visible change is rendered. Qwik can't use this approach because doing so would require that most of the application would be downloaded on the first interaction.

An alternative approach is to use reactivity. A reactive framework keeps a graph of each state property and which component uses it. In this way, a reactive framework can efficiently answer which components need to be invalidated due to state change. However, to build up the graph, a framework must execute the whole application at least once to collect the graph information. The initial collection of the graph means that whole applications must be downloaded and executed before a framework can efficiently answer which component is invalidated due to a specific state change. This approach would invalidate the benefits of Qwik as it would force an early download and execution of the application.

Qwik is reactive on the component level. To avoid the early download and execution of the component, Qwik serializes the reactivity graph on the server and restores it on the client. In this way, Qwik can easily answer which components need to be invalidated without forcing the whole application to download to the browser eagerly.

6. Out-of-order Rendering

At this point, Qwik was able to download minimal code to process the event, restore the application state, and determine a minimal number of components that are invalidated due to the state change. The last major challenge is to rerender the invalidated components and not also require Qwik to render parent or child components.

The component that needs to be rendered will require props from the parent component. The Qwik serialization mechanism must serialize props on the server and restore props in the browser so that the parent component presence is not required. (A more complicated form of this is when the parent component creates projection children that child component does not render on the server. In the browser, the client component rerendering causes the projected content to be rendered. Qwik must restore the content children without forcing the parent component to rerender.)

Qwik also needs to prevent child components from being rerendered due to parent component rerendering. When the parent component rerenders, the child components do not get updated unless their props have been modified.

Lastly, Qwik needs to ensure that downloading the parent component template does not force the download of a child template. Typically a component template directly references a child component as symbols—this forces bundler to include child component in the parent component bundle because there is a direct reference. In practice, this means that having a reference to a root component transitively includes all child components as well. Qwik optimizer is specifically designed to prevent this so that having a reference to any component does not transitively include child components as well.

Summary

As you can see, Qwik is designed so that it can delay the download and execution of most of the code associated with the application for as long as possible. It achieves this through:

  1. The Optimizer: breaks up the application into many small lazy-loadable chunks.
  2. Resumability: runtime is designed so that it does not require hydration; instead, it serializes all of the application and framework state into HTML, allowing the application to resume execution where the server left off.