Skip to content

Commit dc8052e

Browse files
committed
chore: blog for 1.14 release
1 parent 4571b3c commit dc8052e

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
---
2+
title: 'Qwik 1.14: Module Preloader'
3+
authorName: 'Wout Mertens'
4+
tags: ['Web development']
5+
date: 'April 23, 2025'
6+
canonical: 'https://qwik.dev/blog/qwik-1-14-preloader'
7+
---
8+
9+
import { ArticleBlock } from '~/routes/(blog)/blog/components/mdx/article-block';
10+
import { DiscordLink } from '~/routes/(blog)/blog/components/mdx/discord-link';
11+
import CodeSandbox from '~/components/code-sandbox/index.tsx';
12+
13+
<ArticleBlock>
14+
15+
# Qwik 1.14 Introduces a Smarter, Simpler Preloader
16+
17+
With the release of Qwik 1.14, we are introducing a major enhancement to our JavaScript loading strategy: **a new, smarter preloader**. This preloader ensures that JavaScript QRL segments needed by your users are downloaded before they're required, significantly improving application responsiveness.
18+
19+
```json
20+
"@builder.io/qwik": "~1.14.0",
21+
"@builder.io/qwik-city": "~1.14.0",
22+
"eslint-plugin-qwik": "~1.14.0",
23+
```
24+
25+
Let’s explore what’s changed, why it matters, and how you can prepare your apps for this upgrade.
26+
27+
## What's New: Simplified Preloading in Qwik 1.14
28+
29+
Previously, Qwik relied on a service worker to cache and manage JavaScript segments. This gave us precise control over the cache, but also had some drawbacks:
30+
31+
- **Complexity**: Additional layer of service worker configuration and maintenance overhead.
32+
- **Performance Penalty**: Some delays in startup, and every fetch triggered the service worker, both of which are problematic for older devices.
33+
- **Insensitive**: The browser has more information about the user's device and network speed and can make better decisions about when to honor preload requests.
34+
35+
In Qwik 1.14, we've transitioned away from the service worker in favor of a solution leveraging [`<link rel="modulepreload">`](https://devdocs.io/html/attributes/rel/modulepreload). This change:
36+
37+
- Reduces complexity in deployment and maintenance.
38+
- Improves startup performance across all devices.
39+
- Eliminates unnecessary overhead during network fetches.
40+
- Leverages the browser's native preload mechanism, which is more efficient and better suited for our use case. For example, the browser can pre-parse the module before it is run, speeding up execution.
41+
42+
This change is possible because currently 93% of browsers support [`<link rel="modulepreload">`](https://devdocs.io/html/attributes/rel/modulepreload), which wasn't the case when we started using the service worker.
43+
44+
Rest assured, the preloader has a fallback mechanism that will work even if the browser doesn't support `modulepreload`.
45+
46+
## Quick Recap: Qwik Segments and Why They Matter
47+
48+
Before diving deeper, a quick reminder of Qwik QRL segments:
49+
50+
- **QRL segments** are pieces of code identified by the Qwik Optimizer, extracted from calls like `someFunction$(...)` and JSX attributes like `someAttr$={...}`, moved into a separate file, and turned into dynamic imports.
51+
- Qwik’s resumability architecture means there isn’t one entry point for your app; instead, the tiny embedded `qwikloader` orchestrates execution based on browser events (clicks, inputs, loads, custom events, etc.).
52+
53+
## Why Preloading Matters
54+
55+
This approach makes Qwik extremely efficient, but only if the segments are available exactly when needed. If a segment is not loaded yet, the browser has to wait for the network request, and possible import waterfalls after that. The preloader tries to make sure this doesn't happen.
56+
57+
When clicking, user will notice delays of 200ms or more, and it is quite easy for initial script loading to take way longer than that.
58+
59+
Therefore, we must make sure that the segments are available before the user needs them. This is where the preloader comes in.
60+
61+
## How the New Qwik Preloader Works (Technical Deep Dive – Optional)
62+
63+
*(Feel free to skip ahead if you just want the practical details!)*
64+
65+
Here's what's going on behind the scenes in Qwik 1.14:
66+
67+
- Qwik uses a bundler (Vite) to pack segments into `build/q-*.js` files called **bundles**. These bundles are ES modules,group multiple segments, and can import both static and dynamic dependencies.
68+
- Each Qwik segment has a dynamically adjusted probability of usage. For instance, running a `component$` segment usually indicates a high probability that a related `useVisibleTask$` segment will be needed soon, while something like a `window:beforePrint$` event might rarely be preloaded.
69+
- At build time, bundles are scored based on their interactivity impact and static import dependencies (which have a 100% probability).
70+
- This information is used to create a **bundlegraph**, a compact representation of all known bundles and their interaction probabilities.
71+
- This bundlegraph also stores information about which bundles are needed to render each route, for preloading `<Link />` tags.
72+
73+
During server-side rendering (SSR), Qwik collects the event handler segments. These are combined to find the most likely needed bundles.
74+
75+
A small inline script, injected below the SSR HTML output, performs the following sequence:
76+
1. Waits for the `window:load` event.
77+
2. Requests a browser idle callback.
78+
3. Inserts `<link rel="modulepreload">` tags for highly probable bundles.
79+
4. Concurrently dynamically imports the Qwik preloader module itself, setting up further probability-driven preloading.
80+
81+
Steps 1 and 2 combined ensure that the browser is focused on rendering the page. This allows the best LCP (Largest Contentful Paint) scores.
82+
Step 3 then asks the browser to preload the bundles that are most likely to be needed, while step 4 is loading the preloader module itself, which will be used later to preload other bundles. Step 3 reduces the latency for the most important bundles.
83+
84+
Another elegant detail: the preloader module itself is dynamically imported and later reused by Qwik core, preserving state seamlessly across the loading lifecycle.
85+
86+
Once the Qwik Core is active, it informs the preloader about newly important segments, which will update the probabilities, and trigger preloading of most likely needed bundles.
87+
88+
The browser will therefore only download code that is actually needed, before it is needed.
89+
90+
## How to Prepare Your App for Qwik 1.14
91+
92+
Here are some practical considerations for upgrading:
93+
94+
### Service Worker
95+
96+
The service worker is no longer used, but it needs to be unregistered for existing users. Both the qwik-city service worker and the experimental qwik prefetch service worker have been updated to do this.
97+
98+
So do not remove the service worker registration (in `root.tsx`) from your app just yet, wait until your users have loaded your site at least once.
99+
100+
### Cache Headers
101+
102+
With the service worker no longer forcibly caching segments, it’s important you configure appropriate HTTP caching headers.
103+
104+
The bundles and assets are normally served at `/build` and `/assets`, respectively. Their filenames contain a content-based hash, making them immutable. If they change, their name changes as well. You can therefore set long-lived caching headers for these.
105+
106+
The recommended header is:
107+
```
108+
Cache-Control: public, max-age=31536000, immutable
109+
```
110+
111+
You can re-add the starter for your deployment target with `npx qwik add`, which should update the deployment configuration to use the correct headers.
112+
113+
### Caveat for Translations
114+
115+
If your app uses [`compiled-i18n`](https://github.com/wmertens/compiled-i18n) or [`qwik-speak`](https://github.com/robisim74/qwik-speak), translated bundles (`build/[locale]/*.js`) might retain identical filenames across builds, even when translations change. Consider how long you want to cache these files for so users get the latest translations.
116+
117+
Note that this was also a problem with the service worker, and now you have the ability to configure the cache headers for these files, so it's a positive change.
118+
119+
### Qwik Insights
120+
121+
If you're using Qwik Insights, you don't have to do anything. The preloader is fully compatible with it and takes its recommendations into account.
122+
123+
---
124+
125+
*We'd love your feedback—let us know how this update improves your apps!*
126+
127+
</ArticleBlock>
Loading

packages/docs/src/routes/(blog)/data.ts

+11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import preloaderImage from './blog/(articles)/qwik-1-14-preloader/qwik-1-14-preloader.png';
2+
13
export const authors: Record<string, { socialLink: string }> = {
24
'The Qwik Team': { socialLink: 'https://bsky.app/profile/qwik.dev' },
35
'Jack Shelton': { socialLink: 'https://twitter.com/TheJackShelton' },
@@ -6,6 +8,7 @@ export const authors: Record<string, { socialLink: string }> = {
68
'Steve Sewell': { socialLink: 'https://twitter.com/steve8708' },
79
'Yoav Ganbar': { socialLink: 'https://twitter.com/HamatoYogi' },
810
'Miško Hevery': { socialLink: 'https://twitter.com/mhevery' },
11+
'Wout Mertens': { socialLink: 'https://twitter.com/wmertens' },
912
};
1013

1114
type BlogArticle = {
@@ -18,6 +21,14 @@ type BlogArticle = {
1821
};
1922

2023
export const blogArticles: BlogArticle[] = [
24+
{
25+
title: 'Qwik 1.14: Module Preloader',
26+
image: preloaderImage,
27+
path: '/blog/qwik-1-14-preloader/',
28+
tags: ['Web Development'],
29+
featuredTitlePosition: 'top',
30+
readingTime: 4,
31+
},
2132
{
2233
title: 'Moving Forward Together',
2334
image:

0 commit comments

Comments
 (0)