I’ve spent years trying to optimize my workflow, and the biggest friction point has always been the ‘calendar jump’—that momentary lapse in focus when I leave my IDE to check when my next meeting starts. While the official Raycast calendar integration is great, I found that I needed something more tailored to my specific API requirements and workflow. That’s why I decided to put together this raycast calendar extension tutorial.
Whether you’re looking to integrate a niche scheduling tool or just want a more streamlined way to view your availability, building your own extension is the way to go. In my experience, the Raycast SDK makes this surprisingly trivial if you’re comfortable with TypeScript and React.
Prerequisites
Before we dive into the code, make sure you have the following installed and configured on your macOS machine:
- Node.js (v16+): The foundation for our development environment.
- Raycast App: Installed and updated to the latest version.
- Raycast CLI: Install it via terminal using
npm install -g @raycast/api. - An API Key: Depending on the calendar you’re using (Google, Outlook, or a custom REST API), you’ll need an OAuth token or API key. If you’re using Google, I highly recommend checking out my advanced Google Calendar tips for power users to understand how to structure your API requests for maximum efficiency.
Step 1: Initialize Your Extension
First, we need to scaffold the project. Open your terminal and run the following command:
raycast create extension "my-calendar-tool"
When prompted, choose the TypeScript template. This gives us the type safety we need when dealing with complex date-time objects. Once the folder is created, navigate into it and install the dependencies:
cd my-calendar-tool
npm install
Step 2: Defining the Calendar Data Model
A calendar extension is only as good as its data handling. I prefer creating a separate service file to handle the API calls. This keeps the UI logic clean. Create a file named src/calendarService.ts:
import { Detail } from "@raycast/api";
export interface CalendarEvent {
title: string;
startTime: string;
endTime: string;
location: string;
}
export async function fetchUpcomingEvents(apiKey: string): Promise<CalendarEvent[]> {
const response = await fetch(`https://api.yourcalendar.com/v1/events?key=${apiKey}`);
const data = await response.json();
return data.events.map((event: any) => ({
title: event.summary,
startTime: event.start.dateTime,
endTime: event.end.dateTime,
location: event.location || "Remote",
}));
}
Step 3: Building the List View UI
Now, let’s bring that data into the Raycast interface. We’ll use the List component, which is perfect for showing a sequence of upcoming appointments. Open src/index.tsx and implement the following logic:
import { List, ActionPanel, Action } from "@raycast/api";
import { fetchUpcomingEvents } from "./calendarService";
export default async function Command() {
// In a real app, you'd fetch this from Raycast preferences
const API_KEY = "your_api_key_here";
const events = await fetchUpcomingEvents(API_KEY);
return (
<List
searchable
>
{events.map((event) => (
<List.Item
key={event.startTime}
title={event.title}
subtitle={`${new Date(event.startTime).toLocaleTimeString()} - ${event.location}`}
actions={
<ActionPanel>
<Action.CopyToClipboard title="Copy Event Details" content={event.title} />
</ActionPanel>
}
/>
))}
</List>
);
}
As shown in the image below, the key is to keep the subtitle concise. Raycast users value speed; if they have to squint to see the time, the extension fails its primary purpose.
Pro Tips for Calendar Extensions
- Caching: Calendar data doesn’t change every second. Use a caching layer or the Raycast
LocalStorageAPI to avoid hitting rate limits and to make the extension feel instantaneous. - Date Formatting: Use
date-fnsordayjsfor complex date manipulation. Native JS Date objects are notoriously painful for time-zone calculations. - Deep Linking: Add an action to open the event directly in the browser. It’s a small touch, but it’s what separates a basic tool from a professional one.
Troubleshooting Common Issues
During my testing, I encountered two main hurdles:
- CORS Errors: If you’re fetching from a browser-based environment, CORS can be a pain. However, Raycast extensions run in a Node-like environment, so standard
fetchusually works fine. If it doesn’t, double-check your API headers. - Auth Expiry: OAuth tokens expire. I recommend implementing a “Refresh Token” flow or providing a clear error message that directs the user to the preferences panel to re-authenticate.
What’s Next?
Now that you’ve mastered this raycast calendar extension tutorial, you can expand your productivity suite. If you’re working in a Linux environment during the day, you might find that a best command line calendar for linux complements your macOS Raycast setup. Alternatively, if you’re looking for a full-fledged app replacement, I’ve written a detailed Morgen calendar review for developers that compares these custom builds with professional software.
Ready to take your automation further? Explore our other guides on development and productivity tools at ajmani.dev.