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!

Oldest comments (34)

Collapse
 
matyanson profile image
Matyanson • Edited

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

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

Collapse
 
michmich112 profile image
Michel Canta

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

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

Collapse
 
tohodo profile image
Tommy

Typo: should be cd my-extension

Collapse
 
michmich112 profile image
Michel Canta

Fixed! cheers

Collapse
 
tohodo 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

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
 
michmich112 profile image
Michel Canta

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

Collapse
 
tohodo 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

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
 
tohodo profile image
Tommy • Edited

@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

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

Collapse
 
aneesh_arora profile image
Aneesh Arora

how to add "no-check" when running npm run build? I tried to find a solution online but couldn't

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

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
 
rickvian profile image
Rickvian Aldi • Edited

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

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

Collapse
 
yamessays profile image
James Cameron

What's your final verdict? Is SvelteKit worth using or should people just use plain svelte or vanilla javascript?

Collapse
 
michmich112 profile image
Michel Canta

Hey @yamessays! Great question! I wrote another post about it to answer that question: dev.to/michmich112/should-you-use-...

Collapse
 
czachandrew profile image
Andrew Czachowski

Don't know if I'm the only one to come across this but in the current version of svelte.config.js make sure in kit to add prerender: {default: true} if you aren't seeing the generate index.html

Collapse
 
proziam profile image
Eric Biggs

As of a few days ago this will fail due to a breaking adapter change. Issue is here, github.com/michmich112/sveltekit-a...

Collapse
 
tohodo profile image
Tommy

@michmich112 I just noticed there's now another extensions adapter, which threw me off a bit. Would you happen to know what the key differences are? It seems both appear to support MV3 now.

Collapse
 
michmich112 profile image
Michel Canta

Thats great to hear! I haven't seen that project but i'm sure it works well!

Collapse
 
b4dmonkey profile image
Jo

how do you add content.js and background.js to the project? Is this something that needs to be configured in the adapter?

Collapse
 
b4dmonkey profile image
Jo

after playing around with this it looks like the proper place would be under the static dir

Collapse
 
takaebato profile image
Takahiro Ebato • Edited

Thanks for the great adapter and blog post! About development flow, I'd like to know if I need to rebuild (and reload extension) to reflect the changes in the source code. If there is a more efficient way, could you tell me about it?
Thanks!

Collapse
 
michmich112 profile image
Michel Canta

Yes we need to for now, it might be possible with something like nodemon. i'll look into it :)

Collapse
 
takaebato profile image
Takahiro Ebato

Thanks! I'm looking forward to it!

Collapse
 
relepega profile image
Relepega

In svelte v4, the adding of target: '#svelte' in svelte.config.js isn't necessary, as it throws an error and without it it compiles just fine. Anyway, great guide!

Collapse
 
michmich112 profile image
Michel Canta

Thank you for the find @relepega will add to the docs!