# PHASE 13: Next.js 15 Optimization **Status:** ⬜ Not Started **Estimated Time:** 15-20 hours **Priority:** HIGH **Depends On:** Phase 12 (Next.js Pages Migration) **Blocks:** Phase 14 (Cleanup & Testing) --- ## 🎯 Phase Goal Optimize the Next.js 15 application for production with Turbopack, implement advanced features, and ensure peak performance. --- ## 📋 Prerequisites - [ ] Phase 12 complete (all pages migrated to Next.js App Router) - [ ] All services converted from Supabase to Django API - [ ] Sacred Pipeline working in Next.js - [ ] Bun is configured as package manager - [ ] Environment variables properly configured --- ## ✅ Task 13.1: Turbopack Configuration (3 hours) ### Configure next.config.js for Turbopack Create/update `next.config.js`: ```javascript /** @type {import('next').NextConfig} */ const nextConfig = { // Enable Turbopack for development experimental: { turbo: { rules: { '*.svg': { loaders: ['@svgr/webpack'], as: '*.js', }, }, resolveAlias: { '@': './src', }, }, }, // Image optimization images: { remotePatterns: [ { protocol: 'https', hostname: 'imagedelivery.net', // CloudFlare Images }, { protocol: 'https', hostname: process.env.NEXT_PUBLIC_DJANGO_API_URL?.replace('https://', '') || 'api.thrillwiki.com', }, ], formats: ['image/avif', 'image/webp'], }, // Environment variables env: { NEXT_PUBLIC_DJANGO_API_URL: process.env.NEXT_PUBLIC_DJANGO_API_URL, }, // Production optimizations compress: true, poweredByHeader: false, reactStrictMode: true, // Output configuration output: 'standalone', // For Docker deployment }; module.exports = nextConfig; ``` ### Checklist - [ ] `next.config.js` created/updated - [ ] Turbopack rules configured - [ ] Image optimization configured for CloudFlare - [ ] Environment variables properly referenced - [ ] Production flags enabled - [ ] Development server uses Turbopack - [ ] Hot reload works correctly --- ## ✅ Task 13.2: Server vs Client Component Optimization (4 hours) ### Identify Component Types **Server Components (Default):** - Data fetching pages - Static content - SEO-critical pages - No user interaction **Client Components (Need 'use client'):** - Forms with state - Interactive UI elements - Hooks (useState, useEffect, etc.) - Event handlers - Context providers ### Optimization Checklist #### Server Components - [ ] All data-fetching pages are Server Components - [ ] Park listing page is Server Component - [ ] Ride listing page is Server Component - [ ] Detail pages use Server Components for data - [ ] SEO metadata generated server-side #### Client Components - [ ] Form components marked with 'use client' - [ ] Interactive UI (modals, dropdowns) marked - [ ] Authentication components marked - [ ] Submission forms marked - [ ] No unnecessary 'use client' directives #### Component Structure ```typescript // Good: Server Component with Client Components inside // app/parks/page.tsx export default async function ParksPage() { const parks = await fetch(API_URL).then(r => r.json()); return (

Parks

