Skip to content
Back to skills

React Vite Performance

ASecurity

React + Vite performance optimization — code splitting, lazy loading, bundle analysis, Web Vitals, and modern React patterns for fast web applications

  • 4 stars
  • 0 votes
  • 0 copies
  • 4 views
  • Added September 3, 2026
ai-agentstypescriptbashreactapiperformance

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned September 3, 2026

npx -y skills add fabioc-aloha/Alex_Skill_Mall --skill react-vite-performance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React Vite Performance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for React Vite Performance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/fabioc-aloha-react-vite-performance/badge)](https://www.skillsdirectory.com/skills/fabioc-aloha-react-vite-performance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: react-vite-performance
description: "React + Vite performance optimization — code splitting, lazy loading, bundle analysis, Web Vitals, and modern React patterns for fast web applications"
lastReviewed: 2026-04-30
---

# React + Vite Performance Optimization


> Fast by default, optimized by design — sub-300KB bundles and sub-2s load times.
>
> **Targets**: React 19+ / Vite 6+ | **Last validated**: April 2026

## Performance Targets

| Metric | Target | Tool |
|--------|--------|------|
| Initial JS (gzipped) | < 300 KB | `rollup-plugin-visualizer` |
| First Contentful Paint | < 1.5s | Lighthouse |
| Time to Interactive | < 3s | Lighthouse |
| Largest Contentful Paint | < 2.5s | Web Vitals |
| Cumulative Layout Shift | < 0.1 | Web Vitals |

## Vite Build Configuration

```typescript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    target: 'esnext',
    minify: 'esbuild',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          'react-vendor': ['react', 'react-dom', 'react-router-dom'],
          'ui': ['@headlessui/react', '@heroicons/react'],
          // Group large dependencies into separate chunks
        },
      },
    },
    chunkSizeWarningLimit: 500,
  },
  optimizeDeps: {
    include: ['react', 'react-dom', 'react-router-dom'],
  },
});
```

### Bundle Analysis

```bash
npm install -D rollup-plugin-visualizer
```

```typescript
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';

plugins: [
  react(),
  visualizer({ filename: 'dist/stats.html', gzipSize: true }),
]
```

## Code Splitting

### Route-Based Splitting

```typescript
import { lazy, Suspense } from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />,
    children: [
      { path: 'dashboard', element: <Dashboard /> },
      { path: 'settings', element: <Settings /> },
    ],
  },
]);

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <RouterProvider router={router} />
    </Suspense>
  );
}
```

### Component-Based Splitting

```typescript
const Chart = lazy(() => import('./components/Chart'));

function Dashboard() {
  return (
    <Suspense fallback={<ChartSkeleton />}>
      <Chart data={data} />
    </Suspense>
  );
}
```

### Preloading on Hover

```typescript
function NavLink({ to, loader, children }) {
  const preload = () => loader(); // e.g., () => import('./pages/Settings')

  return (
    <Link to={to} onMouseEnter={preload} onFocus={preload}>
      {children}
    </Link>
  );
}
```

## Modern React Patterns

### Compiler-Friendly Code (React 19+)

React 19's compiler auto-memoizes. Write straightforward components:

```typescript
// ✅ Let the compiler optimize
function UserCard({ user }: { user: User }) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

// ❌ Avoid manual memoization the compiler handles
const UserCard = React.memo(({ user }) => { ... });
```

**Vite setup** — install `babel-plugin-react-compiler` and add to `vite.config.ts`:

```typescript
// vite.config.ts
import react from '@vitejs/plugin-react';
export default defineConfig({
  plugins: [
    react({ babel: { plugins: ['babel-plugin-react-compiler'] } }),
  ],
});
```

### use() Hook for Data Loading

```typescript
import { use, Suspense } from 'react';

function UserProfile({ userPromise }: { userPromise: Promise<User> }) {
  const user = use(userPromise);
  return <div>{user.name}</div>;
}

function App() {
  const userPromise = fetchUser(userId);
  return (
    <Suspense fallback={<UserSkeleton />}>
      <UserProfile userPromise={userPromise} />
    </Suspense>
  );
}
```

### useTransition for Non-Urgent Updates

```typescript
import { useState, useTransition } from 'react';

function SearchableList({ items }: { items: Item[] }) {
  const [query, setQuery] = useState('');
  const [filtered, setFiltered] = useState(items);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (value: string) => {
    setQuery(value); // Urgent: update input immediately
    startTransition(() => {
      setFiltered(items.filter(item =>
        item.name.toLowerCase().includes(value.toLowerCase())
      ));
    });
  };

  return (
    <>
      <input value={query} onChange={e => handleSearch(e.target.value)} />
      {isPending && <Spinner />}
      <List items={filtered} />
    </>
  );
}
```

## TanStack Query Optimization

```typescript
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,    // 5 minutes
      gcTime: 30 * 60 * 1000,      // 30 minutes
      refetchOnWindowFocus: false,
      retry: 1,
    },
  },
});
```

### Optimistic Updates

```typescript
const updateUser = useMutation({
  mutationFn: (data: UserUpdate) => api.updateUser(data),
  onMutate: async (newData) => {
    await queryClient.cancelQueries({ queryKey: ['user'] });
    const previous = queryClient.getQueryData(['user']);
    queryClient.setQueryData(['user'], old => ({ ...old, ...newData }));
    return { previous };
  },
  onError: (_err, _new, context) => {
    queryClient.setQueryData(['user'], context?.previous);
  },
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['user'] });
  },
});
```

## Asset Optimization

### Images

```typescript
// Non-critical images: lazy load
<img src={src} alt={alt} loading="lazy" decoding="async" fetchpriority="low" />

// LCP image: load immediately
<img src={src} alt={alt} fetchpriority="high" />
```

### Fonts

```css
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-var.woff2') format('woff2-variations');
  font-display: swap;
  font-weight: 100 900;
}
```

```html
<link rel="preload" href="/fonts/Inter-var.woff2" as="font" type="font/woff2" crossorigin>
```

## Web Vitals Monitoring

> **INP replaced FID** as a Core Web Vital in March 2024. INP measures overall responsiveness (all interactions), not just the first one.

```typescript
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';

function reportMetric(metric: Metric) {
  analytics.track('web-vitals', {
    name: metric.name,
    value: metric.value,
    rating: metric.rating,
  });
}

onCLS(reportMetric);
onINP(reportMetric);   // Interaction to Next Paint (replaced FID)
onLCP(reportMetric);
onFCP(reportMetric);
onTTFB(reportMetric);
```

## Performance Checklist

### Build

- [ ] Manual chunks for large vendor libs
- [ ] Tree shaking enabled (ESM imports)
- [ ] Dependency pre-bundling configured
- [ ] Bundle size tracked in CI

### Runtime

- [ ] Routes lazy-loaded with Suspense
- [ ] Heavy components split into separate chunks
- [ ] `useTransition` for non-urgent state updates
- [ ] Virtual scrolling for long lists
- [ ] Debounce expensive operations

### Assets

- [ ] Images: WebP, lazy loading, responsive sizes
- [ ] Fonts: `font-display: swap`, preload critical
- [ ] Brotli/gzip compression enabled
- [ ] Critical CSS inlined

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…