I Built a Browser Extension to Bring Back the Like/Dislike Badge in YouTube Studio

javascript dev.to

YouTube Studio used to show a small like/dislike badge right next to each video in the content tab. At some point it just... disappeared. No announcement, no reason — it was simply gone.

As someone who likes to keep an eye on how videos are performing at a glance, this was annoying. Opening each video individually just to check the ratio felt like a massive step backwards.

So I did what developers do: I built a fix.


The Problem

If you manage a YouTube channel, you know the content tab is your command center. At a glance you can see views, comments, watch time — but no like/dislike ratio. YouTube quietly stopped showing it.

The numbers aren't gone though. YouTube Studio still loads them internally when you open the page. They're just... not displayed anymore.


The Idea: Intercept, Don't Request

My first instinct was to use the YouTube Data API. But that comes with OAuth flows, quota limits, and a setup process that's anything but seamless.

Then I noticed something while digging through DevTools: YouTube Studio's own frontend was already fetching the like counts via internal youtubei endpoints — it just wasn't rendering them.

That changed everything. Instead of making new API calls, I could simply listen to what the page was already loading and inject the badge myself.


How It Works

The extension has two core scripts:

src/inject.js — Intercepts the internal network responses from studio.youtube.com by wrapping the native XMLHttpRequest. When a response contains video metadata (detected via known field names like likeCount and videoId), it extracts the data and broadcasts it via a custom DOM event.

// Simplified version of the interception logic
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (...args) {
  this.addEventListener("load", function () {
    try {
      const json = JSON.parse(this.responseText);
      const videos = extractVideos(json); // walk the response tree
      if (videos.length) {
        document.dispatchEvent(new CustomEvent("ytsr-data", { detail: videos }));
      }
    } catch (_) {}
  });
  return originalOpen.apply(this, args);
};
Enter fullscreen mode Exit fullscreen mode

src/content.js — Listens for those custom events and injects the badge into the correct DOM row for each video. It uses a MutationObserver to handle YouTube's dynamic rendering.

document.addEventListener("ytsr-data", ({ detail: videos }) => {
  for (const video of videos) {
    const row = findRowByVideoId(video.id);
    if (row) injectBadge(row, video.likes);
  }
});
Enter fullscreen mode Exit fullscreen mode

The result: a 👍 92.4% badge appears directly in the content tab, with exact numbers on hover.


Privacy First — By Design

This was a non-negotiable constraint from the start:

  • ✅ Runs only on studio.youtube.com
  • ✅ Makes zero additional network requests
  • ✅ Sends no data to any third-party server
  • ✅ Only reads data from your own logged-in channel
  • ✅ Nothing is stored beyond your local browser session

The extension is essentially a read-only lens on data that was already traveling through your browser.


The Tricky Parts

YouTube Changes Without Warning

Internal endpoints aren't documented. YouTube can rename field keys at any time — and they do. To handle this, I built a debug mode directly into the popup. When enabled, it logs every intercepted payload to the console under a [YTSR] prefix, making it easy to spot which field names changed.

Dislikes Are Not There (Yet)

Here's the honest limitation: YouTube does not include dislike counts in the Studio content endpoint. The extension currently shows like counts only. The "ratio" in the name reflects the roadmap — once I integrate the YouTube Analytics API (which does expose dislikes), the badge will become a true ratio. That requires OAuth per user though, so the UX tradeoff needs careful thought.

MV3 Restrictions

Manifest V3 removed webRequest blocking, which is commonly used for response interception. The workaround here is wrapping XMLHttpRequest from an injected script — which works, but requires careful coordination between the injected world and the content script world via DOM events.


Tech Stack

Layer Tool
Extension API Manifest V3 (Chrome, Edge, Brave, Firefox)
Core Logic Vanilla JavaScript
Build None — ships as unpacked source
Dev Environment VS Code
Version Control GitHub

Deliberately no framework, no bundler, no dependencies. The whole extension is a handful of files.


What I Learned

Building a browser extension that intercepts live page data taught me a lot about how modern web apps load and render dynamic content. The gap between "the data exists" and "the data is visible" is often just a missing render step — and that's a surprisingly powerful place to work.

It also reinforced something I keep coming back to: the best tools solve your own problem first. I built this because I genuinely missed that badge. Every decision — no external requests, no OAuth, debug mode built-in — came from thinking about what I'd want as a user.


Try It Yourself

The extension is open source and ready to install locally:

  1. Clone the repo: git clone https://github.com/daonware-it/yt-studio-ratio
  2. Open chrome://extensions and enable Developer Mode
  3. Click "Load unpacked" and select the folder
  4. Head to YouTube Studio → Content tab

👉 GitHub — daonware-it/yt-studio-ratio
👉 Google Chrome Extension — YT Studio Like/Dislike Ratio

If the badge doesn't appear, flip on Debug Mode in the popup and open the console — you'll see exactly what's happening. And if you run into issues or have ideas, open an issue or drop a comment below. I'd love to hear how it works for you.

Source: dev.to

arrow_back Back to Tutorials