6 hidden gems in the JavaScript API you should be using

Written by Rahul Padalkar✏️ Modern web browsers ship with a growing number of powerful, native JavaScript APIs that let developers build more dynamic, performant, and user-friendly applications — no external libraries required. In this post, we’ll explore six of these APIs: structuredClone, EyeDropper,  AbortController, Intersection Observer, ResizeObserver, and the Clipboard API. For each, we’ll explain what problems it solves, when to use it, and how to implement it. Before we dive in, it’s important to know why these APIs aren’t more widely known, despite how useful they are: They’re relatively new: Many of these APIs have only been introduced recently. In the meantime, mature and popular npm packages have already become the go-to alternatives Lack of awareness: Popular sites like Stack Overflow and coding tutorials often rely on older, battle-tested solutions, which means there is a general lack of awareness of newer APIs Limited browser support: Some of these APIs aren’t fully supported across all browsers yet. However, support is growing as major browsers like Chrome, Firefox, Safari, etc., continue to adopt them They address niche use cases: These APIs often solve specific problems that not every developer encounters. For example, not every app will require you to run code when an element resizes structuredClone Deep copying objects has always been difficult in JavaScript. Aside from using an external library, one of the go-to solutions was to stringify the object and then JSON parse it. Though this works for simple objects, it fails when: Dealing with special values like undefined, NaN , Infinity, etc. Dealing with objects that have circular references Dealing with values such as Dates, Maps, Sets, RegExp, etc. Dealing with deeply nested objects Enter structuredClone, a widely supported method for deep cloning complex objects without relying on an external library. While it’s been used internally for a while, it was only made available in the public API a few years ago. Say goodbye to Lodash! // Original object with nested data const original = { name: "Richardson", age: 28, address: { city: "New York", street: "Wall Street", zip: "10001" }, hobbies: ["reading", "hiking"], created: new Date() }; // Create a deep clone using structuredClone const copy = structuredClone(original); // Modify the clone copy.name = "Jane"; copy.address.city = "Los Angeles"; copy.hobbies.push("music"); // The original object remains unchanged console.log("Original:", original); console.log("Clone:", copy); See how the original object remains unchanged? Also, observe that the date in the created field is a date object that is independent of original.created. There is no explicit conversion of string to date here — it is all taken care of by the structuredClone function. The map against the addressfield is a completely new object, and so is the array against the hobbies key. EyeDropper API The EyeDropper API allows developers to build a color picker natively without relying on any component library or package. It is still experimental and available only in Chrome, Edge, and Opera. It comes in handy when building apps that allow users to edit or manipulate something, such as a photo editor or a whiteboarding or drawing application. Below is a quick example code of how to use the EyeDropper API: Pick a Color Pick a color document.getElementById("pickColor").addEventListener("click", async () => { if ("EyeDropper" in window) { const eyeDropper = new EyeDropper(); try { const result = await eyeDropper.open(); document.getElementById("colorDisplay").style.backgroundColor = result.sRGBHex; } catch (e) { console.error("Error using EyeDropper:", e); } } else { alert("EyeDropper API is not supported in this browser."); } }); In this code snippet, we create a button element with ID pickColor, and add a click listener. When clicked, we check if the browser supports the EyeDropper API. If it does, we create a new EyeDropper instance and open it. The open function returns a promise that resolves to a color value selected using the eye dropper tool. When the color is selected, we change the div's style to match the selected background color. If the browser doesn’t support the EyeDropper API, we throw an alert with an error message. EyeDropper demo AbortController One of the common problems when building a search UI component is handling stale requests. For example, a request is triggered when a user types a character in an input box. To prevent unnecessary requests from being triggered, we can add a debounce. But now, imagine the user starts typing again after the debounce duration or maybe navigates to a different page. In that case, the already triggered request cannot be canceled, and we might ge

Apr 29, 2025 - 15:41
 0
6 hidden gems in the JavaScript API you should be using

Written by Rahul Padalkar✏️

Modern web browsers ship with a growing number of powerful, native JavaScript APIs that let developers build more dynamic, performant, and user-friendly applications — no external libraries required.

In this post, we’ll explore six of these APIs: structuredClone, EyeDropper,  AbortController, Intersection Observer, ResizeObserver, and the Clipboard API. For each, we’ll explain what problems it solves, when to use it, and how to implement it. Before we dive in, it’s important to know why these APIs aren’t more widely known, despite how useful they are:

  • They’re relatively new: Many of these APIs have only been introduced recently. In the meantime, mature and popular npm packages have already become the go-to alternatives
  • Lack of awareness: Popular sites like Stack Overflow and coding tutorials often rely on older, battle-tested solutions, which means there is a general lack of awareness of newer APIs
  • Limited browser support: Some of these APIs aren’t fully supported across all browsers yet. However, support is growing as major browsers like Chrome, Firefox, Safari, etc., continue to adopt them
  • They address niche use cases: These APIs often solve specific problems that not every developer encounters. For example, not every app will require you to run code when an element resizes

