Friends, many of you have asked for a way to let website visitors submit events to your Event Calendar widget.
While this is not supported out of the box yet, our awesome devs came with a great solution:
-
visitors submit events via Form Builder
-
you approve them in Google Sheets
-
approved events are pushed to your Google Calendar
-
Event Calendar widget pulls them in automatically
If you want to try this on your site, follow the steps below. We’ll walk you through the full setup ![]()
What are the steps? 
Create Form Builder to collect events
The 1st step is to create a Form Builder widget to let users submit events to your calendar, The Form Builder widget must include the following fields:
- Short text — Event name (required)
- Long text — Event description
- File — Event image
- Dropdown — Event type
Scheduled(default)All day
- Date — Start date (required)
- Time — Start time
Condition: show only if the event type is set to “Scheduled”
- Date — End date (required)
- Time — End time
Condition: show only if the event type is set to “Scheduled”
- Dropdown — Event repeat
Does Not Repeat(default)
Once all the fields are created, open the Integrations section and enable the Google Sheets integration:
Create a calendar in Google Calendar
- Click on the “+” button in the Other Calendars section in the Google Calendar and create a new calendar (name can be anything)
- The new calendar will appear in My Calendars section. Click on the 3 dots next to this calendar and choose Settings and Sharing
- Scroll down to the Integrate calendar section and save your Calendar ID. You’ll need it later:
Set up Google Spreadsheet
Fill in the form and submit a test event. The connected spreadsheet should then contain these columns:
- Event name — Event name
- Event description — Event description
- Event image — Event image
- Event type — Event type
- Start date — Start date
- Start time — Start time
- End date — End date
- End time — End time
- Event repeat — Event repetition
- Date — Additional field for selecting a date (if needed)
After that add an additional column called Approved.
Add Apps Script integrations
- Open Extensions → Apps Script.
- Create a script file named constants and add:
const CALENDAR_ID = "your_calendar_id@group.calendar.google.com";
Do not forget to replace "your_calendar_id@group.calendar.google.com" with the ID of your Calendar from the step 2.
- Create a file named testCalendarAccess and add:
function testCalendarAccess() {
const cal = CalendarApp.getCalendarById(CALENDAR_ID);
Logger.log("Calendar object: " + cal);
if (cal) Logger.log("Calendar name: " + cal.getName());
}
- While in testCalendarAccess, click Run. The execution log should open.
- If you see an error, try again.
- After successful authorization, the execution log should show script messages like:
Calendar object: CalendarCalendar name: calendar name
- Create the following script files:
- utils
function getHeader(sheet) {
return sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
}
function findColumn(header, name) {
return header.indexOf(name) + 1;
}
function getHoursMinutes(value) {
if (!value) return null;
if (value instanceof Date) {
return {
h: value.getHours(),
m: value.getMinutes()
};
}
if (typeof value === 'number') {
const totalMinutes = Math.round(value * 24 * 60);
return {
h: Math.floor(totalMinutes / 60) % 24,
m: totalMinutes % 60
};
}
if (typeof value === 'string') {
const match = value.match(/(\d+):(\d+)/);
if (match) {
return { h: parseInt(match[1]), m: parseInt(match[2]) };
}
}
return null;
}
- getEventValues
function getEventValues(e) {
const sheet = e.source.getActiveSheet();
const header = getHeader(sheet);
const range = e.range;
const row = range.getRow();
const eventNameCol = findColumn(header, "Event name");
const eventDescriptionCol = findColumn(header, "Event description");
const eventImageCol = findColumn(header, "Event image");
const startDateCol = findColumn(header, "Start date");
const endDateCol = findColumn(header, "End date");
const startTimeCol = findColumn(header, "Start time");
const endTimeCol = findColumn(header, "End time");
const eventName = sheet.getRange(row, eventNameCol).getValue();
const eventDescription = sheet.getRange(row, eventDescriptionCol).getValue();
const eventImage = sheet.getRange(row, eventImageCol).getValue();
const startDate = sheet.getRange(row, startDateCol).getValue();
const endDate = sheet.getRange(row, endDateCol).getValue();
const startTime = startTimeCol ? sheet.getRange(row, startTimeCol).getValue() : null;
const endTime = endTimeCol ? sheet.getRange(row, endTimeCol).getValue() : null;
return { eventName, eventDescription, eventImage, startDate, endDate, startTime, endTime };
}
- onEditApproved
function onEditApproved(e) {
const sheet = e.source.getActiveSheet();
const header = getHeader(sheet);
const range = e.range;
const approvedCol = findColumn(header, "Approved");
if (!approvedCol || range.getColumn() !== approvedCol) {
return;
}
const editedValue = String(e.value || "").toLowerCase();
if (editedValue === "approved") {
const { eventName } = getEventValues(e);
createCalendarEvent(e);
Logger.log(eventName + " approved and added to Calendar");
}
}
- createCalendarEvent
function createCalendarEvent(e) {
const {
eventName,
eventDescription,
eventImage,
startDate,
endDate,
startTime,
endTime
} = getEventValues(e);
if (!eventName || !startDate || !endDate) {
return;
}
let start, end;
if (typeof startDate === 'string') {
const [day, month, year] = startDate.split('.');
start = new Date(year, month - 1, day);
} else {
start = new Date(startDate);
}
if (typeof endDate === 'string') {
const [day, month, year] = endDate.split('.');
end = new Date(year, month - 1, day);
} else {
end = new Date(endDate);
}
if (startTime && endTime) {
const s = getHoursMinutes(startTime);
const en = getHoursMinutes(endTime);
if (s && en) {
start.setHours(s.h, s.m, 0, 0);
end.setHours(en.h, en.m, 0, 0);
}
}
const calendar = CalendarApp.getCalendarById(CALENDAR_ID);
if (!calendar) {
Logger.log("Calendar not found: " + CALENDAR_ID);
return;
}
const descriptionParts = [];
if (eventImage) {
descriptionParts.push(
`<a href="${eventImage}" target="_blank" aria-hidden="true"></a>`
);
}
if (eventDescription) {
descriptionParts.push(eventDescription);
}
const fullDescription = descriptionParts.join("\n\n");
try {
calendar.createEvent(eventName, start, end, {
description: fullDescription
});
Logger.log(eventName + " approved and added to Calendar");
} catch (error) {
Logger.log("Error creating event: " + error.message);
}
}
- Go to Triggers and click Create a new trigger:
- Choose function: onEditApproved
- Deployment: Head
- Event source: From spreadsheet
- Event type: On edit
Create the Event Calendar widget
Create the Event Calendar widget and connect to your Google Calendar
Publish submitted events to your calendar
You’re all set.
Open your Form Builder widget (created in the 1st step) and the connected Google Sheet. Each new submission will land there as a new row. To publish an event to your calendar, type Approved in the Approved column for that row:
![]()
This video demonstrates the feature in action:
Have you tested this solution yet? Please let us know if it worked for you - we’re all ears






















