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$

Qwik components are declared with component$ API.

const Counter = component$((props: { step?: number; initial?: number }) => {
  const store = useStore({ count: props.initial || 0 });
  return $(() => (
    <button onClick$={() => store.count += props.step||1}>
      {store.count}
    </button>
  ));
});

NOTE:

  • For an explanation of $ see lazy-loading and Optimizer discussion.
  • For a detailed discussion of props, see Component/props discussion.

OnMount

Qwik components have an OnMount function. The OnMount function is invoked exactly once during the lifetime of the component. Remember that the component's lifetime may start at a server and continue on the client. For a component that is instantiated on the server, the OnMount function may never be downloaded to the client. This is common for layout components that are long-lived in your application.

const Counter = component$(
  function OnMount(props: {step?: number, initial?: number}) {
    const store = useStore({count: props.initial || 0});
    ...
    return ${...}
  }
);

An OnMount function can optionally take a reference to props object. See separate props discussion.

Typical use for the OnMount function is to declare stores, watches, or other third-party APIs that return objects. In our example:

const Counter = component$((props: {step:number, initial?: number}) => {
  const store = useStore({count: props.initial || 0});
  return ...;
});

In the above example, the OnMount function invokes useStore(), representing the component's state. (See Store discussion)

OnRender

The OnMount function is responsible for returning an OnRender function. OnRender function is invoked whenever the component needs to be re-rendered.

For layout components, it is not uncommon to never need to invoke the re-render outside of the initial render on the server. In such a case, the render function will never lazy-load.

const Counter = component$((props: { step?: number; initial?: number }) => {
  const store = useStore({ count: props.initial || 0 });
  // Return OnRender function
  return $(() => (
    <button onClick$={() => store.count += props.step||1}>
      {store.count}
    </button>
  ));
});

OnRender function is responsible for returning JSX. JSX is then reconciled against the DOM.

An important property of Qwik components is out-of-order rendering. This means that if a component is invalidated (needs re-rendering) it can re-render without forcing invalidation on parent or child components. This is very important to keep rendering to a minimum. While minimal rendering has positive performance implications, the main reason is to prevent unnecessary lazy-loading of other components' render functions.

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 function execution lets Qwik know that the OnRender function 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.

Events

Events handlers are closures placed in the JSX.

const Counter = component$(() => {
  const store = useStore({count: 0});
  return $(() => (
    <button onClick$={() => store.count++}>
      {store.count}
    </button>
  ));
});

In the above example, () => store.count++ is the event handler that is invoked on the user clicking on the <button>.

When () => store.count++ is invoked the store is a proxy that intercepts the count write. It then looks up all of the subscriptions on count (finds that Counter OnRender has a subscription due to {store.count}) and invalidates the associated components. Once the component is invalidated, the Counter OnRender method is lazy-loaded and executed to update the view.

NOTE: All reactive systems must at the beginning execute all of the templates in order to build up the subscription graph. Qwik is no different; however, this is performed, and the server and the subscription graph is then serialized into HTML and shipped to the browser. The browser can use the serialized graph to resume components without forcing a full-scale render to re-build the graph.

See Also

  • Lite components