In the previous section we added a popup where the user can enter their note but we’re not persisting the data anywhere
Right now we will figure out how can we store the note that the user entered
And to do it the best place to start is our Chrome documentation
Finding the storage API
So when I arrive to the Chrome extension documentation I can just search for “storage” and see that there is a reference which says “Use the chrome.storage API to store retrieve and track changes to user data” which is exactly what we want to do
And immediately after it says that it requires the storage permission which we saw previously that we could do it in the manifest.json file
However as I told you before we’re not directly editing manifest.json but rather setting the configuration in wxt.config.ts which is super empty
Now let’s find out how to set the permissions there
I head over to wxt.dev and see their guides about configuration manifest and I see that it has a section for permissions
It seems like it already mentions storage and tabs both of them which I will need so I can just copy and paste it
// wxt.config.ts
export default defineConfig({
manifest: {
permissions: ['storage', 'tabs']
}
});
Let’s save our config file and head back to the Chrome documentation which has a section for usage
And I can just copy it to see what it does
Main.ts I can store it here
Not bad I mean
Chrome is underlined and that is because I’m using WXT which doesn’t know what Chrome is
Since it’s a browser agnostic framework I need to replace it simply with browser
Then it will take care of it
All good
Let’s save and see what happens
I open my popup and type in something
That’s my storage
If I click on inspect I can see that the value is set 15 times
Probably that’s how long my text is
But where is it set
Where is the storage information
Viewing storage in DevTools
To get there I can open the extensions click on the service worker
Remember if it’s not visible click on the developer mode
And here head over application
You can see a bunch of stuff here that’s totally not relevant but there is one item called extension storage which I click on and go to local which has key that’s my storage
So let’s try to make sense of it
Browser.storage.local this local will probably refer to this one
And then set the key which is key
To our note text value
Amazing
We get some basic idea but let’s try to make sense of this local first
If I move the mouse above it it says items in the local storage area are local to each machine
But what other storage areas exist
Storage areas
If I go back to the API reference I can immediately see that there is a section called storage areas
There are four types
If you choose the local storage this means that the data is stored on the user’s computer
If you use sync this means that if the user is logged in with their Google profile the data that you store will be persisted across the various devices
And if you use session then you might have guessed the data is only stored in the current user session and will be gone when the user restarts the browser
The fourth storage area is called managed and it’s mostly relevant for enterprises to enforce policies
We will not deal with that
In fact I would prefer to use sync because it seems like the best option and it feels like it’s more user friendly
So let’s change it simply to sync
It’s not that difficult
await browser.storage.sync.set({ note: noteText.value });
And maybe the key shouldn’t be key but let’s say note
Okay I made some changes
I don’t have to restart anything
I just add a new note
And if I open the service worker and click on the sync tab I can see that the key is note and the value is add a new note which I just typed in
Fantastic
Storing notes per page
Now how to move forward from here
I want to store a note for every page the user visits assigned to the given page
So whenever we visit the new page we can store a note and later when we return we can retrieve the note that belongs to that page
To get started we need to figure out on which page we are
I come back to my main.ts file and here I access the browser tabs not chrome browser.tabs.query function which queries the given tabs get all tabs that have specific properties or all tabs if no properties are specified
Well I want to fetch the one that is active and that is in the current window
const [tab] = await browser.tabs.query({
active: true,
currentWindow: true
});
You can learn more about the tabs API in the same reference where we learned about storage but I don’t go there now
Instead I load tabs into this constant and I want to load it to see what’s in it
If I come back to my extension let’s open a test page and it is still tied to the typing functionality
So I will just add it here type something
And if I come to the logs I see I got a lot of promises promises promises but no information about the tab
And that is because this query function is an async function so I need to wait for it
And since now I have an await here I need to make this function an async
Okay let’s try once again
All right
Now I indeed get an array back which has one item and these are currently active tab
With the title Wikipedia URL status complete and a bunch of other information such as the tab ID we don’t need any of this except the URL
You might be wondering why do I get back an array and not just a single tab if there is only one tab active
And the reason is that the query method on the tabs API is always returning an array because you can imagine that you’re querying tabs by different criteria and multiple tabs match those criteria
So as simple as it is we always return an array
However we can be sure that there is only one active tab in the current window
So we can simply do this or to take advantage of JavaScript syntax I can also do this
Let’s print its URL
Let me just check if it indeed works
And I can see that I got the URL
Creating a page key
Now this URL might be very complex
It might contain hashes or query strings and I don’t really care about those when I’m storing the notes
I only care about the domain and the path
So I’ll create a helper method that can extract just that
My helper method is going to be getPageKey
It takes a string for a URL
It will return a string
Now I will convert a URL into a URL object
And I simply return the URL object hostname and the URL object pathname
const getPageKey = (url: string): string => {
const urlObj = new URL(url);
return urlObj.hostname + urlObj.pathname;
};
And I can use this method to get the keys for our notes
Storing note data
I want to store three information
The text of the note the createdAt and the updatedAt date
The note is coming from noteText.value
CreatedAt is a new Date
And updatedAt is also a new Date
Before I move forward I want to mention an important thing about storage
Storage can store any data that is JSON formatted and new Date is actually an object
So it’s better to convert it into an ISO string
const noteData = {
note: noteText.value,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
There
I can test it in the browser
My first note
And remember to see the saved data I need to click on storage note
And I can see that everything is indeed saved as I expected
But how would I store the notes for the various pages
Should I create a new item for every URL
I don’t like this approach because what if later on I want to have special keys such as settings
So instead I will create a new key called notesStorage
And notesStorage will contain an object where every key is the page key and every object is a note data object
So let me just create up here
It’s an empty object for now
And here I can assign our notes data to the relevant key
Which I haven’t set yet so that will be the next thing I do
Which I do it here
GetPageKey tab.url
Hmm
It’s underlined
And it says that undefined is not assignable to type string
Now the problem that I’m facing is that as I told you query might return 0 1 2 or many tabs
And although we can be sure right now that there is always an active tab the expected results might be empty
So I need to have a check for that
And it’s this simple
I just put everything in this if
const [tab] = await browser.tabs.query({
active: true,
currentWindow: true
});
if (!tab || !tab.url) return;
const pageKey = getPageKey(tab.url);
const notesStorage = await storage.getItem('sync:notesStorage') || {};
notesStorage[pageKey] = {
note: noteText.value,
createdAt: notesStorage[pageKey]?.createdAt || new Date().toISOString(),
updatedAt: new Date().toISOString()
};
await storage.setItem('sync:notesStorage', notesStorage);
Loading existing notes
Now we need to load the existing note when the popup opens
So I’ll create a function that loads the note for the current tab
const loadExistingNote = async () => {
const [tab] = await browser.tabs.query({
active: true,
currentWindow: true
});
if (!tab || !tab.url) return;
const pageKey = getPageKey(tab.url);
const notesStorage = await storage.getItem('sync:notesStorage');
if (notesStorage?.[pageKey]) {
noteTextarea.value = notesStorage[pageKey].note;
}
};
// Load when popup opens
loadExistingNote();
Perfect
Now when you open the popup it loads the existing note for that page
And when you type it saves automatically
WXT storage helper
WXT provides a simpler storage API that works across browsers
import { storage } from 'wxt/storage';
// Save data
await storage.setItem('sync:notesStorage', notesStorage);
// Load data
const notesStorage = await storage.getItem('sync:notesStorage');
The prefix sync: local: session: determines the storage area
Much cleaner than using browser.storage directly
What’s next
With storage working your extension can now remember user data across sessions making it truly useful
In the next chapter we’ll add visual feedback by changing the extension icon when a page has notes