BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

Core Web VitalsLargest Contentful Paint (LCP)Interaction to Next Paint (INP)Cumulative Layout Shift (CLS)Image OptimizationNative Lazy LoadingResponsive ImagesModern Format dengan FallbackNext.js Image ComponentCode SplittingDynamic Import (Vanilla JS)React Lazy & SuspenseNext.js Dynamic ImportRoute-based Code Splitting (Webpack)Font OptimizationPreload Critical FontsFont Display StrategyNext.js Font OptimizationCSS OptimizationCritical CSS InlineRemove Unused CSSResource HintsPreconnectDNS PrefetchPreload Critical ResourcesPrefetch Future PagesScript Loading StrategiesAsync vs DeferNext.js Script ComponentReact PerformanceuseMemouseCallbackReact.memoVirtual ListsCompressionEnable Gzip/Brotli (nginx)Next.js CompressionCachingCache HeadersService Worker CacheMonitoringWeb Vitals LibraryPerformance APIQuick Wins ChecklistToolsTarget Metrics
PerformanceWeb DevelopmentOptimization

Web Performance Cheat Sheet

Quick reference untuk optimasi performa website. Core Web Vitals, lazy loading, code splitting, dan teknik optimization lainnya.

JavaScript7 min read1.345 kata
Cheat sheet ini adalah konten premium. Login atau daftar untuk mengakses konten premium.

#Core Web Vitals

Metrik-metrik penting yang diukur Google untuk menilai pengalaman pengguna pada website.

#Largest Contentful Paint (LCP)

Waktu yang dibutuhkan untuk memuat konten terbesar yang terlihat di viewport.

javascript
// Target: < 2.5s
// Mengukur: Loading performance (konten terbesar)
 
// Monitor LCP
import { onLCP } from 'web-vitals';
 
onLCP((metric) => {
 console.log('LCP:', metric.value);
});

Optimization:

  • Optimize images (WebP/AVIF)
  • Preload critical resources
  • Server-side rendering (SSR)
  • CDN untuk static assets

#Interaction to Next Paint (INP)

Waktu responsifitas halaman terhadap semua interaksi pengguna.

javascript
// Target: < 200ms
// Mengukur: Responsiveness (semua interaksi)
 
// Monitor INP
import { onINP } from 'web-vitals';
 
onINP((metric) => {
 console.log('INP:', metric.value);
});

Optimization:

  • Minimize JavaScript execution
  • Break long tasks
  • Defer/async non-critical scripts
  • Web workers untuk heavy computation

#Cumulative Layout Shift (CLS)

Mengukur stabilitas visual halaman selama loading.

javascript
// Target: < 0.1
// Mengukur: Visual stability
 
// Monitor CLS
import { onCLS } from 'web-vitals';
 
onCLS((metric) => {
 console.log('CLS:', metric.value);
});

Optimization:

  • Set width & height pada images
  • Reserve space untuk ads/embeds
  • Avoid inserting content di atas existing
  • CSS aspect-ratio

#Image Optimization

Teknik-teknik untuk mengoptimalkan gambar agar lebih cepat dimuat.

#Native Lazy Loading

Memuat gambar hanya saat akan terlihat di viewport.

html
<!-- Basic lazy loading -->
<img
 src="photo.jpg"
 alt="Description"
 loading="lazy"
 width="800"
 height="600"
/>
 
<!-- Lazy iframe -->
<iframe
 src="video.html"
 loading="lazy"
></iframe>

#Responsive Images

html
<!-- srcset untuk different sizes -->
<img
 srcset="
  small.webp 400w,
  medium.webp 800w,
  large.webp 1200w
 "
 sizes="(max-width: 600px) 400px,
     (max-width: 900px) 800px,
     1200px"
 src="medium.webp"
 alt="Responsive image"
 loading="lazy"
/>

#Modern Format dengan Fallback

html
<picture>
 <source srcset="image.avif" type="image/avif">
 <source srcset="image.webp" type="image/webp">
 <img src="image.jpg" alt="Modern format fallback">
</picture>

#Next.js Image Component

javascript
import Image from 'next/image';
 
<Image
 src="/photo.jpg"
 alt="Photo"
 width={800}
 height={600}
 loading="lazy"
 placeholder="blur"
 blurDataURL="data:image/..."
/>

#Code Splitting

#Dynamic Import (Vanilla JS)

javascript
// Load module on demand
button.addEventListener('click', async () => {
 const module = await import('./heavyModule.js');
 module.initialize();
});