structuredClone

Deep copying objects has always been difficult in JavaScript. Aside from using an external library, one of the go-to solutions was to stringify the object and then JSON parse it. Though this works for simple objects, it fails when:

  • Dealing with special values like undefined, NaN , Infinity, etc.
  • Dealing with objects that have circular references
  • Dealing with values such as Dates, Maps, Sets, RegExp, etc.
  • Dealing with deeply nested objects

Enter structuredClone, a widely supported method for deep cloning complex objects without relying on an external library. While it’s been used internally for a while, it was only made available in the public API a few years ago. Say goodbye to Lodash!

// Original object with nested data
const original = {
    name: "Richardson",
    age: 28,
    address: {
        city: "New York",
        street: "Wall Street",
        zip: "10001"
    },
    hobbies: ["reading", "hiking"],
    created: new Date()
};

// Create a deep clone using structuredClone
const copy = structuredClone(original);

// Modify the clone
copy.name = "Jane";
copy.address.city = "Los Angeles";
copy.hobbies.push("music");

// The original object remains unchanged
console.log("Original:", original);
console.log("Clone:", copy);

Console See how the original object remains unchanged? Also, observe that the date in the created field is a date object that is independent of original.created.

There is no explicit conversion of string to date here — it is all taken care of by the structuredClone function. The map against the addressfield is a completely new object, and so is the array against the hobbies key.

EyeDropper API

The EyeDropper API allows developers to build a color picker natively without relying on any component library or package. It is still experimental and available only in Chrome, Edge, and Opera.

It comes in handy when building apps that allow users to edit or manipulate something, such as a photo editor or a whiteboarding or drawing application. Below is a quick example code of how to use the EyeDropper API:

<button id="pickColor">Pick a Color</button>
<div
  id="colorDisplay"
  style="width: 100px; height: 100px; border: 1px solid #ccc"
></div>
<br />
<div>Pick a color</div>
<br />
<div style="display: flex; flex-direction: row; height: 5rem">
  <div style="background-color: brown; width: 5rem"></div>
  <div style="background-color: crimson; width: 5rem"></div>
  <div style="background-color: blueviolet; width: 5rem"></div>
  <div style="background-color: chartreuse; width: 5rem"></div>
  <div style="background-color: darkgreen; width: 5rem"></div>
</div>
<script>
  document.getElementById("pickColor").addEventListener("click", async () => {
    if ("EyeDropper" in window) {
      const eyeDropper = new EyeDropper();
      try {
        const result = await eyeDropper.open();
        document.getElementById("colorDisplay").style.backgroundColor =
          result.sRGBHex;
      } catch (e) {
        console.error("Error using EyeDropper:", e);
      }
    } else {
      alert("EyeDropper API is not supported in this browser.");
    }
  });
</script>

In this code snippet, we create a button element with ID pickColor, and add a click listener. When clicked, we check if the browser supports the EyeDropper API. If it does, we create a new EyeDropper instance and open it. The open function returns a promise that resolves to a color value selected using the eye dropper tool. When the color is selected, we change the div's style to match the selected background color.

If the browser doesn’t support the EyeDropper API, we throw an alert with an error message. EyeDropper demo EyeDropper API

AbortController

One of the common problems when building a search UI component is handling stale requests. For example, a request is triggered when a user types a character in an input box. To prevent unnecessary requests from being triggered, we can add a debounce. But now, imagine the user starts typing again after the debounce duration or maybe navigates to a different page.

In that case, the already triggered request cannot be canceled, and we might get stale results that we don’t care about. This can hamper user experience and load the API server unnecessarily. To abort requests that are already sent, we can use AbortController. Let’s take a look at an example of this API in action:

import React, { useEffect, useState } from 'react';

function SearchComponent({ query }) {
  const [results, setResults] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;

    const debounceRequest = setTimeout(() => {
      setLoading(true);
      fetch(`https://api.example.com/search?q=${query}`, { signal })
        .then(res => res.json())
        .then(data => {
          setResults(data.results);
          setLoading(false);
        })
        .catch(err => {
          if (err.name === 'AbortError') {
          console.log("Request was aborted")
          } else {
            console.log('Search failed', err);
            setLoading(false);
          }
        });
    }, 500);

    return () => {
      clearTimeout(debounceRequest);
      controller.abort();
    };
  }, [query]);

  return (
    <div>
      {loading && <p>Searching...</p>}
      <ul>
        {results.map(r => (
          <li key={r.id}>{r.name}</li>
        ))}
      </ul>
    </div>
  );
}

