Platform Design

The Headless/UI Split: Building an SDK That Two Audiences Can Both Use

How splitting a fintech SDK into a headless layer and a UI layer let the platform work for both fast-moving and design-strict client teams, without forcing either to compromise.

By Anshul Kapoor·May 2026·7 min read
SDK DesignPlatform ArchitectureReact NativeAPI Design
LinkedIn

The setup

At BillGo, every new fintech integration started as a custom build. Each financial institution we partnered with had its own design system, its own engineering velocity, and its own definition of "ready to ship." On paper, that sounded fine. In practice, we kept rebuilding the same payment flows, the same auth handshake, and the same data-sync logic from scratch for every client.

The commercial team was selling integrations measured in months. The engineering team was carrying technical debt from every bespoke build. Some kind of SDK was the obvious fix. The harder question was what shape it should take.

The trap of picking one extreme

The instinct in a project like this is to pick a side. You can ship a headless library (pure logic, state, and API contracts) and let clients build all the UI themselves. Or you can ship a fully-built UI library and let clients drop in pre-styled components.

Both options solve the problem badly.

A headless-only SDK looks elegant to engineers but is brutal in practice. Every client team has to rebuild the same payment forms, the same error states, the same loading skeletons. The "fast time-to-integration" promise dies the moment a client realizes they still have weeks of UI work ahead of them.

A UI-only SDK looks great in demos but breaks at the first client with a real design team. The moment someone says "your modal doesn't match our typography scale," you're shipping a custom build anyway. At that point, the SDK is a starting point, not a platform.

Picking one extreme optimizes for the demo, not the actual integration.

The decision: ship both layers, make them composable

We built the SDK as two libraries. The core was headless: state management, API integration, data synchronization, validation rules. No UI, no opinions about how anything should look. Just the things every client would need exactly the same way.

The UI library sat on top of the core and shipped pre-built React Native components. It was opinionated but optional. Clients who wanted to ship fast could drop in the components and theme them with a single configuration object. Clients with their own design system could ignore the UI library entirely and consume just the headless layer. Same payment flows, same auth, but their own pixels.

The boundary between the two was strict, on purpose. The UI library could not add business logic. The headless library could not assume how its consumers would render anything. That contract is what made the SDK testable, documentable, and (most importantly) durable as new client requirements came in.

What it took to make the split actually work

The architecture was the easy part. Making the split actually pay off took investment in three places most SDK projects under-invest in.

First, reference apps. We built complete native iOS (Swift) and Android (Kotlin) apps using the SDK end-to-end. Not demos. Real apps. They caught integration edge cases before those edge cases ever reached clients, and they gave new client engineers something concrete to learn from.

Second, theming as a first-class API. We built the UI library on CSS variables and design primitives, exposing a single configuration object for brand colors, typography, and spacing. White-labeling needed zero custom builds. That was the difference between "you can theme it" and "you can theme it without us in the loop."

Third, automated end-to-end testing wired into CI. Detox running against real device simulators on every PR. For an SDK shipping to dozens of clients, this was non-negotiable. Regressions had to surface in our CI, not in their production.

The result and the lesson

The headless/UI split cut integration timelines from multi-month custom builds to days. End-to-end testing time dropped 50% once automation was in place. Most importantly, the SDK started serving two very different client types out of the same codebase, without either type feeling like they were compromising.

The lesson I keep coming back to is this: when you're building a platform, the highest-leverage decision is usually about boundaries. The headless/UI split looked like more work upfront. Two libraries instead of one. But it was the boundary itself that made the SDK actually useful at scale. Every decision after that (testing strategy, theming API, reference apps) got easier once the boundary was clear.

If you're building anything that has to serve multiple consumer profiles, whether that's internal teams moving at different speeds, external clients with different design systems, or products at different stages of polish, the question worth asking early is this: what's the seam that lets one foundation serve all of them?

The highest-leverage decision in a platform is usually about boundaries.

Found this useful?

Share it with someone who might be wrestling with the same problem.

ShareLinkedIn

Thanks for reading.

If this resonated and you're hiring for Senior/Staff Frontend roles, I'd love to chat.