How to Use Asynchronous Loading for Faster Websites

Use asynchronous loading to speed up your website. Learn techniques to load resources efficiently and enhance user experience.

In today’s fast-paced digital world, having a fast website is crucial. Users expect quick load times, and search engines reward speed with better rankings. One effective way to make your website faster is by using asynchronous loading. This technique allows you to load content without blocking the rest of the page, resulting in a smoother and quicker experience for your visitors. In this article, we’ll explore how to use asynchronous loading to enhance your website’s performance, improve user experience, and boost SEO.

Understanding Asynchronous Loading

What is Asynchronous Loading?

Asynchronous loading is a web development technique where different parts of a webpage load independently of one another. Instead of waiting for one part to load before moving on to the next, asynchronous loading allows multiple parts to load simultaneously.

This speeds up the overall page load time, making your website feel faster and more responsive.

Synchronous vs. Asynchronous Loading

In synchronous loading, each resource (like scripts, styles, and images) loads one after the other. This means the browser must wait for each resource to finish loading before starting the next one, which can slow down your site.

Asynchronous loading, on the other hand, allows resources to load at the same time. This reduces the waiting time and makes your site load faster.

Benefits of Asynchronous Loading

The primary benefit of asynchronous loading is speed. By loading multiple resources at once, you reduce the time users spend waiting for your site to appear. This leads to a better user experience, lower bounce rates, and higher engagement.

Additionally, faster sites tend to rank better in search engines, giving you an SEO boost.

Implementing Asynchronous Loading

Asynchronous JavaScript

JavaScript is often a major culprit in slowing down websites. By default, the browser stops rendering the page while it downloads and executes JavaScript. To make JavaScript load asynchronously, you can use the async attribute.

This tells the browser to download the script without blocking the rest of the page.

For example:

<script src="your-script.js" async></script>

This simple change can make a big difference in how quickly your page loads.

Defer Attribute

Another option for loading JavaScript is the defer attribute. Unlike async, which runs the script as soon as it’s downloaded, defer ensures that the script runs only after the HTML document has been fully parsed.

This can be useful for scripts that depend on the DOM being fully loaded.

Example:

htmlCopy code<script src="your-script.js" defer></script>

Using defer can help ensure that your scripts run in the correct order while still improving load times.

Asynchronous CSS

CSS is crucial for the look and feel of your website, but it can also slow down your load times if not handled properly. To make CSS load asynchronously, you can use the media attribute with a value that doesn’t match the current device.

Once the CSS is downloaded, you can switch the media attribute to all.

Example:

<link rel="stylesheet" href="your-styles.css" media="none" onload="if(media!='all')media='all'">

This approach allows the rest of the page to load while the CSS is being downloaded.

Lazy Loading Images

Images are often the largest files on a webpage and can significantly impact load times. Lazy loading is a technique that loads images only when they are about to enter the viewport.

This reduces the initial load time and saves bandwidth.

To implement lazy loading, you can use the loading="lazy" attribute on your image tags.

Example:

<img src="your-image.jpg" loading="lazy" alt="Description of image">

This simple change can drastically reduce the initial load time, especially on image-heavy pages.

Asynchronous Fonts

Web fonts can also slow down your website if not loaded efficiently. To load fonts asynchronously, you can use the font-display property in your CSS.

Example:

@font-face {
font-family: 'YourFont';
src: url('your-font.woff2') format('woff2');
font-display: swap;
}

The font-display: swap value ensures that the browser displays fallback fonts until the custom font is fully loaded, improving perceived performance.

Optimizing Asynchronous Loading

Combining and Minifying Files

Combining multiple JavaScript or CSS files into one reduces the number of HTTP requests, speeding up load times. Additionally, minifying these files by removing unnecessary whitespace and comments can further reduce their size.

Using a Content Delivery Network (CDN)

A CDN stores copies of your site’s resources at multiple locations around the world. When a user visits your site, the CDN serves resources from the nearest location, reducing latency and speeding up load times.

Many CDNs also offer built-in support for asynchronous loading techniques.

Caching Strategies

Implementing caching strategies ensures that frequently accessed resources are stored locally on the user’s device, reducing load times for subsequent visits.

Use HTTP headers like Cache-Control to specify caching policies for your resources.

Monitoring and Testing

Regularly monitor and test your website’s performance to identify areas for improvement. Tools like Google PageSpeed Insights, GTmetrix, and WebPageTest provide detailed reports and recommendations for optimizing load times.

Use these insights to fine-tune your asynchronous loading strategies.

Advanced Techniques for Asynchronous Loading

Code Splitting

