DEV Community

Cover image for Writing Chrome Extensions Using Svelte-Kit and Manifest v3
Michel Canta
Michel Canta

Posted on • Updated on

Writing Chrome Extensions Using Svelte-Kit and Manifest v3

Svelte-Kit has been an amazing tool to create great responsive and reactive websites. But with its roots as a compiler, I wondered how I could use it to create a Chrome Extension, and as it turns out… it's really simple.


Today, we're going to see how we can build a simple extension with Svelte-kit and use Chrome's Manifest V3 which will allow us to use promisified versions of many of the Chrome API's methods. 

Note: this method can work with Manifest V2 as well

1. Setting Up Our Repo

We'll start with an empty repo where we will create the template svelte kit project.

npm init svelte@next my-extension
cd my-extension
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

We need to update the svelte.config.js file to rename our destination app directory by setting the appDir parameter to app (thank you @Matyanson for catching this in the comments).

Image description

We are going to replace the default index page that is created with the following code that will allow us to get the url of the current tab:

src/routes/index.svelte

Note: We are using the Chrome api as other methods are blocked by the extension executer for safety reasons.

Update the body tag in the the app.html file to configure the max size of the extension popup as such:

src/app.html

Once updated we will create our manifest file in the /static folder. We will be using manifest V3 here as that is the latest from the Chrome team.

