(02) — SOFTWARE ENGINEER / STELLARWP (LIQUID WEB) / 2022—2025
The leading WordPress donation plugin, powering 100,000+ nonprofits. I shipped frontend and full-stack features across two major releases — building the visual form builder, admin dashboard interfaces, and mailing addon integrations.
GiveWP is an open-source WordPress plugin that turns any WordPress site into a fundraising platform. Donation forms, recurring giving, donor management, payment gateway integrations, email marketing — it's a full-stack fundraising toolkit that competes with platforms like Donorbox and Classy, but runs entirely within WordPress.
At the time I joined, GiveWP had 100,000+ active installations and was the most-used donation plugin on WordPress.org. The codebase had been in production since 2015 — a mature PHP monolith with jQuery-driven admin interfaces and a legacy form editor built on WordPress meta boxes.
I was hired as a frontend engineer on the core team at StellarWP (a division of Liquid Web). Over three years and 253 pull requests, I worked across the full stack — React frontends, PHP service providers, REST API endpoints, and addon integrations — shipping features in both the 3.0 and 4.0 major releases.
The 3.0 release was the biggest rewrite in GiveWP's history — 664,253 lines of code changed. The centerpiece was a completely new visual donation form builder that replaced the legacy meta-box editor with a modern block-based experience built on the WordPress Gutenberg infrastructure.
The form builder is a React 18 / TypeScript application that runs inside the WordPress admin. It uses @wordpress/block-editor, @wordpress/data, and react-beautiful-dnd for a drag-and-drop editing experience. State management runs through a React Context provider stack — EditorStateProvider, FormStateProvider, and ShortcutProvider — using useReducer with discrete actions for form settings, block state, and dirty tracking.
The builder renders an iframe preview of the live donation form with responsive viewport modes. Blocks span five categories — input fields, content, layout, custom fields, and addon blocks — each registered through WordPress's block API and backed by a PHP BlockModel on the server.
My contributions
I built responsive preview modes that replaced the fixed sidebar positioning with a width-responsive preview container. I worked on the block insertion point indicator, modal rendering across iframe contexts, the email template settings panel, header image controls with overlay configuration, and the onboarding tour flow for new users.
I also built the Terms and Conditions block, the anonymous donation block with its placeholder UI/UX, and the Donation Form Block v3 that supported both reveal and modal display modes on the frontend. In the shared form-builder-library package, I contributed the BlockNotice component, OptionsPanel enhancements, and currency control refactors.
Donation amount level descriptions
One of the larger features I owned end-to-end was donation amount level descriptions — allowing fundraisers to add contextual labels to each giving level (e.g., "$25 — feeds a family for a week"). This touched 33 files across the form builder UI, the migration pipeline, and the frontend templates. It required building the settings UI in the form builder, writing the v2-to-v3 migration step to carry over existing level data, and updating the rendering templates to display descriptions across all form designs.
GiveWP's admin dashboard follows a PHP service provider + React frontend pattern. Each entity — donations, donors, subscriptions, campaigns — has its own ListTable component backed by PHP endpoint classes and consumed by React frontends using swr for data fetching and Chart.js for visualizations.
I built the empty state designs for the donation, donor, and subscription tables — giving new users a clear onboarding path instead of a blank screen. I drove consistency work across list table pages, standardizing spacing, typography, and interactive patterns.
A more substantial piece was adding stat tiles to the Donor List Table. This required building a new REST API endpoint at give-api/v2/admin/donors/stats on the PHP side and rendering the stat tiles in React on the frontend. I followed the same pattern to add subscription stats and donor sorting by total amount given.
Later, I refactored the Donor Overview page into modular container components — extracting tightly coupled rendering logic into composable pieces. That single PR touched 645 lines and set the pattern the team followed for subsequent admin page architectures.
GiveWP's mailing addons — Mailchimp, Constant Contact, ActiveCampaign, ConvertKit — are separate plugins that register Gutenberg blocks into the form builder. When a donor submits a form, the block attributes determine which mailing list they're subscribed to, whether double opt-in is required, and what tags are applied.
The 3.0 release introduced a completely new block-based form architecture. Every existing v2 form needed a migration path to the new system — including addon configurations. I spearheaded the migration pipeline for all four mailing addons, writing the PHP migration steps that read legacy form meta and created the corresponding v3 blocks with the correct attributes.
class Mailchimp extends FormMigrationStep
{
public function process(FormMigrationPayload $payload): void
{
$meta = $payload->formMeta;
$attributes = [
'label' => $meta->getMailchimpLabel(),
'checked' => $meta->isMailchimpDefaultChecked(),
'doubleOptIn' => $meta->isMailchimpDoubleOptIn(),
'subscriberTags' => $meta->getMailchimpTags(),
'selectedAudience' => $meta->getMailchimpAudience(),
'sendFFMData' => $meta->shouldSendFFMDataToMailchimp(),
];
$payload->formBlocks->insertAfter(
'givewp/email',
BlockModel::make('givewp/mailchimp', $attributes)
);
}
}Each migration step followed the same contract: read the legacy meta through a FormMetaDecorator, build the block attributes, and insert the block at the correct position in the form layout. I wrote corresponding PRs in the private addon repos to register the new blocks and handle the runtime subscription logic.
The migration had to be non-destructive — v2 forms stayed functional while v3 forms ran alongside them. Donors never saw a broken subscription flow during the transition.
Modal rendering across iframes
The form builder renders the donation form inside an iframe for accurate preview. But modals — the StyledPopover, confirmation dialogs, and block settings panels — needed to render in the parent document, not the iframe. The iframe context created a mismatch between the DOM tree the modal targeted and the one the user interacted with. I fixed this by ensuring portal targets were correctly resolved based on the rendering context and that event propagation worked across the iframe boundary.
Migrating four mailing addons without breaking subscriptions
Each mailing addon stored configuration differently in v2 — some in per-form meta, some in global settings, some in both. Mailchimp had subscriber tags, audience selection, and a double opt-in flag. Constant Contact had email list selection. ActiveCampaign had its own meta structure. The migration steps needed to read these heterogeneous formats, normalize them, and produce consistent v3 block attributes.
The FormMetaDecorator pattern abstracted the per-addon meta access, letting each migration step focus on attribute mapping rather than meta-key archaeology. I wrote unit tests for each addon's migration to verify that legacy configurations round-tripped correctly into the new block format.
Shipping 3.0 on a team
This was my first major release cycle on an open-source product used by over 100,000 sites. The 3.0 release started as a separate feature plugin (givewp-next-gen) that was developed in parallel, then merged into core. Coordinating between the feature branch and main, managing addon compatibility, and ensuring v2 forms remained functional throughout the transition required a level of release discipline that solo projects don't. I shipped 16 PRs in the 3.0.0 release alone — form builder features, onboarding flows, design polish, and bug fixes across the final push.
The 4.0 release introduced campaign-based fundraising — organizing multiple donation forms under a single campaign with a shared goal, landing page, and performance analytics. This was a significant data model change: a one-to-many relationship between campaigns and forms, with new admin pages, WordPress blocks, and REST endpoints.
I built the Campaign Cover Block and Campaign Stats Block — two of the core WordPress blocks that render campaign data on the frontend. I designed the campaign overview page styling and structure, updated confirmation modals across list tables, and built the campaign welcome banner and intro modal for existing users.
On the dashboard side, I added campaign list table styling, shortcode support for embedding campaigns, the default form indicator, and the draft campaign page notice. I also replaced the donations table form filter with campaigns to reflect the new data hierarchy.
Over three years at StellarWP, I contributed across 6 repositories — the core plugin, the next-gen feature plugin, the form-builder-library, addon repos, and the design system. My work spanned both major releases and the ongoing maintenance between them.
In the final year, I led accessibility improvements across the admin interface — adding ARIA labels, keyboard focus management, color contrast fixes, semantic HTML, and proper heading hierarchy. Working on a product used by nonprofits reinforced that accessibility isn't a nice-to-have — it's a requirement.