<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Neil</title>
    <description>The latest articles on DEV Community by Neil (@hewitt).</description>
    <link>https://dev.to/hewitt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1230794%2F33aca37e-b815-4ec2-b1c8-3fef8ec429cd.png</url>
      <title>DEV Community: Neil</title>
      <link>https://dev.to/hewitt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hewitt"/>
    <language>en</language>
    <item>
      <title>How to Structure a Production-Ready Chrome Extension (Manifest V3)</title>
      <dc:creator>Neil</dc:creator>
      <pubDate>Fri, 13 Mar 2026 15:46:11 +0000</pubDate>
      <link>https://dev.to/hewitt/how-to-structure-a-production-ready-chrome-extension-manifest-v3-2hlf</link>
      <guid>https://dev.to/hewitt/how-to-structure-a-production-ready-chrome-extension-manifest-v3-2hlf</guid>
      <description>&lt;p&gt;I've built several Chrome extensions over the past few years and kept running into the same problem.&lt;/p&gt;

&lt;p&gt;Most Chrome extension templates are fine for a quick demo but fall apart when you try to build a real product.&lt;/p&gt;

&lt;p&gt;As soon as you introduce things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;popup UI
&lt;/li&gt;
&lt;li&gt;side panel UI
&lt;/li&gt;
&lt;li&gt;content scripts
&lt;/li&gt;
&lt;li&gt;background service workers
&lt;/li&gt;
&lt;li&gt;message passing
&lt;/li&gt;
&lt;li&gt;storage
&lt;/li&gt;
&lt;li&gt;API calls
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the architecture gets messy very quickly.&lt;/p&gt;

&lt;p&gt;The first couple of extensions I built ended up with logic scattered between popup scripts, content scripts, and the background worker. It worked, but it quickly became difficult to maintain as the extension grew.&lt;/p&gt;

&lt;p&gt;After rebuilding the same structure multiple times, I eventually settled on an architecture that works reliably for production extensions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basic Architecture
&lt;/h2&gt;

&lt;p&gt;The architecture that has worked best for me looks like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonusbfmzbtzn1c7s9n44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonusbfmzbtzn1c7s9n44.png" alt="Building Neurolink The Browser Extension"&gt;&lt;/a&gt;&lt;br&gt;
Each layer has a clear responsibility.&lt;/p&gt;
&lt;h2&gt;
  
  
  Content Scripts
&lt;/h2&gt;

&lt;p&gt;Content scripts stay very thin and mainly extract page data.&lt;/p&gt;

&lt;p&gt;They should &lt;strong&gt;avoid business logic whenever possible&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Their job is simply to interact with the webpage and send data to the extension runtime.&lt;/p&gt;
&lt;h2&gt;
  
  
  Background Service Worker
&lt;/h2&gt;

&lt;p&gt;The background service worker becomes the central coordinator for the extension.&lt;/p&gt;

&lt;p&gt;It typically handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API calls
&lt;/li&gt;
&lt;li&gt;authentication
&lt;/li&gt;
&lt;li&gt;storage
&lt;/li&gt;
&lt;li&gt;privileged extension logic
&lt;/li&gt;
&lt;li&gt;message routing
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping this logic centralized prevents extension code from becoming fragmented.&lt;/p&gt;
&lt;h3&gt;
  
  
  UI Layer (Popup &amp;amp; Side Panel)
&lt;/h3&gt;

&lt;p&gt;The UI layer is purely presentation.&lt;/p&gt;

&lt;p&gt;Popup and side panel components communicate with the background worker through message passing.&lt;/p&gt;

&lt;p&gt;This keeps UI code simple and avoids mixing extension logic with interface logic.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example Project Structure
&lt;/h2&gt;

&lt;p&gt;The project structure I now use looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
  popup/
  sidepanel/
  options/
  background/
  content/
  core/
    messaging/
    storage/
    api/
    auth/
    logging/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating UI, extension runtime, and shared utilities makes the codebase much easier to maintain.&lt;/p&gt;

&lt;p&gt;This structure allows the extension to grow without turning into a tangled codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Structure Works
&lt;/h2&gt;

&lt;p&gt;A few principles made the biggest difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content scripts stay thin&lt;/li&gt;
&lt;li&gt;Background worker handles privileged logic&lt;/li&gt;
&lt;li&gt;UI stays separate from extension logic&lt;/li&gt;
&lt;li&gt;Shared systems live in a core layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once these boundaries are clear, the extension becomes much easier to reason about. &lt;/p&gt;

&lt;h2&gt;
  
  
  Turning This Into a Reusable Starter
&lt;/h2&gt;

&lt;p&gt;After rebuilding this architecture several times, I eventually packaged it into a reusable starter kit called &lt;strong&gt;NeuroLaunch&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;popup UI&lt;/li&gt;
&lt;li&gt;side panel UI&lt;/li&gt;
&lt;li&gt;background worker&lt;/li&gt;
&lt;li&gt;message passing layer&lt;/li&gt;
&lt;li&gt;storage abstraction&lt;/li&gt;
&lt;li&gt;API helpers&lt;/li&gt;
&lt;li&gt;authentication module&lt;/li&gt;
&lt;li&gt;example extension feature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're interested, you can check it out here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackfactory.gumroad.com/l/neurolaunch" rel="noopener noreferrer"&gt;https://stackfactory.gumroad.com/l/neurolaunch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The goal was simply to create a clean extension foundation so developers can focus on building features instead of rebuilding boilerplate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Chrome extensions are deceptively simple at first.&lt;/p&gt;

&lt;p&gt;But once you combine UI, content scripts, background workers, and APIs, the architecture matters a lot.&lt;/p&gt;

&lt;p&gt;Hopefully this breakdown helps anyone building production Manifest V3 extensions.&lt;/p&gt;

&lt;p&gt;I'm always interested to see how other developers structure their extension projects.&lt;/p&gt;

</description>
      <category>chrome</category>
      <category>javascript</category>
      <category>vue</category>
      <category>browserextensions</category>
    </item>
  </channel>
</rss>
