Skip to content

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

bash
/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:

javascript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

typescript
// 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:

bash
# .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.

Built with ❤️ for the AI Coding community, by Praney Behl