{/* Client Component */} {/* Can be Server Component */}
); } // Client Component for interactivity // components/ParkFilters.tsx 'use client'; export function ParkFilters() { const [filters, setFilters] = useState({}); // ... } ``` --- ## ✅ Task 13.3: Data Fetching Optimization (3 hours) ### Implement Caching Strategies ```typescript // app/parks/[parkSlug]/page.tsx export async function generateStaticParams() { // Pre-render top 100 parks at build time const parks = await fetch(`${API_URL}/parks/?page_size=100`); return parks.results.map((park) => ({ parkSlug: park.slug, })); } export default async function ParkDetailPage({ params }: { params: { parkSlug: string } }) { // Cache for 1 hour, revalidate in background const park = await fetch(`${API_URL}/parks/${params.parkSlug}/`, { next: { revalidate: 3600 } }).then(r => r.json()); return ; } ``` ### Caching Strategy Checklist - [ ] Static pages use ISR (Incremental Static Regeneration) - [ ] Dynamic pages have appropriate revalidation times - [ ] Listing pages cache for 5-10 minutes - [ ] Detail pages cache for 1 hour - [ ] User-specific pages are not cached - [ ] Search results are not cached ### Parallel Data Fetching ```typescript // Fetch multiple resources in parallel export default async function RideDetailPage({ params }) { const [ride, reviews, photos] = await Promise.all([ fetch(`${API_URL}/rides/${params.rideSlug}/`), fetch(`${API_URL}/rides/${params.rideSlug}/reviews/`), fetch(`${API_URL}/rides/${params.rideSlug}/photos/`), ]); return ; } ``` - [ ] Parallel fetching implemented where possible - [ ] No waterfall requests - [ ] Loading states for slow requests --- ## ✅ Task 13.4: Loading States & Suspense (2 hours) ### Create Loading Components ```typescript // app/parks/loading.tsx export default function Loading() { return (
{[...Array(6)].map((_, i) => (
))}
); } ``` ### Implement Streaming ```typescript // app/parks/[parkSlug]/page.tsx import { Suspense } from 'react'; export default async function ParkPage({ params }) { return (
}> }>
); } ``` ### Checklist - [ ] Loading.tsx for each major route - [ ] Suspense boundaries for slow components - [ ] Skeleton screens match final UI - [ ] Streaming SSR enabled - [ ] No flash of loading state --- ## ✅ Task 13.5: Metadata API for SEO (2 hours) ### Implement Dynamic Metadata ```typescript // app/parks/[parkSlug]/page.tsx import { Metadata } from 'next'; export async function generateMetadata({ params }: { params: { parkSlug: string } }): Promise { const park = await fetch(`${API_URL}/parks/${params.parkSlug}/`) .then(r => r.json()); return { title: `${park.name} - ThrillWiki`, description: park.description || `Discover ${park.name}, a ${park.park_type} featuring exciting rides and attractions.`, openGraph: { title: park.name, description: park.description, images: [ { url: park.banner_image_url || '/og-image.png', width: 1200, height: 630, alt: park.name, }, ], type: 'website', siteName: 'ThrillWiki', }, twitter: { card: 'summary_large_image', title: park.name, description: park.description, images: [park.banner_image_url || '/og-image.png'], }, alternates: { canonical: `https://thrillwiki.com/parks/${park.slug}`, }, }; } ``` ### SEO Checklist - [ ] All pages have unique titles - [ ] All pages have meta descriptions - [ ] OpenGraph tags for social sharing - [ ] Twitter Card tags - [ ] Canonical URLs - [ ] Structured data (JSON-LD) - [ ] Robots.txt configured - [ ] Sitemap.xml generated --- ## ✅ Task 13.6: Performance Optimization (3 hours) ### Bundle Analysis ```bash # Install bundle analyzer bun add @next/bundle-analyzer # Update next.config.js const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }); module.exports = withBundleAnalyzer(nextConfig); # Run analysis ANALYZE=true bun run build ``` ### Code Splitting ```typescript // Use dynamic imports for heavy components import dynamic from 'next/dynamic'; const HeavyChart = dynamic(() => import('./HeavyChart'), { loading: () =>

Loading chart...

