Qwik's component system design follows several core principles that enable high performance and fine-grained code splitting:
1. Component Definition
Qwik components use the $ prefix to identify them, which tells the compiler that the component needs special handling:
tsximport { component$ } from '@builder.io/qwik'; export const MyComponent = component$(() => { return <div>Hello Qwik</div>; });
component$ is a compile-time macro that converts the component into a resumable format.
2. Component Features
Lazy Loading
All Qwik components are lazy-loaded by default. The compiler automatically splits each component into independent chunks, which are only loaded when the component is rendered.
Resumability
The component's state and execution context are serialized into HTML and can seamlessly resume execution on the client.
Fine-grained Updates
Qwik only updates DOM nodes that have changed, rather than re-rendering the entire component tree.
3. Props and State
Props
Props are type-checked at compile time and automatically handled during serialization:
tsxexport const ChildComponent = component$((props: { name: string; count: number }) => { return <div>{props.name}: {props.count}</div>; });
State Management
Qwik provides two main state management approaches:
useSignal: For simple value state management
tsximport { useSignal } from '@builder.io/qwik'; export const Counter = component$(() => { const count = useSignal(0); return ( <button onClick$={() => count.value++}> Count: {count.value} </button> ); });
useStore: For complex object state management
tsximport { useStore } from '@builder.io/qwik'; export const Form = component$(() => { const form = useStore({ name: '', email: '' }); return ( <form> <input value={form.name} onInput$={(e) => form.name = (e.target as HTMLInputElement).value} /> </form> ); });
4. Event Handling
Qwik event handlers use the $ suffix, indicating they are resumable:
tsxexport const Button = component$(() => { const handleClick$ = () => { console.log('Button clicked'); }; return <button onClick$={handleClick$}>Click me</button>; });
Event handler functions are automatically split and lazy-loaded by the compiler.
5. Lifecycle
Qwik lifecycle hooks also use the $ suffix:
useTask$: Executes when component mounts and updatesuseVisibleTask$: Executes when component becomes visible (for client-specific logic)useResource$: For asynchronous data fetching
tsxexport const DataComponent = component$(() => { useTask$(() => { console.log('Component mounted or updated'); }); return <div>Data Component</div>; });
6. Component Communication
Parent-Child Communication
Pass data through props:
tsxexport const Parent = component$(() => { return <Child message="Hello from parent" />; }); export const Child = component$((props: { message: string }) => { return <div>{props.message}</div>; });
Context
Use useContext and Context for cross-component communication:
tsximport { createContext, useContext } from '@builder.io/qwik'; const ThemeContext = createContext('light'); export const ThemeProvider = component$(() => { return ( <ThemeContext.Provider value="dark"> <Child /> </ThemeContext.Provider> ); }); export const Child = component$(() => { const theme = useContext(ThemeContext); return <div>Current theme: {theme}</div>; });
7. Component Optimization
The Qwik compiler automatically handles component optimization, so developers don't need to manually use React.memo or similar optimization techniques. The compiler will:
- Automatically split component code
- Only load necessary code
- Avoid unnecessary re-renders
- Optimize event handler loading
Summary: Qwik's component system achieves automatic code splitting and lazy loading through compile-time optimization and unique $ syntax, allowing developers to write high-performance applications without worrying about low-level performance optimization details.