mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 09:51:13 -05:00
12 KiB
12 KiB
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:
/** @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.jscreated/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
// 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 (
<div>
<h1>Parks</h1>
<ParkFilters /> {/* Client Component */}
<ParksList parks={parks} /> {/* Can be Server Component */}
</div>
);
}
// 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
// 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 <ParkDetail park={park} />;
}
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
// 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 <RideDetail ride={ride} reviews={reviews} photos={photos} />;
}
- Parallel fetching implemented where possible
- No waterfall requests
- Loading states for slow requests
✅ Task 13.4: Loading States & Suspense (2 hours)
Create Loading Components
// app/parks/loading.tsx
export default function Loading() {
return (
<div className="space-y-4">
<div className="h-8 bg-gray-200 rounded animate-pulse" />
<div className="grid grid-cols-3 gap-4">
{[...Array(6)].map((_, i) => (
<div key={i} className="h-48 bg-gray-200 rounded animate-pulse" />
))}
</div>
</div>
);
}
Implement Streaming
// app/parks/[parkSlug]/page.tsx
import { Suspense } from 'react';
export default async function ParkPage({ params }) {
return (
<div>
<Suspense fallback={<ParkHeaderSkeleton />}>
<ParkHeader slug={params.parkSlug} />
</Suspense>
<Suspense fallback={<RidesListSkeleton />}>
<RidesList parkSlug={params.parkSlug} />
</Suspense>
</div>
);
}
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
// app/parks/[parkSlug]/page.tsx
import { Metadata } from 'next';
export async function generateMetadata({
params
}: {
params: { parkSlug: string }
}): Promise<Metadata> {
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
# 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
// Use dynamic imports for heavy components
import dynamic from 'next/dynamic';
const HeavyChart = dynamic(() => import('./HeavyChart'), {
loading: () => <p>Loading chart...</p>,
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:
# 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
// 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
// app/error.tsx
'use client';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
// app/global-error.tsx (catches errors in root layout)
'use client';
export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<html>
<body>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</body>
</html>
);
}
Not Found Pages
// app/not-found.tsx
export default function NotFound() {
return (
<div>
<h2>404 - Page Not Found</h2>
<p>The page you're looking for doesn't exist.</p>
</div>
);
}
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
🔗 Related Documentation
- Next.js 15 Migration Guide
- Environment Variables Guide
- Bun Setup Guide
- Next.js Documentation: https://nextjs.org/docs
Document Version: 1.0
Last Updated: November 9, 2025