Containers
Every Qwik application is contained inside a element, usually the <html>
element. This element becomes the container for the application. The container is the root element for the application and all components, state and events are contained within.
<html q:container="paused" q:version="0.12.1" q:base="/build">
...
</html>
Container Attributes
Since containers are implicitally rendered by the Qwik runtime, it's not possible to define custom HTML attributes using JSX, however, the SSR render APIs, like renderToString
and renderToStream
, provide the containerAttributes
option to define custom attributes:
renderToStream(<Root />, {
containerAttributes: {
lang: 'en',
},
});
The code above will render the following HTML:
<html lang="en" q:container="paused" q:version="0.12.1" q:base="/build">
...
</html>
In the example above, the lang
attribute is added to the <html>
container element.
Note that this attribute will not be reactive, if the app needs to change this value dynamically, it will need to do through manual DOM manipulation.
Along with the custom attributes, Qwik will automatically render the q:container
, q:version
, q:render
and q:base
attributes.
-
q:container
- The container state. This attribute is used by the Qwik runtime to determine if the container is in a paused state or not. The value of this attribute is eitherpaused
orresumed
. -
q:version
- The version of the Qwik runtime. -
q:render
- Indicated how the container was rendered. The value of this attribute is eitherssr
,ssr-dev
,dom
ordom-dev
.
Properties
Since the runtime ensures isolation across containers, several containers can coexist within the same document, a container can even contain another nested container, this property allows containers to break up an application into smaller parts.
- 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.
<html q:container="paused" q:version="0.12.1" q:base="/build">
<head>
<title>My Qwik Application</title>
</head>
<body>
<header q:container="resumed" q:version="0.11.1" q:base="https://server.a/build">
<div>
<h1>This is a header form a container</h1>
</div>
</header>
<footer q:container="paused" q:version="0.13.0" q:base="https://footer.server.b/">
<div>
<h1>This is a footer</h1>
</div>
</footer>
</body>
</html>
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 use cases:
- Routing
- Micro-frontend architecture
Routing
A typical site is composed of two logical parts:
- The navigation that tends to stay constant across many pages.
- 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:
- 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.
- 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.
- 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.