Performance

Comprehensive guide to performance optimization in ShipKit

Performance Optimization

This document outlines the performance optimization strategies and best practices implemented in ShipKit.

Core Web Vitals

Metrics

  • LCP (Largest Contentful Paint): < 2.5s
  • FID (First Input Delay): < 100ms
  • CLS (Cumulative Layout Shift): < 0.1
  • TTFB (Time to First Byte): < 800ms

Monitoring

// src/lib/vitals.ts
import { type CLSMetric, type FCPMetric, type FIDMetric, type LCPMetric, type TTFBMetric } from 'web-vitals'

type MetricType = CLSMetric | FCPMetric | FIDMetric | LCPMetric | TTFBMetric

export function reportWebVitals(metric: MetricType) {
  console.log(metric)

  // Example implementation
  const body = {
    value: metric.value,
    rating: metric.rating,
    name: metric.name,
    id: metric.id,
  }

  fetch('/api/vitals', {
    method: 'POST',
    body: JSON.stringify(body),
  })
}

Image Optimization

Next.js Image Component

// src/components/ui/optimized-image.tsx
import Image from 'next/image'
import { type ImageProps } from 'next/image'

export function OptimizedImage(props: ImageProps) {
  return (
    <div className="relative aspect-video">
      <Image
        {...props}
        alt={props.alt}
        fill
        className="object-cover"
        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
        quality={85}
        priority={props.priority}
      />
    </div>
  )
}

Image Configuration

// next.config.mjs
import { withImages } from 'next-images'

export default withImages({
  images: {
    domains: ['assets.example.com'],
    formats: ['image/avif', 'image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
})

Database Optimization

Query Optimization

// src/server/db/posts.ts
import { db } from './client'

export async function getPostsByAuthor(authorId: string) {
  // Use composite index
  return db.post.findMany({
    where: {
      authorId,
      published: true,
    },
    orderBy: {
      createdAt: 'desc',
    },
    // Include only necessary relations
    include: {
      _count: {
        select: {
          comments: true,
        },
      },
    },
  })
}

Connection Pooling

// src/server/db/client.ts
import { Pool } from '@neondatabase/serverless'
import { PrismaNeon } from '@prisma/adapter-neon'
import { PrismaClient } from '@prisma/client'
import { env } from '@/env.mjs'

declare global {
  var prisma: PrismaClient | undefined
}

const connectionString = env.DATABASE_URL
const pool = new Pool({ connectionString })
const adapter = new PrismaNeon(pool)

export const db =
  globalThis.prisma ||
  new PrismaClient({
    adapter,
    log: ['query', 'error', 'warn'],
  })

if (env.NODE_ENV !== 'production') {
  globalThis.prisma = db
}

Best Practices

  1. Rendering

    • Use React Server Components
    • Implement code splitting
    • Optimize images
    • Minimize client-side JavaScript
  2. Data Fetching

    • Implement caching
    • Use server actions
    • Optimize API routes
    • Handle loading states
  3. Caching

    • Use Redis for server-side caching
    • Implement browser caching
    • Cache static assets
    • Invalidate cache strategically
  4. Monitoring

    • Track Web Vitals
    • Monitor server performance
    • Set up error tracking
    • Analyze bundle size

Performance Checklist

  1. Initial Load

    • [ ] Optimize bundle size
    • [ ] Enable compression
    • [ ] Implement caching
    • [ ] Optimize images
  2. Runtime

    • [ ] Minimize re-renders
    • [ ] Optimize event handlers
    • [ ] Implement virtualization
    • [ ] Use web workers
  3. Data

    • [ ] Optimize queries
    • [ ] Implement caching
    • [ ] Use pagination
    • [ ] Handle loading states
  4. Monitoring

    • [ ] Track metrics
    • [ ] Set up alerts
    • [ ] Monitor errors
    • [ ] Analyze performance