React Adapter
First-class React 18 support with Oxc-powered JSX compilation, React Fast Refresh HMR, automatic code splitting, and zero-config TypeScript.
TypeScript and JSX compiled by Oxc, including React Refresh. No Babel config needed.
Component-level HMR with state preserved. Powered by the official react-refresh runtime.
React.lazy() calls automatically create separate chunks loaded on demand.
TypeScript works out of the box. No tsconfig plugins or extra dependencies.
Experimental React Server Components support via the 'react-rsc' preset.
React.StrictMode is enabled in development by default for early detection of side effects.
Setup
npx lunx-dev@latest create my-react-app --template react --ts cd my-react-app && npm install && npm run dev
import { defineConfig } from 'lunx-dev' export default defineConfig({ framework: 'react', // React-specific options react: { // JSX runtime: 'automatic' (default) | 'classic' jsxRuntime: 'automatic', // Enable React Fast Refresh in dev (default: true) fastRefresh: true, // TypeScript-aware transforms typescript: { strictNullChecks: true, }, }, })
Project structure
my-react-app/ ├── src/ │ ├── main.tsx # ReactDOM.createRoot entry │ ├── App.tsx # Root component │ ├── components/ # Shared UI components │ ├── pages/ # Route-level components │ ├── hooks/ # Custom hooks │ └── index.css # Global styles ├── index.html ├── lunx.config.ts └── tsconfig.json
Entry point
import React from 'react' import ReactDOM from 'react-dom/client' import { BrowserRouter } from 'react-router-dom' import App from './App' import './index.css' ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> )
TypeScript paths
{ "compilerOptions": { "target": "ES2020", "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "moduleResolution": "bundler", "jsx": "react-jsx", "strict": true, "paths": { "@/*": ["./src/*"] } } }
Using React Router v6
import { Routes, Route, Navigate } from 'react-router-dom' import { Suspense, lazy } from 'react' import { Layout } from '@/components/Layout' // Code-split routes — each becomes a separate chunk const HomePage = lazy(() => import('@/pages/HomePage')) const DashBoard = lazy(() => import('@/pages/Dashboard')) const ProfilePage = lazy(() => import('@/pages/Profile')) const NotFound = lazy(() => import('@/pages/NotFound')) export default function App() { return ( <Suspense fallback={<div className="p-8 text-center">Loading...</div>}> <Routes> <Route path="/" element={<Layout />}> <Route index element={<HomePage />} /> <Route path="dash" element={<DashBoard />} /> <Route path="me" element={<ProfilePage />} /> <Route path="*" element={<NotFound />} /> </Route> </Routes> </Suspense> ) }
State management with Zustand
npm install zustandimport { create } from 'zustand' import { persist } from 'zustand/middleware' interface CounterState { count: number increment: () => void decrement: () => void reset: () => void } export const useCounter = create<CounterState>()( persist( (set) => ({ count: 0, increment: () => set(s => ({ count: s.count + 1 })), decrement: () => set(s => ({ count: s.count - 1 })), reset: () => set({ count: 0 }), }), { name: 'counter-storage' } ) )
Data fetching with TanStack Query
npm install @tanstack/react-queryimport { QueryClient, QueryClientProvider } from '@tanstack/react-query' const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 1000 * 60, // 1 minute gcTime: 1000 * 60 * 5, // 5 minutes retry: 2, }, }, }) ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <QueryClientProvider client={queryClient}> <BrowserRouter> <App /> </BrowserRouter> </QueryClientProvider> </React.StrictMode> )
React Compiler
Install babel-plugin-react-compiler (with @babel/core) and lunx runs it on your components and hooks in dev and builds: automatic memoization, no config. react: { compiler: false }turns it off; an object passes the compiler's options.
npm install -D babel-plugin-react-compiler @babel/coreReact Server Components
An app on @vitejs/plugin-rsc (its rsc, ssr and client environments, as npm create vite -- --template rsc sets up) runs lunx dev, lunx build and lunx preview on lunx's engine: server components render on the server, "use client" components hydrate. Frameworks built on RSC (Waku, React Router RSC) work too.
Storybook and Vitest
lunx exec storybook dev -p 6006 lunx exec vitest run