You may have heard by now that we’ve recently released the Orbit Browser Extension. This is an open-source widget that brings Orbit data directly to Github, Twitter, LinkedIn, and Gmail. To support our public building of it and help folks eager to contribute in the future, we wanted to put this blog post together to share our thoughts, feelings, and decisions we made during development.
Just between us ( 🤐), there was a version of the extension that existed before we started our revamp. We weren’t exactly starting from square one, but we wanted to rebuild the extension from the ground up with a focus on clean, extensible architecture to promote future contributions.
The main drive for our work to extend the extensions capabilities was to add support for other sites. The previous version of the extension was fully functional but tightly coupled to GitHub & not too pretty (sorry Nico 😖). It really did make sense to start from scratch…
So, in direct contradiction with the general wisdom, we did.
The latter of these set up hot reloading, which significantly enhanced our development workflow—before you’d need to rebuild & re-import the extension for every single change. With these cleared, we were in a much better position to move forward with the new widget.
So, preamble aside: we wanted to build this extension to be easily extensible. Which begs the question, “how?”. Here’s some of the data models & ideas we ended up with 👀
A page stores the logic for interacting with various elements of the DOM. When planning, we realised we would face a number of challenges as part of injecting the widget across different websites, and it made sense to group that logic together. For example:
All logic regarding the DOM of a specific page, for example a “TwitterProfilePage”, is kept within these classes.
Each website requires unique event handling for widget rendering, which is all defined at the entrypoint.
We needed a variety of these because different websites require us to listen to different events in order to decide when to render the widget. For example GitHub, the simplest, just needs to listen to `DOMContentLoaded` & `turbo:render` events - for when the page loads, and for when the user navigates from page to page, respectively.
Once these events are hooked up, the most important responsibility of the entrypoint is to boot up the WidgetOrchestrator.
The orchestrator detects which page the user is on, and inserts the widget into the DOM; the elements it inserts are an Orbit button, additional data slot, and the widget itself.
Sidenote: the orchestrator is stateless, which is ideal for reducing coupling & supports writing automated tests - it’s a series of functions with a specific input & do a single thing, which makes them easier to test in isolation.
Once we've inserted the widget elements into the DOM, we need to actually populate it with useful content, which is where the widget itself takes over.
The widget handles user interactions, fetching data from the Orbit API and managing display states. It offers a quick, at-a-glance overview of a member's data. This is how it looks:
It shows practical and helpful information about a member so you can understand them at a glance. We are always open to suggestions if you think there’s something else we could show!
As part of rebuilding the application, we moved API requests out of the main widget and into a `background.js` runner on a separate thread. The primary benefit of this is that the API requests are non-blocking; we can make requests without slowing down the actual user interface, which helps deliver a more streamlined and performative experience. It also makes CORS rules easier, as all requests now come from a single place, rather than multiple websites.
So, with these puzzle pieces all together we end up with a fairly comprehensive code architecture as follows:
It’s something we’re really excited to work on in the future, and can’t wait to bring even more support and functionality to the browser extension.
Getting the extension to this point has been a genuinely fun programming experience, with all kinds of challenges and puzzles to solve. We’re planning to do a few more blog posts in this vein because there is SO MUCH we want to talk about. At this stage we’re thinking a few key topics could be
Give us a poke if any of these interest you. Any issues with the extension, or ideas for how we could improve it? Get in touch!