vercel/platforms: hostname routing for multi-tenant Next.js, and the gaps the proxy leaves open
A full-stack Next.js app with multi-tenancy.
At a glance
- What is it?
- A full-stack Next.js 16 example that gives every tenant a subdomain and stores tenant data in Upstash Redis under one key pattern. Worth reading as a routing reference. Thin on admin permissions, tenant indexing and licensing.
- Who is it for?
- Treat vercel/platforms as a reference for hostname based routing rather than a base to fork. The subdomain key pattern and the middleware to proxy.ts rename are worth taking, the single key Redis layout and the missing admin permissions are not, and the absent licence makes the code safer to read than to reuse.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 128 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- vercel/platforms6,728 ★ · +21
| Repository | 2026-09-23 | 2026-11-13 |
|---|---|---|
| vercel/platforms | 6.7k | 6.7k |
21 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Subdomain routing is the entire tenancy model
This repository presents itself as a production ready example of a multi tenant application built with Next.js 16, and tenancy is expressed entirely through hostnames. Each tenant gets a subdomain in the form tenant.yourdomain.com, the proxy handles routing requests to the correct tenant, and subdomains are dynamically mapped to tenant specific content. The main domain hosts the landing page and the admin interface, so the apex and the tenants are separate surfaces rather than one app with a tenant switcher.
What that buys you is a shared React tree. Components and layouts are shared across tenants, and the pages underneath them vary by hostname, not by a URL parameter or a request header. The stack behind it is Next.js 16 with the App Router, React 19, Tailwind 4 for styling and shadcn/ui for the design system.
Tenant records live under one Redis key pattern
Storage is Upstash Redis, reached through the @upstash/redis package pinned at ^1.34.9, and tenant data is kept using a subdomain:{name} key pattern. There is no relational schema anywhere in the top level entries: the repository carries app/, components/ and lib/ plus next.config.ts, proxy.ts and tsconfig.json, with no migrations directory, no ORM dependency and no seed file. Every attribute a tenant has has to fit inside a value stored under one subdomain prefixed key.
That choice has a cost worth naming before you copy it. Redis gives you a fast read for the one tenant whose hostname you already know, and nothing for the questions a growing application keeps asking: which tenants exist, when was one created, what does an aggregate across all of them look like. Answering those means walking the keyspace or maintaining a second index the example never defines. Two credentials, KV_REST_API_URL and KV_REST_API_TOKEN, are what cover the whole thing.
Four commands from clone to a tenant URL
Prerequisites are Node.js 20.9.0 or later, pnpm as the recommended package manager with npm or yarn as alternatives, and an Upstash Redis account for production. Getting to a running tenant takes four commands:
git clone https://github.com/vercel/platforms.git
cd platforms
pnpm install
pnpm devThe dev script resolves to next dev through package.json, which also defines build as next build and start as next start. Before the first request you need a .env.local file in the root directory holding two values:
KV_REST_API_URL=your_redis_url
KV_REST_API_TOKEN=your_redis_tokenThen three URLs matter. The main site is http://localhost:3000, the admin panel is http://localhost:3000/admin, and a tenant is reached at http://[tenant-name].localhost:3000. Subdomain access in local development needs no hosts file edit, since the README's setup stops at those four commands and browsers send anything under .localhost back to the loopback address.
proxy.ts took over the job middleware.ts used to do
The file at the repository root that intercepts every request is proxy.ts, presented as the Next.js 16 replacement for middleware.ts. If you are porting a route from an older Next.js project, that rename is the first thing to check.
The README states that the proxy detects subdomains across various environments: local development, production, and Vercel preview deployments. A preview deployment gets a generated host per push, and the file has to tell a generated host apart from a real tenant subdomain before it looks anything up in Redis. The README says it handles this without describing the matching rules. Nothing describes per tenant environment separation either, and nothing says what happens when a hostname matches no key.
The admin page has no documented access control
http://localhost:3000/admin is where tenants are managed, and an admin interface for managing tenants is listed as a feature. What is not listed is who may reach it. There is no mention of authentication, roles, session handling or an audit trail anywhere in the README, and the dependency list gives no hint either: @radix-ui/react-dialog, @radix-ui/react-label, @radix-ui/react-popover and @radix-ui/react-slot are interface primitives, @vercel/analytics and @vercel/speed-insights are telemetry, and no auth provider appears in the file.
Read this as a routing demonstration rather than a permissions model. Anyone who can resolve that path gets whatever the page does, which in this example means writing to Redis. If you build on this, the tenant write path is where your own identity check belongs, and the example hands you nothing to inherit.
Wildcard DNS and preview hosts are where the instructions thin out
Deployment is described in four steps: push the repository to GitHub, connect it to Vercel, configure environment variables, and deploy. For custom domains there are two more, and both matter: add your root domain to Vercel, then set up a wildcard DNS record for `*.yourdomain.com` on Vercel.
The wildcard is what makes tenant subdomains resolve without a DNS record per tenant, and it also marks the boundary of the approach. A record of that shape covers names under one root, so a tenant that needs its own apex domain sits outside what these instructions cover. Nothing describes per tenant custom domains, and nothing describes how a tenant keeps a hostname after the root moves. Preview deployments are called compatible, yet no text explains how a preview host keeps its tenants apart from production ones in Redis, where the subdomain:{name} key carries no environment marker of its own.
A pinned pnpm lockfile, an unknown licence and no releases
A few things about the packaging are worth checking before you depend on it. package.json is marked private, and its packageManager field pins [email protected]. The toolchain is TypeScript ^5.8.3 with @types/node ^22.15.17, styled through @tailwindcss/postcss ^4.1.6 and tailwindcss ^4.1.6, with tw-animate-css ^1.2.9 for animation.
The licence field is unknown and the top level entries contain no LICENSE file, so the code carries no stated grant for reuse. There are also no GitHub releases, which means no tagged version to pin and no changelog to read. The last recorded push is 2026-07-08. For a repository whose own description claims production readiness, that absence is the most practical gap to plan around: read the routing logic, then write your own tenancy layer on top of it.
Editorial conclusion
Treat vercel/platforms as a reference for hostname based routing rather than a base to fork. The subdomain key pattern and the middleware to proxy.ts rename are worth taking, the single key Redis layout and the missing admin permissions are not, and the absent licence makes the code safer to read than to reuse.
Frequently asked questions
What is vercel/platforms actually used for?
It is a full-stack Next.js 16 example of subdomain multi-tenancy. Each tenant gets its own subdomain, the proxy at proxy.ts routes the request, and tenant data is stored in Upstash Redis under a subdomain:{name} key pattern.
How do I run vercel/platforms on my own machine?
Clone the repository, install with pnpm, put KV_REST_API_URL and KV_REST_API_TOKEN into a .env.local file at the root, then run pnpm dev. Node.js 20.9.0 or later is required. The main site is on port 3000, the admin panel at /admin, and tenants at http://[tenant-name].localhost:3000.
Does vercel/platforms handle custom domains and Vercel preview deployments?
It is designed for Vercel. Custom domains need your root domain added to Vercel plus a wildcard DNS record for *.yourdomain.com. Preview deployments are listed as compatible, and the proxy is said to detect subdomains across local development, production and preview environments.
What database does vercel/platforms use?
Upstash Redis, through the @upstash/redis package at ^1.34.9, reached over its REST API using KV_REST_API_URL and KV_REST_API_TOKEN. The repository carries no database schema, no migrations and no ORM dependency, so tenant records sit directly in Redis keys.
What licence is vercel/platforms released under?
The repository states no licence and its top level entries contain no LICENSE file, so there is no stated grant for reusing the code. There are also no GitHub releases, which leaves no tagged version to pin.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/vercel-platforms)Star history chart for your README
[](https://hysenlabs.com/projects/vercel-platforms)Was this useful?