Use when optimizing performance or reviewing security. Covers code-splitting, React Compiler patterns, asset optimization, a11y testing, and security hardening for React apps.
Production-ready patterns for building fast, accessible, and secure React applications.
Automatic with TanStack Router:
Manual code-splitting:
import { lazy, Suspense } from 'react'
// Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'))
function Dashboard() {
return (
<Suspense fallback={<Spinner />}>
<HeavyChart data={data} />
</Suspense>
)
}
Route-level lazy loading:
// src/routes/dashboard.lazy.tsx
export const Route = createLazyFileRoute('/dashboard')({
component: DashboardComponent,
})
The React Compiler automatically optimizes performance when you write compiler-friendly code:
ā Do:
ā Avoid:
Verify optimization:
Use Vite asset pipeline:
// Imports are optimized and hashed
import logo from './logo.png'
<img src={logo} alt="Logo" />
Prefer modern formats:
// WebP for photos
<img src="/hero.webp" alt="Hero" />
// SVG for icons
import { ReactComponent as Icon } from './icon.svg'
<Icon />
Lazy load images:
<img src={imageSrc} loading="lazy" alt="Description" />
Responsive images:
<img
srcSet="
/image-320w.webp 320w,
/image-640w.webp 640w,
/image-1280w.webp 1280w
"
sizes="(max-width: 640px) 100vw, 640px"
src="/image-640w.webp"
alt="Description"
/>
# Build with analysis
npx vite build --mode production
# Visualize bundle
pnpm add -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
export default defineConfig({
plugins: [
react(),
visualizer({ open: true }),
],
})
staleTime per queryā Use semantic elements:
// Good
<nav><a href="/about">About</a></nav>
<button onClick={handleClick}>Submit</button>
<main><article>Content</article></main>
// Bad
<div onClick={handleNav}>About</div>
<div onClick={handleClick}>Submit</div>
<div><div>Content</div></div>
Only add ARIA when semantic HTML isn't enough:
// Custom select component
<div
role="listbox"
aria-label="Select country"
aria-activedescendant={activeId}
>
<div role="option" id="us">United States</div>
<div role="option" id="uk">United Kingdom</div>
</div>
// Loading state
<button aria-busy={isLoading} disabled={isLoading}>
{isLoading ? 'Loading...' : 'Submit'}
</button>
Ensure all interactive elements are keyboard accessible:
function Dialog({ isOpen, onClose }: DialogProps) {
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose()
}
if (isOpen) {
document.addEventListener('keydown', handleEscape)
return () => document.removeEventListener('keydown', handleEscape)
}
}, [isOpen, onClose])
return isOpen ? (
<div role="dialog" aria-modal="true">
{/* Focus trap implementation */}
<button onClick={onClose} aria-label="Close dialog">Ć</button>
{/* Dialog content */}
</div>
) : null
}
Use accessible queries (by role/label):
import { render, screen } from '@testing-library/react'
test('button is accessible', () => {
render(<button>Submit</button>)
// ā
Good - query by role
const button = screen.getByRole('button', { name: /submit/i })
expect(button).toBeInTheDocument()
// ā Avoid - query by test ID
const button = screen.getByTestId('submit-button')
})
Common accessible queries:
// By role (preferred)
screen.getByRole('button', { name: /submit/i })
screen.getByRole('textbox', { name: /email/i })
screen.getByRole('heading', { level: 1 })
// By label
screen.getByLabelText(/email address/i)
// By text
screen.getByText(/welcome/i)
ā Wrong - secrets in code:
const API_KEY = 'sk_live_abc123' // Exposed in bundle!
ā Correct - environment variables:
// Only VITE_* variables are exposed to client
const API_KEY = import.meta.env.VITE_PUBLIC_KEY
In .env.local (not committed):
VITE_PUBLIC_KEY=pk_live_abc123 # Public key only!
Backend handles secrets:
// Frontend calls backend, backend uses secret API key
await apiClient.post('/process-payment', { amount, token })
// Backend has access to SECRET_KEY via server env
At boundaries (API responses):
import { z } from 'zod'
const UserSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
})
async function fetchUser(id: string) {
const response = await apiClient.get(`/users/${id}`)
// Validate response
return UserSchema.parse(response.data)
}
User input:
const formSchema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Password must be 8+ characters'),
})
type FormData = z.infer<typeof formSchema>
function LoginForm() {
const handleSubmit = (data: unknown) => {
const result = formSchema.safeParse(data)
if (!result.success) {
setErrors(result.error.errors)
return
}
// result.data is typed and validated
login(result.data)
}
}
React automatically escapes content in JSX:
// ā
Safe - React escapes
<div>{userInput}</div>
// ā Dangerous - bypasses escaping
<div dangerouslySetInnerHTML={{ __html: userInput }} />
If you must use HTML:
import DOMPurify from 'dompurify'
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(trustedHTML)
}} />
Add CSP headers on server:
# nginx example
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
connect-src 'self' https://api.example.com;
";
Pin versions in package.json:
{
"dependencies": {
"react": "19.0.0", // Exact version
"@tanstack/react-query": "^5.59.0" // Allow patches
}
}
Audit regularly:
pnpm audit
pnpm audit --fix
Use Renovate or Dependabot:
// .github/renovate.json
{
"extends": ["config:base"],
"automerge": true,
"major": { "automerge": false }
}
Run with --ignore-scripts:
# Prevents malicious post-install scripts
pnpm install --ignore-scripts
Scan for secrets:
# Add to CI
git-secrets --scan
VITE_* env vars to clientpnpm audit regularly--ignore-scripts in CI