#React Lazy & Suspense

javascript
import { lazy, Suspense } from 'react';
 
// Lazy load component
const HeavyComponent = lazy(() => import('./HeavyComponent'));
 
function App() {
 return (
  <Suspense fallback={<div>Loading...</div>}>
   <HeavyComponent />
  </Suspense>
 );
}

#Next.js Dynamic Import

javascript
import dynamic from 'next/dynamic';
 
// Without SSR
const DynamicComponent = dynamic(
 () => import('../components/heavy'),
 { ssr: false }
);
 
// With loading state
const DynamicWithLoading = dynamic(
 () => import('../components/heavy'),
 {
  loading: () => <p>Loading...</p>,
  ssr: false
 }
);

#Route-based Code Splitting (Webpack)

javascript
// webpack.config.js
module.exports = {
 optimization: {
  splitChunks: {
   chunks: 'all',
   cacheGroups: {
    vendor: {
     test: /[\\/]node_modules[\\/]/,
     name: 'vendors',
     priority: 10
    }
   }
  }
 }
};

#Font Optimization

#Preload Critical Fonts

html
<head>
 <!-- Preload font -->
 <link
  rel="preload"
  href="/fonts/inter.woff2"
  as="font"
  type="font/woff2"
  crossorigin
 />
</head>

#Font Display Strategy

css
@font-face {
 font-family: 'Inter';
 src: url('/fonts/inter.woff2') format('woff2');
 font-display: swap; /* Show fallback immediately */
 font-weight: 400;
 font-style: normal;
}

#Next.js Font Optimization

javascript
import { Inter, Roboto_Mono } from 'next/font/google';
 
const inter = Inter({
 subsets: ['latin'],
 display: 'swap',
});
 
const robotoMono = Roboto_Mono({
 subsets: ['latin'],
 weight: ['400', '700'],
 display: 'swap',
});
 
export default function Layout({ children }) {
 return (
  <html className={inter.className}>
   <body>{children}</body>
  </html>
 );
}

#CSS Optimization

#Critical CSS Inline

html
<head>
 <!-- Inline critical CSS -->
 <style>
  /* Above-the-fold styles */
  body { margin: 0; font-family: sans-serif; }
  .hero { min-height: 100vh; }
 </style>
 
 <!-- Defer non-critical CSS -->
 <link
  rel="preload"
  href="/styles.css"
  as="style"
  onload="this.onload=null;this.rel='stylesheet'"
 />
 <noscript>
  <link rel="stylesheet" href="/styles.css">
 </noscript>
</head>

#Remove Unused CSS

javascript
// PurgeCSS config (Tailwind CSS)
module.exports = {
 content: [
  './pages/**/*.{js,jsx,ts,tsx}',
  './components/**/*.{js,jsx,ts,tsx}',
 ],
 // PurgeCSS akan remove unused styles
};

#Resource Hints

#Preconnect

html
<!-- Preconnect ke external domains -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdn.example.com">

#DNS Prefetch

html
<!-- DNS prefetch untuk less critical domains -->
<link rel="dns-prefetch" href="https://analytics.example.com">
<link rel="dns-prefetch" href="https://ads.example.com">

#Preload Critical Resources

html
<!-- Preload critical assets -->
<link rel="preload" href="/hero.webp" as="image">
<link rel="preload" href="/critical.css" as="style">
<link rel="preload" href="/app.js" as="script">

#Prefetch Future Pages

html
<!-- Prefetch pages yang mungkin dikunjungi -->
<link rel="prefetch" href="/about">
<link rel="prefetch" href="/products">

#Script Loading Strategies

#Async vs Defer

html
<!-- async: Load parallel, execute ASAP -->
<script src="analytics.js" async></script>
 
<!-- defer: Load parallel, execute after HTML parsed -->
<script src="app.js" defer></script>
 
<!-- blocking: Avoid kalo bisa -->
<script src="blocking.js"></script>

#Next.js Script Component

javascript
import Script from 'next/script';
 
export default function Page() {
 return (
  <>
   {/* Load after page interactive */}
   <Script
    src="https://analytics.example.com/script.js"
    strategy="lazyOnload"
   />
 
   {/* Load after page hydrated */}
   <Script
    src="https://widget.example.com/script.js"
    strategy="afterInteractive"
   />
 
   {/* Load in <head> before page interactive */}
   <Script
    src="https://critical.example.com/script.js"
    strategy="beforeInteractive"
   />
  </>
 );
}

