For the application to be resumable it needs to have lots of entry points. For example, clicking on button A is a different entry point than clicking on button B. When we implement an application we don't usually think about entry points and so we typically end up with just one entry point or the main() function.

The Optimizer does its job by looking for functions that end in $ character. For example, the Optimizer will transform a call to component$() into an entry point. Notice that the name of the function doesn't matter only that it ends with the $.

Every time you see $ you should think, there is a lazy-loaded boundary here. The implication is that the lazy-loaded content may require lazy-loading and hence can't be accessed synchronously.

While the Optimizer can serialize any data that Qwik can serialize, it has special handling for closures. Closures are functions that are created inside of other functions and that may capture variables in the lexical scope. The ability to serialize closures is a key property that makes Qwik resumable. Without closure serialization, it would be difficult to have resumable applications.


In this example notice that we have two lazy-loaded chunks because we have two $ in our code.

Open the Symbols tab and notice how Optimizer turned the onClick$ function into an entry point. Specifically, notice that the onClick$ entry point does not import the @builder.io/qwik module.

Now change the onClick$ callback to store.count++.

Open the Symbols tab again and notice that this time the Optimizer imported @builder.io/qwik and inserted useLexicalScope() call to restore the captured state of the event handler. Restoring the captured state of the function is what makes Qwik resumable.

Edit Tutorial