DEV Community

Cover image for Build your own Raycast extension, step by step, tutorial
orliesaurus
orliesaurus

Posted on

Build your own Raycast extension, step by step, tutorial

I’m a huge user of Raycast: I use it for everything from window resizing, to code snippets, to screenshots and shortcuts to my Github favorites.

While I was creating my own extension Dashcam extension to capture videos, I thought, maybe I could write something so that anyone can learn how to make their extension!

I think it would become handy, eventually. This guide explains how to create and debug your Raycast extension’s while you learn how its ecosystem works.

All of what I wrote in this article can be achieved using 3 ingredients:

Raycast, your favorite code editor and the Dashcam screen recorder for developers!

Intro

Raycast is a fast, customizable macOS launcher that lets you quickly and easily create shortcuts to save time and make your workflow easier. This tiny, free app lets you open programs, search for files, and create keyboard shortcuts for literally anything! It has gained popularity as a powerful Spotlight alternative. This guide covers creating and publishing a Raycast extension from start to finish, adding a little oomph to traditional tutorials by leveraging videos and interactivity!

I hope this can help anyone get started build their own extension.

Getting started with your extension

We’re going to be using JavaScript to build our extension!

I assume you have already downloaded and set up to Raycast. This guide complements, but does not replace, the official Raycast documentation – so if you’re a complete beginner read it here.

In this article I will do a walkthrough of the full process of creating an extension from nada to hero!

Create your project

This is a Dash showing you how to create a project and extension in Raycast

I created a folder where I am going to initiate my Raycast project. To achieve this, I simply created an empty folder somewhere on my drive that I can get to easily

mkdir raycast-ext

Initiate a new extension

Next, as the Raycast documentation indicates, you have to create the scaffolding of your project.

Open Raycast and Type Create Extension

The Create Extension – Developer command shows up, run it by clicking Open Command

This part enables the proper project structure to be created by the Raycast app itself.

Then, go through the following screens and configure them as shown below.

Set up your extension details

These are the options I chose, but you can choose to pick whatever makes most sense for you:

– Organization: None

– Unless you’re part of an Organization – most likely scenario is that you are not at first, this article explains what teams are

– Template: Detail

– Extension Name: Extension Demo

– Description: This is just a demo

– Categories: Fun

– Location: Select the folder you created a moment ago

– Command name: Demo command

– Subtitle: Demo subtitle

– Description: This is just a test

After completing these fields, create the extension by using the key combo

⌘+Return
Enter fullscreen mode Exit fullscreen mode

or click the Create extension text in the right corner

Congratulations! You’ll see in the next screen that the extension has been successfully created and registered inside the Raycast app!

Easy, right?

Running the extension for the first time

Next, let’s run the demo extension for the first time!

To see your extension run for the first time, open the directory you created, locate the project, and run the command:

Pretty cool!

The extension is running, showing you a hello world message, the reason for that is that we chose a template in the previous step – we still haven’t touched the code!

So let’s do that next.

Running and editing the template

To build your own extension, you can use Raycast’s own SDK, which includes set of UI components that you can use to assemble your extensions.

Before we continue, get familiar with the Raycast SDK: There are multiple components in the SDK. You can find a listing here: https://developers.raycast.com/api-reference/user-interface

Another important thing is to become familiar with the extension folder structure: If you use an IDE editor like VScode, open your extension folder, and you should be presented with the following project structure:

Now, we’re ready to start developing our extension!

Inspecting the main file

If you open src/index.tsx you will see the following code:

```
import { Detail } from "@raycast/api";

export default function Command() {

return <Detail markdown="# Hello World" />;

}
```
Enter fullscreen mode Exit fullscreen mode

To test this command again in Raycast, ensure you’re running it with npm in your terminal.

Type demo “Demo command” should show up and now hit return:

Building your own

Now let’s change it up: let’s build an extension that.. will fetch a random picture from a dog API, as it might bring a little happiness to the world!

We can call this extension “Dogjoy” or something funny like that.

Let’s create a new file called fetchdata.tsx inside the src folder

We’re going to import useFetch from the utils SDK and use it to call an endpoint.

Specifically, it is the dog.ceo API endpoint which returns random pictures of dogs, one at a time.

Installing Raycast helper library

Before we continue let’s raise a glass of your favorite cheering drink to ourselves

👏

We’re using the useFetch hook so this extension will use a helper library that we need to install called @raycast/utils

Using this helper library will allow us to use a custom React.js hook called useFetch: using this hook, we can execute an API request to an endpoint.

Let’s go ahead and install this library through npm by executing this in the root of our project:

npm install @raycast/utils

Calling the API

We specifically want to retrieve the message – which corresponds to the URL of the image – like in the code below.

    import { useFetch } from "@raycast/utils";

    export default function showDog () {

    console.log('Fetching API')

    return useFetch("https://dog.ceo/api/breeds/image/random").data.message;

    }