Code splitting is a technique that breaks your JavaScript into smaller chunks that can be loaded on demand. This reduces the initial load time by only loading the necessary code for the current page, while other parts of the code are loaded as needed.

Implementing Code Splitting

If you are using modern JavaScript frameworks like React, Vue, or Angular, code splitting is often built into the framework’s tooling.

For React, you can use React.lazy and Suspense to achieve code splitting:

import React, { Suspense, lazy } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}

export default App;

In this example, LazyComponent is only loaded when it’s needed, reducing the initial load time.

Service Workers

Service workers are scripts that run in the background, separate from the web page, and can intercept network requests to cache resources or fetch them from the network.

They enable advanced caching strategies and offline functionality, significantly improving performance and user experience.

Setting Up a Service Worker

Here is a simple example of a service worker that caches static assets:

// sw.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then((cache) => {
return cache.addAll([
'/index.html',
'/styles.css',
'/script.js',
'/image.jpg',
]);
})
);
});

self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});

Register the service worker in your main JavaScript file:

if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').then(
(registration) => {
console.log('ServiceWorker registration successful:', registration);
},
(error) => {
console.log('ServiceWorker registration failed:', error);
}
);
});
}

Prefetching and Preloading

Prefetching and preloading are techniques that load resources ahead of time to improve the user experience. Prefetching loads resources that might be needed in the near future, while preloading loads resources that are needed for the current navigation.

Using Prefetch

To prefetch resources, use the rel="prefetch" attribute:

<link rel="prefetch" href="/future-page.html">

This tells the browser to fetch the resource in the background, so it’s ready when the user navigates to that page.

Using Preload

To preload critical resources, use the rel="preload" attribute:

<link rel="preload" href="/styles.css" as="style">
<link rel="preload" href="/script.js" as="script">

Preloading ensures these resources are available as soon as they are needed, improving the loading performance of critical assets.

Best Practices for Asynchronous Loading

Identify and prioritize resources that are essential for rendering the above-the-fold content. Use asynchronous loading techniques for non-critical resources to ensure the main content loads quickly.

Prioritize Critical Resources

Identify and prioritize resources that are essential for rendering the above-the-fold content. Use asynchronous loading techniques for non-critical resources to ensure the main content loads quickly.

Use Async and Defer Wisely

While async and defer attributes improve loading times, use them wisely based on the dependency of your scripts. Use async for independent scripts and defer for scripts that rely on the DOM.

Monitor and Optimize Regularly

Performance optimization is an ongoing process. Regularly monitor your website’s performance using tools like Lighthouse, GTmetrix, and WebPageTest.

Analyze the reports and continuously optimize your asynchronous loading strategies based on the insights.

Fallbacks and Graceful Degradation

Ensure that your website still functions properly even if some asynchronous resources fail to load. Implement fallbacks and graceful degradation to provide a basic but functional user experience under all circumstances.

Integrating Asynchronous Loading with Modern JavaScript Frameworks

React

React is a popular JavaScript library for building user interfaces, and it provides built-in support for asynchronous loading techniques.

Lazy Loading Components

React’s React.lazy and Suspense make it easy to lazy load components. This helps reduce the initial bundle size and improve load times.

Example:

import React, { Suspense, lazy } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}

export default App;

In this example, LazyComponent is only loaded when it’s needed, reducing the initial load time.

Vue

Vue.js also supports asynchronous loading with its dynamic import feature and the defineAsyncComponent function.

Dynamic Imports

You can use dynamic imports to load components asynchronously in Vue:

<template>
<div>
<component :is="asyncComponent"></component>
</div>
</template>

<script>
export default {
data() {
return {
asyncComponent: () => import('./AsyncComponent.vue')
}
}
}
</script>

Using defineAsyncComponent

Vue 3 introduces the defineAsyncComponent function for better control over asynchronous components:

import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() =>
import('./AsyncComponent.vue')
);

export default {
components: {
AsyncComponent
}
};

Angular

Angular uses the Angular Router’s loadChildren syntax to lazily load modules. This helps split your application into smaller chunks and improve performance.

Lazy Loading Modules

Set up lazy loading in Angular by using the loadChildren property in your routing configuration:

const routes: Routes = [
{
path: 'feature',
loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
}
];

This code will lazy load the FeatureModule when the user navigates to the /feature route.

Combining Asynchronous Loading with Server-Side Rendering (SSR)

Next.js

Next.js is a React framework that supports SSR and asynchronous loading out of the box. Combining these features can significantly improve both performance and SEO.

Implementing SSR with Async Components

