optimize-performance
Access: /template optimize-performance or /t optimize-performance
Analyzes and optimizes your MakerKit application's performance, implementing best practices for speed, efficiency, and user experience.
What It Does
The optimize-performance command helps you:
- Analyze current performance metrics
- Implement code splitting and lazy loading
- Optimize images and assets
- Configure caching strategies
- Reduce bundle sizes
- Implement performance monitoring
- Add loading optimizations
Usage
/template optimize-performance "Description"
# or
/t optimize-performance "Description"When prompted, specify:
- Performance goals (Core Web Vitals targets)
- Areas to optimize (bundle size, runtime, etc.)
- Caching strategy preferences
- Image optimization requirements
Prerequisites
- A working MakerKit application
- Performance baseline measurements
- Commercial MakerKit license from MakerKit
What Gets Created
1. Performance Analysis
Bundle analysis configuration:
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// Existing config...
// Performance optimizations
swcMinify: true,
compress: true,
images: {
domains: ['your-domain.com'],
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 60 * 60 * 24 * 365, // 1 year
},
experimental: {
optimizeCss: true,
legacyBrowsers: false,
},
// Webpack optimizations
webpack: (config, { isServer }) => {
// Tree shaking
config.optimization.usedExports = true;
// Module concatenation
config.optimization.concatenateModules = true;
// Split chunks
if (!isServer) {
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
default: false,
vendors: false,
framework: {
name: 'framework',
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|scheduler|prop-types|use-subscription)[\\/]/,
priority: 40,
enforce: true,
},
lib: {
test(module) {
return module.size() > 160000 &&
/node_modules[/\\]/.test(module.identifier());
},
name(module) {
const hash = crypto.createHash('sha1');
hash.update(module.identifier());
return hash.digest('hex').substring(0, 8);
},
priority: 30,
minChunks: 1,
reuseExistingChunk: true,
},
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 20,
priority: 20,
},
},
};
}
return config;
},
});2. Dynamic Imports
Implement code splitting:
// components/heavy-component.tsx
import dynamic from 'next/dynamic';
import { Skeleton } from '@kit/ui/skeleton';
// Heavy chart component
export const DashboardChart = dynamic(
() => import('./dashboard-chart').then(mod => mod.DashboardChart),
{
loading: () => <Skeleton className="h-[400px] w-full" />,
ssr: false, // Disable SSR for client-only components
}
);
// Modal that's not immediately needed
export const CreateProjectModal = dynamic(
() => import('./create-project-modal').then(mod => mod.CreateProjectModal),
{
loading: () => null,
}
);
// Feature flag for progressive enhancement
export const AdvancedFeatures = dynamic(
() => import('./advanced-features'),
{
loading: () => <div>Loading advanced features...</div>,
ssr: false,
}
);3. Image Optimization
Optimize images automatically:
// components/optimized-image.tsx
import Image from 'next/image';
import { useState } from 'react';
interface OptimizedImageProps {
src: string;
alt: string;
priority?: boolean;
className?: string;
width?: number;
height?: number;
}
export function OptimizedImage({
src,
alt,
priority = false,
className,
width,
height,
}: OptimizedImageProps) {
const [isLoading, setIsLoading] = useState(true);
return (
<div className={`relative overflow-hidden ${className}`}>
{isLoading && (
<div className="absolute inset-0 bg-gray-200 animate-pulse" />
)}
<Image
src={src}
alt={alt}
width={width}
height={height}
priority={priority}
quality={85}
placeholder="blur"
blurDataURL={generateBlurDataURL(src)}
onLoadingComplete={() => setIsLoading(false)}
className={`
duration-700 ease-in-out
${isLoading ? 'scale-110 blur-2xl grayscale' : 'scale-100 blur-0 grayscale-0'}
`}
/>
</div>
);
}
// Generate blur placeholder
function generateBlurDataURL(src: string): string {
// In production, generate actual blur data URL
return 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ...';
}4. React Query Optimization
Configure efficient data fetching:
// lib/query-client.ts
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
// Stale time: 5 minutes
staleTime: 5 * 60 * 1000,
// Cache time: 10 minutes
cacheTime: 10 * 60 * 1000,
// Refetch on window focus in production only
refetchOnWindowFocus: process.env.NODE_ENV === 'production',
// Retry with exponential backoff
retry: (failureCount, error) => {
if (error.status === 404) return false;
if (failureCount > 3) return false;
return true;
},
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
},
},
});
// Prefetch critical data
export async function prefetchDashboardData(accountId: string) {
await Promise.all([
queryClient.prefetchQuery({
queryKey: ['account', accountId],
queryFn: () => getAccount(accountId),
}),
queryClient.prefetchQuery({
queryKey: ['projects', accountId],
queryFn: () => getProjects(accountId),
}),
queryClient.prefetchQuery({
queryKey: ['team-members', accountId],
queryFn: () => getTeamMembers(accountId),
}),
]);
}5. Database Query Optimization
Optimize Supabase queries:
// lib/database/optimized-queries.ts
import { SupabaseClient } from '@supabase/supabase-js';
// Use select to only fetch needed columns
export async function getProjectsOptimized(
client: SupabaseClient,
accountId: string
) {
const { data, error } = await client
.from('projects')
.select('id, name, status, created_at')
.eq('account_id', accountId)
.order('created_at', { ascending: false })
.limit(20);
return { data, error };
}
// Batch related queries
export async function getDashboardData(
client: SupabaseClient,
accountId: string
) {
const [projects, members, activity] = await Promise.all([
client
.from('projects')
.select('id, name, status')
.eq('account_id', accountId)
.limit(5),
client
.from('account_members')
.select('user_id, role, user:users(display_name, picture_url)')
.eq('account_id', accountId),
client
.from('activity_log')
.select('action, created_at, user:users(display_name)')
.eq('account_id', accountId)
.order('created_at', { ascending: false })
.limit(10),
]);
return {
projects: projects.data,
members: members.data,
activity: activity.data,
};
}
// Use RPC for complex queries
export async function getProjectStats(
client: SupabaseClient,
accountId: string
) {
const { data, error } = await client.rpc('get_project_stats', {
p_account_id: accountId,
});
return { data, error };
}6. Caching Strategy
Implement smart caching:
// middleware.ts
import { NextResponse } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Cache static assets
if (request.nextUrl.pathname.startsWith('/_next/static')) {
response.headers.set(
'Cache-Control',
'public, max-age=31536000, immutable'
);
}
// Cache images
if (request.nextUrl.pathname.startsWith('/_next/image')) {
response.headers.set(
'Cache-Control',
'public, max-age=86400, stale-while-revalidate'
);
}
// API responses
if (request.nextUrl.pathname.startsWith('/api')) {
response.headers.set(
'Cache-Control',
'private, max-age=0, must-revalidate'
);
}
return response;
}
// Redis caching for expensive operations
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});
export async function getCachedData<T>(
key: string,
fetcher: () => Promise<T>,
ttl: number = 300 // 5 minutes default
): Promise<T> {
// Try cache first
const cached = await redis.get<T>(key);
if (cached) return cached;
// Fetch and cache
const data = await fetcher();
await redis.setex(key, ttl, data);
return data;
}7. Loading States
Optimize perceived performance:
// components/optimized-loading.tsx
import { Suspense } from 'react';
import { Skeleton } from '@kit/ui/skeleton';
// Skeleton loader that matches content
export function ProjectListSkeleton() {
return (
<div className="space-y-4">
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="flex items-center space-x-4">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
</div>
</div>
))}
</div>
);
}
// Progressive enhancement wrapper
export function ProgressiveFeature({
children,
fallback,
}: {
children: React.ReactNode;
fallback?: React.ReactNode;
}) {
return (
<Suspense fallback={fallback || <Skeleton className="h-40 w-full" />}>
{children}
</Suspense>
);
}
// Optimistic UI updates
export function useOptimisticUpdate() {
const queryClient = useQueryClient();
return {
updateOptimistically: <T,>(
queryKey: QueryKey,
updater: (old: T) => T
) => {
queryClient.setQueryData(queryKey, updater);
},
rollback: (queryKey: QueryKey) => {
queryClient.invalidateQueries(queryKey);
},
};
}8. Web Workers
Offload heavy computations:
// workers/data-processor.worker.ts
self.addEventListener('message', async (event) => {
const { type, data } = event.data;
switch (type) {
case 'PROCESS_LARGE_DATASET':
const result = await processLargeDataset(data);
self.postMessage({ type: 'RESULT', data: result });
break;
case 'GENERATE_REPORT':
const report = await generateReport(data);
self.postMessage({ type: 'REPORT', data: report });
break;
}
});
// hooks/use-worker.ts
export function useDataProcessor() {
const workerRef = useRef<Worker>();
useEffect(() => {
workerRef.current = new Worker(
new URL('../workers/data-processor.worker.ts', import.meta.url)
);
return () => workerRef.current?.terminate();
}, []);
const processData = useCallback((data: any) => {
return new Promise((resolve) => {
if (!workerRef.current) return;
workerRef.current.onmessage = (event) => {
if (event.data.type === 'RESULT') {
resolve(event.data.data);
}
};
workerRef.current.postMessage({
type: 'PROCESS_LARGE_DATASET',
data,
});
});
}, []);
return { processData };
}9. Performance Monitoring
Track real user metrics:
// lib/performance/monitoring.ts
export function initPerformanceMonitoring() {
// Core Web Vitals
if (typeof window !== 'undefined') {
import('web-vitals').then(({ getCLS, getFID, getLCP, getFCP, getTTFB }) => {
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
getFCP(sendToAnalytics);
getTTFB(sendToAnalytics);
});
}
}
function sendToAnalytics(metric: Metric) {
// Send to your analytics service
if (window.gtag) {
window.gtag('event', metric.name, {
value: Math.round(metric.value),
metric_id: metric.id,
metric_value: metric.value,
metric_delta: metric.delta,
});
}
// Log warnings for poor performance
const thresholds = {
CLS: 0.1,
FID: 100,
LCP: 2500,
FCP: 1800,
TTFB: 800,
};
if (metric.value > thresholds[metric.name]) {
console.warn(`Poor ${metric.name}: ${metric.value}`);
}
}10. Build Optimization
Production build configuration:
# .env.production
# Enable SWC minification
NEXT_TELEMETRY_DISABLED=1
# Build script with analysis
"build:analyze": "ANALYZE=true pnpm build",
"build:prod": "pnpm build && pnpm optimize:fonts && pnpm optimize:images"Performance Checklist
After optimization, verify:
- [ ] Lighthouse score > 90
- [ ] First Contentful Paint < 1.8s
- [ ] Largest Contentful Paint < 2.5s
- [ ] Cumulative Layout Shift < 0.1
- [ ] First Input Delay < 100ms
- [ ] Time to Interactive < 3.8s
- [ ] Bundle size reduced by 30%+
- [ ] Images optimized and lazy loaded
- [ ] Critical CSS inlined
- [ ] JavaScript bundle split effectively
License Requirement
Important: This command requires a commercial MakerKit license from https://makerkit.dev?atp=MqaGgc MakerKit is a premium SaaS starter kit and requires proper licensing for commercial use.
