[{"data":1,"prerenderedAt":1300},["ShallowReactive",2],{"writing-\u002Fwriting\u002Fbrowser-extension-messaging":3},{"id":4,"title":5,"author":6,"body":7,"date":1285,"description":1286,"extension":1287,"meta":1288,"navigation":318,"path":1290,"seo":1291,"stem":1292,"tags":1293,"__hash__":1299},"writing\u002Fwriting\u002Fbrowser-extension-messaging.md","Understanding Browser Extension Messaging",null,{"type":8,"value":9,"toc":1275},"minimark",[10,14,19,23,26,30,38,45,49,54,57,178,183,199,482,486,497,658,662,673,802,806,817,821,830,953,960,1096,1103,1214,1218,1238,1241,1245,1271],[11,12,5],"h1",{"id":13},"understanding-browser-extension-messaging",[15,16,18],"h2",{"id":17},"why-im-writing-this","Why I'm Writing This",[20,21,22],"p",{},"While building a Chrome extension for automated documentation generation (a story for another day), I found myself drowning in a sea of browser extension messaging concepts. Background scripts, content scripts, popup windows, offscreen documents – each piece seemed simple in isolation, but orchestrating communication between them felt like conducting an orchestra where every musician was in a different room.",[20,24,25],{},"After many late nights of debugging and several \"aha\" moments, I've developed a mental model that I wish I had when starting.",[15,27,29],{"id":28},"the-key-mental-model","The Key Mental Model",[20,31,32,33,37],{},"Think of a browser extension as a distributed system running in a single browser. Each component (background worker, popup, content script) is like a microservice with its own lifecycle and constraints. The key to mastery? Understanding not just how they communicate, but ",[34,35,36],"em",{},"why"," they're separated in the first place.",[20,39,40],{},[41,42],"img",{"alt":43,"src":44},"Extension Components Diagram","\u002Fimages\u002Fcontent\u002Fbrowser-architecture.webp",[15,46,48],{"id":47},"deep-dive-into-extension-messaging","Deep Dive into Extension Messaging",[50,51,53],"h3",{"id":52},"_1-the-players-in-our-distributed-system","1. The Players in Our Distributed System",[20,55,56],{},"Let's break down each component and its role:",[58,59,64],"pre",{"className":60,"code":61,"language":62,"meta":63,"style":63},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F Example message type definition\ninterface Message {\n  target: \"background\" | \"content-script\" | \"popup\" | \"offscreen\";\n  action: string;\n  data?: unknown;\n}\n","typescript","",[65,66,67,76,91,144,158,172],"code",{"__ignoreMap":63},[68,69,72],"span",{"class":70,"line":71},"line",1,[68,73,75],{"class":74},"sutJx","\u002F\u002F Example message type definition\n",[68,77,79,83,87],{"class":70,"line":78},2,[68,80,82],{"class":81},"sbsja","interface",[68,84,86],{"class":85},"sbgvK"," Message",[68,88,90],{"class":89},"sP7_E"," {\n",[68,92,94,98,102,106,110,113,116,118,121,123,125,127,130,132,134,136,139,141],{"class":70,"line":93},3,[68,95,97],{"class":96},"sucvu","  target",[68,99,101],{"class":100},"smGrS",":",[68,103,105],{"class":104},"sjJ54"," \"",[68,107,109],{"class":108},"s_sjI","background",[68,111,112],{"class":104},"\"",[68,114,115],{"class":100}," |",[68,117,105],{"class":104},[68,119,120],{"class":108},"content-script",[68,122,112],{"class":104},[68,124,115],{"class":100},[68,126,105],{"class":104},[68,128,129],{"class":108},"popup",[68,131,112],{"class":104},[68,133,115],{"class":100},[68,135,105],{"class":104},[68,137,138],{"class":108},"offscreen",[68,140,112],{"class":104},[68,142,143],{"class":89},";\n",[68,145,147,150,152,156],{"class":70,"line":146},4,[68,148,149],{"class":96},"  action",[68,151,101],{"class":100},[68,153,155],{"class":154},"sZMiF"," string",[68,157,143],{"class":89},[68,159,161,164,167,170],{"class":70,"line":160},5,[68,162,163],{"class":96},"  data",[68,165,166],{"class":100},"?:",[68,168,169],{"class":154}," unknown",[68,171,143],{"class":89},[68,173,175],{"class":70,"line":174},6,[68,176,177],{"class":89},"}\n",[179,180,182],"h4",{"id":181},"background-service-worker","Background Service Worker",[184,185,186,190,193,196],"ul",{},[187,188,189],"li",{},"The orchestrator",[187,191,192],{},"Always running (but can be inactive)",[187,194,195],{},"Can't access DOM",[187,197,198],{},"Handles long-running tasks",[58,200,202],{"className":60,"code":201,"language":62,"meta":63,"style":63},"\u002F\u002F From my actual implementation\nexport default defineBackground(() => {\n  browser.runtime.onMessage.addListener((message, sender, sendResponse) => {\n    if (message.target !== \"background\") return;\n\n    const handleMessage = async () => {\n      switch (message.action) {\n        case \"start\":\n          \u002F\u002F Handle start action\n          break;\n        case \"stop\":\n          \u002F\u002F Handle stop action\n          break;\n      }\n    };\n\n    handleMessage();\n    return true; \u002F\u002F Important for async responses!\n  });\n});\n",[65,203,204,209,234,282,314,320,342,362,378,384,392,406,412,419,425,431,436,446,462,472],{"__ignoreMap":63},[68,205,206],{"class":70,"line":71},[68,207,208],{"class":74},"\u002F\u002F From my actual implementation\n",[68,210,211,215,218,222,226,229,232],{"class":70,"line":78},[68,212,214],{"class":213},"sVHd0","export",[68,216,217],{"class":213}," default",[68,219,221],{"class":220},"sGLFI"," defineBackground",[68,223,225],{"class":224},"su5hD","(",[68,227,228],{"class":89},"()",[68,230,231],{"class":81}," =>",[68,233,90],{"class":89},[68,235,236,239,242,245,247,250,252,255,258,260,264,267,270,272,275,278,280],{"class":70,"line":93},[68,237,238],{"class":224},"  browser",[68,240,241],{"class":89},".",[68,243,244],{"class":224},"runtime",[68,246,241],{"class":89},[68,248,249],{"class":224},"onMessage",[68,251,241],{"class":89},[68,253,254],{"class":220},"addListener",[68,256,225],{"class":257},"skxfh",[68,259,225],{"class":89},[68,261,263],{"class":262},"s99_P","message",[68,265,266],{"class":89},",",[68,268,269],{"class":262}," sender",[68,271,266],{"class":89},[68,273,274],{"class":262}," sendResponse",[68,276,277],{"class":89},")",[68,279,231],{"class":81},[68,281,90],{"class":89},[68,283,284,287,290,292,294,297,300,302,304,306,309,312],{"class":70,"line":146},[68,285,286],{"class":213},"    if",[68,288,289],{"class":257}," (",[68,291,263],{"class":224},[68,293,241],{"class":89},[68,295,296],{"class":224},"target",[68,298,299],{"class":100}," !==",[68,301,105],{"class":104},[68,303,109],{"class":108},[68,305,112],{"class":104},[68,307,308],{"class":257},") ",[68,310,311],{"class":213},"return",[68,313,143],{"class":89},[68,315,316],{"class":70,"line":160},[68,317,319],{"emptyLinePlaceholder":318},true,"\n",[68,321,322,325,329,332,335,338,340],{"class":70,"line":174},[68,323,324],{"class":81},"    const",[68,326,328],{"class":327},"sfCm-"," handleMessage",[68,330,331],{"class":100}," =",[68,333,334],{"class":81}," async",[68,336,337],{"class":89}," ()",[68,339,231],{"class":81},[68,341,90],{"class":89},[68,343,345,348,350,352,354,357,359],{"class":70,"line":344},7,[68,346,347],{"class":213},"      switch",[68,349,289],{"class":257},[68,351,263],{"class":224},[68,353,241],{"class":89},[68,355,356],{"class":224},"action",[68,358,308],{"class":257},[68,360,361],{"class":89},"{\n",[68,363,365,368,370,373,375],{"class":70,"line":364},8,[68,366,367],{"class":213},"        case",[68,369,105],{"class":104},[68,371,372],{"class":108},"start",[68,374,112],{"class":104},[68,376,377],{"class":89},":\n",[68,379,381],{"class":70,"line":380},9,[68,382,383],{"class":74},"          \u002F\u002F Handle start action\n",[68,385,387,390],{"class":70,"line":386},10,[68,388,389],{"class":213},"          break",[68,391,143],{"class":89},[68,393,395,397,399,402,404],{"class":70,"line":394},11,[68,396,367],{"class":213},[68,398,105],{"class":104},[68,400,401],{"class":108},"stop",[68,403,112],{"class":104},[68,405,377],{"class":89},[68,407,409],{"class":70,"line":408},12,[68,410,411],{"class":74},"          \u002F\u002F Handle stop action\n",[68,413,415,417],{"class":70,"line":414},13,[68,416,389],{"class":213},[68,418,143],{"class":89},[68,420,422],{"class":70,"line":421},14,[68,423,424],{"class":89},"      }\n",[68,426,428],{"class":70,"line":427},15,[68,429,430],{"class":89},"    };\n",[68,432,434],{"class":70,"line":433},16,[68,435,319],{"emptyLinePlaceholder":318},[68,437,439,442,444],{"class":70,"line":438},17,[68,440,441],{"class":220},"    handleMessage",[68,443,228],{"class":257},[68,445,143],{"class":89},[68,447,449,452,456,459],{"class":70,"line":448},18,[68,450,451],{"class":213},"    return",[68,453,455],{"class":454},"syTEX"," true",[68,457,458],{"class":89},";",[68,460,461],{"class":74}," \u002F\u002F Important for async responses!\n",[68,463,465,468,470],{"class":70,"line":464},19,[68,466,467],{"class":89},"  }",[68,469,277],{"class":257},[68,471,143],{"class":89},[68,473,475,478,480],{"class":70,"line":474},20,[68,476,477],{"class":89},"}",[68,479,277],{"class":224},[68,481,143],{"class":89},[179,483,485],{"id":484},"content-scripts","Content Scripts",[184,487,488,491,494],{},[187,489,490],{},"Your \"eyes and ears\" in the webpage",[187,492,493],{},"Can access DOM",[187,495,496],{},"Limited access to extension APIs",[58,498,500],{"className":60,"code":499,"language":62,"meta":63,"style":63},"\u002F\u002F Content script message handling\nbrowser.runtime.onMessage.addListener(async (message) => {\n  if (message.target !== \"content-script\") return;\n\n  switch (message.action) {\n    case \"track-dom\":\n      startDomTracking();\n      break;\n    case \"stop-tracking\":\n      stopDomTracking();\n      break;\n  }\n});\n",[65,501,502,507,539,566,570,587,601,610,617,630,639,645,650],{"__ignoreMap":63},[68,503,504],{"class":70,"line":71},[68,505,506],{"class":74},"\u002F\u002F Content script message handling\n",[68,508,509,512,514,516,518,520,522,524,526,529,531,533,535,537],{"class":70,"line":78},[68,510,511],{"class":224},"browser",[68,513,241],{"class":89},[68,515,244],{"class":224},[68,517,241],{"class":89},[68,519,249],{"class":224},[68,521,241],{"class":89},[68,523,254],{"class":220},[68,525,225],{"class":224},[68,527,528],{"class":81},"async",[68,530,289],{"class":89},[68,532,263],{"class":262},[68,534,277],{"class":89},[68,536,231],{"class":81},[68,538,90],{"class":89},[68,540,541,544,546,548,550,552,554,556,558,560,562,564],{"class":70,"line":93},[68,542,543],{"class":213},"  if",[68,545,289],{"class":257},[68,547,263],{"class":224},[68,549,241],{"class":89},[68,551,296],{"class":224},[68,553,299],{"class":100},[68,555,105],{"class":104},[68,557,120],{"class":108},[68,559,112],{"class":104},[68,561,308],{"class":257},[68,563,311],{"class":213},[68,565,143],{"class":89},[68,567,568],{"class":70,"line":146},[68,569,319],{"emptyLinePlaceholder":318},[68,571,572,575,577,579,581,583,585],{"class":70,"line":160},[68,573,574],{"class":213},"  switch",[68,576,289],{"class":257},[68,578,263],{"class":224},[68,580,241],{"class":89},[68,582,356],{"class":224},[68,584,308],{"class":257},[68,586,361],{"class":89},[68,588,589,592,594,597,599],{"class":70,"line":174},[68,590,591],{"class":213},"    case",[68,593,105],{"class":104},[68,595,596],{"class":108},"track-dom",[68,598,112],{"class":104},[68,600,377],{"class":89},[68,602,603,606,608],{"class":70,"line":344},[68,604,605],{"class":220},"      startDomTracking",[68,607,228],{"class":257},[68,609,143],{"class":89},[68,611,612,615],{"class":70,"line":364},[68,613,614],{"class":213},"      break",[68,616,143],{"class":89},[68,618,619,621,623,626,628],{"class":70,"line":380},[68,620,591],{"class":213},[68,622,105],{"class":104},[68,624,625],{"class":108},"stop-tracking",[68,627,112],{"class":104},[68,629,377],{"class":89},[68,631,632,635,637],{"class":70,"line":386},[68,633,634],{"class":220},"      stopDomTracking",[68,636,228],{"class":257},[68,638,143],{"class":89},[68,640,641,643],{"class":70,"line":394},[68,642,614],{"class":213},[68,644,143],{"class":89},[68,646,647],{"class":70,"line":408},[68,648,649],{"class":89},"  }\n",[68,651,652,654,656],{"class":70,"line":414},[68,653,477],{"class":89},[68,655,277],{"class":224},[68,657,143],{"class":89},[179,659,661],{"id":660},"popup-ui","Popup UI",[184,663,664,667,670],{},[187,665,666],{},"Temporary lifecycle",[187,668,669],{},"Rich UI capabilities",[187,671,672],{},"Dies when closed",[58,674,676],{"className":60,"code":675,"language":62,"meta":63,"style":63},"\u002F\u002F Popup component\nfunction PopupApp() {\n  const sendMessage = async () => {\n    await browser.runtime.sendMessage({\n      target: \"background\",\n      action: \"start\",\n      data: {\n        \u002F* configuration *\u002F\n      },\n    });\n  };\n}\n",[65,677,678,683,695,713,734,750,765,774,779,784,793,798],{"__ignoreMap":63},[68,679,680],{"class":70,"line":71},[68,681,682],{"class":74},"\u002F\u002F Popup component\n",[68,684,685,688,691,693],{"class":70,"line":78},[68,686,687],{"class":81},"function",[68,689,690],{"class":220}," PopupApp",[68,692,228],{"class":89},[68,694,90],{"class":89},[68,696,697,700,703,705,707,709,711],{"class":70,"line":93},[68,698,699],{"class":81},"  const",[68,701,702],{"class":327}," sendMessage",[68,704,331],{"class":100},[68,706,334],{"class":81},[68,708,337],{"class":89},[68,710,231],{"class":81},[68,712,90],{"class":89},[68,714,715,718,721,723,725,727,730,732],{"class":70,"line":146},[68,716,717],{"class":213},"    await",[68,719,720],{"class":224}," browser",[68,722,241],{"class":89},[68,724,244],{"class":224},[68,726,241],{"class":89},[68,728,729],{"class":220},"sendMessage",[68,731,225],{"class":257},[68,733,361],{"class":89},[68,735,736,739,741,743,745,747],{"class":70,"line":160},[68,737,738],{"class":257},"      target",[68,740,101],{"class":89},[68,742,105],{"class":104},[68,744,109],{"class":108},[68,746,112],{"class":104},[68,748,749],{"class":89},",\n",[68,751,752,755,757,759,761,763],{"class":70,"line":174},[68,753,754],{"class":257},"      action",[68,756,101],{"class":89},[68,758,105],{"class":104},[68,760,372],{"class":108},[68,762,112],{"class":104},[68,764,749],{"class":89},[68,766,767,770,772],{"class":70,"line":344},[68,768,769],{"class":257},"      data",[68,771,101],{"class":89},[68,773,90],{"class":89},[68,775,776],{"class":70,"line":364},[68,777,778],{"class":74},"        \u002F* configuration *\u002F\n",[68,780,781],{"class":70,"line":380},[68,782,783],{"class":89},"      },\n",[68,785,786,789,791],{"class":70,"line":386},[68,787,788],{"class":89},"    }",[68,790,277],{"class":257},[68,792,143],{"class":89},[68,794,795],{"class":70,"line":394},[68,796,797],{"class":89},"  };\n",[68,799,800],{"class":70,"line":408},[68,801,177],{"class":89},[179,803,805],{"id":804},"offscreen-documents","Offscreen Documents",[184,807,808,811,814],{},[187,809,810],{},"Modern replacement for background pages",[187,812,813],{},"Handles tasks requiring DOM but no UI",[187,815,816],{},"Perfect for audio processing, canvas operations",[50,818,820],{"id":819},"_2-common-pitfalls-and-solutions","2. Common Pitfalls and Solutions",[822,823,824],"ol",{},[187,825,826],{},[827,828,829],"strong",{},"Race Conditions",[58,831,833],{"className":60,"code":832,"language":62,"meta":63,"style":63},"\u002F\u002F BAD: Fire and forget\nbrowser.runtime.sendMessage({ action: \"do_something\" });\n\n\u002F\u002F GOOD: Wait for response\nconst response = await browser.runtime.sendMessage({ action: \"do_something\" });\nif (response.success) {\n  \u002F\u002F Continue\n}\n",[65,834,835,840,876,880,885,929,944,949],{"__ignoreMap":63},[68,836,837],{"class":70,"line":71},[68,838,839],{"class":74},"\u002F\u002F BAD: Fire and forget\n",[68,841,842,844,846,848,850,852,854,857,860,862,864,867,869,872,874],{"class":70,"line":78},[68,843,511],{"class":224},[68,845,241],{"class":89},[68,847,244],{"class":224},[68,849,241],{"class":89},[68,851,729],{"class":220},[68,853,225],{"class":224},[68,855,856],{"class":89},"{",[68,858,859],{"class":257}," action",[68,861,101],{"class":89},[68,863,105],{"class":104},[68,865,866],{"class":108},"do_something",[68,868,112],{"class":104},[68,870,871],{"class":89}," }",[68,873,277],{"class":224},[68,875,143],{"class":89},[68,877,878],{"class":70,"line":93},[68,879,319],{"emptyLinePlaceholder":318},[68,881,882],{"class":70,"line":146},[68,883,884],{"class":74},"\u002F\u002F GOOD: Wait for response\n",[68,886,887,890,894,896,899,901,903,905,907,909,911,913,915,917,919,921,923,925,927],{"class":70,"line":160},[68,888,889],{"class":81},"const",[68,891,893],{"class":892},"s_hVV"," response",[68,895,331],{"class":100},[68,897,898],{"class":213}," await",[68,900,720],{"class":224},[68,902,241],{"class":89},[68,904,244],{"class":224},[68,906,241],{"class":89},[68,908,729],{"class":220},[68,910,225],{"class":224},[68,912,856],{"class":89},[68,914,859],{"class":257},[68,916,101],{"class":89},[68,918,105],{"class":104},[68,920,866],{"class":108},[68,922,112],{"class":104},[68,924,871],{"class":89},[68,926,277],{"class":224},[68,928,143],{"class":89},[68,930,931,934,937,939,942],{"class":70,"line":174},[68,932,933],{"class":213},"if",[68,935,936],{"class":224}," (response",[68,938,241],{"class":89},[68,940,941],{"class":224},"success) ",[68,943,361],{"class":89},[68,945,946],{"class":70,"line":344},[68,947,948],{"class":74},"  \u002F\u002F Continue\n",[68,950,951],{"class":70,"line":364},[68,952,177],{"class":89},[822,954,955],{"start":78},[187,956,957],{},[827,958,959],{},"Message Handler Memory Leaks",[58,961,963],{"className":60,"code":962,"language":62,"meta":63,"style":63},"\u002F\u002F BAD: Listeners pile up\nfunction addListener() {\n  browser.runtime.onMessage.addListener(handler);\n}\n\n\u002F\u002F GOOD: Clean up listeners\nconst handler = (message) => {\n  \u002F* ... *\u002F\n};\nbrowser.runtime.onMessage.addListener(handler);\nreturn () => browser.runtime.onMessage.removeListener(handler);\n",[65,964,965,970,981,1006,1010,1014,1019,1038,1043,1048,1069],{"__ignoreMap":63},[68,966,967],{"class":70,"line":71},[68,968,969],{"class":74},"\u002F\u002F BAD: Listeners pile up\n",[68,971,972,974,977,979],{"class":70,"line":78},[68,973,687],{"class":81},[68,975,976],{"class":220}," addListener",[68,978,228],{"class":89},[68,980,90],{"class":89},[68,982,983,985,987,989,991,993,995,997,999,1002,1004],{"class":70,"line":93},[68,984,238],{"class":224},[68,986,241],{"class":89},[68,988,244],{"class":224},[68,990,241],{"class":89},[68,992,249],{"class":224},[68,994,241],{"class":89},[68,996,254],{"class":220},[68,998,225],{"class":257},[68,1000,1001],{"class":224},"handler",[68,1003,277],{"class":257},[68,1005,143],{"class":89},[68,1007,1008],{"class":70,"line":146},[68,1009,177],{"class":89},[68,1011,1012],{"class":70,"line":160},[68,1013,319],{"emptyLinePlaceholder":318},[68,1015,1016],{"class":70,"line":174},[68,1017,1018],{"class":74},"\u002F\u002F GOOD: Clean up listeners\n",[68,1020,1021,1023,1026,1028,1030,1032,1034,1036],{"class":70,"line":344},[68,1022,889],{"class":81},[68,1024,1025],{"class":327}," handler",[68,1027,331],{"class":100},[68,1029,289],{"class":89},[68,1031,263],{"class":262},[68,1033,277],{"class":89},[68,1035,231],{"class":81},[68,1037,90],{"class":89},[68,1039,1040],{"class":70,"line":364},[68,1041,1042],{"class":74},"  \u002F* ... *\u002F\n",[68,1044,1045],{"class":70,"line":380},[68,1046,1047],{"class":89},"};\n",[68,1049,1050,1052,1054,1056,1058,1060,1062,1064,1067],{"class":70,"line":386},[68,1051,511],{"class":224},[68,1053,241],{"class":89},[68,1055,244],{"class":224},[68,1057,241],{"class":89},[68,1059,249],{"class":224},[68,1061,241],{"class":89},[68,1063,254],{"class":220},[68,1065,1066],{"class":224},"(handler)",[68,1068,143],{"class":89},[68,1070,1071,1073,1075,1077,1079,1081,1083,1085,1087,1089,1092,1094],{"class":70,"line":394},[68,1072,311],{"class":213},[68,1074,337],{"class":89},[68,1076,231],{"class":81},[68,1078,720],{"class":224},[68,1080,241],{"class":89},[68,1082,244],{"class":224},[68,1084,241],{"class":89},[68,1086,249],{"class":224},[68,1088,241],{"class":89},[68,1090,1091],{"class":220},"removeListener",[68,1093,1066],{"class":224},[68,1095,143],{"class":89},[822,1097,1098],{"start":93},[187,1099,1100],{},[827,1101,1102],{},"Context Death",[58,1104,1106],{"className":60,"code":1105,"language":62,"meta":63,"style":63},"\u002F\u002F BAD: Assuming context is always alive\n\u002F\u002F GOOD: Handle disconnects gracefully\ntry {\n  await browser.runtime.sendMessage({\n    \u002F* ... *\u002F\n  });\n} catch (error) {\n  if (error.message.includes(\"receiving end does not exist\")) {\n    \u002F\u002F Handle disconnected context\n  }\n}\n",[65,1107,1108,1113,1118,1125,1144,1149,1157,1169,1201,1206,1210],{"__ignoreMap":63},[68,1109,1110],{"class":70,"line":71},[68,1111,1112],{"class":74},"\u002F\u002F BAD: Assuming context is always alive\n",[68,1114,1115],{"class":70,"line":78},[68,1116,1117],{"class":74},"\u002F\u002F GOOD: Handle disconnects gracefully\n",[68,1119,1120,1123],{"class":70,"line":93},[68,1121,1122],{"class":213},"try",[68,1124,90],{"class":89},[68,1126,1127,1130,1132,1134,1136,1138,1140,1142],{"class":70,"line":146},[68,1128,1129],{"class":213},"  await",[68,1131,720],{"class":224},[68,1133,241],{"class":89},[68,1135,244],{"class":224},[68,1137,241],{"class":89},[68,1139,729],{"class":220},[68,1141,225],{"class":257},[68,1143,361],{"class":89},[68,1145,1146],{"class":70,"line":160},[68,1147,1148],{"class":74},"    \u002F* ... *\u002F\n",[68,1150,1151,1153,1155],{"class":70,"line":174},[68,1152,467],{"class":89},[68,1154,277],{"class":257},[68,1156,143],{"class":89},[68,1158,1159,1161,1164,1167],{"class":70,"line":344},[68,1160,477],{"class":89},[68,1162,1163],{"class":213}," catch",[68,1165,1166],{"class":224}," (error) ",[68,1168,361],{"class":89},[68,1170,1171,1173,1175,1178,1180,1182,1184,1187,1189,1191,1194,1196,1199],{"class":70,"line":364},[68,1172,543],{"class":213},[68,1174,289],{"class":257},[68,1176,1177],{"class":224},"error",[68,1179,241],{"class":89},[68,1181,263],{"class":224},[68,1183,241],{"class":89},[68,1185,1186],{"class":220},"includes",[68,1188,225],{"class":257},[68,1190,112],{"class":104},[68,1192,1193],{"class":108},"receiving end does not exist",[68,1195,112],{"class":104},[68,1197,1198],{"class":257},")) ",[68,1200,361],{"class":89},[68,1202,1203],{"class":70,"line":380},[68,1204,1205],{"class":74},"    \u002F\u002F Handle disconnected context\n",[68,1207,1208],{"class":70,"line":386},[68,1209,649],{"class":89},[68,1211,1212],{"class":70,"line":394},[68,1213,177],{"class":89},[15,1215,1217],{"id":1216},"what-id-do-differently","What I'd Do Differently",[822,1219,1220,1226,1232],{},[187,1221,1222,1225],{},[827,1223,1224],{},"Start with TypeScript"," - Define your message types early. I started without it and regretted it.",[187,1227,1228,1231],{},[827,1229,1230],{},"Use a Message Bus Pattern"," - Centralize message handling logic instead of spreading it across files.",[187,1233,1234,1237],{},[827,1235,1236],{},"Build with Testing in Mind"," - Mock the messaging system for easier testing.",[20,1239,1240],{},"While I used WXT (a fantastic framework) for my extension, these principles apply to any browser extension. The framework handles the boilerplate, but understanding the underlying architecture is crucial.",[15,1242,1244],{"id":1243},"resources","Resources",[184,1246,1247,1256,1263],{},[187,1248,1249],{},[1250,1251,1255],"a",{"href":1252,"rel":1253},"https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fextensions\u002Fmv3\u002Farchitecture-overview\u002F",[1254],"nofollow","Chrome Extension Architecture Overview",[187,1257,1258],{},[1250,1259,1262],{"href":1260,"rel":1261},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FMozilla\u002FAdd-ons\u002FWebExtensions\u002FContent_scripts#communicating_with_background_scripts",[1254],"Browser Extension Messaging Guide",[187,1264,1265,1270],{},[1250,1266,1269],{"href":1267,"rel":1268},"https:\u002F\u002Fwxt.dev\u002F",[1254],"WXT Framework"," - If you want a modern development experience",[1272,1273,1274],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sucvu, html code.shiki .sucvu{--shiki-light:#E53935;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":63,"searchDepth":78,"depth":78,"links":1276},[1277,1278,1279,1283,1284],{"id":17,"depth":78,"text":18},{"id":28,"depth":78,"text":29},{"id":47,"depth":78,"text":48,"children":1280},[1281,1282],{"id":52,"depth":93,"text":53},{"id":819,"depth":93,"text":820},{"id":1216,"depth":78,"text":1217},{"id":1243,"depth":78,"text":1244},"2024-03-21","A little dive into browser extension messaging architecture, born from building a real SaaS product. Learn how different extension contexts communicate and how to architect your messaging system properly.","md",{"slug":1289},"browser-extension-messaging","\u002Fwriting\u002Fbrowser-extension-messaging",{"title":5,"description":1286},"writing\u002Fbrowser-extension-messaging",[1294,1295,1296,1297,1298],"Browser Extensions","Architecture","Chrome Extensions","TypeScript","WXT","X43s65cDHVO8rtdFvQTLHLdevTtARnL1vIEOYKPpPu8",1780955296665]