DEV Community

Alex Spinov
Alex Spinov

Posted on

Qwik Has a Free API You're Not Using

Qwik achieves instant page loads by sending zero JavaScript to the browser by default. It resumes interactivity lazily — only loading code when the user actually interacts.

The Free APIs You're Missing

1. useSignal — Fine-Grained Reactivity

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

export const Counter = component$(() => {
  const count = useSignal(0);
  const doubled = useComputed$(() => count.value * 2);

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

The $ suffix is Qwik's magic — it marks lazy-loading boundaries. The click handler only loads when clicked.

2. useTask$ — Server & Client Side Effects

import { useTask$, useSignal } from "@builder.io/qwik";

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

  useTask$(async ({ track }) => {
    track(() => userId.value);
    const resp = await fetch(`/api/users/${userId.value}`);
    user.value = await resp.json();
  });
  // Runs on SERVER during SSR, then on CLIENT when userId changes

  return <div>{user.value?.name}</div>;
});
Enter fullscreen mode Exit fullscreen mode

3. routeLoader$ — Server Data Loading

// src/routes/users/[id]/index.tsx
import { routeLoader$ } from "@builder.io/qwik-city";

export const useUser = routeLoader$(async ({ params, status }) => {
  const user = await db.users.findUnique({ where: { id: params.id } });
  if (!user) {
    status(404);
    return null;
  }
  return user;
});

export default component$(() => {
  const user = useUser();
  return <h1>{user.value?.name}</h1>;
});
Enter fullscreen mode Exit fullscreen mode

4. routeAction$ — Form Handling Without JS

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

export const useCreatePost = routeAction$(
  async (data, { redirect }) => {
    await db.posts.create({ data });
    throw redirect(302, "/posts");
  },
  zod$({
    title: z.string().min(3),
    content: z.string().min(10),
  })
);

export default component$(() => {
  const action = useCreatePost();
  return (
    <Form action={action}>
      <input name="title" />
      {action.value?.fieldErrors?.title && <span>{action.value.fieldErrors.title}</span>}
      <textarea name="content" />
      <button type="submit">Create</button>
    </Form>
  );
});
Enter fullscreen mode Exit fullscreen mode

Works without JavaScript. Progressive enhancement built in.

5. useVisibleTask$ — Client-Only Code

import { useVisibleTask$ } from "@builder.io/qwik";

export const Analytics = component$(() => {
  useVisibleTask$(() => {
    // Only runs in browser, only when component is visible
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        trackPageView();
      }
    });
    return () => observer.disconnect();
  });

  return <div>Tracked section</div>;
});
Enter fullscreen mode Exit fullscreen mode

Getting Started

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

Need data from any website delivered as clean JSON? I build production web scrapers that handle anti-bot, proxies, and rate limits. 77 scrapers running in production. Email me: Spinov001@gmail.com

Check out my awesome-web-scraping list for the best scraping tools and resources.

Top comments (0)