Containers

Containers are a way to break up an application into smaller parts. We call these parts containers. Containers bring several benefits. Each container on the page can independently be:

  • resumed: Each container can be resumed independently from all other components on the page. Independent resumability further reduces the amount of state which resume deserializes.
  • updated: Each container can be updated/replaced at any point using innerHTML. This allows a portion of the page to update without forcing a full re-fetch of a complete HTML document without downloading or executing JavaScript.
  • compiled: Each container can be compiled and deployed separately from other containers. Separate compilation is especially useful for large-scale applications and large-scale teams working on the applications.
  • versioned: Each container can run a different version of the Qwik framework. Allowing for the composability of the website from many small containers.

Containers can be nested in a tree and can communicate and share data. The inter-component communication requires that the components have well-defined boundaries, which we call container protocols.

Containers vs. Components

Containers sound very similar to components; what are the differences? You can think of a container as a more restricted component. However, components can do a few things which containers can't.

  • Containers can only have read-only properties passed into them. This restriction is because container inputs may need to be serialized for SSR requests.
  • Containers don't understand projection.
  • Containers can't modify the state which has been passed into them.

Components have restrictions:

  • Components must be compiled together and, as a result, share the same bundle artifacts and same Qwik version.
  • On pause, all of the components in the container are serialized together (and then they are resumed together.)

What do containers solve?

Containers allow multiple independent Qwik applications to run on the page and behave as a single application to the user. There are two most common uses cases:

  1. Routing
  2. Micro-frontend architecture

Routing

A typical site is composed of two logical parts:

  1. The navigation that tends to stay constant across many pages, and
  2. The outlet, which is the part of the page that changes based on which route the user navigated to.

We can model the two parts as two navigation and outlet containers. When the user first navigates to a route, the server responds with HTML, that contains containers for both the navigation and the outlet. Once the user navigates to the second route, there are three ways to solve the navigation:

  1. The simplistic approach is to make a full round trip and download an entirely new page. The main downside is that the application loses all of its states on the client.
  2. The classical approach is to treat any further navigation in JavaScript. We replace the current outlet component with the new outlet component and let the new component render. The disadvantage is that we need to download and execute the JavaScript.
  3. The Qwik approach treats the navigation and the outlet as two different containers. The first navigation downloads HTML representing the full page (with both containers.) The subsequent navigation fetches the HTML only for the outlet container. This approach is the best of both worlds. The navigation is fast (no JavaScript download or execution), and the application keeps its state in the parent container.

Micro-frontend

When an application gets very large, it becomes impractical to think of it as a single application. A better mental model is that many applications work together to give the user the impression of a single application.

For large apps, the teams also become large. Large teams usually have different goals and, as a result, different release schedules.

Containers allow a large team to break up the application into many smaller parts and treat each part as a unit with a separate deployment, testing, and version upgrade schedule.

Teams break up the application into containers and clearly define protocols between the containers. As long as the protocols are satisfied, each team can deploy the two containers independently.