Calrows
You have a list of shifts, sessions, bookings or deadlines in a spreadsheet, and you want them on a calendar. There are three ways, and the right one depends on how often the list changes.
Google Calendar™ has no "import from Sheets" button. The closest built-in route is to save the sheet as CSV and use Calendar's Settings › Import, which expects the exact column names Subject, Start Date, Start Time, End Date, End Time, All Day Event, Description, Location. It works once. It cannot update an event later; importing the same file again creates duplicates.
A short script reads each row and calls CalendarApp.createEvent(title, start, end). This is what most tutorials show, and it's a good way to learn Apps Script. The parts tutorials skip are the ones that bite later: storing the event ID so a second run updates instead of duplicating, handling all-day and multi-day events, staying under Google's six-minute execution limit on large sheets, and telling you which row failed and why. Each is a few dozen lines, and they're yours to maintain when Google changes something.
function rowsToCalendar() {
var cal = CalendarApp.getCalendarById('you@example.com');
var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
for (var i = 1; i < rows.length; i++) {
var r = rows[i];
if (!r[0] || !(r[1] instanceof Date)) continue;
cal.createEvent(r[0], r[1], r[2] || new Date(r[1].getTime() + 3600000));
}
}
That's the minimum. Run it twice and you have every event twice.
Calrows is a Sheets add-on that does the script's job with the missing parts included. You map your columns once, click "Send rows to Calendar", and each row becomes an event. A hidden column remembers the event ID, so editing the sheet and sending again updates the events. Big sheets are processed in chunks that never hit the time limit. Every run is written to a Sync Log tab with the row number and the reason for any failure.
The free plan covers 50 new events a month. Pro adds automatic sync on a schedule, so the sheet becomes the source of truth for the calendar without anyone clicking.
| Column | Example |
|---|---|
| Title | Team standup |
| Start | 2026-10-01 09:00 (a real date-time cell, not text) |
| End | 2026-10-01 09:30 (optional; default length is 60 minutes) |
| All day | TRUE or a checkbox |
| Description, Location, Guests, Color | optional |
The most common failure across all three methods is a Start column stored as text. Format the column as Date time before you begin.