Building Chrome Extensions with Svelte: Complete Developer Guide

17 min read

Building Chrome Extensions with Svelte: Complete Developer Guide

Building Chrome Extensions with Svelte: Complete Developer Guide

Svelte has revolutionized how developers build web applications by shifting compilation from the browser to the build step. This same philosophy makes Svelte an exceptional choice for Chrome extension development. If you are looking to create fast, lightweight, and maintainable Chrome extensions, combining Svelte with Chrome’s extension APIs provides an incredibly powerful development experience.

In this comprehensive guide, we will explore how to build Chrome extensions using Svelte, covering everything from project setup to advanced patterns for svelte popup development and content script integration.


Why Use Svelte for Chrome Extensions?

Before diving into the technical details, it is essential to understand why Svelte is an excellent choice for Chrome extension development. The framework offers several compelling advantages that align perfectly with extension development requirements.

Blazing Fast Performance

Svelte compiles components to highly efficient vanilla JavaScript that updates the DOM directly without the overhead of a virtual DOM. This means your svelte chrome extension will have minimal memory footprint and lightning-fast interactions, which is crucial for extensions that need to remain lightweight and responsive.

Chrome extensions run in a constrained environment where performance directly impacts user experience. Unlike React or Vue, which ship with runtime libraries, Svelte’s compile-time approach produces smaller bundle sizes — a critical factor when Chrome imposes strict limits on extension package sizes.

Reactive Simplicity

Svelte’s reactive model is intuitive and requires less boilerplate code than other frameworks. For extension developers, this means faster development cycles and easier maintenance. State management in Svelte feels natural, and you will find yourself writing less code to achieve more functionality.

First-Class TypeScript Support

TypeScript integration with Svelte is excellent out of the box. Given that Chrome extension APIs are well-typed, using TypeScript with Svelte provides a seamless development experience with comprehensive type checking and excellent IDE support.


Setting Up Your Svelte Chrome Extension Project

The first step in building a svelte chrome extension is setting up your development environment. While you can manually configure everything, using a template specifically designed for Svelte extensions will save significant time and ensure best practices.

Using the SvelteKit Chrome Extension Template

The recommended approach is to use a well-maintained template that handles the complex configuration required for building extensions. Here is how to get started:

# Create a new project using the SvelteKit template
npm create svelte@latest my-svelte-extension
# Select "Skeleton project" and "TypeScript"

# Navigate to your project
cd my-svelte-extension

# Install dependencies
npm install

Configuring Vite for Chrome Extension Build

Your vite.config.ts needs specific settings to generate an extension-compatible build. The key is to output to a directory that can be loaded as an unpacked extension:

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [sveltekit()],
  build: {
    outDir: 'dist',
    target: 'esnext',
    minify: true
  },
  server: {
    port: 5173,
    strictPort: true
  }
});

Setting Up the Manifest File

Create a static/manifest.json file in your Svelte project. This is the core configuration that tells Chrome about your extension:

{
  "manifest_version": 3,
  "name": "My Svelte Extension",
  "version": "1.0.0",
  "description": "A powerful Chrome extension built with Svelte",
  "permissions": [
    "storage",
    "activeTab",
    "scripting"
  ],
  "action": {
    "default_popup": "/",
    "default_icon": {
      "16": "/icons/icon16.png",
      "48": "/icons/icon48.png",
      "128": "/icons/icon128.png"
    }
  },
  "background": {
    "service_worker": "/build/background.js",
    "type": "module"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["/build/content.js"],
      "css": ["/build/content.css"]
    }
  ]
}

Notice that the popup uses / — this tells SvelteKit to serve the popup from your app’s root, which is perfect for svelte popup development.


Building the Svelte Popup Extension Component

The popup is the most visible part of your extension, and Svelte makes it incredibly easy to create interactive, responsive popups. Let us build a complete svelte popup example that demonstrates common patterns.

Creating the Popup Layout

