This document outlines all performance optimizations implemented in the Betirement website.
The Betirement website is optimized for maximum performance with a target Lighthouse score of 90+ across all metrics. This guide documents the strategies and implementations used to achieve optimal performance.
- Performance Score: 90+ (mobile and desktop)
- First Contentful Paint (FCP): < 1.8s
- Largest Contentful Paint (LCP): < 2.5s
- Time to Interactive (TTI): < 3.8s
- Cumulative Layout Shift (CLS): < 0.1
- First Input Delay (FID): < 100ms
Implementation: Using next/font/google for automatic font optimization.
Location: app/layout.tsx
import { Inter, Open_Sans } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
const openSans = Open_Sans({
subsets: ['latin'],
display: 'swap',
variable: '--font-open-sans',
});Benefits:
- Automatic font subsetting
- Self-hosting of Google Fonts
- Zero layout shift with
font-display: swap - CSS variable approach for easy theming
- Preloading of critical fonts
Implementation: Next.js Image component with WebP/AVIF support.
Configuration: next.config.mjs
images: {
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
remotePatterns: [
{
protocol: 'https',
hostname: 'i.ytimg.com',
pathname: '/vi/**',
},
],
}Utilities: src/lib/image-optimization.ts
Benefits:
- Automatic format conversion (WebP/AVIF)
- Responsive image sizing
- Lazy loading by default
- Blur placeholders
- CDN optimization via Netlify
Usage Examples:
// Hero image (above fold)
import { getImageProps } from '@/src/lib/image-optimization';
<Image
src="/images/hero.jpg"
alt="Hero"
width={1920}
height={1080}
{...getImageProps('hero')}
/>
// Thumbnail (below fold)
<Image
src="/images/thumb.jpg"
alt="Thumbnail"
width={400}
height={300}
{...getImageProps('thumbnail')}
/>Implementation: Dynamic imports for heavy components that are below the fold or conditionally rendered.
Optimized Components:
ExitIntentPopup- Only loads when neededSlideInEmailCapture- Delayed load (30s)SocialProofNotification- Delayed load (10s)
const ExitIntentPopup = dynamic(
() => import('@/src/components/sections').then((mod) => mod.ExitIntentPopup),
{ ssr: false }
);InteractiveTimeline- Heavy animation component
const InteractiveTimeline = dynamic(
() => import('@/src/components/sections').then((mod) => mod.InteractiveTimeline),
{
loading: () => <LoadingSpinner />,
}
);RetirementCalculator- Complex calculation logicBitcoinAllocationCalculator- Complex calculation logic
const RetirementCalculator = dynamic(
() => import("@/src/components/calculators/RetirementCalculator"),
{
loading: () => <LoadingSpinner />,
ssr: false,
}
);InteractiveQuiz- Interactive form with state managementLearningPathRecommendation- Results display
BookingRequestForm- Form component below the fold
ForumPlaceholder- External integrationSuccessStoryForm- Form componentInstagramFeed- External API callsTwitterTimeline- External widgetSocialFollowerStats- API calls
Benefits:
- Reduced initial bundle size
- Faster Time to Interactive (TTI)
- Better First Contentful Paint (FCP)
- Improved Largest Contentful Paint (LCP)
- Conditional loading saves bandwidth
Implementation: Automatic lazy loading for below-fold images via Next.js Image component.
Priority Loading:
- Hero images:
priority={true} - Above-fold content:
priority={true} - Below-fold content: Default lazy loading
Example:
// Above fold - load immediately
<Image src="/hero.jpg" alt="Hero" priority={true} />
// Below fold - lazy load
<Image src="/content.jpg" alt="Content" /> // priority defaults to falseConfiguration: next.config.mjs
swcMinify: true, // Use SWC for faster minification
output: 'standalone', // Optimized for NetlifyAutomatic Optimizations:
- Tree shaking of unused code
- Minification of JavaScript and CSS
- Automatic code splitting by route
- Shared chunk optimization
Static Assets: next.config.mjs
{
source: '/images/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
}ISR (Incremental Static Regeneration):
- Video pages: Revalidate every hour
- Blog posts: Revalidate on demand
- Static pages: Build-time generation
Implementation: Tailwind CSS with JIT compiler
Configuration: tailwind.config.ts
content: [
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/**/*.{js,ts,jsx,tsx,mdx}",
],Benefits:
- Only includes used CSS classes
- Minimal CSS bundle size
- Automatic purging of unused styles
- Critical CSS inlined automatically
Strategies:
- Client components only when needed
- Server components by default
- Dynamic imports for heavy libraries
- Debounced search inputs
- Memoized callbacks
Example:
// Debounced search
const debouncedSearch = useMemo(
() => debounce((query: string) => {
setSearchQuery(query);
}, 300),
[]
);Vercel Analytics: Automatic Core Web Vitals tracking
import { VercelAnalytics } from '@/src/components/analytics';
<VercelAnalytics />Plausible Analytics: Privacy-focused page view tracking
import { PlausibleAnalytics } from '@/src/components/analytics';
<PlausibleAnalytics />Track custom performance metrics:
if (typeof window !== 'undefined' && window.plausible) {
window.plausible('Performance', {
props: {
metric: 'LCP',
value: lcpValue,
},
});
}Run Lighthouse audits locally:
npm run build
npm run start
# In another terminal
npx lighthouse http://localhost:3000 --viewTest on real devices and connections:
- Visit https://www.webpagetest.org/
- Enter your URL
- Select test location and device
- Run test and analyze results
- Open DevTools (F12)
- Go to Lighthouse tab
- Select categories to test
- Click "Generate report"
- Run Lighthouse audit (target: 90+ on all metrics)
- Check bundle size (
npm run build) - Verify image optimization (WebP/AVIF served)
- Test on slow 3G connection
- Verify lazy loading works
- Check for layout shifts (CLS)
- Test on mobile devices
- Verify font loading (no FOIT/FOUT)
- Check JavaScript bundle size
- Verify caching headers
- Monitor Core Web Vitals in Vercel Analytics
- Check real user metrics
- Monitor error rates
- Track page load times
- Monitor API response times
- Check CDN cache hit rates
Causes:
- Large images above the fold
- Slow server response
- Render-blocking resources
Solutions:
- Use
priority={true}for hero images - Optimize image sizes
- Use CDN for static assets
- Implement ISR for dynamic content
Causes:
- Images without dimensions
- Dynamic content insertion
- Web fonts loading
Solutions:
- Always set width/height on images
- Use
font-display: swap - Reserve space for dynamic content
- Use skeleton loaders
Causes:
- Large JavaScript bundles
- Long-running scripts
- Heavy event handlers
Solutions:
- Use dynamic imports
- Debounce user inputs
- Use Web Workers for heavy computation
- Optimize event handlers
Causes:
- Too much JavaScript
- Render-blocking resources
- Unoptimized third-party scripts
Solutions:
- Code splitting
- Lazy load below-fold components
- Defer non-critical scripts
- Use dynamic imports
- Always use Next.js Image component for images
- Set explicit dimensions on all images
- Use dynamic imports for heavy components
- Implement lazy loading for below-fold content
- Optimize fonts with next/font
- Monitor Core Web Vitals continuously
- Test on real devices and slow connections
- Use CDN for static assets
- Implement caching strategies
- Minimize JavaScript bundle size
- Next.js Performance Documentation
- Web.dev Performance Guide
- Core Web Vitals
- Lighthouse Documentation
- Image Optimization Guide
- Weekly: Review Core Web Vitals metrics
- Monthly: Run full Lighthouse audit
- Quarterly: Review and optimize bundle sizes
- As needed: Update dependencies for performance improvements
Set and monitor performance budgets:
- JavaScript: < 200KB (gzipped)
- CSS: < 50KB (gzipped)
- Images: < 200KB per image
- Total Page Weight: < 1MB
- Requests: < 50 per page
Performance optimization is an ongoing process. Regular monitoring and testing ensure the Betirement website maintains excellent performance scores and provides a fast, smooth experience for all users.