Build a QR Code Chrome Extension with Svelte: A Step‑by‑Step Guide

This article walks through creating a Chrome QR‑code extension using Svelte, covering project setup with SvelteKit, manifest configuration for MV3, component development, reactive logic, download handling, custom adapter creation to satisfy CSP, build steps, debugging, and the final result.

ELab Team
ELab Team
ELab Team
Build a QR Code Chrome Extension with Svelte: A Step‑by‑Step Guide

Background

Origin Chrome 96 moved the QR‑code entry from the address bar to a submenu, which is inconvenient for H5 developers. The idea of a QR‑code Chrome Extension was born.

After evaluating several technologies (React, native, Vue, Svelte), Svelte was chosen for its simple syntax, small runtime, and reactivity.

Creation & Development

2.1 Project Creation

2.1.1 Project Initialization

Use npm init svelte@next qrcode-extension to create a SvelteKit project. The directory structure includes src (source files), lib (component library), routes (file‑based routing), app.html (entry template), static (static assets), and svelte.config.js (configuration).

Run npm run dev to start development.

2.1.2 Supporting Extension Development

The extension is a collection of front‑end resources defined by manifest.json. Important fields include: manifest_version: use Manifest V3. permissions: declare required Chrome Extension APIs (e.g., tabs, downloads). action with default_popup: the popup page. icons: extension icons.

{
  "name": "QrCode",
  "description": "A simple qrcode extension powered by Svelte",
  "version": "1.0",
  "manifest_version": 3,
  "permissions": ["tabs", "downloads"],
  "action": {"default_popup": "index.html"},
  "icons": {
    "16": "/images/qrcode-16.png",
    "32": "/images/qrcode-32.png",
    "48": "/images/qrcode-48.png",
    "128": "/images/qrcode-128.png"
  }
}

Add @types/chrome to devDependencies and reference it in tsconfig.json under compilerOptions.types.

2.2 Feature Development

2.2.1 Requirement Breakdown

Reference Chrome's built‑in QR‑code feature.

2.2.2 Link Display

Use the chrome.tabs API to obtain the current tab URL (declare tabs permission). The URL is displayed in an input field and passed to the QR‑code component.

async function getCurrentTab() {
  if (typeof chrome === 'undefined' || typeof chrome.tabs === 'undefined') {
    return { url: '' };
  }
  const queryOptions = { active: true, currentWindow: true };
  const [tab] = await chrome.tabs.query(queryOptions);
  return tab;
}

import { onMount } from 'svelte';
let url = '';
onMount(() => {
  (async () => {
    const tab = await getCurrentTab();
    url = tab.url || '';
  })();
});

2.2.3 Svelte Component

The QR‑code component resides in libs/QrCode.svelte and follows Svelte’s single‑file component format with <script>, HTML markup, and <style> sections.

<script lang="ts">
  import { toDataURL } from 'qrcode';
  import type { QRCodeToDataURLOptions } from 'qrcode';
  import { writable } from 'svelte/store';
  import { createEventDispatcher } from 'svelte';

  export let text;
  export let option: QRCodeToDataURLOptions = { type: 'image/png', margin: 2, width: 240 };

  const dataUrl = writable('');
  const dispatch = createEventDispatcher();

  $: if (text) {
    toDataURL(text, option).then(url => {
      dataUrl.set(url);
      dispatch('ready', { url });
    });
  } else {
    dataUrl.set('');
  }
</script>

<div class="qrcode">
  {#if $dataUrl}
    <img src={$dataUrl} alt="qrcode">
  {/if}
</div>

<style>
  .qrcode { width: 240px; height: 240px; border: 2px solid #e8eaed; border-radius: 10px; background: #f1f3f4; }
  img { width: 100%; height: auto; border-radius: 10px; }
</style>

2.2.4 QR‑code Download

Use chrome.downloads.download (declare downloads permission) to save the generated QR‑code image.

function downloadQrCode() {
  if (!dataUrl) return;
  chrome.downloads?.download({
    url: dataUrl,
    filename: getFilename(url),
  });
}
<button on:click={downloadQrCode}>Download</button>

3 Debugging & Build

3.1 Build

Svelte uses adapters to generate output for different platforms. The default @sveltejs/adapter-auto can be replaced with @sveltejs/adapter-static for static assets. The build output goes to the build directory.

Because MV3 disallows inline scripts, a custom adapter rewrites HTML files: it extracts inline scripts, writes them to separate .js files, and replaces the inline block with a <script src="..."></script> tag.

import type { Adapter } from '@sveltejs/kit';
import * as glob from 'fast-glob';
import { readFileSync, writeFileSync } from 'fs';
import { dirname, join } from 'path';

function uuid() { return Math.random().toString(36).slice(2); }
function handleHtml(htmlPath, scriptTag) {
  const html = readFileSync(htmlPath).toString();
  const matchReg = /<script\b[^>]*>([\s\S]*)<\/script>/gm;
  const result = matchReg.exec(html);
  return result && result[1]
    ? { html: html.replace(matchReg, scriptTag), script: result[1] }
    : null;
}
export function extensionAdapter({ dest }) {
  return {
    name: 'crx-adapter',
    async adapt({ utils }) {
      utils.rimraf(dest);
      utils.copy_static_files(dest);
      utils.copy_client_files(dest);
      utils.rimraf(join(dest, '_app'));
      await utils.prerender({ all: true, dest });
      const fileNames = await glob(join(dest, '**', '*.html'));
      for (const fileName of fileNames) {
        const dir = dirname(fileName);
        const scriptFileName = `start-${uuid()}.js`;
        const res = handleHtml(
          fileName,
          `<script type="module" src="/${scriptFileName}"></script>`
        );
        if (res) {
          writeFileSync(fileName, res.html);
          writeFileSync(join(dir, scriptFileName), res.script);
        }
      }
    }
  };
}

3.2 Debugging

After building, open chrome://extensions/, enable developer mode, and click “Load unpacked” to select the build directory. Re‑build and reload the extension when changes are made.

3.3 Result

The final extension shows a QR‑code for the current page when the toolbar button is clicked, matching Chrome’s native QR‑code feature but with a custom UI.

Conclusion

Developed a QR‑code Chrome Extension using Svelte.

Created a custom Svelte adapter to satisfy Chrome Extension CSP requirements.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendChrome ExtensionQR codeadapterSvelteMV3
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.