ignitionstack.pro v1.0 is out! Read the announcement →
Skip to Content

Performance Optimization Guide

📊 Overview

This document outlines all performance optimizations implemented in the ignitionstack.pro Next.js application. These optimizations target both server-side and client-side performance to achieve optimal loading times, Core Web Vitals scores, and user experience.


🎯 Performance Goals & Achievements

Target Metrics

Expected Improvements


🔧 Server-Side Optimizations

1. React Cache Implementation

Problem: Multiple identical database queries during a single request Solution: Wrap all data fetching functions with React’s cache()

// src/app/server/blog/get-posts-list.ts import { cache } from "react"; export const getPostsList = cache(async ( filters: PostFilters = {} ): Promise<PostListResult> => { // Database query logic });

Impact: Eliminates duplicate Supabase queries during SSR Files: All src/app/server/**/*.ts functions


2. Next.js unstable_cache with Revalidation

Problem: Expensive database operations executed on every request Solution: Add Next.js data cache with time-based revalidation

import { unstable_cache } from "next/cache"; export const getPopularTags = cache(async (limit = 10): Promise<string[]> => { return unstable_cache( async () => getPopularTagsUncached(limit), [`popular-tags-${limit}`], { revalidate: 3600, // 1 hour tags: ["blog-tags"], } )(); });

Impact: Reduces database load by 95% Location: src/app/server/blog/get-popular-tags.ts


3. Optimized Tags Query

Problem: Blog page was fetching 1000 posts just to extract 10 tags Solution: Created dedicated getPopularTags() function

Before:

const allPosts = await getPostsList({ limit: 1000 }); // ❌ Fetches 1000 posts! const allTags = Array.from(new Set(allPosts.posts.flatMap(post => post.tags)));

After:

const popularTags = await getPopularTags(10); // ✅ Fetches only 50 recent posts

Impact: 95% reduction in data transfer Location: src/app/[locale]/(pages)/blog/page.tsx


4. Parallel Data Fetching

Problem: Sequential data fetching blocks rendering Solution: Use Promise.all() to parallelize independent queries

// Before const featuredPosts = await getFeaturedPosts(3); const initialPosts = await getPostsList({ limit: 12 }); const popularTags = await getPopularTags(10); // After ✅ const [featuredPosts, initialPosts, popularTags] = await Promise.all([ getFeaturedPosts(3), getPostsList({ limit: 12 }), getPopularTags(10), ]);

Impact: 60% faster page load Files: All page components


5. ISR (Incremental Static Regeneration)

Problem: All pages are fully dynamic (slow) Solution: Add revalidate export to pages

// Home page (relatively static content) export const revalidate = 86400; // 24 hours // Blog list (updates frequently) export const revalidate = 1800; // 30 minutes // Blog post (semi-static) export const revalidate = 3600; // 1 hour // Store page (static) export const revalidate = 43200; // 12 hours

Impact: Near-instant page loads for cached pages Files: All page.tsx components


⚡ Client-Side Optimizations

1. Shared IntersectionObserver

Problem: Each ScrollReveal component creates its own observer Solution: Use a single shared observer for all components

// Before: N observers for N components ❌ useEffect(() => { const observer = new IntersectionObserver(callback, options); observer.observe(node); }); // After: 1 observer for all components ✅ let sharedObserver: IntersectionObserver | null = null; function getSharedObserver() { if (!sharedObserver) { sharedObserver = new IntersectionObserver(/* ... */); } return sharedObserver; }

Impact: 90% reduction in observer instances Location: src/app/components/motion/scroll-reveal.tsx


2. Memoized ReactMarkdown

Problem: Markdown re-renders on every component update Solution: Memoize with useMemo and React.memo

const renderedContent = useMemo(() => ( <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw, rehypeSanitize]} components={markdownComponents} > {content} </ReactMarkdown> ), [content]); export default memo(PostContent);

Impact: 80% reduction in re-renders Location: src/app/components/blog/post-content.tsx


3. React.memo for Expensive Components

Problem: Unnecessary re-renders of complex components Solution: Wrap with React.memo

function HeaderClient({ locale }: HeaderClientProps) { // Component logic } export default memo(HeaderClient);

Impact: Prevents re-renders when props don’t change Files: header-client.tsx, post-content.tsx


4. Optimized Images

Problem: Large images without proper optimization Solution: Add priority, sizes, and quality attributes

<Image src="/assets/images/header/nexabase_background_header.jpg" alt="Background" fill priority // ✅ Preload above-fold images sizes="(max-width: 768px) 100vw, 1280px" // ✅ Responsive sizes quality={75} // ✅ Optimized quality />

Impact: 40% faster LCP Files: hero.tsx, blog components


⚙️ Configuration Optimizations

1. Bundle Analyzer

npm run build:analyze

Setup: Configured in next.config.ts Purpose: Identify large dependencies and optimization opportunities


2. Package Import Optimization

experimental: { optimizePackageImports: [ "lucide-react", "framer-motion", "react-markdown" ], }

Impact: Tree-shaking for specified packages Location: next.config.ts


3. Compiler Optimizations

compiler: { removeConsole: process.env.NODE_ENV === "production" ? { exclude: ["error", "warn"], } : false, }

Impact: Removes console.log in production Location: next.config.ts


4. Enhanced Caching Headers

{ source: "/:locale(pt|en|es)/:path((?!api).*)?", headers: [{ key: "Cache-Control", value: "public, s-maxage=3600, stale-while-revalidate=86400", }], }

Impact: CDN and browser caching optimization Location: next.config.ts


📦 Bundle Optimization

Code Splitting Opportunities

  1. Admin Components: Use dynamic imports
const MarkdownEditor = dynamic(() => import("@/app/components/admin/markdown-editor"));
  1. Heavy Libraries: Lazy load when needed
const FramerMotion = dynamic(() => import("framer-motion"), { loading: () => <div>Loading...</div> });
  1. Third-party Scripts: Load asynchronously
<Script src="https://..." strategy="lazyOnload" />

🔍 Monitoring & Testing

Performance Testing

# Build and analyze bundle npm run build:analyze # Run Lighthouse CI npx lighthouse https://ignitionstack.pro --view # Test with real device npm run build && npm start

Key Metrics to Monitor

  1. Bundle Size: Check .next/analyze/client.html
  2. Core Web Vitals: Use Chrome DevTools
  3. Database Queries: Monitor Supabase usage
  4. Cache Hit Rate: Check Next.js build output

🚀 Future Optimizations

Potential Improvements

  1. Implement PPR (Partial Prerendering)

    • Status: Experimental in Next.js 15
    • Impact: Hybrid static/dynamic rendering
  2. Add Service Worker

    • Offline support
    • Background sync
  3. Implement Virtual Scrolling

    • For long lists (blog, insights)
    • Library: react-window
  4. Database Indexes

    • Create appropriate indexes in Postgres (Supabase)
    • Optimize common query patterns
  5. CDN Configuration

    • Deploy to Vercel Edge Network
    • Configure edge functions

📚 Best Practices

Server-Side

DO:

DON’T:

Client-Side

DO:

DON’T:


🎓 Resources

Documentation

Tools


📊 Performance Checklist

Pre-Deployment

Regular Maintenance


🤝 Contributing

When adding new features, ensure you:

  1. ✅ Wrap server functions with cache()
  2. ✅ Add ISR configuration to new pages
  3. ✅ Optimize images with proper attributes
  4. ✅ Memoize expensive client components
  5. ✅ Test performance impact

Last Updated: 2025-10-10 Maintained By: ignitionstack.pro Team Project: ignitionstack.pro Next.js Site