What You'll Build
By the end of this tutorial, you'll have a working Firefox extension that replaces the new tab page. We'll build something simple first, then add features.
No build tools, no frameworks, no npm. Just HTML, CSS, and JavaScript.
Prerequisites
- Firefox (obviously)
- A text editor
- Basic knowledge of HTML/CSS/JS
That's it.
Step 1: Create the Extension Structure
Create a folder called my-new-tab. Inside it, create:
my-new-tab/
├── manifest.json
├── newtab.html
├── style.css
└── script.js
Step 2: Write the Manifest
manifest.json tells Firefox what your extension does:
{
"manifest_version": 3,
"name": "My New Tab",
"version": "1.0",
"description": "A custom new tab page",
"chrome_url_overrides": {
"newtab": "newtab.html"
},
"permissions": [
"storage"
]
}
The key part is chrome_url_overrides.newtab — this tells Firefox to load your HTML when the user opens a new tab.
Step 3: Create the HTML
newtab.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>New Tab</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<div id="clock"></div>
<form id="search-form" action="https://www.google.com/search">
<input type="text" name="q" placeholder="Search..." autocomplete="off">
</form>
</div>
<script src="script.js"></script>
</body>
</html>
Step 4: Add Some Style
style.css:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: #1a1a2e;
color: white;
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
text-align: center;
width: 100%;
max-width: 600px;
padding: 20px;
}
#clock {
font-size: 4rem;
font-weight: 200;
margin-bottom: 40px;
letter-spacing: 0.05em;
}
#search-form input {
width: 100%;
padding: 12px 20px;
font-size: 1rem;
border: 1px solid rgba(255,255,255,0.3);
border-radius: 24px;
background: rgba(255,255,255,0.1);
color: white;
outline: none;
}
#search-form input::placeholder {
color: rgba(255,255,255,0.5);
}
Step 5: Add JavaScript
script.js:
// Clock
function updateClock() {
const now = new Date();
document.getElementById('clock').textContent =
now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
const delay = 1000 - now.getMilliseconds();
setTimeout(updateClock, delay);
}
updateClock();
// Focus search on load
document.querySelector('input').focus();
Step 6: Load it in Firefox
- Open Firefox
- Go to
about:debugging - Click "This Firefox"
- Click "Load Temporary Add-on"
- Select your
manifest.jsonfile
Open a new tab — you should see your clock!
Adding Persistence with Storage
To save user preferences, use Firefox's storage.local API:
// Save
browser.storage.local.set({ theme: 'dark' });
// Load
browser.storage.local.get('theme').then(result => {
if (result.theme === 'dark') {
document.body.classList.add('dark');
}
});
Note: In Manifest V3, use browser.storage.local (not localStorage). Both work for new tab pages, but browser.storage is safer as it persists even if the extension updates.
Publishing to AMO (addons.mozilla.org)
When you're ready to share:
- Create a zip of your extension folder
- Go to https://addons.mozilla.org/developers/
- Create an account (free)
- Click "Submit a New Add-on"
- Submit for review
Mozilla reviews all extensions manually. Simple extensions with no backend typically get approved in a few days.
What Next?
You've built the foundation. From here, you can add:
- Weather (using the free Open-Meteo API)
- Multiple search engines with a selector
-
Dark/light mode toggle (save preference with
storage.local) - World clocks for different timezones
- Bookmarks or quick links
If you want to see a fully-featured version of what you just built, Weather & Clock Dashboard is open source and free to use as reference.
Questions about the build? Post them in the comments and I'll help.
Top comments (0)