Building Cross-Platform Browser Extensions
With Svelte
Why?


const url = "https://securepubads.g.doubleclick.net/gampad/ads?...";
const searchParams = new URL(url).searchParams;
const kvPairs = Object.fromEntries(searchParams.entries());
console.log({ kvPairs })
{
"pvsid": "2681769169518727",
"correlator": "4500362199754403",
"eid": "31091864,83321073,31086809,31091827",
"output": "ldjh",
"gdfp_req": "1",
"vrg": "202504150101",
"ptt": "17",
"impl": "fif",
"cust_params": "pt=art&nlayout=default&mvt=ads-explainer-widget%3Avariant%2Cads-pg-article%3Avariant%2Cadsmvt2%3Acontrol%2Carticle-bullet-point-summary-v2%3Abullet-points-on%2Ccomments-discussion-question-2%3Acontrol%2Cproarticlecontextwidgetabtest%3Avariant-2%2Cproask%3Aon&rootid=cm9skff5f00013b6s80o6a8hl&guid=63c4feab-5de0-4eff-8742-f8a07f8e19d7&device_spoor_id=bf2771bb-142b-444b-aa17-9ad8cf18016f&slv=int&loggedIn=true&gender=M&05=map&06=itc&07=ts&auuid=7a65a578-044a-44bf-802e-84379a77f806&title=Donald%20Trump%20has%20given%20BYD%20the%20edge%20over%20Tesla&ad=HT01%2CHT03%2CHT05%2CHT06%2CHT07%2CHT14%2CS_11831%2CS_12984%2CS_13314%2CS_13449%2CS_13748%2CS_16042%2CS_16450%2CS_16717%2CS_17165%2CS_17168%2CS_18190%2CS_19214%2CS_24494%2CS_24575%2CS_24599%2CS_2542%2CS_2547%2CS_8379%2CS_9533%2Cbs02%2Cbs50%2Ccs01%2Cft43%2Cft69%2Cft70%2Cft72%2Cr03%2Cr03a%2Cr03b%2Cr03c%2Cr03g%2Cr03l%2Cr03m%2Cr05%2Cr05b%2Cr05j%2Cr07c%2Cr07r%2Ct01&ca=car%20industry%2Celectric%20vehicle%2Cluxury%2Cpolitics&topic=Trump%20tariffs%2CUS%20companies%2CUS-China%20trade%20dispute%2CAsia-Pacific%20companies%2CElectric%20vehicles%2CChinese%20business%20%26%20finance%2CUS%20trade%2CGlobal%20trade%2CGlobal%20Economy%2CAsia-Pacific%20economy%2CTrade%20disputes%2CUS%20economy%2CWorld%2CChinese%20economy%2CCompanies%2CIndustrials%2CChinese%20trade%2CAutomobiles%2CEconomy%2CEmerging%20markets&secbrand=Business%20Insight&mvt2=control&cc=y&ts=20250422145336&res=extra&url=https%3A%2F%2Fwww.ft.com%2Fcontent%2F7a65a578-044a-44bf-802e-84379a77f806&permutive=102213%2C103258%2C117171%2C122045%2C158581%2C158583%2C158585%2C158589%2C158591%2C16780%2C16781%2C16782%2C16842%2C16843%2C17068%2C17075%2C17117%2C17124%2C17128%2C17138%2C171478%2C17173%2C17182%2C17183%2C17186%2C18238%2C18245%2C18248%2C18260%2C18263%2C18268%2C18310%2C18312%2C18314%2C18323%2C18356%2C18445%2C18731%2C18732%2C188019%2C188465%2C195959%2C197394%2C197395%2C197397%2C19902%2C20008%2C20283%2C203239%2C20431%2C20469%2C207153%2C207935%2C20833%2C209455%2C214858%2C21535%2C21541%2C217968%2C218494%2C219014%2C219015%2C219772%2C220220%2C220935%2C220949%2C23027%2C23937%2C26440%2C55914%2C55934%2C56089%2C56090%2C66495%2C73402%2C74657%2C74659%2C74661%2C74664%2C74665%2C86173%2C87696%2C87738%2C87739%2C87740%2C87741%2C87742%2C87744%2C88113%2C92621%2C92622%2C99536%2Cadv%2Cbrxc%2Crts&permutive-id=e78042f1-83ed-4378-883f-f9d95aee4983&prmtvsdk=web&puid=e78042f1-83ed-4378-883f-f9d95aee4983&prmtvvid=a87e2d27-9457-4017-985a-f9efe43d0a3d&prmtvsid=2d4867c9-5e0d-4984-82db-47f34b3e3799&prmtvwid=e1c3fd73-dd41-4abd-b80b-4278d52bf7aa&fr=false&adt=veryLow&alc=veryLow&dlm=veryLow&drg=veryLow&hat=veryLow&off=veryLow&vio=veryLow&ias-kw=IAS_1516717_PG%2CIAS_1517168_PG%2CIAS_1517165_PG%2CIAS_1516042_PG%2CIAS_1516450_PG%2CIAS_3010239_PG%2CIAS_3007857_PG%2CIAS_3010449_PG%2CIAS_3008527_PG%2CIAS_1509533_PG%2CIAS_1510285_PG%2CIAS_1500942_PG%2CIAS_1513314_PG%2CIAS_1513748_PG%2CIAS_1513449_PG%2CIAS_3005022_PG%2CIAS_1500902_PG%2CIAS_1511831_PG%2CIAS_3006643_PG%2CIAS_1512984_PG%2CIAS_1519214_PG%2CIAS_1518190_PG%2CIAS_3008351_PG&view-id=",
"gdpr_consent": "CQN2PcAQN2PcAAGABCENBfFgAPPAAELAAB6YH9wGAAGAAVABkAEeAJoAnABdADcAH4AQIA7gB7AEIAIsATsArIBaQC6gGmAROAvMBkgDfgH1AP7A36ASAD8AI4AfgB3AEIAIsAvMBtADfgBQkAUACoAMgB3AD2AZIOgEAAVABkAF0AdwA9gETgMkAf2QgBgDuAROA_slABAZIUgDgAVABkAH4AiwCJwGSAP7.YAAACFgAAAAA",
"gdpr": "1",
"addtl_consent": "1~196.2072",
"us_privacy": "1---",
"gpp_sid": "-1",
"iu_parts": "5887,ft.com,companies,us.and.canada",
"enc_prev_ius": "/0/1/2/3",
"prev_iu_szs": "1x1",
"ifi": "3",
"dids": "miniBillboard-gpt",
"adfs": "1334086546",
"sfv": "1-0-41",
"eri": "4",
"sc": "1",
"cookie": "ID=c1f1376e4506574f:T=1744816867:RT=1745330016:S=ALNI_MZSK9sjZkrzAXIcnnGT-rSsqnm_Vw",
"abxe": "1",
"dt": "1745330026828",
"lmt": "1745330026",
"adxs": "-12245933",
"adys": "-12245933",
"biw": "1446",
"bih": "597",
"scr_x": "0",
"scr_y": "0",
"btvi": "-1",
"ucis": "3",
"oid": "2",
"u_his": "7",
"u_h": "1117",
"u_w": "1728",
"u_ah": "1079",
"u_aw": "1728",
"u_cd": "30",
"u_sd": "2",
"u_tz": "60",
"dmc": "8",
"bc": "31",
"nvt": "3",
"uach": "WyJtYWNPUyIsIjE1LjQuMSIsImFybSIsIiIsIjEzNC4wLjY5OTguMjA3IixudWxsLDAsbnVsbCwiNjQiLFtbIkNocm9taXVtIiwiMTM0LjAuNjk5OC4yMDciXSxbIk5vdDpBLUJyYW5kIiwiMjQuMC4wLjAiXSxbIkdvb2dsZSBDaHJvbWUiLCIxMzQuMC42OTk4LjIwNyJdXSwwXQ..",
"uas": "1",
"url": "https://www.ft.com/content/7a65a578-044a-44bf-802e-84379a77f806",
"ref": "https://www.ft.com/content/b4c868aa-28e7-4f35-8357-88e59a4fa9c2",
"vis": "1",
"psz": "0x0",
"msz": "0x0",
"fws": "128",
"ohw": "0",
"psts": "AOrYGslr7db69t4c-MKr7kBkANY2CFjg3Q4RlRawsMCPezFh8rtHnZIME_UXWHHphJf0kPRWY6olj97cSvDEir8d4SYpe0_iWMT8aEcQ0zRNrQ,AOrYGslN2gGiIH-zOM_qk9IFLC4vZ56xUNNiyotldVx4fflW9b8sz8cHeX7n4xS1EZ1tYk38DXnSly1dtVTtq8c_eXA",
"nt": "1",
"psd": "WzI3LFtdLG51bGwsM10.",
"dlt": "1745330015695",
"idt": "465",
"ppid": "bf2771bb-142b-444b-aa17-9ad8cf18016f",
"prev_scp": "pos=top&id=2fe208bd-1f81-11f0-8113-7ec3216436b3",
"adks": "3361870855",
"frm": "20",
"eo_id_str": "ID=f9ed9fb12ffe8b57:T=1744816867:RT=1745330016:S=AA-AfjZaxjpAZumuzXJbxfLP-zvm",
"td": "1",
"tan": "bfda6740-2e58-48b5-839f-08ee83e200e0",
"tdf": "2"
}
const url = "https://securepubads.g.doubleclick.net/gampad/ads?...";
const searchParams = new URL(url).searchParams;
const kvPairs = Object.fromEntries(searchParams.entries());
const custParamsRaw = new URLSearchParams(kvPairs.cust_params);
const custParams = Object.fromEntries(custParamsRaw.entries());
console.log({ custParams })
{
"pt": "art",
"nlayout": "default",
"mvt": "ads-explainer-widget:variant,ads-pg-article:variant,adsmvt2:control,article-bullet-point-summary-v2:bullet-points-on,comments-discussion-question-2:control,proarticlecontextwidgetabtest:variant-2,proask:on",
"rootid": "cm9slla3200003b7i3wzpq83z",
"guid": "63c4feab-5de0-4eff-8742-f8a07f8e19d7",
"device_spoor_id": "bf2771bb-142b-444b-aa17-9ad8cf18016f",
"slv": "int",
"loggedIn": "true",
"gender": "M",
"05": "map",
"06": "itc",
"07": "ts",
"adt": "veryLow",
"alc": "veryLow",
"dlm": "veryLow",
"drg": "veryLow",
"hat": "veryLow",
"off": "veryLow",
"vio": "medium",
"auuid": "77a70ee0-6646-4d2c-97ba-d51386605f63",
"title": "Ukraine accuses Russia of fresh attacks despite Putin ceasefire",
"ad": "HT11,S_11831,S_12565,S_12984,S_13314,S_13449,S_13748,S_15033,S_15973,S_16011,S_16042,S_16417,S_16420,S_16450,S_16717,S_17165,S_17168,S_18190,S_19214,S_24494,S_24575,S_24599,S_7033,S_9089,S_9100,S_9533,bs01,bs02,bs03,bs24,bs28,bs50,e2,ft57,ft70,r03,r03c,r03g,r03m,r05c,r05k,t01",
"ca": "foreign policy,unrest and war",
"topic": "Russian politics,War in Ukraine,Politics,World,Emerging markets",
"mvt2": "control",
"cc": "y",
"ts": "20250422152609",
"res": "extra",
"url": "https://www.ft.com/content/77a70ee0-6646-4d2c-97ba-d51386605f63",
"permutive": "102213,103258,117171,122045,158581,158583,158585,158589,158591,16780,16781,16782,16842,16843,17068,17075,17117,17124,17128,17138,171478,17173,17182,17183,17186,18238,18245,18248,18260,18263,18268,18310,18312,18314,18323,18356,18445,18731,18732,188019,188465,195959,197394,197395,197397,19902,20008,20283,203239,20431,20469,207153,207935,20833,209455,214858,21535,21541,217968,218494,219014,219015,219772,220220,220935,220949,23027,23937,26440,55914,55934,56089,56090,66495,73402,74657,74659,74661,74664,74665,86173,87696,87738,87739,87740,87741,87742,87744,88113,92621,92622,99536,adv,brxc,rts",
"permutive-id": "e78042f1-83ed-4378-883f-f9d95aee4983",
"prmtvsdk": "web",
"puid": "e78042f1-83ed-4378-883f-f9d95aee4983",
"prmtvvid": "0ca431d9-8b43-43e5-9dc3-92da5f2444ba",
"prmtvsid": "2d4867c9-5e0d-4984-82db-47f34b3e3799",
"prmtvwid": "e1c3fd73-dd41-4abd-b80b-4278d52bf7aa",
"fr": "false",
"ias-kw": "IAS_1516420_PG,IAS_1503483_PG,IAS_1516011_PG,IAS_1517165_PG,IAS_1516042_PG,IAS_3010239_PG,IAS_1509533_PG,IAS_1513449_PG,IAS_1500903_PG,IAS_1511831_PG,IAS_1508970_PG,IAS_1518190_PG,IAS_1519214_PG,IAS_1516717_PG,IAS_1512565_PG,IAS_1517168_PG,IAS_1507080_PG,IAS_1511377_PG,IAS_1516450_PG,IAS_1516417_PG,IAS_1503195_PG,IAS_1509981_PG,IAS_1515973_PG,IAS_1500942_PG,IAS_1513314_PG,IAS_1510285_PG,IAS_1513748_PG,IAS_1503224_PG,IAS_1503222_PG,IAS_1500902_PG,IAS_1507653_PG,IAS_1512984_PG,IAS_1509089_PG,IAS_1509100_PG"
}



