Skip to content

Qwik Version โ€‹

HaloLight Qwik version is built on Qwik City, featuring Qwik resumability architecture + TypeScript for zero hydration and ultimate performance.

Live Preview: https://halolight-qwik.h7ml.cn

GitHub: https://github.com/halolight/halolight-qwik

Features โ€‹

  • ๐Ÿ”„ Resumability - No hydration needed, server state directly resumed
  • โšก Lazy Load Everything - Code loaded on demand, minimal initial JS (~1KB)
  • ๐ŸŽจ Theme System - 11 skins, dark mode, View Transitions
  • ๐Ÿ” Authentication - Complete login/register/password recovery flow
  • ๐Ÿ“Š Dashboard - Data visualization and business management
  • ๐Ÿ›ก๏ธ Permission Control - RBAC fine-grained permission management
  • ๐Ÿ“ก Signals - Fine-grained reactive system
  • ๐ŸŒ Edge Deployment - Native support for Cloudflare Workers and other edge platforms

Tech Stack โ€‹

TechnologyVersionDescription
Qwik2.xResumable framework
Qwik City2.xFull-stack framework
TypeScript5.xType safety
Tailwind CSS4.xAtomic CSS
Qwik UIlatestUI component library
Modular FormslatestForm handling
Zod3.xData validation
ECharts5.xChart visualization
Mock.js1.xData mocking

Core Features โ€‹

  • Configurable Dashboard - 9 widgets, drag & drop layout, responsive adaptation
  • Permission System - RBAC permission control, route guards, permission components
  • Theme System - 11 skins, dark mode, View Transitions
  • Server-side Rendering - Built-in SSR support, SEO optimization
  • File-based Routing - Directory-based routing system
  • Real-time Notifications - WebSocket push, notification center

Directory Structure โ€‹

halolight-qwik/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ routes/                    # File-based routing
โ”‚   โ”‚   โ”œโ”€โ”€ index.tsx            # Home
โ”‚   โ”‚   โ”œโ”€โ”€ layout.tsx           # Root layout
โ”‚   โ”‚   โ”œโ”€โ”€ (auth)/              # Auth route group
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ layout.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ login/
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ index.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ register/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ forgot-password/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ reset-password/
โ”‚   โ”‚   โ”œโ”€โ”€ (dashboard)/         # Dashboard route group
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ layout.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dashboard/
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ index.tsx
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ users/
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ index.tsx
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ create/
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ [id]/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ roles/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ permissions/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ settings/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ profile/
โ”‚   โ”‚   โ””โ”€โ”€ api/                 # API endpoints
โ”‚   โ”‚       โ””โ”€โ”€ auth/
โ”‚   โ”‚           โ””โ”€โ”€ login/
โ”‚   โ”‚               โ””โ”€โ”€ index.ts
โ”‚   โ”œโ”€โ”€ components/              # Component library
โ”‚   โ”‚   โ”œโ”€โ”€ ui/                  # Qwik UI components
โ”‚   โ”‚   โ”œโ”€โ”€ layout/              # Layout components
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ admin-layout/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ auth-layout/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ sidebar/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ header/
โ”‚   โ”‚   โ”œโ”€โ”€ dashboard/           # Dashboard components
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dashboard-grid/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ widget-wrapper/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ stats-widget/
โ”‚   โ”‚   โ””โ”€โ”€ shared/              # Shared components
โ”‚   โ”‚       โ””โ”€โ”€ permission-guard/
โ”‚   โ”œโ”€โ”€ stores/                  # State management
โ”‚   โ”‚   โ”œโ”€โ”€ auth.ts
โ”‚   โ”‚   โ”œโ”€โ”€ ui-settings.ts
โ”‚   โ”‚   โ””โ”€โ”€ dashboard.ts
โ”‚   โ”œโ”€โ”€ lib/                     # Utilities
โ”‚   โ”‚   โ”œโ”€โ”€ api.ts
โ”‚   โ”‚   โ”œโ”€โ”€ permission.ts
โ”‚   โ”‚   โ””โ”€โ”€ cn.ts
โ”‚   โ”œโ”€โ”€ mock/                    # Mock data
โ”‚   โ””โ”€โ”€ types/                   # Type definitions
โ”œโ”€โ”€ public/                      # Static assets
โ”œโ”€โ”€ vite.config.ts              # Vite config
โ”œโ”€โ”€ tailwind.config.ts          # Tailwind config
โ””โ”€โ”€ package.json