, ssr: false, // Don't render on server }); ``` ### Performance Checklist - [ ] Bundle size < 500KB initial load - [ ] Code splitting for large dependencies - [ ] Images use next/image - [ ] Fonts optimized with next/font - [ ] CSS modules for component styles - [ ] No unused dependencies - [ ] Tree shaking working ### Core Web Vitals Targets - [ ] LCP (Largest Contentful Paint) < 2.5s - [ ] FID (First Input Delay) < 100ms - [ ] CLS (Cumulative Layout Shift) < 0.1 - [ ] TTFB (Time to First Byte) < 600ms --- ## ✅ Task 13.7: Environment Variables & Security (1 hour) ### Environment Configuration Create `.env.local`: ```bash # Django API (required for build) NEXT_PUBLIC_DJANGO_API_URL=https://api.thrillwiki.com # CloudFlare (public) NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID=xxx # Server-only secrets (no NEXT_PUBLIC_ prefix) CLOUDFLARE_API_TOKEN=xxx DJANGO_SECRET_KEY=xxx ``` ### Security Checklist - [ ] No secrets in client-side code - [ ] Server-only vars don't have NEXT_PUBLIC_ prefix - [ ] .env.local in .gitignore - [ ] .env.example documented - [ ] Environment validation on startup - [ ] Sensitive data not logged ### Runtime Validation ```typescript // lib/env.ts import { z } from 'zod'; const envSchema = z.object({ NEXT_PUBLIC_DJANGO_API_URL: z.string().url(), NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: z.string().min(1), }); export const env = envSchema.parse({ NEXT_PUBLIC_DJANGO_API_URL: process.env.NEXT_PUBLIC_DJANGO_API_URL, NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID: process.env.NEXT_PUBLIC_CLOUDFLARE_ACCOUNT_ID, }); ``` --- ## ✅ Task 13.8: Error Boundaries & Monitoring (2 hours) ### Global Error Handling ```typescript // app/error.tsx 'use client'; export default function Error({ error, reset, }: { error: Error & { digest?: string }; reset: () => void; }) { return (

Something went wrong!

); } // app/global-error.tsx (catches errors in root layout) 'use client'; export default function GlobalError({ error, reset, }: { error: Error & { digest?: string }; reset: () => void; }) { return (

Something went wrong!

); } ``` ### Not Found Pages ```typescript // app/not-found.tsx export default function NotFound() { return (

404 - Page Not Found

The page you're looking for doesn't exist.

); } ``` ### Checklist - [ ] Error boundaries for each route segment - [ ] Global error handler - [ ] 404 page styled - [ ] Error logging implemented - [ ] User-friendly error messages - [ ] Errors don't expose sensitive data --- ## 🎯 Phase Completion Criteria ### Configuration - [ ] Turbopack fully configured - [ ] Image optimization working - [ ] Environment variables validated - [ ] next.config.js optimized ### Performance - [ ] Bundle size optimized - [ ] Code splitting implemented - [ ] Images optimized - [ ] Fonts optimized - [ ] Core Web Vitals meet targets ### SEO - [ ] All pages have metadata - [ ] OpenGraph tags present - [ ] Sitemap generated - [ ] Robots.txt configured ### User Experience - [ ] Loading states implemented - [ ] Error boundaries working - [ ] 404 pages styled - [ ] No jarring layout shifts ### Code Quality - [ ] Server/Client components optimized - [ ] No unnecessary 'use client' directives - [ ] Parallel data fetching where possible - [ ] Proper caching strategies --- ## 📊 Progress Tracking **Started:** [Date] **Completed:** [Date] **Time Spent:** [Hours] ### Tasks Completed - [ ] Task 13.1: Turbopack Configuration - [ ] Task 13.2: Server vs Client Components - [ ] Task 13.3: Data Fetching Optimization - [ ] Task 13.4: Loading States & Suspense - [ ] Task 13.5: Metadata API for SEO - [ ] Task 13.6: Performance Optimization - [ ] Task 13.7: Environment Variables - [ ] Task 13.8: Error Boundaries --- ## ⏭️ Next Phase Once this phase is complete, proceed to [Phase 14: Cleanup & Testing](./PHASE_14_CLEANUP_TESTING.md) --- ## 🔗 Related Documentation - [Next.js 15 Migration Guide](./NEXTJS_15_MIGRATION_GUIDE.md) - [Environment Variables Guide](./ENVIRONMENT_VARIABLES.md) - [Bun Setup Guide](./BUN_GUIDE.md) - Next.js Documentation: https://nextjs.org/docs --- **Document Version:** 1.0 **Last Updated:** November 9, 2025