Build Your Own Chrome Extension: A Complete Walkthrough for a Text Snipping Tool
For Western developers, this guide demystifies Chrome extension development with a practical, complete example that solves a real problem. It shows how MV3 works in practice, including the shift from background pages to service workers, and provides patterns for cross-component communication that apply to any extension project.
This guide walks through building a complete Chrome extension from scratch: a tool that lets users select text on any webpage, click a floating save button, and store the quote along with its source URL and timestamp. The extension uses Manifest V3 and covers all four core components: manifest.json for permissions and configuration, a content script that injects a save button into pages, a popup HTML page that displays the saved list, and a background service worker that adds right-click menu support.
The project evolves from a basic version with just selection-based saving to a more complete tool that adds right-click context menu saving and JSON export functionality. Along the way, the guide explains common debugging pitfalls like the "Extension context invalidated" error, the difference between chrome.storage.local and chrome.storage.sync, and how message passing works between components.
The final section covers packaging for the Chrome Web Store, including the $5 registration fee, the importance of keeping the .pem private key for updates, and the practical alternative of simply sharing the unpacked folder with friends instead of publishing.
The guide's progression from a basic selection-saver to adding right-click and export features mirrors how real-world extension development often evolves—start minimal, then add features based on actual usage pain points.
The decision to use chrome.storage.local over sync is a practical tradeoff that many developers overlook: sync's 100KB limit is surprisingly easy to hit with text-heavy data, making local the safer default for anything beyond simple preferences.
The explicit handling of the async Promise rejection from chrome.tabs.sendMessage (using .catch() instead of try-catch) is a subtle but important MV3 detail that catches many developers off guard, especially those accustomed to synchronous patterns.
The guide's pragmatic advice to skip the Chrome Web Store and just share the unpacked folder reflects a real tension: the $5 fee and review process create friction for small tools, while sideloading works perfectly for personal or small-team use.
The floating button approach using position:fixed with z-index:2147483647 (the maximum) is a clever solution to the problem of overlaying content on arbitrary websites, but the scroll listener that removes the button is a UX compromise—it avoids position drift but means users must reselect after scrolling.