Landlords and tenants were running a tenancy across email, phone calls and paper, so the record of what was agreed depended on who was asked.
Real estate · React Native
Moonrock
Rent, leases and maintenance for tenants and landlords, on one mobile app.
- Product
- Moonrock PM — landlord and tenant property management
- Role
- Developer and team lead, Dixeam
- Scope
- React Native app for iOS and Android, Stripe rent collection, maintenance workflow

The tension
Rent, leases and repairs are three conversations between the same two people.
The two sides need different views of the same tenancy, not different products. Once that was the frame, one app with two roles replaced the idea of two apps.
Rent collected on schedule without a reminder, and a maintenance request that both sides can see the state of.
Recurring money and a subscription both run through the same app, on two platforms, for users who will not tolerate a second tool.
The product story
One tenancy, two roles.
The relationship
Simplifying the landlord-tenant relationship.
The landing view frames the product as the relationship rather than as property software, because that is the problem both sides recognise.

Narrow first
This is a phone product.
The marketing site reflows to the width the app itself is used at, keeping signup and pricing reachable throughout.

The offer
One subscription, both platforms.
Pricing is a single monthly plan, presented alongside the app availability so the commitment is legible before signup.

Walkthrough
The product in motion
Recorded from the live site.
The trust test
The happy path is only half the product.
A rent payment fails
Payment state is separate from tenancy state, so a failed charge is a recoverable payment rather than a tenancy that looks broken to either side.
The tenant has no signal
The app is written against an offline-tolerant read path, so viewing a lease or a request does not require connectivity that a basement flat may not have.
Both sides act on one request
State transitions are server-owned, so a stale client cannot close a maintenance request the other side has already reopened.
The decisions
Three moves carried the story.
One codebase, two roles
React Native across iOS and Android, with role deciding the view rather than the binary, so a tenancy has one implementation and cannot drift between platforms or sides.
Make maintenance a tracked state
Requests move through explicit states rather than living as messages, so both sides can see where a repair is without reconstructing it from a thread.
Let Stripe own the recurring money
Rent collection runs on Stripe subscriptions rather than a scheduler of our own, so retries, failures and receipts are handled by the system built for them.
What I would test next
