3 Lessons from Building a Chrome Extension with TypeScript
The lessons learned from someone who has never made a Chrome Extension, never used TypeScript and likes milkshakes
Yes, the subtitle wasn't lying. I do like milkshakes.
I had a very specific problem. Since moving from Notion to Obsidian, I lost the ability to quickly capture notes from a webpage that would be instantly accessible from Obsidian.
To maximize pain, I decided to not do any research on whether this tool already existed while also deciding to build it using a language that I'd never used before. Oh also, I barely know how to code in general.
So... I built my own Chrome Extension. One that allowed me to open it up on any webpage, take notes and save it as a .md file to my local device where my Obsidian Vault lives.
Here is the finished project if you'd like to take a look and use it for yourself: https://github.com/ShawnSomething/ObsidianWebCaptureExtension
📖 How to Use it
Go to chrome://extensions/ and turn on Developer Mode
Click on Load Unpacked and select the
build
file to be uploadedPin it to your Chrome bar by clicking on the puzzle piece to the right of the URL address bar and clicking on the pin button
When firing it up: the title will auto-populate with your current URL
Add notes as needed
Click on Review Notes to open up a new web page
Save it to the file of your choice
Notes persist when the extension is closed and opened, so manually clear them out when ready
🏗️ Building Blocks
The main building blocks required for this Chrome Extension are:
A manifest.json
Allows Chrome to define permissions, and background services, access the code, and run the required scripts.
A HTML file
The components and styling of the Chrome Extension (React can be used too)
A couple of JS files
This is where the operations and scripts live. Everything that you want the Chrome Extension to do will be defined here
A Compiler
I've chosen Snowpack for its quick load times and beginner-friendly nature, but any compiler can be used to output the JS file
I'm not going to bore you with the build, I've structured the TS files in a way that explains every function, what it does and how it relates to the other pieces. (Happy to go into more tutorial-style details if requested)
But otherwise, here are the …
📝 Lessons Learned
🛝 1. TypeScript is Fun
TypeScript feels like Python but JavaScript. It might be a reductive way of describing it, but it feels a lot friendlier to read for a non-techie, self-taught pleb like myself.
The Type interface (even though not as heavily utilized in this project) is amazing. Not struggling with broken code just cause I've inputted a different type that is not supposed to be there.
The streamlined approach to getting what you want, rather than the run around that JS makes you do, helps provide a greater understanding of what is happening from function to function.
However, I have to admit that I wouldn't have been able to pick it up in the manner that I did without some cursory knowledge of JS. Even though chatGPT helped immensely with this project, I was able to jig things to the specific needs of the project a lot easier (this might be my fault as well, more on this in Lesson 3).
Alright, I raved about TypeScript above, but am aware that it might not be the best language to use for Chrome Extensions.
Compilers are needed in every TS project that is going to the web, turning it into a JS file for execution. So it might be faster to just build it with JS, rather than constantly compiling it over and over.
Frankly, the main reason I decided on TS here was cause I didn't know any better (and also cause I wanted to learn it). Now that I do know somewhat better, I would still use TS to write it regardless.
It's just more fun. And isn't that the whole point of building side projects?
🎨 2. Permissions, Limitations and Creativity
Before going into this project, I knew exactly how I wanted it to look and what I wanted it to do. However, I underestimated how many permissions and limitations there were. I understand that these are for security reasons, but it did throw a huge wrench in the vision, to be able to make notes, select a folder destination and download it all from the extension, without needing to open a second page for it.
Just to list a few:
To get the URL for the Title Field, I needed permission to access the Tabs
To have the inputted text persist when the extension has been closed and reopened, Storage permissions were needed
To download a file, it can only be downloaded from a standalone webpage, not from the Chrome Extension
Scripts can be used to copy texts from a Chrome Extension to the device clipboard, but unable to access the clipboard to paste it - the pasting has to be done manually by the user
Defining permissions in the manifest.json and not using said permissions is a no-no. The Chrome Web Store will reject your submission
These forced me to think about workarounds, like opening up the notes on a different HTML page before downloading it, using a relative path to determine the local save folder destination, and so on.
There was a (now deleted) tweet from Julian Schapiro with the quote:
Writer’s block happens when you try to make something that fills up the universe. But when you try to make something that fills up a glass of water and you do it right. You will notice a bit later that it’s got the universe in it. 🌠
This is a beautiful, albeit lofty, way of describing it. But the idea transfers here. Limitations are what define creativity. It made me learn how to use the chrome.storage API which I wasn't intending to use when I started this project, what with already needing to learn about Chrome Extensions and TypeScript. But I'm glad I did.
Constraints are good for creativity and innovation.
💨 3. The Quickest Way to Learn Anything
Generally, learning something brand new tends to be overwhelming. To make it less scary, we were taught to pick up new concepts chapter by chapter. All while being promised that these seemingly distinct puzzle pieces will build to something larger in the future. Even though that might work for math or some other area of study, it is not incredibly helpful when the thing you are trying to learn is tech, which has endless and constantly evolving concepts.
Having an end in mind and working backwards to break down each step into small manageable pieces, would be a better approach to learning. If that step is too hard, break it down even more, and make it so small that it is impossible to not achieve it. This inversion of the learning method removes the pressure of retaining all the information for potential future utility. Instead, learning and applying what we need from the outset provides a more holistic view of how the sausage is made.
I was stuck in the JS tutorial hell for so long that I lost interest in it. Eventually turning to shortcuts like chatGPT to ghostwrite the entire program for me while I passively copied and pasted the code and errors back and forth until I had something somewhat functional.
This is not inherently bad, but that isn't the point. I lost interest because I wanted to have coded something rather than seeking out the joy of building and breaking through the puzzle. For TypeScript and this project in particular, I jumped into a project of my own pretty much from the get-go. Only sought help when I got stuck.
🛠️ Here it is in action.
With this Chrome Extension, the end goal was an extension that can take notes and save them to my Obsidian Vault. The first breakdown step is to define the requirements.
The Chrome extension has to be able to pop up on any webpage
There has to be a Title Field
There has to be a Text Field
There has to be a File Picker
There has to be a Save Button
Then, working backwards, I broke it down again and again, making it smaller and smaller with each obstacle.
The Title Field needs to auto-populate the URL the moment the extension is opened so the quick saving would be easier as the title
The Text Field needs to persist input text even if it has been closed or it will be a nightmare on accidental closes during meetings
Have the ability to clear notes manually, instead of automatically
The File Picker has to be able to access the local file storage
Hit a problem with permissions, have to use a relative path
A Save Button is needed but it doesn’t work on the Extension
Open the notes on another page, move the downloading there
Have a button on the Extension to open it up on another page
Have the button say Review Notes
This in turn created a whole new set of requirements to fit the workarounds.
In the new review notes page
It has to mirror the title and text from the extension
It has to be able to access the file picker
It has to be able to download the file to the destination
Remove the file picker from the extension
Just keep the clear notes and the review notes buttons
Working backwards has been immensely helpful in getting to the bottom of each step while also helping with understanding how everything links together. Repeating this learning model with different projects would quickly expose us to most of the fundamentals. If nothing else, it provides a greater sense of appreciation for how much goes into every single thing we interact with daily.
uhm... I don’t know how to end this.
ok, bye.