{
  "name": "My first Svelte Extension",
  "description": "A browser extension made with Svelte Kit",
  "version": "1.0.0",
  "manifest_version": 3,
  "permissions": [
    "activeTab"
  ],
  "action": {
    "default_title": "Svelte Extension",
    "default_icon": "favicon.png",
    "default_popup": "index.html"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now when we build our extension, the manifest file will be included in our build folder making it much easier and cleaner.

Finally we need to change our adapter to the sveltekit-adapter-chrome-extension as the usual @sveltekit/adapter-static creates an inline script that is blocked in Manifest V3 due to Content Security Policies (The same is true for Manifest V2 but you are able to define a Content Security Policy rule with a the sha256 of the script or a nonce, that bypasses it in Manifest V2).

npm install -D sveltekit-adapter-chrome-extension
Enter fullscreen mode Exit fullscreen mode

We are then able to build our project running npm run build

2.Testing our Extension

Click on the extension icon in your chrome browser and then click on manage extensions

Navigation to get to Extension management page

Make sure that developer mode is turned on (on the top right side of the page) then click on Load Unpacked.

Extension management page top bar

Select your build folder from the file picker and open it. You should now see your extension on the page.

Extension appears on the extension management page

Make sure it is enabled and navigate to any page you want. Toggle the extension and press the button!

Success!

There you go! You've just made your first Chrome extension using Svelte Kit and Manifest v3!

Discussion (20)

Collapse
10basetom profile image
Tommy

Thanks for writing this up! I just tried to follow this tutorial using the latest version of SvelteKit (create-svelte version 2.0.0-next.115) and here was my experience...

  1. Upon running npm run build I got this error: "config.kit.target is no longer required, and should be removed"; so I removed this line and also reverted <div id="svelte"> back to <div> in app.html. After this change, the build was fine and I was able to locally install the extension.

  2. Upon clicking on the "Reveal current URL" button, the popup threw this JavaScript error: "Uncaught TypeError: Cannot read properties of null (reading 'parentNode') at script-13oyv3b}.js:4:62" -- the code in question is here:

import { start } from "/app/start-63ed0245.js";
start({
  target: document.querySelector('[data-hydrate="1r88ijv"]').parentNode,
  paths: {"base":"","assets":""},
  session: {},
  route: true,
  spa: false,
  trailing_slash: "never",
  hydrate: {
    status: 200,
    error: null,
    nodes: [
      import("/app/layout.svelte-733b8b62.js"),
        import("/app/pages/index.svelte-16d39772.js")
    ],
    url: new URL("http://sveltekit-prerender/"),
    params: {}
  }
});
Enter fullscreen mode Exit fullscreen mode

I'm not sure what's going on here, but it looks like it's not able to find an element with the data-hydrate attribute. Does anyone know what might be the problem?

Collapse
michmich112 profile image
Michel Canta Author

Fixed with v1.0.1-next.1. Thank you for commenting about this problem :)

Collapse
michmich112 profile image
Michel Canta Author

I know there were breaking changes in the new svelte version. I will check, there might be changes that need to be made to the adapter.

Collapse
matyanson profile image
Matyanson • Edited on

Thanks, your post was helpful!
Just one thing I'd like to add. When you build the app and select the build folder in the extension manager, chrome has problem with the file naming:
"Cannot load extension with file or directory name _app. Filenames starting with "_" are reserved for use by the system."
I fixed that by changing the appDir property in the svelte.config.js:

extension_fix

Collapse
matyanson profile image
Matyanson

I tried using the "webextension-polyfill" library for cross browser compability.
It seems it is not compatible with the "sveltekit-adapter-chrome-extension" adapter.
The following error message is thrown while building at "Using sveltekit-adapter-chrome-extension" step:
"[Error: This script should only be loaded in a browser extension.]"

Perhaps I could hot fix it, but I'll stick with the chrome API for now.
Is this even necessary now when the manifest v3 is out?(for cross browser compability)

Collapse
michmich112 profile image
Michel Canta Author

I have not use the "webextension-polyfill" library but that would be interesting. I'll check it out!

Collapse
michmich112 profile image
Michel Canta Author

Yes! thank you for trying it out and noting this out!
I had to do this as well and forgot to add it to this tutorial, I will add it right away!

Collapse
matyanson profile image
Matyanson

Here is a template I made, hope it helps.
github.com/Matyanson/SvelteKit-bro...

Collapse
michmich112 profile image
Michel Canta Author

This is Awesome! Definitely much cleaner than what I was working with before! Starred💫

Collapse
kena profile image
KenA

Thanks for the article! One thing though, when I build the app there will be another manifest file inside the build>app folder that has no relation with the manifest file used by the chrome extension. That's ok, but if one tries to upload the package via the chrome developers dashboard in order to publish the app, it will not be possible and a msg complaining about the app having 2 manifest files will be thrown.

Collapse
michmich112 profile image
Michel Canta Author

Yes great catch, so, you're going to hate me, but you can just delete that manifest file. It has a use when you're creating a web app but not in the case of an extension. Could you create an issue in the adapter's repo and i'll be updating it to remove that manifest file automatically

Collapse
kena profile image
KenA

Thanks man, just added an issue in the adapter's repo as well.

Collapse
10basetom profile image
Tommy

Typo: should be cd my-extension

Collapse
michmich112 profile image
Michel Canta Author

Fixed! cheers

Collapse
10basetom profile image
Tommy

@michmich112 how do you handle chrome not being available inside a .svelte file? For example, I have route/options.svelte and inside this file I have on:input={handleInput} which saves some data using chrome.storage.local.set(). Right now it's not possible since you don't have access to chrome, so you'd have to put the logic inside scripts/options.ts which means now you have logic in two locations: options.svelte (inside <script>) and options.ts.

Collapse
michmich112 profile image
Michel Canta Author

yeah that is a problem since the chrome variable will be available globally in the execution environment. I add the no-check as its an IDE issue and not actually an issue when running it.

Collapse
10basetom profile image
Tommy • Edited on

@michmich112 thanks for the reply. The error I got was not in the IDE but when I build from the console:

"ReferenceError: chrome is not defined"

Whenever I get this build error, it seems to also break the extension because when I reload the extension it throws this error as soon as I load the Options page:

Extension error

When I remove the code containing chrome from options.svelte and rebuild, the extension error on the Options page goes away. I ended up not using SvelteKit to build my extension. My original reason for trying out SvelteKit was the increase in performance and size when serving single-page apps, but I realized since extensions are loaded from your hard drive it's really not a big deal. I will still keep an eye on SvelteKit for other applications though :-).

Thread Thread
michmich112 profile image
Michel Canta Author

Thanks for the update, i haven't run into that problem yet but definitely something to keep an eye on! Thank you!

Collapse
rickvian profile image
Rickvian Aldi • Edited on

i got this error, its says could not detect production environment,
build folder doesn't show up,
will appreciate your help
dev-to-uploads.s3.amazonaws.com/up...

Collapse
michmich112 profile image
Michel Canta Author

Hey Rickvian!
Can show your svelte.config.js file? that would help me diagnose the issue easier.