Migrating from React to Qwik is a gradual process that can be done step by step. Here are detailed migration strategies and best practices:
1. Assessment and Preparation
Evaluate Existing Project
Before starting migration, evaluate the following aspects:
- Project scale and complexity
- Third-party libraries used
- Performance requirements and goals
- Team's familiarity with Qwik
Create Qwik Project
bash# Create new Qwik project npm create qwik@latest # Or add Qwik to existing project npm install @builder.io/qwik
2. Core Concept Mapping
Component Definition
React:
tsximport React from 'react'; export const MyComponent = ({ name }: { name: string }) => { return <div>Hello {name}</div>; };
Qwik:
tsximport { component$ } from '@builder.io/qwik'; export const MyComponent = component$(({ name }: { name: string }) => { return <div>Hello {name}</div>; });
State Management
React:
tsximport { useState } from 'react'; export const Counter = () => { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); };
Qwik:
tsximport { 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> ); });
Event Handling
React:
tsxexport const Button = () => { const handleClick = () => { console.log('Clicked'); }; return <button onClick={handleClick}>Click me</button>; };
Qwik:
tsxexport const Button = component$(() => { const handleClick$ = () => { console.log('Clicked'); }; return <button onClick$={handleClick$}>Click me</button>; });
3. Step-by-Step Migration Strategy
Phase 1: Infrastructure Migration
- Set up Qwik project structure
- Configure build tools
- Set up routing system (Qwik City)
- Configure TypeScript and ESLint
Phase 2: Simple Component Migration
Start migrating simple components:
- Stateless components
- Presentational components
- Independent functional components
tsx// React export const Header = ({ title }: { title: string }) => { return <header><h1>{title}</h1></header>; }; // Qwik export const Header = component$(({ title }: { title: string }) => { return <header><h1>{title}</h1></header>; });
Phase 3: State Management Migration
Migrate components using state management:
- Use
useSignalto replaceuseState - Use
useStorefor complex state - Use
useContextto replace Context API
tsx// React import { useState, useContext } from 'react'; export const Counter = () => { const [count, setCount] = useState(0); const theme = useContext(ThemeContext); return ( <div className={theme}> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; // Qwik import { component$, useSignal, useContext } from '@builder.io/qwik'; export const Counter = component$(() => { const count = useSignal(0); const theme = useContext(ThemeContext); return ( <div class={theme}> <p>Count: {count.value}</p> <button onClick$={() => count.value++}>Increment</button> </div> ); });
Phase 4: Complex Component Migration
Migrate complex components:
- Components with side effects
- Components using hooks
- Async data fetching components
tsx// React import { useEffect, useState } from 'react'; export const UserList = () => { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetchUsers().then(data => { setUsers(data); setLoading(false); }); }, []); if (loading) return <p>Loading...</p>; return ( <ul> {users.map(user => <li key={user.id}>{user.name}</li>)} </ul> ); }; // Qwik import { component$, useResource$ } from '@builder.io/qwik'; export const UserList = component$(() => { const users = useResource$(() => fetchUsers()); return ( <div> {users.value ? ( <ul> {users.value.map(user => <li key={user.id}>{user.name}</li>)} </ul> ) : ( <p>Loading...</p> )} </div> ); });
Phase 5: Routing and Layout Migration
Migrate routing and layout system:
- Use Qwik City's file-system routing
- Migrate layout components
- Migrate route guards and middleware
4. Common Issues and Solutions
Issue 1: Third-Party Library Compatibility
Solution:
- Find Qwik-compatible alternatives
- Wrap incompatible libraries with
useClientEffect$ - Create adapter layer
tsximport { component$, useVisibleTask$ } from '@builder.io/qwik'; export const ThirdPartyComponent = component$(() => { useVisibleTask$(() => { // Only execute third-party library on client const library = require('third-party-library'); library.init(); }); return <div id="third-party-container"></div>; });
Issue 2: CSS Module Migration
Solution:
- Qwik natively supports CSS modules
- Keep the same import method
tsx// React import styles from './Button.module.css'; export const Button = () => { return <button className={styles.button}>Click</button>; }; // Qwik import styles from './Button.module.css'; export const Button = component$(() => { return <button class={styles.button}>Click</button>; });
Issue 3: Form Handling
Solution:
- Use Qwik City's
action$to replace form handling - Use
Formcomponent instead of native form
tsx// React export const ContactForm = () => { const handleSubmit = async (e) => { e.preventDefault(); await submitForm(data); }; return <form onSubmit={handleSubmit}>...</form>; }; // Qwik import { component$, Form } from '@builder.io/qwik-city'; import { action$ } from '@builder.io/qwik-city'; export const useContactForm = action$(async (data) => { await submitForm(data); return { success: true }; }); export const ContactForm = component$(() => { const action = useContactForm(); return <Form action={action}>...</Form>; });
5. Performance Optimization Migration
React Optimization Techniques to Qwik Mapping
| React | Qwik |
|---|---|
useMemo | useComputed$ |
useCallback | Not needed (auto-optimized) |
React.memo | Not needed (auto-optimized) |
useEffect | useTask$ / useVisibleTask$ |
| Code splitting | Automatic fine-grained splitting |
6. Testing Migration
Unit Tests
tsx// React (Jest) import { render, screen } from '@testing-library/react'; import { Counter } from './Counter'; test('increments count', () => { render(<Counter />); const button = screen.getByText('Increment'); button.click(); expect(screen.getByText('Count: 1')).toBeInTheDocument(); }); // Qwik (Vitest + Testing Library) import { render, screen } from '@builder.io/qwik/testing'; import { Counter } from './Counter'; test('increments count', async () => { const { render } = await render(Counter); const button = screen.getByText('Increment'); await button.click(); expect(screen.getByText('Count: 1')).toBeInTheDocument(); });
7. Best Practices
1. Don't Migrate Entire Project at Once
- Migrate gradually, one module at a time
- Keep React and Qwik code coexisting for a while
2. Leverage Qwik's Automatic Optimization
- No need to manually optimize performance
- Focus on business logic
3. Use Qwik Development Tools
- Qwik DevTools for debugging
- Qwik CLI for rapid development
4. Keep Code Simple
- Qwik's syntax is more concise
- Use
$symbol to simplify code
5. Fully Utilize Qwik City
- Use file-system routing
- Use server-side data loading
- Use form handling features
Summary: Migrating from React to Qwik is a gradual process that can be done step by step. By understanding core concept mapping, following migration strategies and best practices, you can successfully complete the migration and achieve better performance.