DEV Community

Alex Spinov
Alex Spinov

Posted on

Qwik Has Free Resumable Apps — Here's Why It Loads Faster Than Any Other Framework

React hydrates. Qwik resumes. The difference? Your app is interactive instantly — no matter how large.

The Problem with Hydration

Every framework does this:

  1. Server renders HTML
  2. Client downloads JavaScript
  3. Client re-executes everything to make it interactive (hydration)

For large apps, step 3 can take 5-10 seconds. Qwik skips it entirely.

How Resumability Works

Qwik serializes the app's state and event listeners into the HTML. When the browser loads the page, it already knows what to do — no re-execution needed.

Traditional: Download All JS → Execute All → Interactive
Qwik:        HTML loads → Already Interactive → Load JS on demand
Enter fullscreen mode Exit fullscreen mode

Quick Start

npm create qwik@latest
cd my-app && npm start
Enter fullscreen mode Exit fullscreen mode

Basic Component

import { component$, useSignal } from '@builder.io/qwik';

export const Counter = component$(() => {
  const count = useSignal(0);

  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick$={() => count.value++}>Increment</button>
    </div>
  );
});
Enter fullscreen mode Exit fullscreen mode

Notice the $ signs — they tell Qwik where to create lazy-loading boundaries.

Lazy Loading by Default

import { component$, useTask$ } from '@builder.io/qwik';

export const UserProfile = component$(() => {
  const user = useSignal<User | null>(null);

  // This code ONLY loads when it needs to run
  useTask$(async () => {
    const res = await fetch('/api/user');
    user.value = await res.json();
  });

  return (
    <div>
      {user.value ? (
        <div>
          <h2>{user.value.name}</h2>
          <p>{user.value.email}</p>
          {/* onClick handler JS only loads when user clicks */}
          <button onClick$={() => {
            // This entire handler is a separate chunk
            // It only downloads when the button is clicked
            fetch('/api/logout', { method: 'POST' });
          }}>Logout</button>
        </div>
      ) : (
        <p>Loading...</p>
      )}
    </div>
  );
});
Enter fullscreen mode Exit fullscreen mode

useStore (Reactive Objects)

import { component$, useStore } from '@builder.io/qwik';

export const TodoApp = component$(() => {
  const state = useStore({
    todos: [] as Array<{ text: string; done: boolean }>,
    newTodo: '',
  });

  return (
    <div>
      <input
        value={state.newTodo}
        onInput$={(e) => state.newTodo = (e.target as HTMLInputElement).value}
      />
      <button onClick$={() => {
        state.todos.push({ text: state.newTodo, done: false });
        state.newTodo = '';
      }}>Add</button>
      <ul>
        {state.todos.map((todo, i) => (
          <li key={i}>
            <input
              type="checkbox"
              checked={todo.done}
              onChange$={() => todo.done = !todo.done}
            />
            {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
});
Enter fullscreen mode Exit fullscreen mode

Server Actions (Qwik City)

import { routeAction$, Form, zod$, z } from '@builder.io/qwik-city';

export const useCreateUser = routeAction$(
  async (data) => {
    const user = await db.users.create({ data });
    return { success: true, userId: user.id };
  },
  zod$({
    name: z.string().min(1),
    email: z.string().email(),
  })
);

export default component$(() => {
  const action = useCreateUser();

  return (
    <Form action={action}>
      <input name="name" placeholder="Name" />
      <input name="email" placeholder="Email" />
      <button type="submit">Create User</button>
      {action.value?.success && <p>User created!</p>}
    </Form>
  );
});
Enter fullscreen mode Exit fullscreen mode

Qwik vs Next.js vs Remix

Feature Qwik Next.js Remix
Initial JS ~1KB 80-200KB 50-150KB
TTI Instant Seconds Seconds
Hydration None (resumable) Full Full
Lazy Loading Automatic Manual Manual
Bundle Growth O(1) O(n) O(n)

As your app grows, Qwik's initial load stays constant. Other frameworks grow linearly.


Need fast data loading for your Qwik app? Check out my Apify actors — data extraction that matches Qwik's speed. For custom solutions, email spinov001@gmail.com.

Top comments (0)