Creating Custom Raycast Extensions with React

Raycast is a powerful productivity tool that allows you to interact with your system, services, and applications right from a launcher. How can we extend these capabilities to boost our productivity for our own tasks?

We’ll learn how we can use Raycast Extensions to create our own launcher capabilities using React.

Table of Contents

YouTube Preview
View on YouTube

Getting Started with Raycast Extensions

Creating a custom Raycast extension is similar to working with React. Raycast uses React as a mechanism to build different views and offers an easy-to-follow workflow to build an extension.

Start by signing into the Raycast store and then follow these steps:

  1. Open the Raycast launcher.
  2. Type “create extension.”
  3. Choose a template, organization (if applicable), and other details for the extension.
  4. Set a location for the source code of your extension. (this will be the parent directory for your Raycast projects)
Extension created with launcher

Raycast will create a new directory for the extension inside the specified location, complete with a package.json file and source files.

After the extension directory is created, navigate to it in your terminal, run npm install to install dependencies, and npm run dev to start the local development server. Raycast should open the launcher, and you should see the command for your new extension.

Fetching & Displaying Data from the Hyrule Compendium API

In this example, we will create a simple extension to interact with the Hyrule Compendium API. Our extension will allow us to find creatures and display their details.

First, let’s request creature details from the API using the /entry endpoint and the creature ID. Since Raycast doesn’t provide fetch by default, we’ll use the useFetch hook that is included in the @raycast/utils package:

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

interface CreatureResponse {
  isLoading: boolean;
  data?: object;
}

interface Creature {
  description: string;
  name: string;
}

const { isLoading, data, revalidate } = useFetch<CreatureResponse>("https://botw-compendium.herokuapp.com/api/v1/entry/83");
const creature = data?.data as Creature;

This will fetch data for the creature with ID 83 (Fireproof Lizard) from the API, giving us our data, loading state, and the ability to revalidate the request.

Now, you can display the creature’s name and description in the extension’s view:

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

const markdown = `# ${creature?.name}

${creature?.description}`;

<Detail isLoading={isLoading} markdown={markdown} />

At this point, our extension fetches and displays the details of a single creature. However, we want to make it more dynamic by allowing users to search for different creatures.

Adding Search Functionality

To enable search functionality, update the package.json file to include arguments for the command as follows:

{
  ...
  "commands": [
  {
    "name": "index",
    "title": "Find Creature",
    "description": "Show the details of a creature",
    "mode": "view",
    "arguments": [
      {
        "name": "name",
        "placeholder": "Name",
        "type": "text",
        "required": true
      }
    ]
  }
],
}

Now, our command accepts one argument called name, and we can access it inside the extension using the props.arguments property.

First we need to import our props component from the Raycast API:

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

Then make it available in our React component.

interface CreatureArguments {
  name?: string;
}

export default function Command(props: LaunchProps<{ arguments: CreatureArguments }>) {

And our argument will now be available at:

console.log(props.arguments.name) // lizard

To search for creatures by name, we need to first fetch all creatures from the API as there’s not a search API, then look for the matching creature using the search query. We’ll use the food creatures to try this out:

interface CreatureResponse {
  isLoading: boolean;
  data?: {
    creatures: {
      food: object;
    }
  };
}
const { isLoading, data, revalidate } = useFetch<CreatureResponse>("https://botw-compendium.herokuapp.com/api/v2/all");
const creatures = data?.data?.creatures.food as Array<Creature>;
const creature = creatures.find(c => c.name.toLowerCase().includes(props.arguments.name?.toLowerCase() as string))
Creature search with result for cricket

With this setup, our extension now allows users to search for creatures by name, and it will display the details of the matching creature.

Improving the Display with Raycast components

To enhance the display of the creature’s details, we can use Raycast’s Detail and Metadata components to create a richer UI:

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

const markdown = `
# ${creature?.name}

![](${creature?.image})

${creature?.description}
`;

return (
  <Detail
    isLoading={isLoading}
    markdown={markdown}
    navigationTitle={creature?.name}
    metadata={
      <Detail.Metadata>
        <Detail.Metadata.Label title="Category" text={creature?.category} />
        <Detail.Metadata.Label title="Cooking Effect" text={creature?.cooking_effect} />
        <Detail.Metadata.Label title="Hearts Recovered" text={String(creature?.hearts_recovered)} />
        <Detail.Metadata.TagList title="Type">
          {creature?.common_locations.map(location => {
            return (
              <Detail.Metadata.TagList.Item key={location} text={location} color={"#eed535"} />
            )
          })}
        </Detail.Metadata.TagList>
      </Detail.Metadata>
    }
  />
);
Restless cricket with media details UI

Now, our extension not only displays the name and description of the creature but also additional metadata such as category, cooking effect, hearts recovered, and cooking locations.

Leveraging Raycast Extension to Boost Productivity

There are a lot of possibilities for how we can use custom extensions to boost our productivity.

For another example, you can check out the Cloudinary Raycast extension as well as many extensions on the Raycast GitHub.

To learn more about working with Raycast extensions, check out the official documentation.