P2Issue #0

Avoid long main-thread tasks

❓ What does it mean?

❓ What does it mean? The main thread is where the browser executes JavaScript, handles user interactions, and performs rendering tasks. A long main-thread task is any JavaScript execution that takes longer than 50 milliseconds to complete. When long tasks block the main thread: • The browser cannot respond to user input (clicks, scrolls, typing) • The page appears frozen or unresponsive • Animations may stutter or lag Examples of long tasks: • Heavy JavaScript parsing and execution • Complex DOM manipulations • Large data processing operations • Synchronous API calls

🚨 Why is it important for SEO?

🚨 Why is this a problem for SEO? Poor User Experience – Users experience lag when clicking buttons or scrolling. Core Web Vitals Impact – Long tasks directly hurt: • Total Blocking Time (TBT) • Interaction to Next Paint (INP) • First Input Delay (FID) SEO Rankings – Google uses INP as a ranking signal; long tasks hurt this metric. Higher Bounce Rate – Unresponsive pages frustrate users, causing them to leave. Mobile Performance – Mobile devices with slower CPUs are especially affected. ✅ Target: Keep tasks under 50ms ⚠️ Acceptable: 50-200ms ❌ Poor: > 200ms

✅ How to Fix It

✅ Best Practices to Fix Break up long tasks into smaller chunks: • Use setTimeout() or requestIdleCallback() to yield to the browser • Split work into multiple frames Code splitting and lazy loading: • Load only necessary JavaScript for each page • Use dynamic imports in React/Next.js Defer non-critical work: • Move heavy computations to Web Workers • Delay analytics and tracking scripts Optimize JavaScript execution: • Avoid synchronous operations • Minimize DOM manipulation • Use virtual scrolling for long lists Use React/Next.js optimization: • React.memo() to prevent unnecessary re-renders • useMemo() and useCallback() for expensive computations • Concurrent rendering features Profile and identify long tasks: • Chrome DevTools → Performance tab • Lighthouse → Diagnostics section

❌ Bad Example

📌 Example ❌ Bad (Long Task Blocking Main Thread): // Heavy synchronous operation - blocks for 800ms function processLargeData() { const data = fetchLargeDataset(); // 10,000 items // Process all data at once - BLOCKS main thread const results = []; for (let i = 0; i < data.length; i++) { // Complex calculation for each item results.push(expensiveCalculation(data[i])); } return results; } button.addEventListener('click', () => { const results = processLargeData(); // 800ms blocking task! displayResults(results); }); 👉 When user clicks button: • Main thread is blocked for 800ms • User cannot interact with page • Page appears frozen • INP score: Poor (> 500ms)

✅ Good Example

✅ Good (Breaking Long Task into Chunks): // Break work into smaller chunks async function processLargeDataInChunks() { const data = fetchLargeDataset(); // 10,000 items const results = []; const chunkSize = 100; for (let i = 0; i < data.length; i += chunkSize) { const chunk = data.slice(i, i + chunkSize); // Process chunk for (const item of chunk) { results.push(expensiveCalculation(item)); } // Yield to browser after each chunk (allow user interactions) await new Promise(resolve => setTimeout(resolve, 0)); } return results; } button.addEventListener('click', async () => { showLoadingSpinner(); const results = await processLargeDataInChunks(); // Multiple small tasks hideLoadingSpinner(); displayResults(results); }); 👉 Work is split into 100 chunks of 100 items each 👉 Each chunk takes ~8ms (under 50ms threshold) 👉 Browser can respond to user input between chunks 👉 INP score: Good (< 200ms) ✅ Even Better (Using Web Workers): // main.js const worker = new Worker('data-processor.worker.js'); button.addEventListener('click', () => { showLoadingSpinner(); // Offload heavy work to Web Worker (runs in separate thread) worker.postMessage({ data: fetchLargeDataset() }); worker.onmessage = (e) => { hideLoadingSpinner(); displayResults(e.data); }; }); // data-processor.worker.js self.onmessage = (e) => { const results = []; for (const item of e.data.data) { results.push(expensiveCalculation(item)); } self.postMessage(results); }; 👉 Heavy computation runs in background thread 👉 Main thread remains responsive 👉 User can continue interacting with page 👉 Perfect INP score ✅ React/Next.js Example: import { useState, useTransition } from 'react'; function DataProcessor() { const [results, setResults] = useState([]); const [isPending, startTransition] = useTransition(); const handleProcess = () => { startTransition(() => { // React 18 concurrent rendering - breaks into chunks automatically const data = fetchLargeDataset(); const processed = data.map(expensiveCalculation); setResults(processed); }); }; return ( <div> <button onClick={handleProcess} disabled={isPending}> {isPending ? 'Processing...' : 'Process Data'} </button> {results.map(r => <div key={r.id}>{r.value}</div>)} </div> ); }

⚡ Result

⚡ SEO & UX Impact of Fixing Improved Core Web Vitals: • Better INP (Interaction to Next Paint) - Google ranking signal • Lower TBT (Total Blocking Time) • Reduced FID (First Input Delay) Higher SEO Rankings – Google rewards responsive, interactive pages. Better User Experience – Pages feel smooth and responsive. Increased Engagement – Users are more likely to complete actions. Mobile Performance – Critical for slower mobile devices. 👉 Long Task Optimization Checklist: ✅ Break tasks into chunks < 50ms ✅ Use Web Workers for heavy computation ✅ Implement code splitting and lazy loading ✅ Defer non-critical JavaScript ✅ Use React concurrent features (useTransition) ✅ Profile with Chrome DevTools Performance tab ✅ Monitor INP in Google Search Console 👉 Performance Targets: • Individual tasks: < 50ms • INP (Interaction to Next Paint): < 200ms • TBT (Total Blocking Time): < 200ms