#React Performance

#useMemo

javascript
import { useMemo } from 'react';
 
function ExpensiveComponent({ data }) {
 // Memoize expensive calculation
 const processedData = useMemo(() => {
  return data.map(item => heavyComputation(item));
 }, [data]); // Only recompute if data changes
 
 return <div>{processedData}</div>;
}

#useCallback

javascript
import { useCallback } from 'react';
 
function Parent() {
 // Memoize function
 const handleClick = useCallback(() => {
  console.log('Clicked');
 }, []); // Function never changes
 
 return <Child onClick={handleClick} />;
}

#React.memo

javascript
import { memo } from 'react';
 
// Prevent unnecessary re-renders
const ExpensiveChild = memo(({ data }) => {
 return <div>{data}</div>;
});
 
// Custom comparison
const CustomMemo = memo(
 ({ data }) => <div>{data}</div>,
 (prevProps, nextProps) => {
  // Return true if no need to re-render
  return prevProps.data.id === nextProps.data.id;
 }
);

#Virtual Lists

javascript
import { FixedSizeList } from 'react-window';
 
function VirtualList({ items }) {
 const Row = ({ index, style }) => (
  <div style={style}>
   {items[index]}
  </div>
 );
 
 return (
  <FixedSizeList
   height={600}
   itemCount={items.length}
   itemSize={50}
   width="100%"
  >
   {Row}
  </FixedSizeList>
 );
}

#Compression

#Enable Gzip/Brotli (nginx)

nginx
# nginx.conf
http {
 # Gzip compression
 gzip on;
 gzip_vary on;
 gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
 gzip_min_length 1000;
 
 # Brotli compression (better than gzip)
 brotli on;
 brotli_types text/plain text/css application/json application/javascript text/xml application/xml;
}

#Next.js Compression

javascript
// next.config.js
module.exports = {
 compress: true, // Enable gzip compression
};

#Caching

#Cache Headers

javascript
// Next.js API route
export default function handler(req, res) {
 res.setHeader(
  'Cache-Control',
  'public, s-maxage=31536000, immutable'
 );
 res.json({ data: 'cached data' });
}

#Service Worker Cache

javascript
// service-worker.js
const CACHE_NAME = 'v1';
 
self.addEventListener('install', (event) => {
 event.waitUntil(
  caches.open(CACHE_NAME).then((cache) => {
   return cache.addAll([
    '/',
    '/styles.css',
    '/app.js',
    '/logo.png',
   ]);
  })
 );
});
 
self.addEventListener('fetch', (event) => {
 event.respondWith(
  caches.match(event.request).then((response) => {
   return response || fetch(event.request);
  })
 );
});

#Monitoring

#Web Vitals Library

javascript
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';
 
function sendToAnalytics(metric) {
 // Send to your analytics service
 fetch('/analytics', {
  method: 'POST',
  body: JSON.stringify(metric),
  keepalive: true
 });
}
 
// Monitor all metrics
onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);

#Performance API

javascript
// Measure custom timing
performance.mark('start-task');
 
// ... do something ...
 
performance.mark('end-task');
performance.measure('task-duration', 'start-task', 'end-task');
 
// Get measurements
const measures = performance.getEntriesByType('measure');
console.log(measures[0].duration);

#Quick Wins Checklist

  • Enable compression (Gzip/Brotli)
  • Minify HTML, CSS, JavaScript
  • Optimize images (WebP/AVIF, lazy loading)
  • Implement code splitting
  • Add cache headers
  • Preconnect to external domains
  • Use CDN for static assets
  • Defer non-critical JavaScript
  • Inline critical CSS
  • Remove unused dependencies
  • Enable production mode
  • Monitor Core Web Vitals

#Tools

Free:

  • PageSpeed Insights: https://pagespeed.web.dev/
  • Lighthouse (Chrome DevTools)
  • WebPageTest: https://www.webpagetest.org/
  • Chrome DevTools Performance tab

Paid:

  • SpeedCurve
  • Calibre
  • DebugBear

#Target Metrics

MetricGoodNeeds ImprovementPoor
LCP< 2.5s2.5s - 4s> 4s
INP< 200ms200ms - 500ms> 500ms
CLS< 0.10.1 - 0.25> 0.25
FCP< 1.8s1.8s - 3s> 3s
TTFB< 800ms800ms - 1.8s> 1.8s

Baca Cheat Sheet Lengkap

Login untuk mengakses konten premium ini.

LoginDaftar Gratis
Share: