Event Espresso

Senior React Engineer

Oct 2019Apr 20211.5 years

What I did

  • Modernized the WordPress plugin admin with React and GraphQL for the Event Smart SaaS platform, establishing SASS/BEM architecture and component-library patterns.
  • Contributed open-source PRs with unit (Jest) and E2E (Playwright) test coverage.

Tech stack

  • React 16
  • React Apollo
  • GraphQL
  • WPGraphQL
  • WordPress
  • PHP
  • MySQL
  • Jest
  • Playwright

Case study

Event Espresso is a WordPress plugin for selling event tickets and managing registrations. It comes two ways: a plugin you host yourself, and Event Smart, a hosted version. I worked on Barista — the project that rebuilt the plugin's admin screens from scratch in React, backed by a GraphQL API, shipped in Event Espresso 5. Everything below is drawn from the open-source codebase.

What Barista replaced

The old admin was jQuery and PHP: server-rendered pages with bits of JavaScript on top. Barista replaced it with a single-page-app feel — a React interface talking to a typed API.

The code lives in two repositories that move together:

  • Barista — the React and JavaScript front end
  • cafe — the PHP core (published publicly as event-espresso-core)

One feature is one change across both. You open the pull request in Barista and link it to cafe, so the UI and the API ship as a pair.

How the front end is built

Rather than one big bundle, the UI is split into small packages that each build on their own:

  • components — shared React UI building blocks
  • hocs and editor-hocs — reusable behavior wrapped around components
  • data-stores — client-side state, in the WordPress-data style
  • core-blocks and blocks-frontend — Gutenberg editor blocks
  • model, value-objects and validators — the typed data layer the UI shares
  • eejs-core — the loader that registers every package at runtime

Each package compiles to its own versioned file, and a small PHP layer reads a manifest to load the right files into WordPress.

Why this is Module Federation in spirit

Start with the problem. The simplest way to ship a web app is to bundle all the code into one big file. That works until the app grows: every small change rebuilds the whole thing, the file gets slow to download, and separate teams keep stepping on each other because they all live in the same bundle.

Module Federation is a way out of that. The idea, in one sentence: break the app into independent pieces, load each piece only when it is needed, and let the pieces share common libraries instead of each shipping its own copy. A useful picture is a power strip — one set of shared outlets (React, the framework) that many separate appliances (the pieces of the app) plug into, instead of every appliance carrying its own generator.

The pieces have names worth knowing:

  • A host is the app that runs first and provides those shared outlets.
  • A remote is an independent piece the host loads on demand.
  • Shared dependencies are the libraries (like React) that the host hands out once, so every remote uses the same copy.

Barista is built exactly this way — it just predates the "Module Federation" label:

  • eejs-core is the host. It boots first and hands out the shared libraries.
  • Each package is a remote. It is built and shipped on its own and loaded only when its screen is opened.
  • React and the WordPress globals are shared once. Every package reuses that single copy instead of bundling another.
  • Add-ons from other teams plug into the same host by registering with the manifest — no rebuild of the core required.

And the reason this matters: the genuinely hard parts of Module Federation were the everyday work here, not theory.

  • Keeping shared versions in sync. Because the pieces share libraries instead of each bringing their own, they all have to agree on which version to share. If one piece is written for React 17 and another for React 18, they can not safely use the same shared copy — and you get the kind of bugs that are painful to track down: hooks throwing errors, state that silently does not update, or two parts of the screen disagreeing about what is loaded. The fix is coordination, not cleverness: the host declares a single agreed version, every piece is built against that same version, and the build checks the declared versions line up before anything ships. The cost shows up whenever the shared version changes — bumping React, for example, is not a one-line edit in one place. It is a coordinated bump across every package at once, plus the add-ons that plug in, all released together so nothing is left expecting the old version. Treating that shared version as a contract — and keeping every piece honest to it — was a constant, deliberate part of the work.
  • Making sure there is only one copy. Two copies of React loaded at once break things like hooks and shared state, so the host had to guarantee a single shared instance.
  • Loading only what is needed. A screen's code downloads when you open that screen, not on first page load — the same lazy-loading Module Federation is built around.

So moving to a modern Module Federation setup is not new ground — it is the same model with newer tooling and the proper name.

The GraphQL layer

The React admin talks to the server over GraphQL (built on WPGraphQL). The server owns the schema — events, dates, tickets and prices, plus the queries and mutations on them — and the client reads it through React Apollo with typed hooks. One typed contract, from the database to the component.

What I did

  • Built and modernized the admin and event-editor screens in React on top of the GraphQL API — 50+ merged pull requests across the open-source repositories.
  • Set the SASS and BEM styling conventions and the reusable component patterns used across the packages.
  • Wrote higher-order components, form and validation logic, and wired UI state through the data stores.
  • Added Jest unit tests and Playwright end-to-end tests.

A change, end to end

A typical event-editor change touched three things: a React component and its store in Barista, a GraphQL field or mutation in cafe, and the manifest entry that loads the new bundle into WordPress. Jest and Playwright had to pass before it merged. The result was a WordPress plugin that behaves like a single-page app — a typed, component-driven admin over a schema-first API, instead of old server-rendered PHP pages.