Over the last several months, I have invested a significant amount of time in Qwik.
And whenever I mention that, there is an obvious question:
Why Qwik?
I already have significant experience with Angular, so I did not need another frontend framework simply to build complex applications.
And if my goal had merely been to expand into the JSX/TSX ecosystem, the obvious choice would probably have been React.
React has the ecosystem, the adoption, the libraries, the jobs and an enormous amount of community knowledge behind it.
So why spend months learning Qwik instead?
The short answer is that I wasn’t looking for another syntax.
I was looking for a different runtime model.
And that distinction matters.
Qwik and React may look similar. Architecturally, they are not.
At first sight, a Qwik component feels familiar to anyone who has worked with React:
import { component$, useSignal } from '@builder.io/qwik';
export const Counter = component$(() => {
const count = useSignal(0);
return (
<button onClick$={() => count.value++}>
Count: {count.value}
</button>
);
});
There are functional components.
There is JSX.
Props are passed through components.
State changes update the UI.
From a distance, you could reasonably think:
Isn’t this basically another React-like framework?
Not really.
The interesting part of Qwik starts exactly where the syntax stops being interesting.
1. Signals are not an optimization added later
One of the reasons Qwik immediately felt natural to me was its reactive state model.
Coming from modern Angular, I had already become very comfortable with signals.
A signal represents a reactive value:
const count = useSignal(0);
count.value++;
The important part isn’t the API.
The important part is the dependency model behind it.
Instead of thinking primarily in terms of:
State changed → render component again → determine what changed
I can think in terms of:
State changed → notify exactly what depends on that state.
This distinction becomes increasingly valuable as applications grow.
React’s model is fundamentally component-render oriented.
When state changes, React schedules rendering work. React then determines what must actually be committed to the DOM.
That architecture works extremely well and React has spent years becoming very good at it.
But it creates an interesting consequence: developers often end up thinking about render boundaries.
Should this value live higher or lower in the tree?
Should this component be memoized?
Is this callback stable?
Will this context update cause unnecessary work?
Modern React tooling and the React Compiler can reduce the amount of manual optimization required, but the underlying mental model is still different.
With signals, reactivity itself is more granular.
And Qwik was designed around this model from the beginning.
2. The real reason I chose Qwik: resumability
Signals were attractive.
But resumability is what made Qwik genuinely interesting to me.
To understand why, we need to look at what usually happens with SSR.
Suppose the server renders this:
<button>Buy product</button>
Sending HTML from the server is easy.
The difficult question is:
How does the browser know what happens when the user clicks that button?
In the traditional SSR model, server-rendered HTML is only part of the application.
The client still needs enough JavaScript to reconstruct the application runtime.
Conceptually, the browser has to recover things such as:
- the component tree;
- application state;
- event listeners;
- framework runtime information.
This process is generally known as hydration.
The server generated the visible result, but the client has to replay enough of the application to make that result interactive.
This creates an interesting architectural cost.
The server already did work.
Then the browser performs part of that work again.
Qwik asks a different question
Instead of asking:
How can we hydrate this application faster?
Qwik asks:
Why reconstruct the application if the server already knows what its state is?
That leads to resumability.
The server doesn’t simply generate HTML.
It also serializes enough information about the application for execution to be resumed later in the browser.
Conceptually:
Traditional SSR
Server
↓
Render application
↓
HTML
↓
Browser
↓
Download JS
↓
Rebuild application runtime
↓
Attach behavior
↓
Interactive
With resumability, the model becomes closer to:
Qwik
Server
↓
Render + serialize application state
↓
HTML
↓
Browser
↓
Resume execution when needed
That difference looks small in a diagram.
It is not small architecturally.
3. Lazy loading is not merely about routes
Most frontend developers are already familiar with lazy loading.
You might split an application by route:
const AdminPage = lazy(() => import('./admin'));
That is useful.
But Qwik takes the concept much further.
Lazy loading occurs at a much finer granularity.
Consider:
<button onClick$={() => {
console.log('clicked');
}}>
Click me
</button>
That $ is not decoration.
Qwik uses $ boundaries to indicate code that can be extracted and lazily loaded.
The optimizer can transform functions into separately addressable symbols represented through QRLs — Qwik Resource Locators.
Instead of eagerly shipping every possible event handler because an element exists on the page, Qwik can defer loading behavior until that behavior becomes relevant.
That gives Qwik something I find much more interesting than ordinary code splitting:
lazy execution as an architectural default.
The application is not merely split into several large bundles.
Execution itself can be deferred at fine-grained boundaries.
4. JavaScript becomes something that can be streamed according to demand
This changes the way I think about frontend performance.
For years, one of the dominant optimization strategies for JavaScript applications has been:
Build the application, then figure out how to send less of it initially.
Qwik reverses that relationship.
The architecture assumes from the beginning that code should only arrive when the browser actually needs it.
This means application complexity does not have to translate directly into startup complexity.
A large application may contain enormous amounts of code.
But why should the browser execute code for:
- a modal the user never opens;
- a form validation branch they never trigger;
- an administrative feature they cannot access;
- an accordion they never expand;
- an interaction below the fold they never reach?
Ideally, it shouldn’t.
Qwik’s architecture is built around that idea.
5. Instant Loading is not just a marketing term to me
Qwik describes one of the consequences of its architecture as Instant Loading.
Initially, I treated that claim with the usual level of skepticism I apply to framework marketing.
Then I started using Qwik in real projects.
On optimized production pages I have personally reached:
100/100 Lighthouse Performance.
That obviously does not mean:
Qwik automatically gives every application a Lighthouse score of 100.
It doesn’t.
Performance still depends on:
- images;
- fonts;
- third-party scripts;
- network latency;
- caching;
- CDN configuration;
- CSS;
- backend response times;
- layout stability;
- what the application actually does.
A framework cannot compensate for a 4 MB hero image or ten synchronous marketing scripts.
But framework architecture determines the baseline cost you start from.
And having a runtime designed to avoid eager client-side work gives you a very good baseline.
That was something I could actually observe in production rather than simply reading about it.
6. Resumability has a cost: serializability
This is also where it is important not to turn Qwik into magic.
Resumability imposes constraints.
If execution can move from the server to the browser, the state required for that execution must be representable across that boundary.
That means developers need to think about serializability.
This is one reason Qwik’s programming model contains concepts such as $, QRLs and explicit resumable boundaries.
You cannot simply capture arbitrary runtime state and expect the framework to teleport a JavaScript heap from one environment into another.
This is a trade-off.
And personally, I consider it a reasonable one.
The framework makes execution boundaries more explicit in exchange for gaining much more control over when code is transferred and executed.
It occasionally requires changing how you structure code.
But that constraint comes directly from the architecture that enables resumability.
7. Then there is Qwik City
Qwik alone gives me the component and reactivity model.
Qwik City is what makes the ecosystem interesting as a general-purpose web platform.
It provides the pieces I normally expect from a meta-framework:
- file-based routing;
- layouts;
- SSR;
- static site generation;
- data loaders;
- server actions;
- middleware;
- API endpoints;
- validation;
- caching;
- server-side functions.
For example, server-side data loading can remain colocated with a route:
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
export const useProducts = routeLoader$(async () => {
const response = await fetch('https://example.com/api/products');
return response.json();
});
export default component$(() => {
const products = useProducts();
return (
<ul>
{products.value.map((product) => (
<li key={product.id}>
{product.name}
</li>
))}
</ul>
);
});
The loader executes on the server.
The component consumes its result as reactive data.
Server mutations are first-class too
A route action can handle operations that must remain server-side:
import { component$ } from '@builder.io/qwik';
import { Form, routeAction$ } from '@builder.io/qwik-city';
export const useCreateUser = routeAction$(async (data) => {
const user = await db.users.create({
name: data.name,
});
return {
id: user.id,
};
});
export default component$(() => {
const createUser = useCreateUser();
return (
<Form action={createUser}>
<input name="name" />
<button type="submit">Create user</button>
</Form>
);
});
For smaller applications, this can remove an enormous amount of architectural ceremony.
I don’t necessarily need:
frontend repository
↓
REST client
↓
API gateway
↓
backend controller
↓
service
for every application I build.
Sometimes that separation is exactly what I want.
Sometimes it is unnecessary.
Qwik lets me choose.
8. And server$() makes the boundary even smaller
Qwik City can also expose server-only logic through server$().
Conceptually, this allows code like:
const getCurrentUser = server$(async function () {
return db.users.findById(this.cookie.get('userId')?.value);
});
to behave like a typed RPC boundary between browser and server.
This is useful for applications where creating an entire HTTP API abstraction would add more complexity than value.
Again, I wouldn’t necessarily build a huge distributed enterprise backend this way.
But for a small or medium-sized web application?
It can be extremely productive.
9. Why not just use React + Next.js?
This is probably the most obvious alternative.
React has a gigantic ecosystem, and Next.js provides a sophisticated full-stack platform around it.
There is absolutely nothing irrational about choosing them.
My decision wasn’t based on React being incapable.
It was about what architecture I wanted to invest in.
React’s traditional reactive model is centered around component rendering.
Qwik’s model is centered around:
reactive dependency tracking
+
serialization
+
resumability
+
fine-grained lazy execution
That combination is much closer to the direction I personally find interesting.
There is another difference too.
Qwik and Qwik City feel conceptually like parts of the same architecture.
The primitives of the component framework and the behavior of the server framework are designed around resumability.
That coherence matters to me.
10. Why not Astro?
Astro was another serious candidate.
And I actually like Astro.
Its architecture is elegant.
Astro’s default philosophy can roughly be summarized as:
Send HTML. Add JavaScript only where interactivity actually requires it.
That is a very good principle.
Its islands architecture is especially well suited to:
- documentation;
- blogs;
- editorial sites;
- marketing websites;
- landing pages;
- content-heavy applications.
You can have an essentially static page and selectively create interactive islands:
┌──────────────────────────────┐
│ Static header │
├──────────────────────────────┤
│ │
│ Static article │
│ │
│ ┌────────────────┐ │
│ │ React island │ │
│ └────────────────┘ │
│ │
│ Static content │
│ │
├──────────────────────────────┤
│ Static footer │
└──────────────────────────────┘
That is excellent when the content is the application.
And modern Astro goes well beyond purely static sites: it supports SSR, server islands and dynamic applications too.
So the distinction isn’t:
Astro = static
Qwik = dynamic
That would be an unfair comparison.
The distinction I care about is architectural emphasis.
Astro begins with a content-oriented architecture and progressively adds islands of interactivity.
Qwik begins with a resumable application model that can also produce extremely efficient static output.
For my work, that second direction turned out to be more versatile.
11. One framework across a very wide spectrum
This is ultimately the strongest reason I chose Qwik.
With essentially the same mental model, I can build:
A fully static website
Qwik
↓
SSG
↓
HTML / CSS / resumable metadata
↓
CDN
Perfect for:
- landing pages;
- company websites;
- documentation;
- portfolios.
A dynamically rendered website
Browser
↓
Edge / Node runtime
↓
Qwik City SSR
↓
HTML
Useful when pages depend on:
- authentication;
- request-specific content;
- dynamic data;
- personalization.
A highly interactive web application
Signals, components and resumability remain available.
I don’t need to migrate to another UI architecture simply because a previously static project became more interactive.
A frontend with an independent backend
Nothing prevents this:
Qwik frontend
↓
REST / GraphQL / RPC
↓
NestJS
↓
PostgreSQL
And this is still my preferred architecture for many complex systems.
A compact full-stack application
For smaller systems:
Qwik components
+
Qwik City
+
routeLoader$
+
routeAction$
+
server$
+
database
may be all I need.
That is a remarkably large design space for a single ecosystem.
12. Coming from Angular probably influenced my decision
My background with Angular matters here.
Modern Angular has made me increasingly comfortable with:
- signals;
- explicit dependency graphs;
- structured frameworks;
- strong architectural primitives;
- server rendering;
- clear separation of responsibilities.
So when I started seriously experimenting with Qwik, its reactive model did not feel alien.
In some respects, Qwik feels like an interesting middle ground between two worlds I appreciate:
Angular
structured application architecture
signals
strong framework primitives
↕️
Qwik
signals
JSX
resumability
fine-grained lazy execution
↕️
React ecosystem
functional components
JSX / TSX
composition
That combination suited me extremely well.
13. Qwik is not replacing Angular for me
Technology choices do not have to become religious wars.
I still consider Angular extremely valuable for large enterprise applications.
It provides a mature ecosystem around things such as:
- dependency injection;
- routing;
- forms;
- testing;
- tooling;
- application structure;
- large-team conventions.
If I were designing a very large business application with many developers and a long expected lifetime, Angular would remain one of my first candidates.
Qwik does not need to replace Angular to be useful.
They solve overlapping but not identical problems.
14. And Qwik City is not replacing NestJS either
The same applies to the backend.
Yes, I can build server logic directly inside Qwik City.
But once a backend becomes a substantial independent system, I often prefer something like NestJS.
For example:
Qwik
↓
API
↓
NestJS
↓
domain services
↓
queues / workers
↓
PostgreSQL / Redis
provides boundaries that become useful when complexity increases.
A dedicated backend makes sense when I need things such as:
- several independent clients;
- complex authorization;
- workers;
- queues;
- event-driven architecture;
- scheduled jobs;
- integrations;
- independently scalable services;
- large domain models.
The important thing is that Qwik doesn’t force me into either architecture.
15. The trade-offs are real
I would not recommend adopting Qwik without mentioning its disadvantages.
Smaller ecosystem
React’s ecosystem is vastly larger.
There will be situations where a React package already exists while a Qwik-specific equivalent does not.
Smaller community
This affects:
- Stack Overflow answers;
- tutorials;
- third-party integrations;
- debugging resources;
- hiring.
Popularity is not everything, but ecosystem size has real engineering value.
Resumability requires learning new concepts
Understanding:
-
$; - QRLs;
- serialization;
- resumable closures;
- client/server boundaries;
requires some adjustment.
If you approach Qwik as “React with slightly different syntax”, you will probably miss much of what makes the framework interesting.
Architecture does not eliminate bad engineering
Qwik cannot save an application from:
bad database queries
+ huge images
+ blocking third-party scripts
+ poor caching
+ excessive network requests
+ unnecessary dependencies
Resumability gives you an excellent runtime model.
It does not replace engineering discipline.
16. What ultimately convinced me
After several months of working with Qwik, I realized that the reason I kept choosing it wasn’t any single feature.
It was how those features interact.
Signals alone are interesting.
SSR alone is useful.
Static generation alone is useful.
Lazy loading alone is useful.
Server functions alone are useful.
But Qwik combines:
Signals
+
Resumability
+
Serialization
+
Fine-grained lazy execution
+
SSR
+
SSG
+
SPA navigation
+
Server actions
+
Endpoints
+
Middleware
inside a coherent architectural model.
That is what convinced me.
Conclusion
I did not choose Qwik because I wanted to learn another frontend framework.
I chose it because I wanted to invest in an architectural idea.
An application should not necessarily need to download and execute a large JavaScript runtime just because the application itself is large.
Server-rendered work should not necessarily have to be replayed in the browser.
Interactivity should not necessarily imply eager execution.
And the distinction between:
website
and:
web application
should not necessarily force me to change the entire technological model.
With Qwik + Qwik City, I can start with a completely static landing page and progressively move toward a dynamic, stateful, server-rendered or even full-stack application without abandoning the architecture I started with.
That versatility is ultimately why I decided to invest in Qwik.
Not because it is the most popular framework.
It isn’t.
Not because it is appropriate for every project.
It isn’t.
But because its architecture gives me something I value enormously as a developer:
the ability to solve very different web problems while preserving performance, a coherent mental model, and relatively few architectural compromises.
And after actually using it in production, that is a bet I am increasingly comfortable making.
Top comments (0)