Why Performance Matters

Performance is a critical aspect of frontend development that directly impacts user experience, engagement, and conversion rates. Studies have shown that:
  • 53% of mobile users abandon sites that take longer than 3 seconds to load
  • Every 100ms of latency costs Amazon 1% in sales
  • Google uses page speed as a ranking factor for both desktop and mobile searches
Performance optimization is not just about making your site fast—it’s about creating a smooth, responsive experience that keeps users engaged and satisfied.

Performance Metrics

Before optimizing, it’s important to understand what to measure. Here are key performance metrics to track:

First Contentful Paint (FCP)

Time until the browser renders the first bit of content from the DOM.Good target: Under 1.8 seconds

Largest Contentful Paint (LCP)

Time until the largest text or image element is rendered.Good target: Under 2.5 seconds

First Input Delay (FID)

Time from when a user first interacts with your site to when the browser responds.Good target: Under 100 milliseconds

Cumulative Layout Shift (CLS)

Measures visual stability and unexpected layout shifts.Good target: Under 0.1

Time to Interactive (TTI)

Time until the page is fully interactive.Good target: Under 3.8 seconds

Total Blocking Time (TBT)

Sum of all time periods between FCP and TTI when the main thread was blocked.Good target: Under 200 milliseconds

Measuring Performance

Tools for Performance Measurement

1

Lighthouse

Chrome’s built-in auditing tool that provides performance scores and suggestions.Access it through Chrome DevTools > Lighthouse tab or run it from the command line:
npm install -g lighthouse
lighthouse https://example.com --view
2

WebPageTest

Provides detailed performance analysis from multiple locations and browsers.Visit WebPageTest.org to run tests.
3

Chrome DevTools Performance Panel

Offers detailed runtime performance analysis including CPU usage, rendering, and network activity.
  1. Open Chrome DevTools (F12)
  2. Go to the Performance tab
  3. Click Record and interact with your site
  4. Stop recording and analyze the results
4

Core Web Vitals Report

Google’s report on real-user performance metrics.Access it through Google Search Console.

Real User Monitoring (RUM)

While lab testing is valuable, measuring performance with real users provides the most accurate data:
// Using the Web Vitals library
import {getLCP, getFID, getCLS} from 'web-vitals';

function sendToAnalytics({name, delta, id}) {
  // Send metrics to your analytics service
  console.log(`Metric: ${name} | Value: ${delta} | ID: ${id}`);
}

// Monitor Core Web Vitals
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);

Optimizing Asset Delivery

Image Optimization

Images often account for the largest portion of page weight. Here’s how to optimize them:

JavaScript Optimization

CSS Optimization

Fonts Optimization

Rendering Performance

Optimizing the Critical Rendering Path

1

Minimize render-blocking resources

Move non-critical CSS and JavaScript out of the critical rendering path.
<!-- For CSS -->
<link rel="preload" href="non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">

<!-- For JavaScript -->
<script src="non-critical.js" defer></script>
2

Optimize DOM size

Keep the DOM tree small and shallow.
<!-- Avoid -->
<div>
  <div>
    <div>
      <div>
        <p>Deeply nested content</p>
      </div>
    </div>
  </div>
</div>

<!-- Better -->
<p class="content">Flatter DOM structure</p>
3

Avoid layout thrashing

Batch DOM reads and writes to prevent forced reflows.
// Bad: Interleaving reads and writes
const width = element.offsetWidth; // Read
element.style.width = (width + 10) + 'px'; // Write
const height = element.offsetHeight; // Read (forces reflow)
element.style.height = (height + 10) + 'px'; // Write

// Good: Batch reads, then writes
const width = element.offsetWidth; // Read
const height = element.offsetHeight; // Read
element.style.width = (width + 10) + 'px'; // Write
element.style.height = (height + 10) + 'px'; // Write
4

Use efficient CSS animations

Prefer properties that only affect compositing.
/* Expensive (triggers layout) */
.expensive {
  animation: move 1s infinite;
}
@keyframes move {
  from { width: 100px; height: 100px; }
  to { width: 200px; height: 200px; }
}

/* Efficient (only compositing) */
.efficient {
  animation: slide 1s infinite;
}
@keyframes slide {
  from { transform: translateX(0); }
  to { transform: translateX(100px); }
}

Preventing Layout Shifts

Layout shifts create a poor user experience and negatively impact your CLS score.
<!-- Bad: Image without dimensions -->
<img src="image.jpg" alt="Description">

<!-- Good: Image with dimensions -->
<img src="image.jpg" alt="Description" width="800" height="600">
/* Reserve space for dynamic content */
.comments-container {
  min-height: 200px;
}

