Retitle

A Chrome extension for Gmail

Give any email a title that makes sense.

Subject lines are written for the sender, not for you. Retitle lets you rename an email in your own inbox, so you can find it, and know what it is, in a glance.

mail.google.com — live demo, click Rename

Inbox

  • Dana at AcmeRe: Fwd: invoice??
  • CalendarInvitation: Weekly sync
  • Mo Hassanquick q

Re: Fwd: invoice??

Original subject: Re: Fwd: invoice??

Hi, forwarding the invoice again as promised. Total is $4,280 and Finance needs it paid by the 31st. Let me know if anything looks off.

— Dana

Three clicks, and the inbox reads your way.

  1. 01

    Open any email

    A small Rename button appears right beside the subject line.

  2. 02

    Type a better title

    “Re: Fwd: invoice??” becomes “Acme Q3 invoice – pay by Oct 31”.

  3. 03

    See it everywhere

    Your title shows in the conversation and in your inbox list, every time.

Private by design.

Retitle only changes what you see, on your own devices. Nothing goes to the sender, and the sender never knows.

  • Synced by Chrome, not by us. Titles ride on your own Chrome Sync, so your desktop and laptop match. They never touch our servers.

  • The real email is untouched. The original subject, replies and search all keep working.

  • Undo anytime. Clear the title and the original comes back.

  • Minimum permissions. Storage, and access to mail.google.com only.

Install

Load it once on each computer. Your titles follow you.

  1. 1

    Make a new folder on your computer called retitle.

  2. 2

    Download the three files below into that folder.

  3. 3

    In Chrome, open chrome://extensions and switch on Developer mode (top right).

  4. 4

    Click Load unpacked and choose the retitle folder.

  5. 5

    Open Gmail and refresh. Open any email and click Rename.

  6. 6

    On your other computer, repeat steps 1 to 4 and sign in to Chrome with the same Google account, with Sync turned on. Your titles appear there within seconds.

manifest.jsonTells Chrome what the extension is and where it runs. Download content.jsAdds the Rename button and swaps in your titles. Download content.cssStyles the small Rename button. Download

Sync needs Chrome signed in with the same Google account on each computer (Settings, You and Google, Sync). Updating from v1.0? Replace the three files and click reload on chrome://extensions; your old titles move to sync automatically. Titles are matched by the original subject, so the same rename appears for every message in that conversation.

Read the code before you run it.

The whole extension is three small files. Copy them from here or use the downloads above.

manifest.jsonTells Chrome what the extension is and where it runs.
{
  "manifest_version": 3,
  "name": "Retitle for Gmail",
  "version": "1.1.0",
  "description": "Change the title (subject line) of any email in Gmail. Your titles sync to every computer where you are signed in to Chrome.",
  "permissions": ["storage"],
  "host_permissions": ["https://mail.google.com/*"],
  "content_scripts": [
    {
      "matches": ["https://mail.google.com/*"],
      "js": ["content.js"],
      "css": ["content.css"],
      "run_at": "document_idle"
    }
  ]
}
content.jsAdds the Rename button and swaps in your titles.
// Retitle for Gmail - content script (v1.1, synced).
// Each rename is its own chrome.storage.sync item: "rt:<original subject>" -> "your title".
// Chrome Sync copies these to every browser signed in to the same Google account.
(function () {
  var PREFIX = "rt:";
  var OLD_KEY = "retitle:map"; // v1.0 stored renames on this computer only
  var map = {};
  var queued = false;

  function failed() {
    if (chrome.runtime.lastError) {
      window.alert("Retitle could not save: " + chrome.runtime.lastError.message);
      return true;
    }
    return false;
  }

  // Show your title in place of the original, and remember the original.
  function apply(el) {
    var text = el.textContent;
    var shown = el.dataset.rtShown;
    var orig = shown !== undefined && text === shown ? el.dataset.rtOrig : text;
    var want = map[orig] || orig;
    if (text !== want) el.textContent = want;
    el.dataset.rtOrig = orig;
    el.dataset.rtShown = want;
  }

  function rename(h2) {
    var orig = h2.dataset.rtOrig || h2.textContent;
    var current = map[orig] || orig;
    var next = window.prompt(
      "New title for this email (it follows you to every computer signed in to Chrome).\nLeave empty to restore the original.",
      current
    );
    if (next === null) return;
    next = next.trim();
    if (!next || next === orig) {
      delete map[orig];
      chrome.storage.sync.remove(PREFIX + orig, failed);
    } else {
      map[orig] = next;
      var item = {};
      item[PREFIX + orig] = next;
      chrome.storage.sync.set(item, failed);
    }
    scan();
  }

  function addButton(h2) {
    var host = h2.parentElement;
    if (!host || host.querySelector(".rt-btn")) return;
    var b = document.createElement("button");
    b.type = "button";
    b.className = "rt-btn";
    b.textContent = "Rename";
    b.title = "Change this email's title";
    b.addEventListener("click", function (e) {
      e.preventDefault();
      e.stopPropagation();
      rename(h2);
    });
    h2.insertAdjacentElement("afterend", b);
  }

  function scan() {
    queued = false;
    // Subject line inside an open conversation.
    document.querySelectorAll("h2.hP").forEach(function (h2) {
      apply(h2);
      addButton(h2);
    });
    // Subject line in inbox and list rows.
    document.querySelectorAll("tr.zA span.bog").forEach(apply);
  }

  function queue() {
    if (queued) return;
    queued = true;
    requestAnimationFrame(scan);
  }

  // A rename made on another computer arrives here a few seconds after it is saved.
  chrome.storage.onChanged.addListener(function (changes, area) {
    if (area !== "sync") return;
    for (var k in changes) {
      if (k.indexOf(PREFIX) !== 0) continue;
      var orig = k.slice(PREFIX.length);
      if (changes[k].newValue) map[orig] = changes[k].newValue;
      else delete map[orig];
    }
    scan();
  });

  function load() {
    chrome.storage.sync.get(null, function (all) {
      map = {};
      for (var k in all) {
        if (k.indexOf(PREFIX) === 0) map[k.slice(PREFIX.length)] = all[k];
      }
      scan();
      new MutationObserver(queue).observe(document.body, {
        childList: true,
        subtree: true,
        characterData: true
      });
    });
  }

  // One-time move of v1.0 renames from this computer into synced storage.
  chrome.storage.local.get(OLD_KEY, function (r) {
    var old = r[OLD_KEY];
    if (!old) return load();
    var items = {};
    for (var k in old) items[PREFIX + k] = old[k];
    chrome.storage.sync.set(items, function () {
      if (!chrome.runtime.lastError) chrome.storage.local.remove(OLD_KEY);
      load();
    });
  });
})();
content.cssStyles the small Rename button.
.rt-btn {
  margin-left: 10px;
  padding: 2px 10px;
  border: 1px solid #c4c7c5;
  border-radius: 999px;
  background: #fff;
  color: #1a237e;
  font: 500 12px/20px "Google Sans", Roboto, Arial, sans-serif;
  cursor: pointer;
  vertical-align: middle;
}
.rt-btn:hover { background: #e8eaf6; }
.rt-btn:focus-visible { outline: 2px solid #3f51b5; outline-offset: 2px; }

Stop hunting for “Re: Re: Fwd:”.

Get Retitle