Create your main page component that will serve as the popup:

<script lang="ts">
  import { onMount } from 'svelte';
  import { chromeStorage } from '$lib/storage';
  import SettingsPanel from './SettingsPanel.svelte';
  import StatusDisplay from './StatusDisplay.svelte';

  let isEnabled = false;
  let settings = {
    theme: 'light',
    notifications: true,
    autoSave: false
  };
  let loading = true;

  onMount(async () => {
    // Load saved settings from Chrome storage
    const stored = await chromeStorage.get(['isEnabled', 'settings']);
    if (stored.isEnabled !== undefined) {
      isEnabled = stored.isEnabled;
    }
    if (stored.settings) {
      settings = stored.settings;
    }
    loading = false;
  });

  async function toggleExtension() {
    isEnabled = !isEnabled;
    await chromeStorage.set({ isEnabled });
    
    // Notify content script of state change
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    if (tab.id) {
      chrome.tabs.sendMessage(tab.id, { 
        action: 'toggle', 
        enabled: isEnabled 
      });
    }
  }

  async function saveSettings() {
    await chromeStorage.set({ settings });
  }
</script>

<main class="popup-container">
  <header>
    <h1>Extension Name</h1>
    <label class="toggle-switch">
      <input 
        type="checkbox" 
        checked={isEnabled} 
        on:change={toggleExtension}
      />
      <span class="slider"></span>
    </label>
  </header>

  {#if loading}
    <div class="loading">Loading...</div>
  {:else}
    <StatusDisplay {isEnabled} />
    
    <SettingsPanel 
      bind:settings 
      on:save={saveSettings}
    />
  {/if}
</main>

<style>
  .popup-container {
    width: 360px;
    padding: 16px;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  }

  header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
    padding-bottom: 12px;
    border-bottom: 1px solid #e0e0e0;
  }

  h1 {
    font-size: 18px;
    font-weight: 600;
    margin: 0;
  }

  .toggle-switch {
    position: relative;
    display: inline-block;
    width: 48px;
    height: 24px;
  }

  .toggle-switch input {
    opacity: 0;
    width: 0;
    height: 0;
  }

  .slider {
    position: absolute;
    cursor: pointer;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: #ccc;
    transition: 0.3s;
    border-radius: 24px;
  }

  .slider:before {
    position: absolute;
    content: "";
    height: 18px;
    width: 18px;
    left: 3px;
    bottom: 3px;
    background-color: white;
    transition: 0.3s;
    border-radius: 50%;
  }

  input:checked + .slider {
    background-color: #2196F3;
  }

  input:checked + .slider:before {
    transform: translateX(24px);
  }

  .loading {
    text-align: center;
    padding: 20px;
    color: #666;
  }
</style>

This example demonstrates several important patterns for svelte popup development:

  • Chrome Storage Integration: Reading and writing to Chrome’s storage API
  • Tab Communication: Sending messages to content scripts based on user interactions
  • Reactive State: Svelte’s reactive assignments automatically update the UI
  • TypeScript Support: Full type safety for extension-specific APIs

Implementing Svelte Content Script Extension Patterns

Content scripts run in the context of web pages and are where the magic happens for extensions that modify page behavior. Svelte can be used to create sophisticated content script experiences.

Setting Up Content Scripts

Configure your content script in the SvelteKit config. Since content scripts need to run in the page context, you will need to build them separately from your popup:

// svelte.config.js
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: 'index.html',
      precompress: false,
      strict: true
    }),
    // Allow serving extension files
    appDir: 'app'
  }
};

export default config;

Creating a Content Script Component

Build your content script as a Svelte component that mounts to the page:

// src/lib/content/main.ts
import ContentApp from './ContentApp.svelte';
import { mount } from 'svelte';

const app = mount(ContentApp, {
  target: document.body,
  props: {
    pageUrl: window.location.href
  }
});