Next.js handles SSR and client-side rendering seamlessly. Use React.lazy and Suspense for async components, and Next.js will handle the SSR part for you.

Example:

import React, { Suspense, lazy } from 'react';
import { useRouter } from 'next/router';

const LazyComponent = lazy(() => import('../components/LazyComponent'));

function Page() {
const router = useRouter();

return (
<div>
<h1>{router.query.title}</h1>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}

export default Page;

Nuxt.js

Nuxt.js is a framework for Vue.js that also supports SSR and asynchronous loading.

Implementing SSR with Async Components

Nuxt.js makes it easy to combine SSR and async loading. Use dynamic imports and Nuxt’s built-in support for async components.

Example:

<template>
<div>
<client-only>
<async-component />
</client-only>
</div>
</template>

<script>
export default {
components: {
'async-component': () => import('@/components/AsyncComponent.vue')
}
}
</script>

Angular Universal

Angular Universal allows you to render Angular applications on the server side. Combining this with lazy loading can greatly enhance performance.

Implementing SSR with Lazy Loading

Set up Angular Universal and configure your routes for lazy loading:

const routes: Routes = [
{
path: 'feature',
loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
}
];

Angular Universal will handle the server-side rendering, while lazy loading ensures that only necessary code is loaded initially.

Practical Tips and Tricks

Analyze Your Load Time

Use tools like Google PageSpeed Insights, GTmetrix, and WebPageTest to analyze your site’s load time. These tools provide insights into what resources are slowing down your page and how to optimize them.

Use the Network Panel

The browser’s developer tools have a Network panel that lets you see how long each resource takes to load. Use this panel to identify and optimize slow-loading resources.

Optimize Third-Party Scripts

Third-party scripts, such as ads and analytics, can slow down your site. Load these scripts asynchronously to minimize their impact on your load time.

Reduce HTTP Requests

Combining and minifying files can reduce the number of HTTP requests your site makes, improving load times. Tools like Webpack can help you bundle and minify your files efficiently.

Preconnect and DNS Prefetch

Use the preconnect and dns-prefetch links to establish early connections to required origins. This can save valuable time during the loading process.

Example:

<link rel="preconnect" href="https://example.com">
<link rel="dns-prefetch" href="https://example.com">

Compress and Optimize Assets

Compress images and other large assets to reduce their size. Use formats like WebP for images, which offer better compression than traditional formats like JPEG and PNG.

Implement Progressive Web App (PWA) Features

PWAs offer offline capabilities and improve performance. Implementing PWA features can provide a smoother, faster experience for users, even with intermittent internet connections.

Advanced Asynchronous Loading Techniques

Intersection Observer API

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document’s viewport.

This is particularly useful for lazy loading images and other content as they come into view.

Implementing Intersection Observer

Here’s an example of using the Intersection Observer API to lazy load images:

document.addEventListener("DOMContentLoaded", function () {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function (entries, observer) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove("lazy");
lazyImageObserver.unobserve(lazyImage);
}
});
});

lazyImages.forEach(function (lazyImage) {
lazyImageObserver.observe(lazyImage);
});
} else {
// Fallback for browsers that do not support IntersectionObserver
let lazyLoad = function () {
lazyImages.forEach(function (lazyImage) {
if (lazyImage.getBoundingClientRect().top < window.innerHeight && lazyImage.getBoundingClientRect().bottom > 0) {
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove("lazy");
}
});

if (lazyImages.length == 0) {
document.removeEventListener("scroll", lazyLoad);
window.removeEventListener("resize", lazyLoad);
window.removeEventListener("orientationchange", lazyLoad);
}
};

document.addEventListener("scroll", lazyLoad);
window.addEventListener("resize", lazyLoad);
window.addEventListener("orientationchange", lazyLoad);
}
});

In this example, images with the class “lazy” are only loaded when they come into view, reducing the initial load time.

Progressive Image Loading

Progressive image loading improves user experience by initially loading a low-resolution placeholder image and then replacing it with a high-resolution image once it is fully loaded.

Implementing Progressive Image Loading

Here’s how to implement progressive image loading using a combination of the Intersection Observer API and CSS:

<img src="low-res.jpg" data-src="high-res.jpg" class="progressive lazy" alt="Description">

<style>
.progressive {
filter: blur(10px);
transition: filter 0.3s;
}

.progressive.lazy {
filter: none;
}
</style>

<script>
document.addEventListener("DOMContentLoaded", function () {
let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function (entries, observer) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
let lazyImage = entry.target;
lazyImage.src = lazyImage.dataset.src;
lazyImage.classList.remove("lazy");
lazyImage.onload = function () {
lazyImage.classList.add("loaded");
};
lazyImageObserver.unobserve(lazyImage);
}
});
});

