← Blog

08/18/2026

Building My Event Rental Hub: a rental shop's brain, still in progress

I am building a multi-tenant app for event rental shops so they can tell if the chairs are actually free that weekend. It is live, the core loop works, and I am still putting the rest of it together.

saasevent-rentalsupabasebuild-in-public

Event rental shops do not just own chairs, tables, and tents. They have to know if 200 chairs are actually free for a Saturday wedding after last weekend's job, the buffer to get them back, and the quote they already sent that might turn into an order. That is the problem I built My Event Rental Hub around.

I figured it would be a simple inventory app date-range availability engine with quotes, orders, kits, and a customer portal. It is live at myeventrentalhub.com, and I am still building it.

What it is

My Event Rental Hub is a multi-tenant SaaS for rental companies. Each shop gets its own org, inventory, warehouses, quotes, and orders. Staff can build a quote, send a link, take a signature, convert it to an order, and take payment. Customers do not make an account. They just open the link.

The core loop is working: inventory, quotes, orders, a portal, PDFs, and Stripe so the shop gets paid on their own account. Other pages like calendar, deliveries, reports, and scan are still placeholders. I am not going to sit here and pretend the whole ERP is done.

Availability is not CRUD

The hard part is not listing products. It is answering one question for a date range: can I rent this. A quote is a soft hold. It shows up as pressure so you know somebody else is looking at those chairs, but it does not lock them. An order is a hard hold. That writes inventory reservations and actually takes the units.

Then it gets messier. Products have buffer days after a return before they can go back out. Kits are not real stock. A tent package is only as available as the scarcest component inside it. And if the event is far enough out, a shop can oversell a little and put the shortfall on an acquisition list, because they have time to buy or borrow more.

All of that lives in a Postgres function called get_availability. The quote builder just calls it and paints a badge. I have a SQL test file just for that function, because I kept finding cases where the number looked right until a kit or a second quote showed up.

The stack

The app is Next.js 14 with the App Router, TypeScript, and Tailwind. Supabase handles Postgres, auth, storage, and row level security. TanStack Query keeps server state, Zustand is just for UI stuff, and forms go through React Hook Form plus Zod.

PDFs are @react-pdf/renderer. E-sign is a canvas signature mashed into the PDF with pdf-lib. Emails get queued in an outbox table and drained through Resend. Payments for the shop run through Stripe Connect. Hosting is Cloudflare with OpenNext and Wrangler, same family as the other stuff I have been shipping.

Shops stay isolated, and they get paid themselves

Every tenant table has an org_id, and RLS is on. I used a couple of security definer helpers, auth_org_ids and auth_has_role, so a user can only see the orgs they belong to and write if they have the right role. That is the backstop. I do not want Shop A peeking at Shop B's quotes because I forgot a filter on one page.

For money, each shop connects their own Stripe Express account. Checkout and refunds run on that account. The platform is not sitting on their rental payments. SaaS billing for Hub plans is still a placeholder screen. Tenant payments are further along than me charging shops for the software.

The portal is just a link

I did not want customers making passwords for a one-time quote. They get a token link. They can view the quote, accept it, decline it, or sign it. The signature, IP, timestamp, and consent text get stored, then stamped onto a contract PDF. Quotes, contracts, and invoices all render as PDFs.

If they already signed, the same link just hands back the signed file. No customer login. If the token is expired or revoked, the link is dead.

The parts that gave me trouble

Availability kept biting me. First the SQL function had loop variables that shadowed aliases, so Postgres yelled that a record was not assigned yet. Then kits were lying. A package could show as available because one component had no soft holds, even when the scarce piece was already sitting on another quote. I had to stop treating kit soft holds like a simple min() and derive them from what was actually uncommitted.

Turning a quote into an order was the other gnarly one. That has to happen in one transaction. It locks the products in a sorted order so two staff members cannot convert overlapping quotes at the same time, re-checks availability, writes the hard-hold reservations, and either finishes or rolls back. Partial conversion is not allowed. There is a SQL test for that path too, plus a concurrency test, because this is the kind of thing that looks fine until two people click Convert.

Still building it

The site is up. You can sign up, put inventory in, send a quote, get a signature, convert it, and take a payment. That is the part I wanted real first. Calendar, deliveries, reports, warehouse scan, procurement, and the self-serve billing portal are still ahead of me. I would rather say that now than launch this like it is finished.

If you run a rental shop, or you have been anywhere near one, I would love to hear what I am missing. Leave a comment below or email me at jared@jcworkshop.com.

myeventrentalhub.com

Comments

Got thoughts, better tools, or a stack tip? Leave a comment below, or email me at jared@jcworkshop.com.

Loading comments…

Email Jared