There are a few things to note here. The query prop is given as an input to the SearchComponent, and is added to the dependency array of useEffect Hook. So, every time the query changes, the useEffect Hook is called. When the component is mounted and when the query prop changes, the useEffect Hook runs, and it creates an instance of AbortController.

A function with 500ms debounce is created. After 500ms, the function is called, and it makes an API call to fetch data based on the querypassed to it. Upon receiving the response, the state is updated and displays the results in an unordered list.

Also, observe how signal from AbortController is passed to the fetch call. If the component gets unmounted (user navigates to a different route) or if the query changes (user types in a new character after 500ms but before the search results are displayed), the cleanup function is called, which clears the timeout and aborts the fetch call.

N.B., if you are using Axios with AbortController, make sure that you are using v0.22.0 and above. The older versions don’t support AbortController for cancelling requests.

Intersection Observer

Are you trying to lazy-load images to optimize page load speeds? Or maybe load and play videos only when they’re visible? Maybe you’re trying to implement infinite scroll? Well, you're in luck — you don’t need any npm packages to do any of that. Enter the Intersection Observer API.

Intersection Observer is a browser API that allows you to run code when elements enter or leave a viewport or another element. Intersection Observer is natively supported, so it is a clean and performant solution to implement lazy loading or infinite scrolling. Let’s take a look at an example where we can use Intersection Observer to lazy load images:


 lang="en">
  
     charset="UTF-8" />
    </span>Lazy Load Images<span class="nt">
    
  
  
    
      data-src="https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=800"
      alt="Forest"
    />
    
      data-src="https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?w=800"
      alt="Person"
    />
    
      data-src="https://images.unsplash.com/photo-1516117172878-fd2c41f4a759?w=800"
      alt="Laptop"
    />
    
      data-src="https://images.unsplash.com/photo-1741514376184-f7cd449a0978?w=800"
      alt="Mountains"
    />
    
      data-src="https://images.unsplash.com/photo-1743062356649-eb59ce7b8140?w=800"
      alt="Desert"
    />
    
      data-src="https://images.unsplash.com/photo-1470770903676-69b98201ea1c?w=800"
      alt="Bridge"
    />
    
    
      data-src="https://images.unsplash.com/photo-1518609878373-06d740f60d8b?w=800"
      alt="Sunset"
    />
    
  

There are a few interesting things to note here. First, we have many img tags with the image source URL set to the data-src attribute. This prevents them from loading as soon as the page is rendered. In the script tag, we run some JavaScript code. First, we start by collecting all img tags using a querySelector. These will be used later for setting up the observer. Then, we create a single instance of IntersectionObserver.

To the constructor, we pass a function that gets called when the visibility of any of the observed elements changes. Say, for example, when a user scrolls and the fourth image becomes visible in the viewport, the callback is triggered. The callback takes two arguments: entries, which is a list of all observed elements whose visibility has changed, and observer, which is the instance that can be used to perform cleanup actions. Inside the callback, we only process entries (in our case, images) that intersect our viewport.

For each image that is visible in the browser window, we add a src attribute and remove the data-src attribute. This then downloads the images and displays them in the window. As a part of the cleanup, we unobserve the image using the unobserve method on the observer. We pass two options to the constructor:

  • root: This is the element the observer uses to check if the observed elements are intersecting or not. To set it to the viewport, we pass null
  • threshold: This is a number between 0 and 1. This number decides after how much visibility the callback should be triggered. For example, if this value is set to 0.2, then the callback is triggered when the observing elements become 20% visible, either on entry or exit

Intersection Observer is supported by all major browsers.

ResizeObserver

If you are building a web application with a complex UI (e.g., a drag-and-drop website builder or form builder), you might have faced the need to run code when a specific HTML element changes size. However, HTML elements don’t emit resize events (except for the window object, which isn’t helpful in most cases).

Previously, your best options were workarounds like MutationObserver or setInterval. But not anymore! ResizeObserver allows us to observe an HTML element and run code when its size changes. It is supported by all major browsers and can be used in a variety of use cases, including auto-scaling text, rendering responsive charts, handling virtualized lists, and building size-aware UI components. It’s also super easy to use. Let’s see how we can use ResizeObserver to auto-scale text based on its parent's size:


 lang="en">
  
     charset="UTF-8" />
    </span>Auto-Scaling Text<span class="nt">
    
  
  
     class="container" id="textBox">
       class="autoscale-text" id="text">
        Resize container to auto scale this text.