Single codebase to power a website and a Google Chrome extension
Sharath Prabhal
30 May, 2020
Last month, I built Time Travel, a tool to quickly convert timezones. It lets you view time across timezones by simply hovering your mouse. You can read more about it here.
Over time, I realized this wasn't the greatest user experience because I need to leave my current context(calendar, webpage etc) to open this page in a new tab. I figured a chrome extension would be great since I can view it from any page. So, I decided to build one.

My primary goal is to use the same codebase to power both the webapp and the extension.
Step 1: Prep
Time Travel was originally built as a React app. That's a great starting point because it's plain JS and should "work" as a Chrome extension. We need to make a few changes to get it to load as an extension. The code is available here here.

Update manifest.json
This is the absolute minimum to get Chrome to recognize this app as an extension. The official docs are here.
{ "name": "Time Travel", "version": "0.0.1", "manifest_version": 2, "browser_action": { "default_popup": "index.html" } }


Load unpacked extension into Chrome
Run npm run build to build the code. This will result in a build dir with the minified code. Follow the instructions here to load the unpacked extension into Chrome. Now, when you try to run it, this is what you'd see.
Wait, why is it blank?!?!?

It's blank because there are errors. Use the extension error controls to debug the errors.



This is because of a built-in security feature of Chrome extension. You can read more about it here here. Update the manifest.json and you're almost there.
{ "name": "Time Travel", "version": "0.0.1", "manifest_version": 2, "browser_action": { "default_popup": "index.html" }, "content_security_policy": "script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com 'sha256-542g0dYCGij9qXrDxQMMtqVv8KnMUzxA01jVlDKENEc=' 'sha256-OB2OtW2qkm5ZWV3zwAPKIevdEOFYyJQzKukfyVMEbUQ='" }
Now, try to run it.

Wohoo - it works! It does not look perfect but still works!
So, what exactly is not working?
  • The UI is too narrow
  • Some styling is off

Not a shabby start.
Step 2: Extension specific update
I'm not going into details here since styling updates are UI specific. Since one of my key goals is to use the same codebase to power both the webapp and the extension. I plan to achieve that using the following code to identify where the app is running -
export function isExtension() { return window.chrome && chrome.runtime && chrome.runtime.id !== undefined; }
Using the above, I've made changes across the board to support extension specific updates. See code There weren't too many. Here's the result -
Step 3: Submit for review
This is the boring part. Here's the list of items required
  • Icons
  • Product screenshots
  • Marketing copy

Add this script to package.json to create the bundle.
"pack": "NODE_ENV=production npm run build && rm -f build.zip && zip -r build.zip build"
That's it, you're all set! Here are the related links
Hope you found this helpful. If you have any questions, feel free to reach out to me