// Clean up when extension is disabled or page changes
window.addEventListener('unload', () => {
  app.$destroy();
});

Handling Communication Between Popup and Content Scripts

The background service worker acts as a bridge between your svelte popup and content scripts:

// src/lib/background.ts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'toggle') {
    // Broadcast to all content scripts
    chrome.tabs.query({}, (tabs) => {
      tabs.forEach(tab => {
        if (tab.id) {
          chrome.tabs.sendMessage(tab.id, {
            action: 'stateChanged',
            enabled: message.enabled
          });
        }
      });
    });
    sendResponse({ success: true });
  }
  return true;
});

Best Practices for Svelte Chrome Extension Development

Now that you understand the fundamentals, let us explore best practices that will make your extension professional-quality and maintainable.

Use Svelte Stores for Extension State

Svelte stores provide an elegant solution for managing extension state across different contexts:

// src/lib/stores/extensionStore.ts
import { writable } from 'svelte/store';

export const extensionEnabled = writable(false);
export const settings = writable({
  theme: 'light',
  notifications: true,
  autoSave: false
});

export function initializeFromStorage() {
  chrome.storage.local.get(['isEnabled', 'settings'], (result) => {
    if (result.isEnabled !== undefined) {
      extensionEnabled.set(result.isEnabled);
    }
    if (result.settings) {
      settings.set(result.settings);
    }
  });
}

Optimize Bundle Size

Chrome extensions have a 128MB limit for unpacked extensions and 245MB for packed uploads. To keep your extension lightweight:

  • Use dynamic imports for features that are not immediately needed
  • Enable tree-shaking by using ES modules
  • Consider using Svelte’s compile-time optimizations
  • Remove development dependencies from production builds

Handle Extension Lifecycle Properly

Extensions can be installed, updated, or uninstalled. Ensure your extension handles these transitions gracefully:

// In your background service worker
chrome.runtime.onInstalled.addListener((details) => {
  if (details.reason === 'install') {
    // First time installation - set defaults
    chrome.storage.local.set({
      settings: { theme: 'light', notifications: true },
      isEnabled: false,
      installedAt: Date.now()
    });
  } else if (details.reason === 'update') {
    // Extension was updated - handle migrations
    console.log('Extension updated from', details.previousVersion);
  }
});

Test Across Multiple Contexts

Your extension runs in multiple contexts — popup, options page, content scripts, and background. Test each context thoroughly:

  • Popup interactions and state persistence
  • Content script injection on various websites
  • Background worker reliability
  • Communication between all components

Advanced Patterns and Common Pitfalls

As you become more comfortable with svelte chrome extension development, here are advanced patterns to explore and pitfalls to avoid.

Handling SPA Navigation in Content Scripts

Single-page applications (SPAs) change their URL without full page reloads, breaking traditional content script injection. Use the run_at document_idle option and listen for URL changes:

// content script
const urlObserver = new MutationObserver(() => {
  // Re-initialize your content script logic here
  initializeOnPage();
});

urlObserver.observe(document.body, {
  childList: true,
  subtree: true
});

Managing Cross-Origin Requests

Chrome extensions can make cross-origin requests, but you must declare permissions in the manifest:

{
  "permissions": [
    "https://api.example.com/*"
  ],
  "host_permissions": [
    "https://*.google.com/*"
  ]
}

Avoiding Memory Leaks

Content scripts can accumulate memory if not properly cleaned up. Always destroy Svelte components when they are no longer needed:

// Properly clean up
function cleanup() {
  if (app) {
    app.$destroy();
    app = null;
  }
  if (observer) {
    observer.disconnect();
  }
}

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    cleanup();
  }
});

Security Considerations for Svelte Extensions

When building Chrome extensions, security should be a top priority. Extensions have significant access to user data and browser behavior, making them attractive targets for attackers. Here are essential security practices for your svelte chrome extension.

Validate All Inputs

Never trust data from web pages or user input. Always validate and sanitize data in your content scripts:

