Skip to content
← All work

Case study 052025

DevFest Lagos 2025 — Ticketing

As a frontend engineer on the DevFest Lagos team, I worked on the 2025 ticketing platform and its ticket-selection flow. The hard part wasn't selling tickets; it was letting one person buy several ticket types, across several days, for several different people — each with their own code.

DevFest Lagos 2025 ticketing platform
DevFest Lagos 2025 ticketing platform

TL;DR

Problem
One buyer, many tickets: different types, different days, different people.
Built
A selection flow where each ticket is its own object with its own recipient.
Result
Powered ticketing for a 4,000+ attendee conference.

The problem

DevFest Lagos is one of the largest developer conferences in the country — 4,000+ attendees across multiple days. For the 2025 edition we decided to build our own ticketing platform instead of leaning on a generic tool, so the flow could match how the conference actually runs: different ticket types, spread across different days, all under one brand.

I was a frontend engineer on the team. My part was the flow every attendee actually touched: choosing what to buy and who it was for.

What made it hard

On paper, "sell some tickets" sounds trivial. The real requirement wasn't. One person could buy:

  • several ticket types,
  • across several days,
  • and — the part that changed everything — for other people, not just themselves.

That last point is what turned a shopping cart into a real modelling problem. If I buy three tickets, they might be for three different people, and each of them needs to receive their own code at their own email address. A "quantity: 3" counter can't express that. Each ticket is its own thing, with its own owner.

How I built it

I modelled the selection as an array of individual ticket objects in Redux, rather than a map of type → quantity. Each object carried everything one ticket needed to stand on its own:

{ type, day, recipientEmail }

Making the ticket the atomic unit — instead of a quantity — is what let the multi-recipient requirement fall out naturally. Three tickets meant three objects, each free to have a different type, a different day, and a different recipient. Redux held the whole selection as the single source of truth, so it survived across the steps of the flow and was exactly what checkout read from when it was time to pay.

The ticket-selection step: Standard and Pro ticket tabs above a multi-day November calendar, with a live order summary and a stepped Select Dates → Ticket Details → Buyer Information flow.

You can see both requirements in one screen: the Standard / Pro tabs are the ticket types, the calendar is the multi-day pick, and the Select Dates → Ticket Details → Buyer Information steps are where each ticket picks up its recipient. The order summary updates live off the same Redux selection.

The decision

For the guest emails I went with an enter-as-you-add flow: the moment you add a ticket, you say who it's for.

The alternative — let people pile up tickets and assign emails at the very end — front-loads a confusing empty cart and back-loads a wall of anonymous email fields. Enter-as-you-add keeps every ticket complete the instant it's created, so the buyer is never staring at a list of nameless tickets wondering which is which.

The trade-off: it's slower for someone buying a lot of tickets at once (more on that below).

A wrong email meant a real, paid ticket landing in the wrong inbox — so the selection couldn't advance to payment until every ticket had a valid recipient attached. Validating at the point of selection, rather than after checkout, kept a bad code from ever being issued.

Impact

The platform shipped and powered ticketing for the 2025 conference — it's live at the link above.

What I'd do differently

Enter-as-you-add is the right call for a handful of tickets, but a buyer bringing a whole team of ten still types ten emails one at a time. Given another pass, I'd add a bulk-assign step — paste or upload a list of emails and map them to tickets at once — without giving up the per-ticket model underneath, since that model is what made everything else possible.