The below examples use React , but the steps inside the dashboard will be the same for all frameworks.
Related blog content: build an event ticketing app with ButterCMS & Quarkus Develop a functional ticketing app with Quarkus and ButterCMS - book tickets, receive a confirmation message, and more.
What you’ll build
An event management system that includes:
Event listings with dates, times, and locations
Category-based organization
Upcoming/past event filtering
Event detail pages
Integration with registration platforms
Calendar display
Architecture overview
We’ll use a combination of ButterCMS features:
Component ButterCMS Feature Purpose Events Page Type Individual event pages with full details Categories Collection Organize events by type (Conference, Workshop, etc.) Venues Collection Reusable location information Speakers Collection Presenter profiles (optional)
Step 1: Create event categories collection
Create a Collection for event categories:
Go to Content Types → Collections
Create “Event Categories” with fields:
Name (Short Text) - Category name
Slug (Short Text) - URL-friendly identifier
Color (Short Text) - Display color (hex code)
Icon (Media) - Category icon (optional)
Example categories:
Conference
Workshop
Webinar
Meetup
Training
Step 2: Create venues collection
Create a Collection for event venues:
Create “Venues” with fields:
Name (Short Text) - Venue name
Address (Long Text) - Full address
City (Short Text) - City name
Country (Short Text) - Country
Latitude (Number) - For map display
Longitude (Number) - For map display
Image (Media) - Venue photo
Capacity (Number) - Maximum attendees
Website (Short Text) - Venue website URL
Step 3: Create the event Page Type
Create a Page Type for events with these fields:
Basic information
Title (Short Text): Event name
Description (WYSIWYG): Full event description
Featured Image (Media): Event banner image
Category (Reference): Link to Event Categories
Date & time
Start Date (Date): Event start
End Date (Date): Event end
Timezone (Dropdown): Event timezone
Location
Event Type (Dropdown): In-Person, Virtual, Hybrid
Venue (Reference): Link to Venues (for in-person)
Virtual Link (Short Text): Video conference URL (for virtual)
Registration
Registration URL (Short Text): External registration link
Registration Deadline (Date): Last day to register
Price (Number): Ticket price (0 for free)
Max Attendees (Number): Capacity limit
SEO
Meta Title (Short Text)
Meta Description (Long Text)
Step 4: Fetch and display events
Speed things up with an SDK These code examples use native libraries like fetch and requests to pull data from the API. For your actual implementation, we recommend using one of our many SDKs instead, which come with prebuilt support for the various content type API endpoints, as well as features like timeout, error handling, query parameters, and more.
Fetch all events
// Fetch all events
const response = await fetch (
`https://api.buttercms.com/v2/pages/event/?auth_token= ${ API_TOKEN } `
);
const events = ( await response . json ()). data ;
# Python example
from butter_cms import ButterCMS
client = ButterCMS( 'YOUR_API_KEY' )
events = client.pages.list( 'event' )[ 'data' ]
API response structure
{
"data" : [
{
"slug" : "tech-conference-2024" ,
"fields" : {
"title" : "Tech Conference 2024" ,
"description" : "<p>Join us for the biggest tech event...</p>" ,
"featured_image" : "https://cdn.buttercms.com/..." ,
"category" : {
"name" : "Conference" ,
"slug" : "conference" ,
"color" : "#3B82F6"
},
"start_date" : "2024-06-15T09:00:00Z" ,
"end_date" : "2024-06-17T18:00:00Z" ,
"timezone" : "America/New_York" ,
"event_type" : "In-Person" ,
"venue" : {
"name" : "Convention Center" ,
"address" : "123 Main St" ,
"city" : "San Francisco" ,
"country" : "USA"
},
"registration_url" : "https://eventbrite.com/..." ,
"price" : 299 ,
"max_attendees" : 500
}
}
]
}
Step 5: Filter upcoming vs past events
function filterEvents ( events ) {
const now = new Date ();
const upcoming = events . filter (
( event ) => new Date ( event . fields . start_date ) > now
);
const past = events . filter (
( event ) => new Date ( event . fields . end_date ) < now
);
// Sort upcoming by start date (soonest first)
upcoming . sort (( a , b ) =>
new Date ( a . fields . start_date ) - new Date ( b . fields . start_date )
);
// Sort past by start date (most recent first)
past . sort (( a , b ) =>
new Date ( b . fields . start_date ) - new Date ( a . fields . start_date )
);
return { upcoming , past };
}
Step 6: Build the events UI
Event card component
function EventCard ({ event }) {
const { fields } = event ;
const startDate = new Date ( fields . start_date );
return (
< div className = "event-card" >
< img src = { fields . featured_image } alt = { fields . title } />
< div className = "event-date" >
< span className = "month" >
{ startDate . toLocaleDateString ( 'en-US' , { month: 'short' }) }
</ span >
< span className = "day" > { startDate . getDate () } </ span >
</ div >
< div className = "event-info" >
< span
className = "category"
style = { { backgroundColor: fields . category . color } }
>
{ fields . category . name }
</ span >
< h3 > { fields . title } </ h3 >
< div className = "meta" >
< span className = "time" >
{ startDate . toLocaleTimeString ( 'en-US' , {
hour: '2-digit' ,
minute: '2-digit'
}) }
</ span >
{ fields . event_type === 'In-Person' && fields . venue && (
< span className = "location" >
{ fields . venue . city } , { fields . venue . country }
</ span >
) }
{ fields . event_type === 'Virtual' && (
< span className = "location" > Online Event </ span >
) }
</ div >
< div className = "price" >
{ fields . price === 0 ? 'Free' : `$ ${ fields . price } ` }
</ div >
< a href = { `/events/ ${ event . slug } ` } className = "btn" >
Learn More
</ a >
</ div >
</ div >
);
}
Events list with filtering
function EventsList ({ events }) {
const [ filter , setFilter ] = useState ( 'upcoming' );
const [ categoryFilter , setCategoryFilter ] = useState ( 'all' );
const { upcoming , past } = filterEvents ( events );
const displayEvents = filter === 'upcoming' ? upcoming : past ;
const filtered = categoryFilter === 'all'
? displayEvents
: displayEvents . filter ( e => e . fields . category . slug === categoryFilter );
return (
< div className = "events-page" >
< h1 > Events </ h1 >
< div className = "filters" >
< button
className = { filter === 'upcoming' ? 'active' : '' }
onClick = { () => setFilter ( 'upcoming' ) }
>
Upcoming ( { upcoming . length } )
</ button >
< button
className = { filter === 'past' ? 'active' : '' }
onClick = { () => setFilter ( 'past' ) }
>
Past ( { past . length } )
</ button >
</ div >
< div className = "events-grid" >
{ filtered . length === 0 ? (
< p > No events found </ p >
) : (
filtered . map (( event ) => (
< EventCard key = { event . slug } event = { event } />
))
) }
</ div >
</ div >
);
}
Step 7: Event detail page
function EventDetailPage ({ event }) {
const { fields } = event ;
const startDate = new Date ( fields . start_date );
const endDate = new Date ( fields . end_date );
return (
< div className = "event-detail" >
< img
src = { fields . featured_image }
alt = { fields . title }
className = "hero-image"
/>
< div className = "event-header" >
< span className = "category" > { fields . category . name } </ span >
< h1 > { fields . title } </ h1 >
</ div >
< div className = "event-meta" >
< div className = "date-time" >
< h3 > Date & Time </ h3 >
< p >
{ startDate . toLocaleDateString ( 'en-US' , {
weekday: 'long' ,
year: 'numeric' ,
month: 'long' ,
day: 'numeric'
}) }
</ p >
< p >
{ startDate . toLocaleTimeString ( 'en-US' , {
hour: '2-digit' ,
minute: '2-digit'
}) }
{ ' - ' }
{ endDate . toLocaleTimeString ( 'en-US' , {
hour: '2-digit' ,
minute: '2-digit'
}) }
</ p >
< p className = "timezone" > { fields . timezone } </ p >
</ div >
{ fields . event_type === 'In-Person' && fields . venue && (
< div className = "location" >
< h3 > Location </ h3 >
< p className = "venue-name" > { fields . venue . name } </ p >
< p > { fields . venue . address } </ p >
< p > { fields . venue . city } , { fields . venue . country } </ p >
{ fields . venue . website && (
< a href = { fields . venue . website } target = "_blank" >
Venue Website
</ a >
) }
</ div >
) }
{ fields . event_type === 'Virtual' && (
< div className = "virtual-info" >
< h3 > Virtual Event </ h3 >
< p > This event will be held online </ p >
</ div >
) }
</ div >
< div className = "registration-box" >
< div className = "price" >
{ fields . price === 0 ? 'Free Event' : `$ ${ fields . price } ` }
</ div >
{ fields . max_attendees && (
< p className = "capacity" > Limited to { fields . max_attendees } attendees </ p >
) }
< a
href = { fields . registration_url }
className = "register-btn"
target = "_blank"
>
Register Now
</ a >
{ fields . registration_deadline && (
< p className = "deadline" >
Registration closes: { ' ' }
{new Date ( fields . registration_deadline ). toLocaleDateString () }
</ p >
) }
</ div >
< div
className = "description"
dangerouslySetInnerHTML = { { __html: fields . description } }
/>
</ div >
);
}
Integration with external services
Third-party service UIs and embed formats may change. Verify current implementation details on each provider’s documentation.
Eventbrite integration
Store Eventbrite event IDs and use their embed widgets:
function EventbriteWidget ({ eventbriteId }) {
return (
< div
id = { `eventbrite-widget- ${ eventbriteId } ` }
dangerouslySetInnerHTML = { {
__html: `<iframe src="https://www.eventbrite.com/tickets-external?eid= ${ eventbriteId } " height="500" width="100%" frameborder="0"></iframe>`
} }
/>
);
}
Google Calendar integration
Generate “Add to Calendar” links:
function generateGoogleCalendarLink ( event ) {
const { fields } = event ;
const start = new Date ( fields . start_date ). toISOString (). replace ( / [ -: ] / g , '' ). split ( '.' )[ 0 ] + 'Z' ;
const end = new Date ( fields . end_date ). toISOString (). replace ( / [ -: ] / g , '' ). split ( '.' )[ 0 ] + 'Z' ;
const params = new URLSearchParams ({
action: 'TEMPLATE' ,
text: fields . title ,
dates: ` ${ start } / ${ end } ` ,
details: fields . description . replace ( /< [ ^ > ] * >/ g , '' ),
location: fields . venue ? ` ${ fields . venue . name } , ${ fields . venue . address } ` : 'Online' ,
});
return `https://calendar.google.com/calendar/render? ${ params } ` ;
}
Scheduled publishing
Use ButterCMS’s scheduled publishing to automatically publish events at the right time and unpublish them after they’ve ended.
Set up webhooks to trigger notifications when events are published or updated.