Reactive Communication in Qwik


Introduction
Sometimes in a Qwik app, we want our components to communicate. Specifically, we might want a child component to notify its parent when something important happens inside it. This is a common need in developing web apps for example.
Let’s take a simple example:
Imagine we are building a form that asks the user for their age. If the user is 18 or older, we want to show a message that says, "You are an adult!" But the input field for the age lives in a child component. So, how do we let the parent know when the age is 18 or more?
We’ll walk through how to achieve this in Qwik using signals and useTask$.
To keep this tutorial short, we won’t go over the steps on how to install and set up Qwik. You check out the steps here https://qwik.dev/docs/getting-started/
The Goal
Child component has an age input.
Parent listens for updates and reacts when the age is 18 or above.
Step 1: Parent Component
// Parent.tsx
import { component$, useSignal, $ } from '@builder.io/qwik';
import AgeInput from './AgeInput';
export default component$(() => {
const isAdult = useSignal(false);
return (
<div>
<h2>Enter your age:</h2>
<AgeInput
onStatusChange$={$((status: boolean) => {
isAdult.value = status;
})}
/>
{isAdult.value && <p>You are an adult!</p>}
</div>
);
});
Here, the parent:
Tracks if the user is an adult using isAdult signal.
Passes a callback function onStatusChange$ to the child.
Step 2: Child Component
// AgeInput.tsx
import { component$, useSignal, useTask$, QRL } from '@builder.io/qwik';
export interface AgeInputProps {
onStatusChange$: QRL<(isAdult: boolean) => void>;
}
export default component$<AgeInputProps>((props) => {
const age = useSignal<number>(0);
useTask$(({ track }) => {
track(() => age.value);
props.onStatusChange$(age.value >= 18);
});
return (
<input
type="number"
value={age.value}
onInput$={(e) => (age.value = parseInt((e.target as HTMLInputElement).value))}
/>
);
});
In the child:
age is a reactive signal.
We use useTask$ to watch changes to age.value.
When the age updates, we call the parent's onStatusChange$ with a boolean indicating if the user is an adult.
Why Use useTask$ Here?
useTask$ allows the child to react to changes over time in a clean, reactive way. You might think, "Why not just call the function directly in onInput$?" That would work, but useTask$ gives us a few key benefits:
We keep logic separated from UI events.
We can easily track multiple values if needed in future.
Our code becomes more reactive and maintainable.
Summary
This pattern lets components collaborate in a reactive way:
Role | What It Does |
Parent | Receives updates from the child and decides what to do |
Child | Tracks some internal state (like age) and notifies the parent when it changes |
useTask$ | Observes the internal state and triggers side effects |
This example might be simple, but the pattern scales beautifully. Anytime you want a component to inform another about changes, this is a great tool to have in your belt.
Subscribe to my newsletter
Read articles from george isaac directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