lazyImages.forEach(function (lazyImage) {
lazyImageObserver.observe(lazyImage);
});
}
});
</script>

In this example, a low-resolution image is initially displayed with a blur effect. When the high-resolution image is loaded, the blur effect is removed.

Asynchronous Data Fetching with GraphQL

GraphQL is a query language for your API that enables you to fetch exactly the data you need. This can reduce the amount of data transferred and improve performance, especially when fetching data asynchronously.

Using Apollo Client with React

Apollo Client is a popular GraphQL client for React that supports asynchronous data fetching.

import React from 'react';
import { ApolloProvider, useQuery, gql } from '@apollo/client';
import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
uri: 'https://your-graphql-endpoint.com/graphql',
cache: new InMemoryCache()
});

const GET_DATA = gql`
query GetData {
data {
id
name
description
}
}
`;

function DataComponent() {
const { loading, error, data } = useQuery(GET_DATA);

if (loading) return <p>Loading...</p>;
if (error) return <p>Error :(</p>;

return data.data.map(({ id, name, description }) => (
<div key={id}>
<h2>{name}</h2>
<p>{description}</p>
</div>
));
}

function App() {
return (
<ApolloProvider client={client}>
<DataComponent />
</ApolloProvider>
);
}

export default App;

In this example, Apollo Client fetches data from a GraphQL API asynchronously, displaying a loading message while the data is being fetched.

Real User Monitoring (RUM)

Real User Monitoring (RUM) collects data about your users’ interactions with your website in real-time. This helps you understand how real users experience your site and identify areas for improvement.

Implementing RUM

Tools like Google Analytics, New Relic, and Datadog offer RUM capabilities. Here’s a basic example of using Google Analytics for RUM:

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXX-X"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());

gtag('config', 'UA-XXXXXX-X');
</script>

Once set up, Google Analytics will start collecting real user data, allowing you to monitor performance and user interactions.

Optimizing Asynchronous Loading for Different Devices

In today's multi-device world, optimizing your website for various screen sizes and devices is crucial. Asynchronous loading plays a significant role in ensuring that your site performs well across desktops, tablets, and mobile phones.

Responsive Design and Mobile Optimization

In today’s multi-device world, optimizing your website for various screen sizes and devices is crucial. Asynchronous loading plays a significant role in ensuring that your site performs well across desktops, tablets, and mobile phones.

Responsive Design Principles

Ensure that your website uses responsive design principles. This involves using flexible grids, fluid images, and CSS media queries to adapt your site’s layout to different screen sizes.

Example of a simple responsive layout with CSS:

body {
font-family: Arial, sans-serif;
}

.container {
width: 100%;
padding: 15px;
box-sizing: border-box;
}

@media (min-width: 600px) {
.container {
width: 50%;
}
}

@media (min-width: 900px) {
.container {
width: 33.33%;
}
}

Optimizing for Mobile

Mobile optimization involves more than just responsive design. It’s also about ensuring that your site loads quickly and efficiently on mobile devices.

This includes using asynchronous loading techniques to reduce load times and save bandwidth.

Image Optimization for Mobile

Images are often the largest assets on a webpage, and optimizing them for mobile can significantly improve load times. Use responsive images to serve different image sizes based on the device’s screen size.

Example using the srcset attribute:

<img src="small.jpg"
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
alt="Description of image">

This approach ensures that smaller images are served to mobile devices, reducing load times and saving bandwidth.

Adaptive Loading

Adaptive loading is a strategy that adjusts the loading behavior based on the device and network conditions. This involves serving different content or optimizing resources depending on the user’s device capabilities and connection speed.

Implementing Adaptive Loading

Use the Network Information API to detect the user’s network speed and adjust the loading behavior accordingly:

if ('connection' in navigator) {
let connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;

if (connection.saveData) {
// Serve lower-quality images or fewer resources
}

switch (connection.effectiveType) {
case 'slow-2g':
case '2g':
// Serve minimal resources
break;
case '3g':
// Serve optimized resources
break;
case '4g':
// Serve full resources
break;
default:
// Default behavior
break;
}
}

This example adjusts the loading behavior based on the user’s network conditions, ensuring a smoother experience on slower connections.

SEO and Asynchronous Loading

Balancing Performance and SEO

While asynchronous loading can improve performance, it’s essential to ensure that it doesn’t negatively impact SEO. Search engines need to be able to crawl and index your content effectively, even when using asynchronous techniques.

Using Server-Side Rendering (SSR)

SSR can help balance performance and SEO by pre-rendering HTML on the server. This ensures that search engines can crawl your content effectively while still benefiting from asynchronous loading techniques for dynamic content.

Optimizing Meta Tags and Structured Data

Ensure that your meta tags and structured data are available in the initial HTML response. This helps search engines understand your content better and improves your chances of ranking higher in search results.

Example of including structured data in your HTML:

<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Article",
"headline": "How to Use Asynchronous Loading for Faster Websites",
"author": {
"@type": "Person",
"name": "Your Name"
},
"datePublished": "2024-07-16",
"image": "https://example.com/image.jpg",
"articleBody": "In today's fast-paced digital world, having a fast website is crucial..."
}
</script>