Quick Start โ€‹

Environment Requirements โ€‹

  • Node.js >= 18.0.0
  • pnpm >= 9.x

Installation โ€‹

bash
git clone https://github.com/halolight/halolight-qwik.git
cd halolight-qwik
pnpm install

Environment Variables โ€‹

bash
cp .env.example .env
env
# .env example
VITE_API_URL=/api
VITE_USE_MOCK=true
VITE_DEMO_EMAIL=admin@halolight.h7ml.cn
VITE_DEMO_PASSWORD=123456
VITE_SHOW_DEMO_HINT=false
VITE_APP_TITLE=Admin Pro
VITE_BRAND_NAME=Halolight

Start Development โ€‹

bash
pnpm dev

Visit http://localhost:5173

Build for Production โ€‹

bash
pnpm build
pnpm serve

Core Features โ€‹

State Management (Context + Signals) โ€‹

tsx
// stores/auth.ts
import {
  createContextId,
  useContext,
  useStore,
  useComputed$,
  $,
  type Signal,
} from '@builder.io/qwik'

interface User {
  id: number
  name: string
  email: string
  permissions: string[]
}

interface AuthState {
  user: User | null
  token: string | null
  loading: boolean
}

export const AuthContext = createContextId<AuthState>('auth')

export function useAuth() {
  const state = useContext(AuthContext)

  const isAuthenticated = useComputed$(() => !!state.token && !!state.user)
  const permissions = useComputed$(() => state.user?.permissions ?? [])

  const login = $(async (credentials: { email: string; password: string }) => {
    state.loading = true
    try {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        body: JSON.stringify(credentials),
        headers: { 'Content-Type': 'application/json' },
      })
      const data = await response.json()

      state.user = data.user
      state.token = data.token
    } finally {
      state.loading = false
    }
  })

  const logout = $(() => {
    state.user = null
    state.token = null
  })

  const hasPermission = $((permission: string) => {
    const perms = state.user?.permissions ?? []
    return perms.some(p =>
      p === '*' || p === permission ||
      (p.endsWith(':*') && permission.startsWith(p.slice(0, -1)))
    )
  })

  return {
    state,
    isAuthenticated,
    permissions,
    login,
    logout,
    hasPermission,
  }
}

Data Fetching (routeLoader$) โ€‹

tsx
// routes/(dashboard)/users/index.tsx
import { component$ } from '@builder.io/qwik'
import { routeLoader$ } from '@builder.io/qwik-city'

export const useUsers = routeLoader$(async ({ query, cookie, status }) => {
  const token = cookie.get('token')?.value
  const page = Number(query.get('page')) || 1

  // Permission check
  const user = await validateToken(token)
  if (!hasPermission(user, 'users:list')) {
    status(403)
    return { error: 'No permission to access' }
  }

  const response = await fetch(`/api/users?page=${page}`, {
    headers: { Authorization: `Bearer ${token}` },
  })

  return response.json()
})

