Replace Next.js/Supabase/Vercel stack with a self-hostable setup: Vite+React frontend, Fastify+Drizzle+PostgreSQL backend with own JWT auth, deploy via git pull + rsync + systemd (checkout/service separated, updates pulled from Gitea). Adds a reference auth route (register/login/me) as the canonical pattern for new routes. Updates all skills/rules/agents/docs to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMdBJfD9TT6XUU2NqGG1PN
2.0 KiB
2.0 KiB
Performance Monitoring
Lighthouse Check (after every deployment)
- Open Chrome DevTools (F12)
- Go to Lighthouse tab
- Select: Performance, Accessibility, Best Practices, SEO
- Generate Report for both Mobile and Desktop
- Target: Score > 90 in all categories
Common Performance Issues
Unoptimized Images
// Bad - unoptimized, no lazy loading
<img src="/large-image.jpg" />
// Good - explicit dimensions + native lazy loading
<img src="/large-image.jpg" width={800} height={600} loading="lazy" alt="Description" />
Pre-resize/compress source images (e.g. with sharp in a build step, or an image CDN) — Vite does not do this automatically like Next.js Image did.
Large JavaScript Bundle
Use dynamic imports for heavy components that aren't needed on initial load:
import { lazy, Suspense } from "react";
const HeavyChart = lazy(() => import("./HeavyChart"));
<Suspense fallback={<p>Loading chart...</p>}>
<HeavyChart />
</Suspense>
Vite code-splits dynamic imports automatically — check npm run build's output chunk sizes.
Missing Loading States
Always show feedback during data fetching:
// Use shadcn Skeleton component
import { Skeleton } from "@/components/ui/skeleton"
if (isLoading) return <Skeleton className="h-12 w-full" />
No Caching Strategy
Cache slow backend queries in-process (see database-optimization.md) rather than re-querying Postgres on every request for rarely-changing data.
Quick Wins Checklist
- Images have explicit width/height and
loading="lazy"where below the fold - Heavy components use
React.lazy+Suspense - Loading states show skeleton/spinner
- Fonts loaded via
@font-face/Google Fonts<link>, withfont-display: swap - Production build (
npm run build) checked for unexpectedly large chunks
Automated Monitoring
- Self-hosted Plausible or Umami for privacy-friendly traffic analytics
web-vitalsnpm package to report Core Web Vitals from real users to your own backend/logging