Enter fullscreen mode Exit fullscreen mode

Next we need to call this function from the main file

Creating the command

Let’s open index.js and add the following code:

    import { Detail } from "@raycast/api";

    import showDog from "./fetchdata";

    export default function Command() {

    return <Detail markdown={`

    ## Random 🐶

    ![](${showDog()})`} />;

    }
Enter fullscreen mode Exit fullscreen mode

This imports the showDog function from the fetchdata file, then wraps it around the Detail component, which renders, amongst other things, markdown!

We use markdown to render the image because it’s fast!

Debugging with the help of Dashcam

It’s never easy to fix issues in your extension, in fact there’s a whole community effort on Raycast’s community Slack trying to help each other to fix them.

While you’re developing, showing someone else what you were doing and went wrong can be quite powerful.

When you run this extension for the first time in developer mode, you will notice that when you run your extension, there are multiple console.log functions being called.

You might wonder: is this a bug?

The short answer is no. Here’s a Dash that shows you what I am talking about

4x fetch

Watch 4x fetch on Dashcam

I shared this code with someone who understands really well how the render method behind useFetch works, and they assured me that while the function might be re-rendered multiple times, the power of useFetch is that it only triggers one API call to your backend!

Let’s now change the URL on line 5 to another one. this time we’re retrieving memes from imgflip’s API:

    return useFetch("https://api.imgflip.com/get_memes").data.message;
Enter fullscreen mode Exit fullscreen mode

Now run the extension again and see what happens!

imgflip error

Watch imgflip error on Dashcam

As you can see we’re getting an error because there is no object key named message.

If we want to use this API we need to extract the URL by changing the code to look like this


    return useFetch("https://api.imgflip.com/get_memes").data?.data.memes[Math.floor(Math.random() * 100)].url;
Enter fullscreen mode Exit fullscreen mode

This retrieves one meme at random from the imgflip meme API.

Using Dashcam to check for bugs

Now you’re wondering, how can I verify that the API isn’t being called 4 times?

Good question and here’s the answer

You can verify this by replacing the URL on line 5 of fetchdata.tsx with a local endpoint running on your machine (localhost) through a simple HTTP server .

Let’s do this:

npm install

Watch npm install on Dashcam

What I did is the following:

I started a simple HTTP server and changed the fetch-able URL in fetchdata.tsx file to call my local server.

Then I look at the HTTP request logged by the local server and I see it’s only one.

Meanwhile the console.log has output 4 messages saying Fetching API that means, that although it’s re-rendering 4x the API call is only called one.

You can see how by using Dashcam, I am sharing the full context of what I am doing, this is very beneficial when debugging with others!

Now let’s put the files back to its initial state, so I can show some Dogs!

Commands

In Raycast, you can have multiple commands. Your extension might be using them to achieve numerous things.

In this extension we created we called initially the command “Demo” – let’s now change that so our extension is complete.

You can change commands and its associated data through the package.json file or the UI. I’ll do it in the editor – I am confident I can simply edit the data in this file and then reload the extension.

Publishing your extension: debugging build errors

The last part of this tutorial is the most fun one, publishing your extension.

To do this instead of running the code locally with npm run dev you will have to run it through npm run build

This will alert you if the code passes the build checks…

Surprise, it’s not passing the checks YET!

So let’s go ahead and get fixing it

First we’re going to add an interface

The full code should look like this:

    import { useFetch } from "@raycast/utils";

    interface DogAPIResponse {

    message: string;

    }

    export default function showDog () {

    const { data } = useFetch<DogAPIResponse>("https://dog.ceo/api/breeds/image/random");

    return data?.message;

    }

Enter fullscreen mode Exit fullscreen mode

Publishing your extension onto the Raycast Github repo

Now that your extension is valid you can ship it to the Raycast extension repo, where all other extensions live!

You can run npm run publish to publish your extension now. You will be asked to authenticate with GitHub.

Raycast creates an open a pull request in their repository, on your behalf!

Conclusion

Hopefully, this article shows you how simple it is to create your first extension and publish it to the Raycast store.

This is a simple extension, you can add much more – especially fall back and error catching… but hopefully this gives you a good introduction to building extensions.

You’ve noticed that we had a couple of situations where we had to debug issues with the extension, luckily I was able to show you the issues using Dashcam to show you exactly how I solved them, it’s like a YouTube video but with better logs 😉 and context.

If you want to learn more about how to create & publish extensions on Raycast feel free to browse the documentation here.

If you’re interested in how Dashcam works and why it helps developers debug faster, you can find further details on the Dashcam website

👉 If you just want a Raycast PRO code to try Raycast PRO for free, ask in the comments!

Top comments (0)