Components
Components are basic building blocks of Qwik Applications. Qwik components are unique in that:
- Qwik components automatically get broken down into lazy-loaded chunks by the Optimizer. (See Optimizer discussion)
- Are resumable. (A component can get created on a server and continue its execution on the client.) (See resumable discussion)
- Can render independently of other components on the page. (See rendering discussion)
component$()
A component is a small, reusable piece of code that can be used to build a UI.
In Qwik, they are declared using the component$
method:
import { component$, useSignal } from '@builder.io/qwik';
export const MyCmp = component$((props: MyCmpProps) => {
// Declare local state
const count = useSignal(0);
// Returns JSX
return (
<>
<span>
Hello, {props.name} {count.value}
</span>
<div>Times: {count.value}</div>
<button
onClick$={() => {
// This will update the local state and cause a re-render.
// Reactivity is at Qwik's core!
count.value++;
}}
>
Increment
</button>
</>
);
});
NOTE
- For an explanation of
$
see Lazy-loading and Optimizer discussion.- For a detailed discussion of props, see Component/props discussion.
Props
Props are used to pass data into the component. Props are declared as named arguments of the component.
In this example a component Item
declares optional name
, quantity
, description
, and price
.
interface ItemProps {
name?: string;
quantity?: number;
description?: string;
price?: number;
}
export const Item = component$((props: ItemProps) => {
return ...;
});
Using components
Qwik components can use other components.
export const Counter = component$((props: {step?:number, initial?: number}) => {
...
});
export const MyApp = component$(() => {
return (
<>
<div>Single: <Counter /></div>
<div>Dozens: <Counter step={12}/></div>
</>
);
});
The above example shows how the MyApp
component can use the Counter
component. The second example shows how one can use binding to pass values to the Counter
component's props.
Re-rendering on Reactivity
Qwik components are reactive on the component level. Component props, as well as stores, are proxies. These proxies track reads as well as writes.
- A proxy-read during OnRender method execution lets Qwik know that the OnRender method depends on a given property. A read creates a subscription on that property. In our example, OnRender reads
{store.count}
, which creates a subscription that tells Qwik that whenever thestore.count
changes, the component should be invalidated. - A proxy-write notifies Qwik that all associated subscriptions should be invalidated.
When components get invalidated, they are added to the invalidation queue, which is flushed (rendered) on the next requestAnimationFrame
. This acts as a form of coalescing for component rendering.
For a detailed discussion of reactivity, see related discussion.
Storing a reference
Qwik provides the ability to store a reference to any component. To do so, you have to create a signal and pass the signal as ref
attribute to the component. After the component was mounted, the reference has been stored on the signal. Have a look at the example below:
import { component$, useVisibleTask$, useSignal } from '@builder.io/qwik';
export default component$(() => {
const width = useSignal(0);
const height = useSignal(0);
const outputRef = useSignal<Element>();
useVisibleTask$(() => {
if (outputRef.value) {
const rect = outputRef.value.getBoundingClientRect();
width.value = Math.round(rect.width);
height.value = Math.round(rect.height);
}
});
return (
<div>
<div ref={outputRef} style={{ border: '1px solid red', width: '100px' }}>
Change text value here to stretch the box.
</div>
<div>
The above red box is {height.value} pixels high and {width.value} pixels wide.
</div>
</div>
);
});
Lazy Loading
The component also serves an important role when breaking parent-child relationships for bundling purposes.
export const Child = () => <span>child</span>;
const Parent = () => (
<section>
<Child />
</section>
);
In the above example, referring to the Parent
component implies a transitive reference to the Child
component. When the bundler is creating a chunk, a reference to Parent
necessitates bundling Child
as well. (Parent
internally refers to Child
.) These transitive dependencies are a problem because it means that having a reference to the root component will transitively refer to the remainder of the application—something which Qwik tries to avoid explicitly.
export const Child = component$(() => {
return <span>child</span>;
});
export const Parent = component$(() => {
return (
<section>
<Child />
</section>
);
});
In the above example the Optimizer transforms the above to:
const Child = componentQrl(qrl('./chunk-a', 'Child_onMount'));
const Parent = componentQrl(qrl('./chunk-b', 'Parent_onMount'));
const Parent_onMount = () => qrl('./chunk-c', 'Parent_onRender');
const Parent_onRender = () => (
<section>
<Child />
</section>
);
NOTE For simplicity, not all of the transformations are shown; all resulting symbols are kept in the same file for succinctness.
Notice that after the Optimizer transforms the code, the Parent
no longer directly references Child
. This is important because it allows the bundler (and tree shakers) to freely move the symbols into different chunks without pulling the rest of the application with it.
So what happens when the Parent
component renders and Child
component has not yet been downloaded? First, the Parent
component renders its JSX like so.
<div>
<section>
<div></div>
</section>
</div>
As you can see in the above example, the <div/>
acts as a marker where the Child
component will be inserted once it is lazy-loaded.
Mental Model
The Optimizer splits Qwik components into the host element and the behavior of the component. The host element gets bundled with the parent component's OnRender method, whereas the component's behavior is something that gets lazy-loaded on an as-needed basis.
API Overview
State
useSignal(initialStateObject)
- creates a reactive valueuseStore(initialState)
- creates a reactive object that can be used to store statecreateContextId(contextName)
- creates a context referenceuseContextProvider()
- provides a value to a given contextuseContext()
- reads the value of the current context
Styles
useStylesScoped$()
- appends scoped styles to the componentuseStyles$()
- appends unscoped styles to the component
Events
useOn()
- appends a listener to the current component programaticallyuseOnWindow()
- appends a listener to the window object programaticallyuseOnDocument()
- appends a listener to the document object programatically
Lifecycles
useTask$()
- defines a callback that will be called before render and/or when a watched store changesuseResource$()
- creates a resource to asyncronously load datauseVisibleTask$()
- defines a callback that will be called after render in the client only (browser)
Other
$()
- creates a QRLnoSerialize()
useErrorBoundary()
Components
<Slot>
- declares a content projection slot<SSRStreamBlock>
- declares a stream block<SSRStream>
- declares a stream<Fragment>
- declares a JSX fragment