Building Raycast extensions with AI is fun
Table of Contents
I’m a bit of a productivity / software tool obsessive. I’m not as diehard as some of the sickos on r/obsidian or other forums, but I enjoy streamlining my workflows wherever possible.
And that’s why Raycast has been such a trip for us productivity junkies. I’ll save a more detailed writeup of my Raycast setup for a longer post. But in short here’s what makes it shine for me: the ability to open apps with hotkeys, clipboard history, create custom search engines with Quicklinks, and that’s just a small sample. Once you get a baseline setup in Raycast, you’ll likely find yourself tweaking it often in the endless pursuit of optimization.
Extensions are an essential part of Raycast and thanks to generative code tools, it’s surprisingly straightforward to build your own. In this spirit of home-cooked software, you don’t have to make an extension that’s ready for the world. You can build something that fits your own use cases, solving your unique problem.
I’ll walk through some guidance on building your own extension below, and how much it’s impacted my day-to-day computer use.
The itch to scratch
I started using Arc Browser sometime around middle of 2024, and was hooked. This was peak Arc time, where The Browser Company was shipping exciting changes constantly and innovating in the browser design space. My favorite Arc feature is the command bar: type CMD+T and you can search, type a URL, but most interestingly, it also reveals your most recent tabs in the order in which they were accessed.
I started using this all the time, as we live in a browser tab world. Instead of hunting for the tab I was just viewing, I’d just type CMD+T and find that recent tab.
But my productivity junkie brain started finding imperfections. If I were in VS Code, I’d have to tab over to Arc, and then hit CMD+T. Most people probably wouldn’t mind this small friction, but I felt like this was an achievable annoyance.
I wanted Arc’s tab switcher but accessible from anywhere outside Arc. Also Arc’s tab switcher only shows your three most recent tabs, it would be nice to have more.
The requirements
A Raycast extension that emulated Arc’s tab switcher:
- Show the user the most recent tabs they accessed. Note that this is different from when you opened a URL.
- Allow a user to search through this list of recent tabs.
- Selecting that tab in Raycast focuses the Arc app and that tab.
- List of recent tabs is constantly re-ordered as the user navigates the web.
Building the extension
Getting started
Raycast extensions are built in TypeScript and use React for the UI. While I’m not a React or TypeScript enthusiast, I appreciate this choice as this stack is familiar to many, and importantly, LLMs.
Raycast’s docs are solid and they have a nice tutorial on building your first extension. The Raycast team built a series of React components, which you can find here in the User Interface section of their developer docs. When a developer publishes a Raycast extension, it’s packaged program with two main parts:
- The React components that Raycast renders in its app.
- Whatever backend logic that the components eventually display.
I leaned heavily on GitHub Copilot to build my first Raycast extension, as I’m not too strong in React (or at the time, building Raycast extensions).
This was about a year ago as of this writing, and the AI was great building React components, but struggled with understanding my use case: I had to repeatedly remind it that I needed a chronological list of tabs in which they were accessed (i.e. clicked on or opened from an external link).
After a few iterations with GitHub Copilot agent mode in VS Code, I got it working. A few tips I’d recommend for GitHub Copilot in VS Code in particular (though these could be adapted to any LLM coding assistant):
-
Any AI coding chat experience nowadays knows how to fetch a URL. Give the LLM the Raycast docs URL, as building Raycast extensions is relatively niche, so it may not be readily available in the model. You can use a prompt like [1]:
Be sure to reference the developer docs located at #fetch https://developers.raycast.com/api-reference/user-interface when writing the React components. Search the docs as needed. -
All published Raycast extensions live in this repo: https://github.com/raycast/extensions. It’s a huge repo. Browse to the extension directory, and GitHub truncates the visible folders. Either way, you can feed this repo as context for LLMs (it shouldn’t load the entire repo into your context window). Here’s an example I used [2]:
Consult this #githubRepo https://github.com/raycast/extensions for prior art on building Raycast extensions.
When you’re ready to start building your own Raycast extension, just open up Raycast and search for the “Create Extension” command. It should have a green icon with the greyed out text “Developer” next to the command. Hit enter, and Raycast will prompt you with a form.
Once you fill out that form, Raycast will create a new project in the directory location you chose. Before you get started, be sure to npm install the required dependencies, and then npm run dev to start the Raycast extension development server.
Once you’ve got a working version, you can npm run build to create a production build of your extension locally.
Finishing the extension
I used the following utilities and techniques to round out the extension:
- AppleScript: Raycast’s API supports AppleScript execution, meaning it can manipulate macOS apps. In my case, I needed to focus Arc and open a specific tab, which was made possible with this utility.
- Raycast browser extension: Raycast has a fantastic browser extension. When my extension opens in Raycast, it leverages this browser extension to fetch all open tabs in Arc.
- A separate Chrome extension. My key requirement was to track the order in which tabs were accessed. The first-party Raycast extension discussed above doesn’t support this, so (with the help of Copilot) I created a simple Chrome extension that tracks the order in which tabs are clicked or opened from an external link. That former point is key. As a knowledge worker, I’m always racing across different tabs. I wanted the ability to quickly view my recent tabs and switch to any of them. As soon as you switch to a new tab, that tab then becomes the next most recent.
- A local file: I used a simple
.jsonfile to keep track of the order in which tabs are accessed. I consulted a few different approaches, and for an app that I run locally, this seemed perfectly adequate. - Local commands: in order to boot the Chrome extension I run
npm run startfrom my terminal. This is fine for my use case. I don’t want to go through the hassle of publishing the Chrome extension. I plan to automate running thisnpm run startevery time my computer boots.
Wrapping up
Building your own tools to drive day-to-day productivity is immensely satisfying. We’re in a new period of easily spinning up software for discrete tasks and even small efficiency gains.
And kudos to the Raycast teams for writing great developer documentation and open-sourcing their extensions repo. When I first started using Raycast, I was obsessed. That obsession has worn off some (which is probably a good thing), but I still love iterating on my setup.
Just like an F1 team making small adjustments to a car between races, LLMs and tools like GitHub Copilot allow us to continuously refine our toolkit. It’s just a matter of finding the right papercuts to mend.
[1] This example uses an explicit #fetch tool call in VS Code with GitHub Copilot. However, omitting #fetch should still work in VS Code and other AI coding assistants.
[2] Similar to the above footnote, this uses an explicit #githubRepo tool call. I like using these explicit tool calls as I believe it helps add some predictability to the LLM’s non-deterministic nature.