export default component$(() => {
  const users = useUsers()

  return (
    <div>
      <h1>User List</h1>

      {users.value.error ? (
        <div class="text-destructive">{users.value.error}</div>
      ) : (
        <ul>
          {users.value.data.map((user) => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      )}
    </div>
  )
})

Permission Control โ€‹

Route Guards โ€‹

tsx
// routes/(dashboard)/layout.tsx
import { component$, Slot } from '@builder.io/qwik'
import { routeLoader$ } from '@builder.io/qwik-city'
import { useAuth } from '~/stores/auth'
import { AdminLayout } from '~/components/layout/admin-layout'

export const useAuthGuard = routeLoader$(async ({ cookie, redirect, url }) => {
  const token = cookie.get('token')?.value

  if (!token) {
    throw redirect(302, `/login?redirect=${url.pathname}`)
  }

  // Validate token and return user info
  return {
    user: await validateToken(token),
  }
})

export default component$(() => {
  const data = useAuthGuard()

  return (
    <AdminLayout user={data.value.user}>
      <Slot />
    </AdminLayout>
  )
})

Permission Component โ€‹

tsx
// components/shared/permission-guard/index.tsx
import { component$, Slot, useComputed$ } from '@builder.io/qwik'
import { useAuth } from '~/stores/auth'

interface Props {
  permission: string
}

export const PermissionGuard = component$<Props>(({ permission }) => {
  const { hasPermission } = useAuth()

  const allowed = useComputed$(async () => {
    return await hasPermission(permission)
  })

  return (
    <>
      {allowed.value ? (
        <Slot />
      ) : (
        <Slot name="fallback" />
      )}
    </>
  )
})

// Usage
<PermissionGuard permission="users:delete">
  <Button variant="destructive" q:slot="">Delete</Button>
  <span q:slot="fallback" class="text-muted-foreground">No Permission</span>
</PermissionGuard>

Form Submission (routeAction$) โ€‹

tsx
// routes/(auth)/login/index.tsx
import { component$ } from '@builder.io/qwik'
import { routeAction$, zod$, z, Form } from '@builder.io/qwik-city'

export const useLogin = routeAction$(
  async (data, { cookie, redirect, fail }) => {
    try {
      const response = await fetch('/api/auth/login', {
        method: 'POST',
        body: JSON.stringify(data),
        headers: { 'Content-Type': 'application/json' },
      })

      if (!response.ok) {
        return fail(401, { message: 'Invalid email or password' })
      }

      const result = await response.json()

      cookie.set('token', result.token, {
        path: '/',
        httpOnly: true,
        sameSite: 'strict',
        maxAge: 60 * 60 * 24 * 7,
      })

      throw redirect(302, '/dashboard')
    } catch (e) {
      return fail(500, { message: 'Server error' })
    }
  },
  zod$({
    email: z.string().email('Please enter a valid email'),
    password: z.string().min(6, 'Password must be at least 6 characters'),
  })
)

export default component$(() => {
  const action = useLogin()

  return (
    <Form action={action}>
      {action.value?.failed && (
        <div class="text-destructive">{action.value.message}</div>
      )}

      <input type="email" name="email" placeholder="Email" />
      {action.value?.fieldErrors?.email && (
        <span class="text-destructive">{action.value.fieldErrors.email}</span>
      )}

      <input type="password" name="password" placeholder="Password" />
      {action.value?.fieldErrors?.password && (
        <span class="text-destructive">{action.value.fieldErrors.password}</span>
      )}

      <button type="submit" disabled={action.isRunning}>
        {action.isRunning ? 'Logging in...' : 'Login'}
      </button>
    </Form>
  )
})

API Routes โ€‹

ts
// routes/api/auth/login/index.ts
import type { RequestHandler } from '@builder.io/qwik-city'

export const onPost: RequestHandler = async ({ json, parseBody }) => {
  const body = await parseBody()
  const { email, password } = body as { email: string; password: string }

  // Validation logic
  if (!email || !password) {
    json(400, { success: false, message: 'Email and password are required' })
    return
  }

  // Authentication logic...

  json(200, {
    success: true,
    user: { id: 1, name: 'Admin', email },
    token: 'mock_token',
  })
}

Draggable Dashboard โ€‹

tsx
// components/dashboard/dashboard-grid/index.tsx
import { component$, useSignal, useStore, $ } from '@builder.io/qwik'

interface Widget {
  id: string
  type: string
  x: number
  y: number
  w: number
  h: number
}

export const DashboardGrid = component$(() => {
  const widgets = useStore<Widget[]>([
    { id: '1', type: 'stats', x: 0, y: 0, w: 3, h: 2 },
    { id: '2', type: 'chart', x: 3, y: 0, w: 6, h: 4 },
  ])

  const handleLayoutChange = $((newLayout: Widget[]) => {
    widgets.splice(0, widgets.length, ...newLayout)
  })

  return (
    <div class="dashboard-grid">
      {widgets.map((widget) => (
        <div
          key={widget.id}
          class="widget"
          style={{
            gridColumn: `${widget.x + 1} / span ${widget.w}`,
            gridRow: `${widget.y + 1} / span ${widget.h}`,
          }}
        >
          {/* Widget content */}
        </div>
      ))}
    </div>
  )
})

Theme System โ€‹

Skin Presets โ€‹

Supports 11 preset skins, switchable through quick settings panel:

SkinPrimary ColorCSS Variable
DefaultPurple--primary: 51.1% 0.262 276.97
BlueBlue--primary: 54.8% 0.243 264.05
EmeraldEmerald--primary: 64.6% 0.178 142.49
RoseRose--primary: 61.8% 0.238 12.57
OrangeOrange--primary: 68.3% 0.199 36.35
YellowYellow--primary: 88.1% 0.197 95.45
VioletViolet--primary: 57.8% 0.24 305.4
CyanCyan--primary: 73.8% 0.139 196.85
PinkPink--primary: 72.2% 0.218 345.82
LimeLime--primary: 79.2% 0.183 123.7
AmberAmber--primary: 82.5% 0.157 62.24

CSS Variables (OKLch) โ€‹

css
/* Global variable definitions */
:root {
  --background: 100% 0 0;
  --foreground: 14.9% 0.017 285.75;
  --primary: 51.1% 0.262 276.97;
  --primary-foreground: 98% 0 0;
  --secondary: 96.1% 0 0;
  --secondary-foreground: 9.8% 0 0;
  --muted: 95.1% 0.01 286.38;
  --muted-foreground: 45.1% 0.009 285.88;
  --accent: 95.1% 0.01 286.38;
  --accent-foreground: 9.8% 0 0;
  --destructive: 54.3% 0.227 25.78;
  --destructive-foreground: 98% 0 0;
  --border: 89.8% 0.006 286.32;
  --input: 89.8% 0.006 286.32;
  --ring: 51.1% 0.262 276.97;
  --radius: 0.5rem;
}

.dark {
  --background: 0% 0 0;
  --foreground: 98% 0 0;
  --primary: 59.6% 0.262 276.97;
  --primary-foreground: 14.9% 0.017 285.75;
  /* ... */
}

Page Routes โ€‹

PathPagePermission
/HomePublic
/loginLoginPublic
/registerRegisterPublic
/forgot-passwordForgot PasswordPublic
/reset-passwordReset PasswordPublic
/dashboardDashboarddashboard:view
/usersUser Listusers:list
/users/createCreate Userusers:create
/users/[id]User Detailsusers:view
/rolesRole Managementroles:list
/permissionsPermission Managementpermissions:list
/settingsSystem Settingssettings:view
/profileProfileAuthenticated

Common Commands โ€‹

bash
pnpm dev            # Start development server
pnpm build          # Production build
pnpm serve          # Preview production build
pnpm lint           # Code linting
pnpm lint:fix       # Auto-fix linting issues
pnpm type-check     # Type checking
pnpm test           # Run tests
pnpm test:e2e       # E2E tests

Deployment โ€‹

Deploy with Vercel

bash
# Use Vercel Edge adapter
pnpm add -D @builder.io/qwik-city/adapters/vercel-edge

Docker โ€‹

dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/server ./server
COPY --from=builder /app/package.json .
RUN npm install --production
EXPOSE 3000
CMD ["node", "server/entry.express.js"]

Other Platforms โ€‹

  • Node.js Server

    bash
    pnpm build
    node server/entry.express.js
  • Cloudflare Pages

    bash
    # Use Cloudflare Pages adapter
    pnpm add -D @builder.io/qwik-city/adapters/cloudflare-pages
  • Netlify

  • AWS Amplify

  • Azure Static Web Apps

Demo Accounts โ€‹

RoleEmailPassword
Adminadmin@halolight.h7ml.cn123456
Useruser@halolight.h7ml.cn123456

Testing โ€‹

bash
pnpm test           # Run tests
pnpm test:e2e       # E2E tests
pnpm test:coverage  # Coverage report

Test Examples โ€‹

tsx
// src/components/permission-guard/permission-guard.spec.tsx
import { describe, it, expect } from 'vitest'
import { createDOM } from '@builder.io/qwik/testing'
import { PermissionGuard } from './permission-guard'

describe('PermissionGuard', () => {
  it('should render content when has permission', async () => {
    const { screen, render } = await createDOM()

    await render(
      <PermissionGuard permission="users:view">
        <div>Protected Content</div>
      </PermissionGuard>
    )

    expect(screen.innerHTML).toContain('Protected Content')
  })

  it('should render fallback when no permission', async () => {
    const { screen, render } = await createDOM()

    await render(
      <PermissionGuard permission="admin:*">
        <div>Protected Content</div>
        <div q:slot="fallback">No Permission</div>
      </PermissionGuard>
    )

    expect(screen.innerHTML).toContain('No Permission')
  })
})

Configuration โ€‹

Vite Configuration โ€‹

ts
// vite.config.ts
import { defineConfig } from 'vite'
import { qwikVite } from '@builder.io/qwik/optimizer'
import { qwikCity } from '@builder.io/qwik-city/vite'
import tsconfigPaths from 'vite-tsconfig-paths'

export default defineConfig(() => {
  return {
    plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
    preview: {
      headers: {
        'Cache-Control': 'public, max-age=600',
      },
    },
  }
})

CI/CD โ€‹

Project is configured with complete GitHub Actions CI workflow:

yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm typecheck

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm test:coverage
      - uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm build

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: pnpm
      - run: pnpm audit --audit-level=high

Advanced Features โ€‹

Qwik Resumability Principle โ€‹

Qwik's core innovation is "resumability" rather than "hydration":

tsx
// Traditional frameworks (React/Vue): Need to re-execute all code to rebuild state
// Qwik: Directly resume state from HTML without re-execution

// Server serializes state
export default component$(() => {
  const count = useSignal(0)

  // Qwik serializes state into HTML
  return <div>Count: {count.value}</div>
})

// Client directly resumes state from HTML without executing component code
// Code is loaded and executed only on interaction

Lazy Loading Strategy โ€‹

Qwik implements the most aggressive code splitting:

tsx
// Each event handler is an independent lazy loading unit
export default component$(() => {
  const count = useSignal(0)

  // This function won't be downloaded until clicked
  const handleClick = $(() => {
    count.value++
  })

  return (
    <button onClick$={handleClick}>
      Count: {count.value}
    </button>
  )
})

Preload Optimization โ€‹

tsx
// routes/(dashboard)/layout.tsx
import { component$ } from '@builder.io/qwik'
import { routeLoader$, Link } from '@builder.io/qwik-city'

// Preload data
export const usePreloadData = routeLoader$(async () => {
  return {
    navigation: await fetchNavigation(),
  }
})

export default component$(() => {
  const data = usePreloadData()

  return (
    <nav>
      {data.value.navigation.map((item) => (
        // Link automatically preloads target page
        <Link href={item.path} prefetch>
          {item.title}
        </Link>
      ))}
    </nav>
  )
})

Performance Optimization โ€‹

Image Optimization โ€‹

tsx
import { component$ } from '@builder.io/qwik'
import { Image } from '@unpic/qwik'

export default component$(() => {
  return (
    <Image
      src="https://example.com/image.jpg"
      layout="constrained"
      width={800}
      height={600}
      alt="Optimized image"
    />
  )
})

Lazy Loading Components โ€‹

tsx
// Component-level lazy loading
import { component$ } from '@builder.io/qwik'

export default component$(() => {
  return (
    <div>
      {/* Use resource$ for component lazy loading */}
      <Resource
        value={heavyComponentResource}
        onPending={() => <div>Loading...</div>}
        onResolved={(HeavyComponent) => <HeavyComponent />}
      />
    </div>
  )
})

Preload Critical Resources โ€‹

tsx
// routes/layout.tsx
import { component$, useVisibleTask$ } from '@builder.io/qwik'

export default component$(() => {
  useVisibleTask$(() => {
    // Preload critical fonts
    const link = document.createElement('link')
    link.rel = 'preload'
    link.as = 'font'
    link.href = '/fonts/main.woff2'
    link.type = 'font/woff2'
    link.crossOrigin = 'anonymous'
    document.head.appendChild(link)
  })

  return <Slot />
})

FAQ โ€‹

Q: How to handle client-side state? โ€‹

A: Use useSignal and useStore to create reactive state:

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

export default component$(() => {
  // Use useSignal for simple values
  const count = useSignal(0)

  // Use useStore for complex objects
  const state = useStore({
    user: null,
    loading: false,
  })

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

Q: How to integrate third-party libraries? โ€‹

A: Use useVisibleTask$ to execute code on the client:

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

export default component$(() => {
  const chartRef = useSignal<Element>()

  useVisibleTask$(({ cleanup }) => {
    // Initialize third-party library on the client
    import('chart.js').then(({ Chart }) => {
      const chart = new Chart(chartRef.value, {
        // Configuration...
      })

      cleanup(() => chart.destroy())
    })
  })

  return <canvas ref={chartRef} />
})

Q: How to optimize initial load time? โ€‹

A: Qwik optimizes automatically, but you can further:

  1. Use SSR: Enabled by default
  2. Preload critical routes:
    tsx
    <Link href="/dashboard" prefetch>Dashboard</Link>
  3. Defer non-critical resources:
    tsx
    useVisibleTask$(({ track }) => {
      // Load only when component is visible
      track(() => isVisible.value)
      if (isVisible.value) {
        loadAnalytics()
      }
    })

Q: How to handle form submission? โ€‹

A: Use routeAction$ for server-side processing:

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

export const useAddUser = routeAction$(
  async (data) => {
    // Server-side processing
    const user = await createUser(data)
    return { success: true, user }
  },
  zod$({
    name: z.string().min(2),
    email: z.string().email(),
  })
)

export default component$(() => {
  const action = useAddUser()

  return (
    <Form action={action}>
      <input name="name" />
      <input name="email" type="email" />
      <button type="submit">
        {action.isRunning ? 'Submitting...' : 'Submit'}
      </button>
    </Form>
  )
})

Comparison with Other Versions โ€‹

FeatureQwik VersionNext.jsVue
SSR/SSGโœ… Built-inโœ…โœ… (Nuxt)
State ManagementContext + SignalsZustandPinia
Data FetchingrouteLoader$TanStack QueryTanStack Query
Form ValidationModular Forms + ZodReact Hook Form + ZodVeeValidate + Zod
RoutingFile-based RoutingApp RouterVue Router
Build ToolViteNext.jsVite
HydrationResumable (Zero Hydration)Traditional HydrationTraditional Hydration
Initial JS~1KB~85KB~33KB
Server-sideBuilt-in Full-stackAPI RoutesSeparate Backend
Component LibraryQwik UIshadcn/uishadcn-vue