5 Lessons I Learned Building a Firefox New Tab Extension from Scratch
5 Lessons I Learned Building a Firefox New Tab Extension from Scratch I spent the better part of a month building Weather & Clock Dashboard — a Firefox new tab extension that shows live weather, world clocks, and a search bar. No frameworks, no bundlers, just pure HTML/CSS/JS. Here's what I wish someone had told me before I started. manifest_version: 3 Transition Is Mostly Smooth, But Watch for Service Worker Gotchas Firefox now supports Manifest V3, and I decided to go all-in. Most things just work — declarative content scripts, the new permissions model, cleaner background handling. But service workers have a critical limitation: they go inactive after 30 seconds. For a new tab extension that needs to refresh weather data periodically, this matters. My solution: fetch weather data on each new tab open, not on a persistent background schedule. Yes, this means an API call every time you open a tab, but with proper caching (save to localStorage with a timestamp, refresh only if > 10 minutes old), it works great and doesn't drain the battery: async function getWeather() { const cached = JSON.parse(localStorage.getItem('weatherCache') || '{}'); const now = Date.now(); if (cached.data && (now - cached.timestamp) fetchWeatherByCoords(pos.coords), (err) => { // err.code === 1: User denied // err.code === 2: Position unavailable // err.code === 3: Timeout showManualCityInput(err.code === 1 ? 'Location blocked — enter your city manually' : 'Could not detect location' ); }, { timeout: 5000, maximumAge: 3600000 } ); Implementing dark/light mode switching is one place where browser extensions have an advantage over regular web apps. You get prefers-color-scheme support out of the box, plus the option to persist user preference: // Auto-detect system preference const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); // Apply theme function applyTheme(dark) { document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light'); localStorage.setItem('theme', dark ? 'dark' : 'light'); } // Listen for system changes prefersDark.addEventListener('change', (e) => applyTheme(e.matches)); // On load: check user preference first, then system const savedTheme = localStorage.getItem('theme'); if (savedTheme) { applyTheme(savedTheme === 'dark'); } else { applyTheme(prefersDark.matches); } Users get the "just works" experience (matches their OS setting) with the option to override. I was dreading the Firefox Add-On (AMO) review process, expecting a multi-week wait. My experience was surprisingly good: Initial review: ~3 days Reviewer feedback: Specific, actionable (they flagged one eval() call I hadn't noticed) Follow-up review: ~1 day The reviewer actually looked at the code. They pointed out that I was using innerHTML to set a clock time value (unnecessary DOM risk even though the data was internal), and asked me to switch to textContent. Fair point. Things that help get through review faster: Write clean, readable code Minimize permissions to exactly what you need No remote code execution No obfuscation Include source maps if you minify After all this, I shipped Weather & Clock Dashboard — it's open source (MIT), no account required, and genuinely useful. If you're building a Firefox extension, the developer experience is actually really solid. The API documentation has improved dramatically in the past few years, and the Mozilla developer community is helpful. Have questions? Drop them in the comments — happy to share more specifics about the implementation. Built with: Pure HTML/CSS/JS · No build step · MIT Licensed
