# Performance Checklist for Mylder Platform ## Core Web Vitals Targets ### Production Goals (75th Percentile) | Metric | Target | Good | Needs Improvement | Poor | |--------|--------|------|-------------------|------| | **LCP** (Largest Contentful Paint) | <2.5s | <2.5s | 2.5s-4.0s | >4.0s | | **FID** (First Input Delay) | <100ms | <100ms | 100ms-300ms | >300ms | | **CLS** (Cumulative Layout Shift) | <0.1 | <0.1 | 0.1-0.25 | >0.25 | | **INP** (Interaction to Next Paint) | <200ms | <200ms | 200ms-500ms | >500ms | | **TTFB** (Time to First Byte) | <600ms | <800ms | 800ms-1800ms | >1800ms | | **FCP** (First Contentful Paint) | <1.8s | <1.8s | 1.8s-3.0s | >3.0s | ### Current vs Target Performance **Baseline (before optimization):** ``` LCP: ~3.5s → Target: <2.5s (30% improvement needed) FID: ~150ms → Target: <100ms CLS: ~0.15 → Target: <0.1 TTFB: ~800ms → Target: <600ms (edge caching) ``` ### Measurement Tools #### 1. Real User Monitoring (RUM) ```typescript // app/layout.tsx import { SpeedInsights } from '@vercel/speed-insights/next'; import { Analytics } from '@vercel/analytics/react'; export default function RootLayout({ children }) { return ( {children} ); } ``` **Alternative:** Cloudflare Web Analytics (privacy-friendly, no cookies) ```html ``` #### 2. Lab Testing ```bash # Lighthouse (local) npx lighthouse https://mylder.io --view # WebPageTest (global) # https://www.webpagetest.org # Chrome DevTools # DevTools → Performance → Record → Analyze ``` #### 3. Continuous Monitoring ```bash # Lighthouse CI (in CI/CD pipeline) npm install -g @lhci/cli lhci autorun --collect.url=https://mylder.io ``` ## Next.js Optimization Settings ### 1. Production Build Configuration ```typescript // next.config.ts import type { NextConfig } from 'next'; const nextConfig: NextConfig = { // Enable React strict mode for better error handling reactStrictMode: true, // Optimize bundle size swcMinify: true, // Compress output compress: true, // Optimize images images: { formats: ['image/avif', 'image/webp'], deviceSizes: [640, 750, 828, 1080, 1200, 1920], imageSizes: [16, 32, 48, 64, 96, 128, 256], minimumCacheTTL: 2592000, // 30 days dangerouslyAllowSVG: false, contentDispositionType: 'attachment', remotePatterns: [ { protocol: 'https', hostname: 'supabase.mylder.io', pathname: '/storage/v1/object/**', }, ], }, // Optimize fonts (automatic optimization) optimizeFonts: true, // Experimental features experimental: { // Optimize CSS optimizeCss: true, // Tree shaking for smaller bundles optimizePackageImports: ['@radix-ui/react-icons', 'lucide-react'], }, // Custom headers for caching async headers() { return [ { source: '/:path*', headers: [ { key: 'X-DNS-Prefetch-Control', value: 'on', }, { key: 'X-Frame-Options', value: 'SAMEORIGIN', }, ], }, { source: '/_next/static/:path*', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, { source: '/_next/image/:path*', headers: [ { key: 'Cache-Control', value: 'public, max-age=2592000, stale-while-revalidate=86400', }, ], }, ]; }, }; export default nextConfig; ``` ### 2. Bundle Analyzer ```bash # Install npm install -D @next/bundle-analyzer # next.config.ts import bundleAnalyzer from '@next/bundle-analyzer'; const withBundleAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === 'true', }); export default withBundleAnalyzer(nextConfig); # Run analysis ANALYZE=true npm run build ``` **Target:** Total bundle size <500KB (gzipped) ### 3. Code Splitting Strategies #### Dynamic Imports ```typescript // app/dashboard/page.tsx import dynamic from 'next/dynamic'; // Lazy load heavy components const HeavyChart = dynamic(() => import('@/components/HeavyChart'), { loading: () => , ssr: false, // Client-side only if needed }); export default function Dashboard() { return ; } ``` #### Route-Based Splitting ```typescript // Automatic with App Router app/ dashboard/ # Separate chunk settings/ # Separate chunk (marketing)/ # Separate chunk (route group) ``` #### Vendor Splitting ```typescript // next.config.ts experimental: { optimizePackageImports: [ '@radix-ui/react-icons', 'lucide-react', 'date-fns', ], }, ``` ### 4. Server Components (Default) ```typescript // app/page.tsx (Server Component by default) export default async function Home() { // Fetch at build time or request time const data = await fetch('https://api.mylder.io/data', { next: { revalidate: 3600 } // ISR: 1 hour cache }); return
{/* No client-side JS for this component */}
; } ``` **Benefits:** - 0KB JavaScript sent to client - Faster initial page load - Better SEO ### 5. Client Components (Opt-In) ```typescript // components/InteractiveButton.tsx 'use client'; import { useState } from 'react'; export default function InteractiveButton() { const [count, setCount] = useState(0); return ; } ``` **Rule:** Only use 'use client' when you need: - Interactivity (useState, useEffect) - Browser APIs (localStorage, window) - Event handlers (onClick, onSubmit) ## Image Optimization ### 1. Next.js Image Component ```typescript import Image from 'next/image'; // Optimized image with automatic sizing Hero image // Responsive image Hero image ``` ### 2. Image Format Priority 1. **AVIF** (best compression, 50% smaller than JPEG) 2. **WebP** (wide support, 30% smaller than JPEG) 3. **JPEG/PNG** (fallback) ### 3. Cloudflare Image Optimization ```typescript // next.config.ts images: { loader: 'custom', loaderFile: './lib/cloudflare-image-loader.ts', } // lib/cloudflare-image-loader.ts export default function cloudflareLoader({ src, width, quality }) { const params = [`width=${width}`, `quality=${quality || 75}`, 'format=auto']; return `https://mylder.io/cdn-cgi/image/${params.join(',')}/${src}`; } ``` ### 4. Lazy Loading Images ```typescript // Non-critical images (below fold) Feature // Critical images (above fold, LCP) Hero ``` ### 5. Responsive Images Checklist - [ ] Use Next.js Image component (automatic optimization) - [ ] Set explicit width/height (prevent CLS) - [ ] Use `priority` for LCP images - [ ] Use `loading="lazy"` for below-fold images - [ ] Provide blur placeholder for better UX - [ ] Compress images before upload (80-85% quality) - [ ] Use modern formats (AVIF, WebP) ## Font Optimization ### 1. Next.js Font Optimization (Built-In) ```typescript // app/layout.tsx import { Inter, Roboto_Mono } from 'next/font/google'; const inter = Inter({ subsets: ['latin'], display: 'swap', // Prevent invisible text flash variable: '--font-inter', preload: true, }); const robotoMono = Roboto_Mono({ subsets: ['latin'], display: 'swap', variable: '--font-roboto-mono', weight: ['400', '700'], // Only load needed weights }); export default function RootLayout({ children }) { return ( {children} ); } ``` **Benefits:** - Self-hosted fonts (no external requests) - Automatic subset optimization - Zero layout shift (font-display: swap) ### 2. Custom Fonts ```typescript import localFont from 'next/font/local'; const myFont = localFont({ src: './fonts/MyFont.woff2', display: 'swap', variable: '--font-my-font', }); ``` ### 3. Font Loading Best Practices - [ ] Use only 2-3 font families max - [ ] Load only needed weights (400, 700) - [ ] Use `font-display: swap` - [ ] Preload critical fonts - [ ] Self-host fonts (no Google Fonts CDN) ## CSS Optimization ### 1. Tailwind CSS Production Config ```typescript // tailwind.config.ts import type { Config } from 'tailwindcss'; const config: Config = { content: [ './app/**/*.{ts,tsx}', './components/**/*.{ts,tsx}', ], theme: { extend: {}, }, // Remove unused styles in production future: { hoverOnlyWhenSupported: true, // Better mobile UX }, }; export default config; ``` ### 2. Critical CSS Next.js automatically inlines critical CSS for first paint. ### 3. CSS Module Best Practices ```typescript // Use CSS Modules for component-specific styles import styles from './Button.module.css'; export function Button() { return ; } ``` ## JavaScript Optimization ### 1. Tree Shaking ```typescript // Good: Import only what you need import { format } from 'date-fns'; // Bad: Import entire library import * as dateFns from 'date-fns'; ``` ### 2. Remove Unused Dependencies ```bash # Analyze bundle npx depcheck # Remove unused npm uninstall unused-package ``` ### 3. Use Smaller Alternatives | Heavy Package | Lightweight Alternative | Savings | |---------------|------------------------|---------| | Moment.js (232KB) | date-fns (13KB) | 95% | | Lodash (70KB) | Lodash-es (24KB) | 66% | | Axios (30KB) | Fetch API (native) | 100% | | React Icons (full) | Lucide React (tree-shakeable) | 80% | ### 4. Debounce/Throttle Heavy Operations ```typescript // components/Search.tsx 'use client'; import { useState, useCallback } from 'react'; import { debounce } from 'lodash-es/debounce'; export function Search() { const [query, setQuery] = useState(''); const handleSearch = useCallback( debounce(async (value: string) => { const results = await fetch(`/api/search?q=${value}`); // Handle results }, 300), [] ); return handleSearch(e.target.value)} />; } ``` ## Caching Strategy ### 1. Static Generation (Fastest) ```typescript // app/blog/page.tsx export const revalidate = false; // Static at build time export default async function Blog() { const posts = await fetchPosts(); return ; } ``` ### 2. Incremental Static Regeneration (ISR) ```typescript // app/blog/[slug]/page.tsx export const revalidate = 3600; // Revalidate every hour export async function generateStaticParams() { const posts = await fetchPosts(); return posts.map((post) => ({ slug: post.slug })); } export default async function Post({ params }) { const post = await fetchPost(params.slug); return
; } ``` ### 3. Server-Side Rendering (SSR) ```typescript // app/dashboard/page.tsx export const dynamic = 'force-dynamic'; // Always server-render export default async function Dashboard() { const data = await fetchUserData(); // Per-request return ; } ``` ### 4. Client-Side Caching ```typescript // Use SWR or React Query for client-side data fetching 'use client'; import useSWR from 'swr'; export function Profile() { const { data, error } = useSWR('/api/user', fetcher, { revalidateOnFocus: false, revalidateOnReconnect: false, refreshInterval: 60000, // 1 minute }); if (error) return
Error
; if (!data) return
Loading...
; return
{data.name}
; } ``` ### 5. HTTP Cache Headers ```typescript // app/api/data/route.ts export async function GET() { const data = await fetchData(); return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400', }, }); } ``` ## Cache Invalidation Strategy ### 1. On-Demand Revalidation ```typescript // app/api/revalidate/route.ts import { revalidatePath, revalidateTag } from 'next/cache'; export async function POST(request: Request) { const { path, tag } = await request.json(); if (path) { revalidatePath(path); } if (tag) { revalidateTag(tag); } return Response.json({ revalidated: true }); } ``` ### 2. Webhook Triggers (n8n) ```typescript // n8n workflow: CMS Update → POST /api/revalidate { "path": "/blog", "tag": "posts" } ``` ### 3. Cloudflare Cache Purge ```bash # On deploy (CI/CD) curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/purge_cache" \ -H "Authorization: Bearer {api_token}" \ -H "Content-Type: application/json" \ --data '{"purge_everything":true}' ``` ### 4. Selective Purge ```bash # Purge specific URLs --data '{"files":["https://mylder.io/blog","https://mylder.io/about"]}' # Purge by tag --data '{"tags":["blog-posts"]}' ``` ## Performance Testing Workflow ### 1. Pre-Deploy Checks ```bash # Run before every deployment npm run build # Must succeed npm run lint # No errors npm run typecheck # No type errors # Optional but recommended ANALYZE=true npm run build # Check bundle size npx lighthouse https://staging.mylder.io --view ``` ### 2. Post-Deploy Verification ```bash # Production smoke tests curl -I https://mylder.io # Check TTFB curl -I https://mylder.io/_next/static/[hash]/[file].js # Check caching # Full performance audit npx lighthouse https://mylder.io --view # Real user monitoring # Check Cloudflare Analytics or Vercel Analytics dashboard ``` ### 3. Continuous Monitoring ```bash # Set up alerts in Cloudflare/monitoring tool - LCP > 3s → Alert - Error rate > 1% → Alert - TTFB > 1s → Alert ``` ## Quick Wins Checklist ### Immediate (1 day) - [ ] Enable Cloudflare CDN (see cloudflare-setup.md) - [ ] Add proper cache headers to static assets - [ ] Use Next.js Image component everywhere - [ ] Enable `reactStrictMode` and `swcMinify` - [ ] Remove unused dependencies ### Short-term (1 week) - [ ] Implement ISR for blog/content pages - [ ] Lazy load heavy components - [ ] Optimize images (compress, convert to WebP/AVIF) - [ ] Set up bundle analyzer - [ ] Add loading states and skeletons (improve perceived performance) ### Medium-term (1 month) - [ ] Implement edge caching for auth (see cloudflare-workers-roadmap.md) - [ ] Set up real user monitoring (RUM) - [ ] Optimize third-party scripts (defer/async) - [ ] Implement stale-while-revalidate for APIs - [ ] Add Lighthouse CI to deployment pipeline ### Long-term (3 months) - [ ] Move 70% traffic to edge (Cloudflare Workers) - [ ] Implement edge personalization - [ ] Set up A/B testing at edge - [ ] Achieve Core Web Vitals targets consistently - [ ] Reduce VPS load by 60%+ ## Common Performance Issues & Fixes ### Issue: High LCP (>4s) **Causes:** - Large hero image not optimized - Server response time (TTFB) too high - Render-blocking resources **Fixes:** - Use Next.js Image with `priority` for LCP image - Enable Cloudflare CDN for faster TTFB - Inline critical CSS - Preload fonts ### Issue: High CLS (>0.25) **Causes:** - Images without width/height - Web fonts loading late - Dynamic content injections **Fixes:** - Set explicit dimensions on images - Use `font-display: swap` - Reserve space for dynamic content - Avoid inserting content above existing content ### Issue: High FID/INP (>300ms) **Causes:** - Heavy JavaScript execution - Long tasks blocking main thread - Unoptimized event handlers **Fixes:** - Code split large bundles - Debounce/throttle event handlers - Use Web Workers for heavy computation - Optimize third-party scripts ### Issue: Slow TTFB (>1s) **Causes:** - Slow server response - No CDN - Database queries during render **Fixes:** - Enable Cloudflare CDN - Use ISR instead of SSR - Move database queries to edge (D1) - Implement caching layers ## Monitoring Dashboard Setup ### Cloudflare Web Analytics ```html ``` ### Custom Performance Monitoring ```typescript // app/layout.tsx 'use client'; import { useEffect } from 'react'; import { usePathname } from 'next/navigation'; export function PerformanceMonitor() { const pathname = usePathname(); useEffect(() => { // Measure Web Vitals import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { getCLS(console.log); getFID(console.log); getFCP(console.log); getLCP(console.log); getTTFB(console.log); }); }, [pathname]); return null; } ``` ## Resources - [Next.js Performance Docs](https://nextjs.org/docs/app/building-your-application/optimizing) - [Web.dev Core Web Vitals](https://web.dev/vitals/) - [Lighthouse CI](https://github.com/GoogleChrome/lighthouse-ci) - [Cloudflare Performance](https://developers.cloudflare.com/fundamentals/speed/)