Hi everyone! 👋
This is my first proper post on Sqaico (or any writing platform, for that matter). I'm going to talk about a small project I've been working on: an online iCal file generator called https://ical.rip/.
About me
I'm Joe Anderson, a front-end web developer specialising in user interface design. I work as a consultant for users of Plate rich-text editor, which I'm involved in building and maintaining. I've also been helping @Lagu out with Sqaico; he's been a delight to work with!
Background
Recently, I found myself needing to generate an iCal file to share with someone in a different time zone to me, to make it easier for us to meet up at the same time. The iCalendar file format dates back to 1998, predating Apple's software of the same name by four years.
To show you what one looks like, here's an iCal file for an event on 16 September 2023, 08:00 UTC. I've added indentation to make it easier to read.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//sebbo.net//ical-generator//EN
BEGIN:VEVENT
UID:9f02b01e-4df4-4622-aeec-4bafc36011d9
SEQUENCE:0
DTSTAMP:20230916T080551Z
DTSTART:20230916T080000Z
DTEND:20230916T090000Z
SUMMARY:My fabulous event
DESCRIPTION:Created with https://ical.rip/
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER:-PT1H
DESCRIPTION:My fabulous event
END:VALARM
END:VEVENT
END:VCALENDAR
The iCal file format supports multiple events (BEGIN:VEVENT) in the same file. An event has a few fields, including start time, end time, summary and description, and can also contain any number of alarms (BEGIN:VALARM). The trigger of -PT1H in the above alarm means that the user should be notified one hour before the start of the event.
I was looking for an online application to generate a simple iCal file. Of the top five results on DuckDuckGo, two of them didn't support HTTPS.
The other three, while comprehensive, had interfaces that left a lot to be desired. How hard could it be? I thought. I decided to make my own.
shadcn/ui
Normally, I prefer to build and style components myself using Tailwind CSS to give each app its own unique feel. For this app, though, that would have taken more time than I was willing to spend on it.
 shadcn/ui is a collection of pre-styled React components built using Tailwind CSS and Radix UI. Unlike most component libraries, which you install as an NPM module and import into your CSS and/or JSX files, shadcn/ui encourages developers to copy and paste its component source files into your own Git repo. There's a CLI to automate the process.
This lets you make minor changes to your app's design system without having to fork the component library or utilise extension points in the components themselves.
We use shadcn/ui in recent versions of Plate, and we've reused their copy-and-paste pattern for Plate's pre-styled UI components.
The form
The first thing I built was the HTML form. With shadcn/ui, it was remarkably quick and easy to mock up the form inputs directly in React. For the date picker, I was even able to use shadcn/ui's Calendar component; it worked out of the box.
Next was input validation. There are a lot of competing standards for when to show validation errors. For this app, I decided to keep things simple and only show validation errors when the user submits the form. This has the advantage of not causing the layout to shift when the user clicks or tabs away from an invalid field.
The only form fields that needed validating were the title (presence), date (presence), and start/end time (presence and syntax). Parsing the start and end times was an interesting challenge. I eventually settled on a fairly simple function that normalises various input formats (15:00, 3pm, etc.) into a standard HH:MM:SS string. If it fails to normalise the time, it reports it as invalid.
Generating the iCal file
I used the ical-generator NPM module to convert the input data into an iCal file that can be downloaded on submit. It wasn't the greatest experience I've ever had with a library. The TypeScript declarations were often directly contradictory to what the library actually expects, and it has a habit of throwing runtime errors if the data isn't quite right, which defeats the purpose of using TypeScript to begin with.
Another nasty issue was ical-generator's use of the fs.writeFile
Node API, even though the library is supposed to work in the browser. It wasn't a problem in development, since I wasn't calling any code that used the filesystem API, but it resulted in an error when I tried to bundle my application using Vite. In the end, I had to patch ical-generator to stub out the offending code.
Update 19 October 2023: ical-generator has since fixed the Node API issue in version 6.0.0-develop.1.
There were some interesting challenges (in addition to the frustrating ones described above) in generating the iCal data. One such challenge was converting the input date (a JS Date object) and the input start/end times (HH:MM:SS strings) into a pair of date-time objects in UTC.
I solved this by manually formatting a date string (YYYY-MM-DD HH:MM:SS) and parsing this string using new Date(...)
. The resulting Date object is automatically converted from the user's local time zone to UTC, perfect for sharing an event across multiple time zones.
The finished application
I registered a domain name and deployed the application yesterday. You can try it out at https://ical.rip/. You can also see the source code (licensed MIT-0) on GitHub.
Thanks for reading!