2

Bill Splitter

Bill Splitter is a phone-first, offline web app for splitting group bills item by item in any currency. It works out what everyone owes, to the penny, and the fewest payments to settle up. It runs entirely on the device as a PWA on Cloudflare Workers.

Overview

You enter a bill line by line and tap the names of whoever had each dish. The app shows each person's share in the group's home currency, and across the whole trip it suggests who pays whom to settle up. There are no accounts and no server: data stays on the device, and the app works offline once it's on your home screen.

Splitting a bill line by lineEach person's share of a billTrip balances and suggested payments

Features

  • Line-by-line splitting with name buttons, an "Everyone" option and double shares
  • Tips, fees and discounts as an amount or a percentage, split equally or by what each person ordered
  • Any currency: cash bills use the rate from your recorded withdrawals, card bills use the amount your bank charged
  • Arithmetic in every amount field, like 2×350 or 200/173.40
  • Trip balances, with the fewest payments needed to settle up, shareable through the phone's share sheet
  • An optional Splitwise mode, with copy buttons and tracking of bills that changed after they were entered
  • A demo trip, so you can try it without entering anything

Technical details

The app is built with React 19, TypeScript, Vite and Tailwind CSS. It's an offline-first PWA deployed as a static-assets Cloudflare Worker.

The split logic is a pure, fully tested TypeScript module. Each bill is converted to the home currency once, then shared out in minor units using the largest-remainder method. The shares therefore always add up exactly to the amount on the bank statement, including currencies with no decimal places, like yen, or three, like the Kuwaiti dinar. Real bills are kept as regression tests.

State lives in Zustand with Immer, saved to localStorage in a documented, versioned format. Migrations upgrade both saved data and imported backups, which leaves room for sharing trips later. Vitest and Biome run in GitHub Actions on every push.