Best Practices
Inline operations in templates
Qwik optimizer can better optimize the reactivity of the application if the operations are inlined in the template.
// Don't do this!
export default component$(() => {
const signal = useSignal(0);
const isBiggerThanZero = signal.value > 0 ? 'Bigger than zero' : 'Smaller than zero';
return (
<div>
<button onClick$={() => signal.value++}>+</button>
<button onClick$={() => signal.value--}>-</button>
<div>{isBiggerThanZero} - Current value: { signal.value }</div>
</div>
);
});
The above implementation will cause the whole template to be re-rendered when the signal changes. This is because the isBiggerThanZero
is not inlined in the template.
export default component$(() => {
const signal = useSignal(0);
return (
<div>
<button onClick$={() => signal.value++}>+</button>
<button onClick$={() => signal.value--}>-</button>
<div>
{signal.value > 0 ? 'Bigger than zero' : 'Smaller than zero'} - Current
value: {signal.value}
</div>
</div>
);
});
useVisibleTask$()
Avoid registering DOM events in Qwik allows to register event listeners in a declarative way, using the useOn()
or using JSX.
When using useVisibleTask
to programmatically register events, we are downloading and executing JavaScript eagerly, even if the event is not triggered.
// Don't do this!
useVisibleTask$(() => {
const listener = (event) => {
const mouseEvent = event as MouseEvent;
console.log(mouseEvent.x, mouseEvent.y);
};
document.addEventListener('mousemove', listener);
return () => {
document.removeEventListener('mousemove', listener);
};
});
The above implementation causes more JavaScript to load eagerly, rather than responding precisely to user events. Increased upfront JavaScript loading results in slower app performance.
Instead, use the useOnDocument()
hook to register events on the document
object, this way Qwik will not execute any JS until the event is triggered.
useOnDocument(
'mousemove',
$((event) => {
const mouseEvent = event as MouseEvent;
console.log(mouseEvent.x, mouseEvent.y);
// No manual clean up required!
})
);
When in doubt, instead of useVisibleTask$()
use:
useOn()
: listen to events on the root element ofthe current component
.useOnWindow()
: listen to events on thewindow
object.useOnDocument()
: listen to events on thedocument
object.
window
object
Avoid accessing the location from the Don't access window.location
directly, use useLocation()
hook instead.
// Don't do this!
useVisibleTask$(()=> {
if (window.location.href).includes('foo') {
//... do the thing
}
})
// or
if (typeof window !== "undefined") {
const queryParams = new URLSearchParams(window.location.search);
const query: Record<string, string> = {};
queryParams.forEach((value, key) => {
query[key] = value;
})
doTheThing(query);
}
Many actions related to location information can be executed during the initial server-side render, resulting in pure HTML without any JavaScript overhead.
By forcing this logic to run on the client side, it introduces increased upfront JavaScript and leads to eager loading.
Using the if typeof window !== "undefined"
pattern may cause the code to be skipped. On the server, the code block will be skipped since the window is always undefined.
While developers may be accustomed to code running twice, Qwik eliminates this necessity by providing a more efficient approach.
// Do this!
const location = useLocation();
if (location.url.href.includes('foo')) {
// Do the thing
}
doTheThing(location.url.searchParams);
Exception
When using SSG for purely static files, it's inevitable to rely on the server without current location information during the build time.
However, exercise caution! If the required information (such as query parameters) isn't needed until a user event occurs, incorporate the check within your event handling code.
This approach helps to prevent eager loading of JavaScript and improves performance.
See: useLocation() Docs