Build your own Raycast extension, step by step, tutorial
Published: 2023-11-20 23:36:27

---
title: Build your own Raycast extension, step by step, tutorial

published: true
description: 
tags: 
cover_image: https://i0.wp.com/blog.dashcam.io/wp-content/uploads/2023/11/image-1.png?resize=1024%2C569&ssl=1
# Use a ratio of 100:42 for best results.
# published_at: 2023-09-28 17:35 +0000
---

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](https://www.raycast.com/) 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](https://developers.raycast.com/).

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

![](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/ENPvD83tp36A4Z9wkfyx6u1Q.png?ssl=1)

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](https://www.raycast.com/teams)

– 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

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?](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/F48uKMTCLljCK0gmL6vwVgT8.gif?ssl=1)

![](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/l8x1ul75znmTNxVagnrxDQ9P.png?ssl=1)

**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](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:

![](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/preload/hQND72h2lE9b27ANz5UGnH0k.png?w=800&ssl=1)

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" />;
    
    }
    ```
    

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:

![](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/06eVH6hr0869phviBd_FJCXm.png?w=800&ssl=1)

**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!

![](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/tbw2M2u_ROUrEwqhFx-xnkjq.gif?ssl=1)

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](https://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;
    
    }
```
    
    

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()})`} />;
    
    }
 ```   
    

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](https://replayable-api-production.herokuapp.com/replay/64ee33bea4bc310061f566d0/gif?shareKey=DgA0e4gJRAjGwuTu244A)](https://app.dashcam.io/replay/64ee33bea4bc310061f566d0?share=DgA0e4gJRAjGwuTu244A)

Watch [4x fetch](https://app.dashcam.io/replay/64ee33bea4bc310061f566d0?share=DgA0e4gJRAjGwuTu244A) 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;
 ```   
    

Now run the extension again and see what happens!

[![imgflip error ](https://replayable-api-production.herokuapp.com/replay/64ee66f13169ab006313dc84/gif?shareKey=frKJLPgJhLNqOpvGewg)](https://app.dashcam.io/replay/64ee66f13169ab006313dc84?share=frKJLPgJhLNqOpvGewg)

Watch [imgflip error](https://app.dashcam.io/replay/64ee66f13169ab006313dc84?share=frKJLPgJhLNqOpvGewg) 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;
```    
    

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](https://replayable-api-production.herokuapp.com/replay/64ee35633169ab006313dc83/gif?shareKey=bJGP9qsZLSGhZMFQZENUqw)](https://app.dashcam.io/replay/64ee35633169ab006313dc83?share=bJGP9qsZLSGhZMFQZENUqw)

Watch [npm install](https://app.dashcam.io/replay/64ee35633169ab006313dc83?share=bJGP9qsZLSGhZMFQZENUqw) 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.

![](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/XY8zualswbH2KDNJ6GTkf6HF.gif?ssl=1)

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;
    
    }

```
    

**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](https://github.com/raycast/extensions), on your behalf! ![](https://i0.wp.com/slabstatic.com/prod/uploads/vww16xdi/posts/images/Jb0rGNGywy3HR5MQMYqBq0fs.gif?ssl=1)

**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.](https://developers.raycast.com/basics/getting-started)

If you’re interested in how Dashcam works and why it helps developers debug faster, you can find [further details on the Dashcam website](https://www.dashcam.io/)

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