https://developer.chrome.com/docs/extensions

https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions
https://developer.mozilla.org/en-US/docs/Mozilla/
Add-ons/WebExtensions/Chrome_incompatibilities


https://github.com/mdn/webextensions-examples
https://github.com/GoogleChrome/chrome-extensions-samples

What are content scripts?
https://youtu.be/ezhJezGX5ak

😤
// manifest.json
{
"name": "Hello Extensions",
"description": "Base Level Extension",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "hello.html",
"default_icon": "hello_extensions.png"
}
}

Tooling
https://www.plasmo.com/
https://crxjs.dev/vite-plugin
https://vite-plugin-web-extension.aklinker1.io
https://wxt.dev
https://wxt.dev/examples.html
https://wxt.dev/guide/resources/compare.html

├── .vscode
│ └── extensions.json
├── src
│ ├── assets
│ │ └── svelte.svg
│ ├── entrypoints
│ │ ├── popup
│ │ │ ├── app.css
│ │ │ ├── App.svelte
│ │ │ ├── index.html
│ │ │ └── main.ts
│ │ ├── background.ts
│ │ └── content.ts
│ └── lib
│ └── Counter.svelte
├── .gitignore
├── package.json
├── README.md
├── tsconfig.json
├── wxt-env.d.ts
└── wxt.config.ts
// manifest.json
{
"name": "Hello Extensions",
"description": "Base Level Extension",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "hello.html",
"default_icon": "hello_extensions.png"
}
}
import { defineConfig } from 'wxt';
export default defineConfig({
srcDir: 'src',
modules: ['@wxt-dev/module-svelte'],
});
// wxt.config.ts
export default defineConfig({
srcDir: 'src',
modules: ['@wxt-dev/module-svelte'],
svelte: {
vite: {
inspector: import.meta.env.NODE_ENV === "development",
},
},
});
npm i -D @sveltejs/vite-plugin-svelte-inspector



🫙
DEV TOOLS
PAGE
📄
📄
📄
POP-UP
// injected.ts
function onSomePageEvent() {
document.dispatchEvent(
new CustomEvent("myExtension:somePageEvent", {
detail: { ... }
}),
);
}
// content.ts
export default defineContentScript({
async main(ctx) {
storageA = storage.defineItem("session:storageA", {fallback: {}});
ctx.addEventListener(document,
"myExtension:somePageEvent",
async ({ detail }) => {
await Promise.all([
storageA.setValue(detail.propA),
storageB.setValue(detail.propB),
]);
});
},
});
<script lang="ts">
const myState = $state<MyState>({ ... })
storageA.getValue().then(updateMyState);
storageA.watch(updateMyState);
</script>

https://github.com/oliverturner
🦋 oliverturner.cloud

Building Cross-Platform Browser Extensions With Svelte
By Oliver Turner
Building Cross-Platform Browser Extensions With Svelte
- 73