/* Use content-visibility for off-screen content */
.below-fold-section {
  content-visibility: auto;
  contain-intrinsic-size: 1000px; /* Estimate height */
}

Network Optimization

Caching Strategies

Resource Hints

Use resource hints to inform the browser about resources it should load or connect to:
<!-- Preconnect to important third-party domains -->
<link rel="preconnect" href="https://api.example.com">

<!-- DNS prefetch for older browsers -->
<link rel="dns-prefetch" href="https://api.example.com">

<!-- Preload critical resources -->
<link rel="preload" href="critical-script.js" as="script">
<link rel="preload" href="hero-image.jpg" as="image">

<!-- Prefetch resources needed for the next page -->
<link rel="prefetch" href="next-page.html">

<!-- Prerender the next page (use with caution) -->
<link rel="prerender" href="likely-next-page.html">

Framework-Specific Optimizations

React

// Use React.memo for component memoization
const MemoizedComponent = React.memo(function MyComponent(props) {
  // Only re-renders if props change
  return <div>{props.name}</div>;
});

// Use useMemo for expensive calculations
function SearchResults({ query, data }) {
  const filteredData = React.useMemo(() => {
    return data.filter(item => item.name.includes(query));
  }, [query, data]);
  
  return (
    <ul>
      {filteredData.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

// Use useCallback for stable function references
function Parent() {
  const [count, setCount] = useState(0);
  
  const handleClick = React.useCallback(() => {
    console.log('Clicked!');
  }, []); // Empty dependency array = stable reference
  
  return <Child onClick={handleClick} />;
}

Vue.js

<template>
  <div>
    <!-- Use v-once for content that never changes -->
    <header v-once>
      <h1>{{ title }}</h1>
    </header>
    
    <!-- Use v-show instead of v-if for elements that toggle frequently -->
    <div v-show="isVisible">Toggled content</div>
    
    <!-- Use key with v-for -->
    <ul>
      <li v-for="item in items" :key="item.id">{{ item.name }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: 'My App',
      isVisible: true,
      items: []
    };
  },
  // Use computed properties for derived values
  computed: {
    filteredItems() {
      return this.items.filter(item => item.isActive);
    }
  }
};
</script>

Angular

// Use OnPush change detection strategy
@Component({
  selector: 'app-item',
  template: `<div>{{ item.name }}</div>`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemComponent {
  @Input() item: any;
}

// Use trackBy with ngFor
@Component({
  selector: 'app-list',
  template: `
    <div *ngFor="let item of items; trackBy: trackByFn">
      {{ item.name }}
    </div>
  `
})
export class ListComponent {
  items: any[] = [];
  
  trackByFn(index: number, item: any): number {
    return item.id;
  }
}

// Lazy load modules
const routes: Routes = [
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
  }
];

Performance Budgets

Set performance budgets to maintain performance standards as your project evolves:
// webpack.config.js
module.exports = {
  performance: {
    maxAssetSize: 250000, // 250 KB
    maxEntrypointSize: 250000,
    hints: 'error'
  }
};
In your CI/CD pipeline, you can use tools like Lighthouse CI to enforce performance budgets:
// lighthouserc.json
{
  "ci": {
    "collect": {
      "url": ["https://example.com"],
      "numberOfRuns": 3
    },
    "assert": {
      "assertions": {
        "first-contentful-paint": ["warn", {"minScore": 0.8}],
        "interactive": ["error", {"maxNumericValue": 3000}],
        "max-potential-fid": ["error", {"maxNumericValue": 100}],
        "cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
        "largest-contentful-paint": ["error", {"maxNumericValue": 2500}]
      }
    }
  }
}

Checklist for Performance Optimization

Use this checklist to ensure you’ve covered the most important performance optimizations:
1

Measure current performance

  • Run Lighthouse audits
  • Set up real user monitoring
  • Identify the biggest performance bottlenecks
2

Optimize asset delivery

  • Compress and optimize images
  • Minify and compress JavaScript and CSS
  • Implement code splitting
  • Use tree shaking
  • Optimize fonts
3

Improve rendering performance

  • Minimize render-blocking resources
  • Optimize the critical rendering path
  • Prevent layout shifts
  • Use efficient animations
4

Implement caching strategies

  • Configure HTTP caching
  • Implement service workers
  • Use resource hints
5

Apply framework-specific optimizations

  • Use memoization techniques
  • Implement lazy loading
  • Optimize rendering cycles
6

Set up performance monitoring

  • Establish performance budgets
  • Integrate performance testing in CI/CD
  • Monitor real user metrics

Next Steps

Now that you understand frontend performance optimization, you can:
  • Learn about Accessibility to make your sites usable by everyone
  • Explore Security best practices to protect your applications
  • Study Testing strategies to ensure your optimizations don’t break functionality