So far we can create new notes to any web page in our web extension and we can show it to the user whether a page already has some notes or not
But to make the functionality complete I want to have a new page which lists all the notes that the user has created
Luckily it’s super easy to add new pages to our extension so head over to wxt.dev and see the documentation
Creating an unlisted page
You can see that there are a lot of entry points so you can configure the bookmarks page the dev tools when there’s a new tab the popup which you already worked in
But I want to add an unlisted page which is a custom page for our extension and this will be the one where the user can manage the notes
So click on unlisted pages and it gives me some explanation how to do it
Basically naming convention is important
I can create a new folder and in that I add index.html
So let’s follow that path
My route should be called notes so I head over to our code base and create a new folder in the entrypoints called notes
And in that I will just add a new index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My Notes</title>
</head>
<body>
<div id="app">
<h1>All Your Notes</h1>
<div id="notes-grid"></div>
</div>
<script type="module" src="./main.ts"></script>
</body>
</html>
Perfect
Now I can add JavaScript and CSS just like in the popup
// entrypoints/notes/main.ts
import './style.css';
import { storage } from 'wxt/storage';
const loadNotes = async () => {
const notesStorage = await storage.getItem('sync:notesStorage');
const notesGrid = document.getElementById('notes-grid');
if (!notesStorage || Object.keys(notesStorage).length === 0) {
notesGrid.innerHTML = '<p>No notes found</p>';
return;
}
// Render each note
for (const [pageKey, noteData] of Object.entries(notesStorage)) {
const noteCard = createNoteCard(pageKey, noteData);
notesGrid.appendChild(noteCard);
}
};
loadNotes();
Accessing the custom page
Your custom page gets a URL in the format
chrome-extension://{extension-id}/notes.html
To open it programmatically without knowing the extension ID
const url = browser.runtime.getURL('/notes.html');
await browser.tabs.create({ url });
You can also add query parameters
const url = browser.runtime.getURL('/notes.html?welcome=true');
Linking from popup
Provide easy access to your custom page from the popup
// In popup
const notesButton = document.querySelector('#view-notes');
notesButton.addEventListener('click', async () => {
await browser.tabs.create({
url: browser.runtime.getURL('/notes.html')
});
});
Listening to storage changes
Keep the page in sync with storage updates
browser.storage.onChanged.addListener((changes, namespace) => {
if (namespace === 'sync' && changes.notesStorage) {
// Reload and re-render notes
loadNotes();
}
});
Perfect
Now users have a central place to manage all their notes across every website they visit
What’s next
We’ve created a custom page for managing notes
In the next chapter we’ll learn about the extension lifecycle and how to handle installation updates and uninstallation