This list was created after observing that many major websites often overlook crucial performance details, relying primarily on Core Web Vitals (CWV) reports for performance assessments. While CWV reports are essential, they do not capture every aspect of frontend performance optimization. This checklist aims to fill those gaps by providing developers with a comprehensive set of best practices to ensure optimal frontend performance.
- Web Developers: To guide them in implementing best practices for frontend performance during development.
- Project Managers and QA Teams: To use as a reference for setting performance benchmarks and conducting code reviews.
- During Development: Integrate these practices while writing and structuring code to ensure a performance-first approach.
- During PR Reviews: Use this checklist as a standard during pull request (PR) reviews to ensure new code adheres to best practices.
- Before Deployment: Perform a final check against this list before deploying to ensure all performance aspects are covered.
- Avoid Universal Selectors: Minimize the use of
*
selector as it can slow down the rendering of a page. - Limit the Use of Descendant Selectors: Prefer direct child selectors (
>
), class selectors, or ID selectors over deep descendant selectors. - Use Specific Selectors: Use IDs and classes instead of element selectors for better performance.
- Avoid Overly Specific Selectors: Keep selector specificity low to ensure easier maintenance and faster parsing.
- Avoid Inline Styles: Inline styles can trigger reflows and repaints.
- Batch DOM Changes: Group multiple DOM changes together to minimize layout thrashing.
- Use CSS Transitions and Animations Wisely: Prefer
transform
andopacity
for animations to avoid triggering reflows. Learn more about high-performance CSS animations.
- Combine and Minify CSS: Reduce HTTP requests by combining CSS files and minifying them.
- Split CSS into Modules: Keeping CSS modular means that CSS not required at page load can be loaded later on, reducing initial CSS render-blocking and loading times.
- Use CSS Preprocessors: Use preprocessors like SASS or LESS to modularize your CSS and compile them into optimized CSS files.
- Leverage CSS Variables: Use CSS variables for consistent values and to avoid redundant CSS declarations.
- Use CSS Resets Wisely: Apply CSS resets to provide a consistent baseline, but customize them to avoid unnecessary rules.
- Limit the Use of Expensive Properties: Properties like
box-shadow
,border-radius
,filter: blur()
, etc., can be expensive to render. - Use Shorthand Properties: Use shorthand properties to reduce CSS size.
- Use CSS Sprites: Combine small images into a single sprite sheet to reduce HTTP requests. Learn more about CSS sprites.
- Preload Important Assets: Use
rel="preload"
to preload critical CSS files, fonts, and images. - Use Media Queries for Different CSS Files: Serve different CSS files for different screen sizes to avoid loading unnecessary CSS.
- Use CSS Containment: Use properties defined in the CSS containment module to isolate parts of a page and optimize their rendering independently. Learn more about avoiding reflow and repaint.
- Extract Critical CSS: Inline the CSS necessary for rendering above-the-fold content to speed up the initial load.
- Load Fonts Asynchronously: Use
font-display: swap
to ensure text remains visible during font loading. - Subset Fonts: Include only the characters needed for your website to reduce file size.
- Use Modern Font Formats: Prefer WOFF2 over older formats for better compression.
- Use the Right Format: Choose the appropriate image format:
- JPEG: Best for photographs and images with many colors. Use MozJPEG or Guetzli for better compression.
- PNG: Best for images with transparency or simpler graphics. Use PNGQuant or OptiPNG for compression.
- SVG: Best for vector graphics and logos. Use SVGO for optimization.
- WebP: Best for modern browsers, offers superior compression. Use WebP Converter or cwebp for conversion.
- Compress Images: Use tools like TinyPNG, JPEG-Optimizer, or ImageOptim to reduce file sizes without noticeable quality loss.
- Automate Compression: Integrate image compression into your build process using tools like ImageMagick, Gulp, or Webpack plugins.
- Remove Metadata: Strip unnecessary metadata from images to reduce file size.
- Use
srcset
andsizes
: Provide multiple versions of an image for different screen sizes and resolutions. - Implement
picture
Element: Use the<picture>
element for art direction and to serve different image formats. - Specify Image Dimensions: Always set
width
andheight
attributes to prevent layout shifts during loading.
- Use Content Delivery Network (CDN): Serve images from a CDN to reduce latency and improve load times.
- Lazy Load Images: Use the
loading="lazy"
attribute or JavaScript libraries to defer loading off-screen images until needed. - Optimize Image Loading: Load critical images (above-the-fold) first and defer others.
- Use
fetchPriority
Attribute: Control the priority of image loading.
- Provide High-Resolution Images: Use higher resolution images for retina and high-DPI displays.
- Use
srcset
for High-DPI: Include higher resolution versions in thesrcset
for devices that support them.
- Adopt WebP and AVIF: Use modern formats like WebP and AVIF for better compression and quality.
- Fallback for Older Browsers: Provide fallback options for browsers that do not support modern formats.
- Use CSS Sprites: Combine small images into a single sprite sheet to reduce HTTP requests.
- Optimize Sprite Sheets: Ensure the sprite sheet is optimized for performance and use CSS to display individual images.
- Use Progressive JPEGs: Enable progressive rendering for JPEGs to display a lower-quality version of the image while it loads.
- Minify JavaScript Files: Use tools like UglifyJS, Terser, or Webpack to minify your JavaScript files, reducing file size and improving load times.
- Remove Unused Code: Use tools like PurifyCSS and tree shaking to remove dead or unused code.
- Use Latest ECMAScript Features: Take advantage of modern JavaScript features for better performance and cleaner code (e.g.,
let
,const
, arrow functions, and async/await).
- Batch DOM Updates: Minimize direct DOM manipulations by batching updates and using document fragments.
- Limit Reflows and Repaints: Avoid operations that trigger reflows and repaints, such as modifying element dimensions, applying animations, or altering the DOM structure frequently.
- Debounce and Throttle Events: Use debounce and throttle techniques to optimize event handling for events like scroll, resize, and input.
- Delegate Event Handling: Use event delegation to minimize the number of event listeners and improve performance.
- Load Scripts Asynchronously: Use the
async
ordefer
attributes for script tags to prevent blocking page rendering. - Lazy Load Non-Critical Scripts: Load non-essential scripts only when needed (e.g., after the page has fully loaded).
- Bundle JavaScript Files: Combine multiple JavaScript files into a single bundle to reduce HTTP requests.
- Use HTTP/2: Take advantage of HTTP/2’s multiplexing to load multiple assets over a single connection.
- Avoid Blocking JavaScript: Ensure that critical rendering paths are not blocked by long-running JavaScript.
- Optimize Loops: Use efficient loop constructs and avoid unnecessary computations within loops.
- Use Web Workers: Offload heavy computations to Web Workers to keep the main thread responsive.
- Use
requestIdleCallback
: Schedule non-essential work during idle time. - Use
requestAnimationFrame
: Schedule animations and other tasks that require smooth rendering.
- Avoid Memory Leaks: Regularly check for memory leaks and use tools like Chrome DevTools to monitor memory usage.
- Use Efficient Data Structures: Choose the right data structures (e.g., maps, sets) for your use case to optimize memory usage and performance.
- Evaluate Third-Party Scripts: Regularly audit third-party scripts for performance impact.
- Load Third-Party Scripts Asynchronously: Ensure third-party scripts do not block rendering or degrade performance.
- Dynamically Load Modules: Use the
import()
function to dynamically load JavaScript modules when needed.
- Use JSON Wisely: Parse JSON efficiently and avoid large payloads.
- LocalStorage and IndexedDB: Use these for caching data to avoid unnecessary network requests.
- Implement Service Workers: Use service workers for offline caching and background synchronization to improve performance and reliability.
- Cache API Responses: Cache API responses in memory or local storage to reduce redundant network requests.