Volunteer coordinators
Rota
A fair rota, and an answer to "why me again?"
Shift rotation for organisations that run on volunteers. It drafts a fair month, shows the reason for every assignment, and lets people arrange their own cover — so the coordinator is not the person every swap has to go through.
- Next.js 16
- TypeScript
- Supabase
- PostgreSQL
- Row-level security
- Resend
- Scheduled jobs
- Magic-link sign-in
- Building a month
- Draft + publish
- Every shift
- Has a reason
- Swaps
- Volunteer to volunteer
never auto-assigned live
shown to the volunteer
coordinator not in the loop
What was going wrong
The rota was a spreadsheet the coordinator rebuilt every month, and every swap came back to them as a text message. The same two people kept getting Sunday because they were the two who never complained, and nobody could see that had happened until one of them stopped turning up.
What we built
Make the fairness visible and the swaps somebody else's job. The engine drafts a month against who has done what, and writes a reason onto every assignment that the volunteer can read. Cover is arranged between volunteers with a claim that is atomic, so two people cannot both believe they got it. Reminders are deliberately limited to two kinds — an app that mails you every morning gets filtered to a folder.
What it looks like
Every screen shown on a desktop and on a phone, because half of these are used standing up.
Drafting the month
The generated rota before it is published, with the reason on every row and the confirmed shifts left where they were.
/rota/plan
Desktop capture
1440 × 900
Mobile capture
390 × 844
The swap board
Shifts people have offered, and one button to take one. The claim is a conditional update, so a tie has exactly one winner.
/rota/swaps
Desktop capture
1440 × 900
Mobile capture
390 × 844
The public board
Open slots at a guessable address, claimable without an account — and deliberately carrying no names.
/board/[slug]
Desktop capture
1440 × 900
Mobile capture
390 × 844
Your own record
What you have covered, what is coming, and milestones measured against your own history rather than against other volunteers.
/rota/me
Desktop capture
1440 × 900
Mobile capture
390 × 844
What it does
Drafted, not imposed
Generate a month, look at it, change it, then publish. Three separate steps on purpose.
Every assignment has a reason
"You have not had a Sunday since March" is on the row, visible to the volunteer, before they ask.
Self-service swaps
Offer a shift, someone takes it, nobody phones the coordinator. Taking one is an atomic claim, so two people tapping at once cannot both get it.
Claim from a link
A public board with the open slots on it, which someone can claim without an account.
Two reminders, both earned
Day-before, and unfilled-at-48-hours to people who said they were free. Never before eight or after nine.
Coverage as a shared number
One figure that belongs to everybody, rather than a leaderboard that belongs to the loudest.
Who reaches for it, and when
A coordinator on the first of the month
Generates a draft, moves two things, publishes, and is finished with an evening's work in ten minutes.
A volunteer with a wedding on the 12th
Offers cover from their phone and keeps the shift until somebody actually takes it.
A Sunday nobody has claimed
At 48 hours the app messages the handful of people who said they were free that day, and nobody else.
What makes this one different
Offering cover does not release the shift
The volunteer stays on it until someone takes it, so asking for help never creates a hole in the rota. Most swap features get this backwards.
Re-drafting never moves a confirmed shift
Somebody who has arranged their week around a Saturday keeps it. The engine only fills what confirmations have not.
There is no ladder, and there will not be one
Ranking volunteers against each other loses the quiet person doing two shifts a month. Recognition here is against your own record only — the reasoning is written into the migration so it outlives this page.
A public claim is a request, not a booking
The app cannot verify who typed a name into an open board, so a coordinator resolves it and two claims on one slot both arrive. The board also shows no names, because a guessable URL with a rota of names on it is a data leak with a friendly address.
Built with
What each piece is doing here — the people who read this section are usually the ones who would have to maintain it after us.
Next.js 16
The application framework. Pages render on the server by default.
TypeScript
Strict mode everywhere. A field renamed in one place fails to compile in the others.
Supabase
Postgres, authentication, file storage and realtime in one managed service.
PostgreSQL
The database. Constraints and permissions live here, not in the interface.
Row-level security
Who can read which row is decided by Postgres. A UI bug cannot leak data.
Resend
Transactional email — invitations, reminders and sign-in links.
Scheduled jobs
Nightly work — reminders, recomputed state, and the demo reset.
Magic-link sign-in
An emailed link instead of a password nobody can remember.
Want something like Rota?
It does not have to be your industry — the underlying problem is nearly always the same. Tell us how the work moves through your business and we will tell you what it would take.
Every project is quoted on its own. Turnaround starts at two weeks.