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$, useStore } from '@builder.io/qwik';

// Qwik components can be asynchronous
export const MyCmp = component$(async (props: MyCmpProps) => {
  // Declare local state
  const state = useStore({
    count: 0,
  });

  // Returns JSX
  return (
    <>
      <span>
        Hello, {props.name} {state.count}
      </span>
      <div>Times: {state.count}</div>
      <button
        onClick$={() => {
          // This will update the local state and cause a re-render.
          // Reactivity is at Qwik's core!
          state.count++;
        }}
      >
        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.

const Counter = component$((props: {step?:number, initial?: number}) => {
  ...
});

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 the store.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.

Lazy Loading

The component also serves an important role when breaking parent-child relationships for bundling purposes.

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.

const Child = component$(() => {
  return <span>child</span>;
});

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

  • useStore(initialState) - creates a reactive store
  • useRef$(initialState) - creates a ref
  • createContext(contentName) - creates a context
  • useContextProvider$() - creates a context provider
  • useContext$() - use a context

Styles

  • useStyleScoped$() - creates a scoped style
  • useStyle$() - creates a global style

Events

  • useOn() - creates an event listener
  • useOnWindow() - creates a window event listener
  • useOnDocument() - creates a document event listener

Lifecycles

  • useMount$() - creates a post rendering
  • useServerMount$() - creates a post rendering
  • useWatch$() - creates a watch
  • useClientEffect$() - creates a post rendering
  • useResource$() - creates a resource

Other

  • $() - creates a QRL
  • noSerialize()
  • useErrorBoundary()

Components

  • <Slot> - creates a slot
  • <SSRStreamBlock> - creates a stream block
  • <SSRStream> - creates a stream
  • <Fragment> - creates a fragment

See Also

Made with ❤️ by