As a follow up from my previous post about how to use Raycast to get my most recent blog posts, I took it upon myself to investigate how to create a custom Raycast extension for the same purpose.
I did not expect the development process to be so easy.
The end result -
I created the extension Get Blog Post
, which now lives in my favorites
when I open up Raycast.

When I press Enter on the extension, it opens and makes a fetch call to my website for all of of posts.

And when I select a post, its url copies to my clipboard.
The setup
I'm happy to report that, as a React developer, this couldn't have been easier to set up.
Raycast has its own node library with a slim library of UI elements and React hooks and much more.
For more specifics, here are the Raycast docs.
Below is the entirety of my code -
1import { useEffect, useState } from "react";
2import { Action, ActionPanel, List } from "@raycast/api";
3import { useFetch } from "@raycast/utils";
4
5interface BlogPost {
6 title: string;
7 description?: string;
8 slug: string;
9}
10
11export default function Command() {
12 const [searchText, setSearchText] = useState<string>("");
13 const [filteredList, filterList] = useState<BlogPost[]>([]);
14
15 const BASE_URL = "https://natespilman.com";
16 const API_ENDPOINT = `${BASE_URL}/posts`;
17 const BLOG_URL = `${BASE_URL}/blog`;
18
19 const { data, isLoading } = useFetch<BlogPost[]>(API_ENDPOINT);
20 console.log({filteredList})
21
22 useEffect(() => {
23 if (data) {
24 filterList(
25 data.filter((item) => {
26 const searchLower = searchText.toLowerCase();
27 return (
28 (item.title?.toLowerCase() || "").includes(searchLower) ||
29 (item.description?.toLowerCase() || "").includes(searchLower)
30 );
31 })
32 );
33 }
34 }, [searchText, data]);
35
36 return (
37 <>
38 <List
39 filtering={false}
40 onSearchTextChange={setSearchText}
41 navigationTitle="Search Posts"
42 searchBarPlaceholder="Search by title or description"
43 isLoading={isLoading}
44 >
45 {filteredList?.map((item) => (
46 <List.Item
47 key={item.slug}
48 title={item.title}
49 subtitle={item.description}
50 actions={
51 <ActionPanel>
52 <Action.CopyToClipboard
53 title="Select"
54 content={`${BLOG_URL}/${item.slug}`}
55 />
56 </ActionPanel>
57 }
58 />
59 ))}
60 </List>
61 </>
62 );
63}
tsx