Back
Chapter 6

Storing and Retrieving Data

View Source Code

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