Skip to main content
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:
ComponentButterCMS FeaturePurpose
EventsPage TypeIndividual event pages with full details
CategoriesCollectionOrganize events by type (Conference, Workshop, etc.)
VenuesCollectionReusable location information
SpeakersCollectionPresenter profiles (optional)

Step 1: Create event categories collection

Create a Collection for event categories:
  1. Go to Content TypesCollections
  2. 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:
  1. 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.