React vite performance
Skill fabioc-aloha/Alex_Skill_Mall/plugins/platform-tooling/react-vite-performance
284 curated plugins for AI assistants across 16 categories: security, Azure, documentation, code quality, cloud infrastructure, and more. Works with GitHub Copilot. Drop into .github/skills/local/ and go.
npx -y skills add fabioc-aloha/Alex_Skill_Mall --skill react-vite-performanceAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
React + Vite performance optimization — code splitting, lazy loading, bundle analysis, Web Vitals, and modern React patterns for fast web applications
SKILL.md
7.2 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
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
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
npm install -D rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
plugins: [
react(),
visualizer({ filename: 'dist/stats.html', gzipSize: true }),
]
Code Splitting
Route-Based Splitting
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
const Chart = lazy(() => import('./components/Chart'));
function Dashboard() {
return (
<Suspense fallback={<ChartSkeleton />}>
<Chart data={data} />
</Suspense>
);
}
Preloading on Hover
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:
// ✅ 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:
// 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
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
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
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 30 * 60 * 1000, // 30 minutes
refetchOnWindowFocus: false,
retry: 1,
},
},
});
Optimistic Updates
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
// 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
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-var.woff2') format('woff2-variations');
font-display: swap;
font-weight: 100 900;
}
<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.
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
-
useTransitionfor 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