Editor’s note: This article was peer reviewed by a member of the Qwik team for accuracy.
Qwik is a frontend framework for creating web applications that offers lightning fast page load times, regardless of the size and complexity of your site.
In this article, we’ll learn how to get started with Qwik and explore the building blocks of a Qwik application by creating a simple example app. We’ll also review the concept of hydration, learn why it slows down our apps, and see how Qwik avoids hydration, boosting our application’s performance. Let’s get started!
$
Before we can get started with Qwik, we first need to understand the different rendering options that are available in JavaScript frameworks.
Early JavaScript frameworks like AngularJS used client-side rendering. In client-side rendering, all logic, data fetching, routing, and templating happens on the client side. However, a significant downside of client-side rendering is that as an application grows, the time to render the initial page increases.
The concept of lazy loading was introduced to speed up the first render. With lazy loading, instead of sending your whole application to the browser at one time, you can divide your application into multiple smaller parts called chunks. When the application loads for the first time, you’ll only send and execute the required code on the client side, speeding up the first render.
In server-side rendering, your application’s first render is already done on the server side and sent to the browser as HTML, speeding up the time of the initial render significantly.
Most web apps are not a static webpage. Therefore, client-side frameworks must recreate the page after server-side rendering by attaching event listeners, creating a DOM tree, and restoring the application state to make it interactive. This process is called hydration. The downside of hydration is that even though the page is rendered on the user’s screen, it might not be interactive until hydration downloads and re-executes the code again.
As a framework without hydration, this is exactly the problem that Qwik tries to solve.
To get started, you’ll need to have Node.js ≥v16.8 installed.
Qwik comes with a CLI tool that helps you scaffold your application. First, generate your app using the following command:
npm create qwik@latest
The command above will prompt you to name the application; let’s choose dice-roller
. It’ll also prompt you to select the type of application; select Basic App (QwikCity). This will generate a directory called dice-roller
within a Qwik application.
Let’s navigate to the dice-roller
directory using the following command:
cd dice-roller
Now, let’s start the dev server to run our app:
npm start
To see how the app looks, open http://localhost:5174
.
If you open the project directory in your code editor, you’ll see the following folder structure:
├── README.md ├── node_modules ├── package-lock.json ├── package.json ├── public ├── src ├── tsconfig.json └── vite.config.ts
There are a few key things to understand here:
tsconfig.json
src
: By default, the src
directory is used for source codepublic
: The public
directory is used for static assetsFor this app, we’re using QwikCity, which is a meta framework for Qwik, just like Next.js is to React. We’ll use QwikCity to provide a directory-based router and access to the tree of components. If you open src/components/routes/index.tsx
, you’ll see the default route /
exporting a Qwik component with some content.
Replace the content of file with the following code, which simulates a six face dice roll:
import { component$, useStore } from "@builder.io/qwik"; export const randomValue = () => Math.floor(Math.random() * 6) + 1; export default component$(() => { const dice = useStore({ value: 1, }); return ( <div> <h2>Dice roller</h2> <h3>Dice value: {dice.value}</h3> <div> <button onClick$={() => (dice.value = randomValue())}>Roll</button> </div> </div> ); });
If you save the file and open the browser at https://localhost:5174
, you’ll see the following app:
Ignore the header and footer, they’re part of default application. The code that you changed is outlined with a red box. Let’s understand it in detail.
As the name suggests, the component$
method creates a component. The component$
method comes from the builder.io/qwik
package. Notice the $
symbol; it has a special meaning that we’ll discuss soon.
Again, like React, a component can have props
, which you can use to pass inputs to the component. A Qwik component uses jsx
. The JSX template is returned from a component that creates the UI.
useStore
is a Qwik Hook that allows you to create a state like React’s useState
. Whenever an app reads or writes these values, Qwik is aware of it, re-rendering the UI accordingly.
The onClick$
is an attribute that allows you to bind a click event handler on an element. You can create event handlers for various types of events. Finally, the randomValue
method generates a random value between one and six.
In summary, in Qwik, you have components, JSX, Hooks, and events, like React. However, there is a significant difference compared to React, and that’s how Qwik avoids hydration.
$
Qwik comes with an optimizer, which is responsible for extracting code for lazy loading. This optimizer goes through the app code, and when it encounters the dollar $
sign, it creates what is called a symbol. These symbols can then be easily lazy loaded.
In the component example above, you have two places where $
exists; first at component$
, and second in onClick$.
Qwik will generate symbols for the following:
onClick
event handlerThe $
sign is also a signal to the developer that the code has a special meaning.
After the code is rendered on the server-side, Qwik will generate HTML and send it to the browser. The markup will look like the following:
<html> <body q:base="/build/"> <!--qv q:id=0 q:key=Ncbm:0t_0--> <div> <h2>Dice roller</h2> <h3> Dice value: <!--t=1-->1<!----> </h3> <div> <button on:click="app_component_div_div_button_onclick_yuuzzqjuatw.js#app_component_div_div_button_onClick_YuUzZQjuATw[0]" q:id="2" > Roll </button> </div> </div> <!--/qv--> <script> /*Qwikloader script*/ </script> <script type="qwik/json"> {...json...} </script> </body> </html>
You might notice that there are no JavaScript event handlers on the HTML elements. That’s one reason why Qwik is fast when it comes to the first-render. No JavaScript means no hydration. Now, how does interactivity work?
Looking at the <button>
element, you’ll notice that it has a weird attribute, on:click
. The browser will ignore this attribute on its own. However, Qwik utilizes this attribute using Qwikloader, a small JavaScript code delivered as an inlined <script>
tag.
This attribute has a strange-looking value:
app_component_div_div_button_onclick_yuuzzqjuatw.js#app_component_div_div_button_onClick_YuUzZQjuATw[0]
This strange value is called QRL, or a Qwik URL. This URL provides the following information:
Using this URL, Qwikloader will know what code to fetch and execute from the chunk generated by the optimizer. But what about the state? How does the newly fetched code know the current state of the application?
Qwik also saves the state of the application in serialized form using a <script>
tag in the HTML. It looks something like the following:
<script type="qwik/json">{ "refs": { "8": "0!" }, "ctx": {}, "objs": [ { "value": "1" }, 1 ], "subs": [ [ "2 #6 0 #7 data" ] ] }</script>
Qwik uses the serialized state for the following reasons:
This ability to resume the application using lazy-loading and access serialized state without downloading all the application code is known as resumability.
Once you finish your application, you can deploy it to any environment where Node.js is available. Qwik provides built-in to integrations that allow you to quickly deploy to hosting services like Netlify, Vercel, and more.
You can add any integration by running the following command:
npm run qwik add
At the time of writing, Qwik supports the following integrations:
For instance, if you want to deploy on Netlify, you can run the following command:
npm run qwik add netlify-edge
Then, you can run the following commands to deploy your application to Netlify:
npm run build npm run deploy
In this article, we learned how to get started with Qwik. We covered some rendering techniques and explored a few of Qwik’s key APIs including components, hooks, and events.
We learned how Qwik makes applications resumable by using Optimizer, Qwikloader, and QRL. This is just the tip of the iceberg. To learn more, go to https://qwik.builder.io to learn more about QwikCity, Builder, Partytown and so on.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowReact Native’s New Architecture offers significant performance advantages. In this article, you’ll explore synchronous and asynchronous rendering in React Native through practical use cases.
Build scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.