Turn one app into everyone's app
I had a weather page with four pinned family cities and exactly one configuration: mine. This is a build log about making it everyone's, converting a single-instance app into one anybody can spin up their own copy of, sharing the tenancy core across apps, reusing the renderer untouched, and surfacing the whole thing as a tile in an add-ons marketplace.
There's a public weather page on my site. Four family cities pinned, an animated sky, a little colored mark for each person. I love that thing. Problem is, it has exactly one configuration: mine. So this is a build log about making it everyone's - I want to take a single-instance app and turn it into something anybody can spin up their own copy of, then drop that capability somewhere people will actually find it.
One app, then many
The marketplace already treats a few of the site's own apps as launchable tiles: click one, you're in the app. Now I want to push those apps multi-tenant. Give an app real instances, its own members, its own per-instance data, and the tile changes what it promises. Before, it meant "open my app." After, it means "spin up your own."
Weather is the first thing I'm converting. Here's the flow: a signed-in person 1-clicks a board at /w/<slug>, drops in the cities that matter to them (search comes from a key-free geocoder, or there's a one-tap "use my location"), tags who lives where, and emails family an invite to share the board. Meanwhile the public /weather showcase doesn't move at all. Four fixed cities, no account, the front window.
The tenancy core is shared, not copy-pasted
Every multi-tenant app needs the same boring plumbing. Friendly unique slugs. Email normalization. Some stable short id you can pin to each member. My first one, a set of shared to-do boards, already had all of it, so I wasn't writing that code again. I was extracting it. Now the slug/email/member-id primitives live in one framework-agnostic module. The to-do boards re-export them so nothing breaks; the weather boards import them directly. Spin up a new tenant app and you compose those primitives instead of copy-pasting the adjective/noun word-lists for the third time.
Sitting above all that: three small tables. Instances, members, locations. Row-level security is on and there's no anonymous policy at all. The browser never touches a database key. It polls gated API routes, and each route resolves the caller to a membership before it hands back a byte.
Reuse the renderer, don't fork it
When someone says "now make it configurable," the easy move is to copy the pretty component and start hacking on the copy. I didn't. I made the existing dashboard props-driven instead. It takes a list of locations, a forecast URL, and a storage key, and its defaults reproduce the public page byte-for-byte. So a tenant board hands it the board's saved cities and a per-board forecast feed, and gets back the same animated sky, the same glass cards, the same "who lives here" marks. For free.
The upstream forecast fetch was the other trap. I hoisted it into a single module that both the public page and the boards call, so the two surfaces physically can't drift on the query or the shape of the data. One renderer, one data layer, two front doors.
Then a v2 pass to make it feel finished
Shipping a feature and finishing it are two different jobs. The polish pass added the stuff you only notice once real people start touching things: board cards that show your role plus a live city and people count, a first-run state warmer than a flat "no boards yet," and a copy-link button so sharing doesn't hang on whether some email actually shows up.
Then the hardening pass. Tapping "use my location" twice used to drop the same place twice. Now a small, tested helper folds the coordinates into a ~110-meter cell and throws out the duplicate. There's a per-board member cap, so an invite box can't fan out forever, and rate limits on the two writes that actually cost something: creating a board, sending an invite. None of this changes what the app does. It just stops it from doing the wrong thing.
The point isn't weather
The point is that the second conversion is cheaper than the first, and the third cheaper than the second. With the tenancy core shared and the renderer already props-driven, turning the next single-instance app into a spin-up-your-own one is mostly "add three tables and a marketplace tile" instead of a rewrite. A personal chore board. A household budget. A trip log. Each one is now a short hop. Weather just happened to have the prettiest renderer to prove it on.
Get the next one
An occasional note when something genuinely new ships here — essays, free tools, projects. No schedule, no filler, easy out.
Need something like this built?
I design and ship AI tools, full-stack apps, and data pipelines — end to end, to production. Tell me the problem in a sentence; I'll give you an honest read on fit within a day.
Work with me →