function sanitizeInput(input: string): string {
  // Remove potentially dangerous characters
  return input.replace(/[<>'"]/g, '');
}

function validateUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    return parsed.protocol === 'https:' || parsed.protocol === 'http:';
  } catch {
    return false;
  }
}

Use Content Security Policy Wisely

Configure your CSP in the manifest to restrict what your extension can do:

{
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  }
}

Handle Sensitive Data Carefully

Never store sensitive data in local storage without encryption. Use Chrome’s encrypted storage API when handling passwords or tokens:

async function secureStore(key: string, value: string) {
  const encrypted = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv: crypto.getRandomValues(new Uint8Array(12)) },
    await getEncryptionKey(),
    new TextEncoder().encode(value)
  );
  await chrome.storage.session.set({ [key]: encrypted });
}

Testing Your Svelte Chrome Extension

Comprehensive testing is crucial for extension reliability. Unlike regular web applications, extensions must work correctly across multiple contexts and handle various edge cases.

Unit Testing Svelte Components

Use Vitest and Testing Library for Svelte to test your components:

import { render, fireEvent } from '@testing-library/svelte';
import ToggleSwitch from './ToggleSwitch.svelte';

describe('ToggleSwitch', () => {
  it('toggles state on click', async () => {
    const { component } = render(ToggleSwitch, { props: { checked: false }});
    const input = component.querySelector('input') as HTMLInputElement;
    
    await fireEvent.click(input);
    
    expect(input.checked).toBe(true);
  });
});

Integration Testing with Chrome APIs

Use a test environment that can mock Chrome APIs:

import { setupChromeMock } from './chrome-mock';

beforeEach(() => {
  setupChromeMock();
});

test('saves settings to chrome storage', async () => {
  const { saveSettings } = await import('./settings');
  
  await saveSettings({ theme: 'dark' });
  
  expect(chrome.storage.local.set).toHaveBeenCalledWith({
    settings: { theme: 'dark' }
  });
});

Manual Testing Checklist

Before publishing, manually test these scenarios:

  • Extension works in Chrome, Edge, and Brave
  • Popup opens and closes smoothly
  • Content scripts inject correctly on popular websites
  • Settings persist across browser restarts
  • Extension does not conflict with other installed extensions

Deployment and Publishing

Once your svelte chrome extension is ready, you need to package and publish it to the Chrome Web Store.

Building for Production

Configure your build for production release:

npm run build

This generates the final extension files in your output directory. Verify the build output contains all necessary files, including the manifest, icons, and any additional resources.

Publishing to Chrome Web Store

  1. Zip your extension’s build directory
  2. Open the Chrome Developer Dashboard
  3. Create a new item and upload your zip file
  4. Complete the store listing with screenshots and descriptions
  5. Submit for review

Maintaining Your Extension

After publishing, actively maintain your extension:

  • Monitor user reviews and feedback
  • Update regularly for Chrome browser updates
  • Respond to security vulnerabilities promptly
  • Add new features based on user requests

Conclusion

Building Chrome extensions with Svelte provides an exceptional development experience that combines Svelte’s reactive simplicity with Chrome’s powerful extension APIs. Throughout this guide, we have covered essential topics including project setup, svelte popup development, content script integration, and best practices for production-ready extensions.

The combination of Svelte’s small bundle sizes, reactive performance, and clean component model makes it an ideal framework for extension development. Whether you are building a simple utility or a complex enterprise tool, Svelte provides the foundation for creating extensions that are fast, maintainable, and delightful to use.

As you continue your journey with svelte chrome extension development, remember to test thoroughly across different Chrome contexts, optimize for performance from the start, and leverage Svelte’s powerful features to create truly exceptional browser extensions.


Additional Resources

To further enhance your svelte chrome extension development skills, explore these related topics:

Start building your Svelte-powered Chrome extension today and experience the future of browser extension development!

No previous article
No next article