Ensuring Content is Crawlable

Use the noindex attribute cautiously with asynchronously loaded content. Make sure that critical content is available to search engines, even if it loads asynchronously.

Testing your site with Google Search Console and using the Mobile-Friendly Test tool can help ensure your content is crawlable.

Handling Third-Party Scripts

Handling Third-Party Scripts

Loading Third-Party Scripts Asynchronously

Third-party scripts, such as analytics and ad scripts, can significantly impact load times. Load these scripts asynchronously to minimize their impact.

Example of loading a third-party script asynchronously:

<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXX-X"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());

gtag('config', 'UA-XXXXXX-X');
</script>

Managing Dependencies

Ensure that third-party scripts do not block the rendering of your page. Use techniques like async and defer attributes to manage script loading effectively.

Using Defer for Dependent Scripts

When a script depends on another script, use the defer attribute to ensure the scripts are executed in the correct order after the HTML document has been fully parsed.

Example:

<script src="dependent-script.js" defer></script>
<script src="main-script.js" defer></script>

Continuous Monitoring and Optimization

Using Performance Monitoring Tools

Regularly monitor your website’s performance using tools like Google PageSpeed Insights, GTmetrix, and WebPageTest. These tools provide detailed reports and recommendations for optimizing load times and overall performance.

Real-Time Performance Monitoring

Implement real-time performance monitoring using tools like New Relic, Datadog, or Google Analytics. These tools help you track performance metrics and user behavior, allowing you to identify and address issues quickly.

Conducting Regular Audits

Conduct regular performance audits to ensure your website remains optimized. Use the insights from your monitoring tools to make continuous improvements.

Staying Updated with Web Standards

The web is constantly evolving, with new standards and best practices emerging regularly. Stay updated with the latest web development trends and standards to ensure your asynchronous loading strategies remain effective.

Final Tips for Effective Asynchronous Loading

Prioritize Critical Resources

Identify and prioritize the resources critical to rendering the initial view of your webpage. These resources should be loaded first to ensure the page becomes interactive as quickly as possible. Use techniques like preloading and critical CSS to achieve this.

Example of Preloading Critical Resources

<link rel="preload" href="styles.css" as="style">
<link rel="preload" href="script.js" as="script">

Reduce JavaScript Payload

Large JavaScript files can slow down your website. Minimize and split your JavaScript files to reduce the payload. Use tools like Webpack to bundle and minify your code.

Use Resource Hints

Resource hints like dns-prefetch, preconnect, and prerender help the browser anticipate the resources it needs to fetch, improving load times.

Example of Resource Hints

<link rel="dns-prefetch" href="//example.com">
<link rel="preconnect" href="//example.com">
<link rel="prerender" href="//example.com/future-page">

Implement HTTP/2

HTTP/2 improves the loading of web resources by enabling multiplexing, where multiple resources can be loaded simultaneously over a single connection.

Ensure your server supports HTTP/2 to take advantage of these performance benefits.

Optimize Server Response Times

Ensure your server responds quickly to requests by optimizing server-side code and infrastructure. Use caching strategies, optimize database queries, and leverage a Content Delivery Network (CDN) to serve content faster.

Keep Learning and Experimenting

Web performance optimization is an ongoing process. Keep learning about new techniques and experimenting with different approaches to find what works best for your website.

Stay informed about updates in web technologies and standards to keep your site performing optimally.

Wrapping it up

Asynchronous loading is essential for optimizing website performance, enhancing user experience, and improving SEO. By using techniques such as async and defer attributes for scripts, lazy loading for images, code splitting, and service workers, you can significantly reduce load times. Implement advanced strategies like the Intersection Observer API and progressive image loading to further enhance your site’s responsiveness.

Optimizing for different devices, balancing SEO with performance, and managing third-party scripts are crucial steps. Regular monitoring, performance audits, and staying updated with the latest web standards ensure continuous improvement.

READ NEXT: