# vercel/platforms: A Production-Ready Multi-Tenant Next.js Template with Subdomain Routing

> vercel/platforms is a reference application showing how to build a multi-tenant site where each tenant gets its own subdomain, using Next.js 16, Upstash Redis, and Vercel's deployment infrastructure.

**vercel/platforms** — A full-stack Next.js app with multi-tenancy.

- Repository: https://github.com/vercel/platforms
- Website: https://vercel.pub
- Stars: 6,710 · Forks: 1,062
- Language: TypeScript
- License: not declared
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/vercel-platforms

## What multi-tenancy problem vercel/platforms solves

A multi-tenant application serves multiple customers from a single deployed codebase while isolating each customer's content and identity. The most common user-facing pattern is per-tenant subdomains: customer A's site lives at customer-a.yourdomain.com while customer B's lives at customer-b.yourdomain.com, and each sees only their own content.

Building this correctly requires solving several problems at once: routing incoming requests to the right tenant, storing tenant data in a way that is fast to look up on every request, handling local development where subdomains do not naturally resolve to localhost, and keeping Vercel preview deployments working so each branch can be previewed correctly. vercel/platforms provides a working solution to all four, packaged as a runnable application rather than a library.

## How proxy.ts handles subdomain routing

Next.js 16 replaced middleware.ts with proxy.ts for request interception. In vercel/platforms, proxy.ts reads the hostname from the incoming request and extracts the subdomain. The README explains the logic:

- Requests to the main domain (no subdomain) are routed to the landing page and admin panel at /admin
- Requests with a subdomain are matched against the Redis key pattern subdomain:{name} and routed to the corresponding tenant's content
- Preview deployments on Vercel add extra hostname segments, and the proxy handles this case explicitly
- Local development uses [tenant-name].localhost:3000

The tenant's content is fetched from Redis using @upstash/redis for every routed request. This makes Redis a hard runtime dependency: if the Redis connection is down, tenant routing fails.

## Setting up the project locally

The README requires Node.js 20.9.0 or later and recommends pnpm. To start:

```bash
git clone https://github.com/vercel/platforms.git
cd platforms
```

```bash
pnpm install
```

Create a .env.local file with the Upstash Redis credentials:

```
KV_REST_API_URL=your_redis_url
KV_REST_API_TOKEN=your_redis_token
```

Then start the development server:

```bash
pnpm dev
```

The main site runs at http://localhost:3000 and the admin panel at http://localhost:3000/admin. Tenant subdomains use the pattern http://[tenant-name].localhost:3000. For local subdomain routing to work, your local DNS must resolve [anything].localhost to 127.0.0.1. On macOS and Linux this typically works out of the box; on some Windows configurations it may require a manual hosts file entry.

## Deploying to Vercel with wildcard DNS

The README describes deployment as a push-to-GitHub-then-connect-to-Vercel workflow. After deploying:

1. Add your root domain to the Vercel project settings
2. Set up a wildcard DNS record for *.yourdomain.com pointing at Vercel's DNS

The wildcard record is what allows Vercel to accept requests for any subdomain and route them to your application. Without it, only the root domain and explicitly added subdomains would resolve. Vercel handles TLS certificates for wildcard subdomains automatically when the domain is configured through their interface.

## The Redis data model and tenant isolation

Tenant data is stored in Upstash Redis using a key pattern of subdomain:{name}. The admin interface at /admin allows creating and managing tenants through a web form. Each tenant entry in Redis contains the data needed to render that tenant's pages.

The template uses @upstash/redis for the Redis client, which communicates over HTTP rather than the traditional Redis wire protocol. This means it works from serverless functions and edge runtimes where a persistent TCP connection to Redis is not available. The trade-off is that each Redis operation involves an HTTP request, adding latency compared to a persistent connection. Upstash's free plan has rate limits; production workloads with many subdomain requests per second should be tested against those limits.

The components/ directory holds the admin interface components built from Radix UI primitives via shadcn/ui. The lib/ directory contains the Redis utility functions that the proxy and the admin panel share. There is no dedicated type file for the tenant schema; the data shape is inferred from how the Redis keys are read and written in the application code.

## Where the template falls short

The template includes no authentication model for tenant users. The admin panel at /admin has no built-in access control; anyone who can reach the URL can manage tenants. Adding authentication is left entirely to the implementer. This is the most significant gap for a production deployment: any team building on this template should treat adding authentication to the admin panel as the first task before deploying to a public URL.

There is also no database beyond Redis. If a tenant's data model requires relational queries, joins, or structured schemas, Redis alone is insufficient. The template would need to be extended with a relational database or a Vercel-native storage option. Redis key-value pairs work well for simple tenant settings and metadata, but poorly for content with relationships.

The @upstash/redis dependency and the proxy.ts subdomain detection logic both assume Vercel as the deployment target. Running the application on a different platform (AWS Lambda, Fly.io, a self-hosted server) requires replacing or adapting both. The README does not document any alternative deployment path. The UI components use shadcn/ui and Radix primitives; these are composable but not themeable without modifying the component source, since shadcn/ui copies component code directly into the project rather than providing a versioned npm package.

## Maintenance and licensing

The repository has no license file; the package.json lists the license field as absent. Before adopting the template in a commercial product, verify the licensing status directly with Vercel. The last push was on 2026-07-08. There are no GitHub releases.

The tech stack, Next.js 16, React 19, Tailwind 4, and shadcn/ui, represents the current generation of Next.js application tooling. Tailwind 4 and Next.js 16 are relatively recent major versions; check the Next.js and Tailwind changelogs before upgrading them incrementally to avoid proxy.ts breaking changes.

## Conclusion

Developers building a platform product where each customer needs their own subdomain, such as a site builder or a hosted documentation service, should use vercel/platforms as a starting point rather than building subdomain routing from scratch. Organizations that cannot use Vercel for deployment or Upstash for Redis will need to replace both integrations manually, since neither is abstracted behind a swappable interface in the current codebase. The last push was on 2026-07-08; review the proxy.ts logic carefully before deploying, since it encodes environment-specific subdomain detection that may need tuning for non-Vercel hosts.

## FAQ

### Does vercel/platforms require an Upstash account?

Yes. The application uses @upstash/redis for tenant data storage, and the KV_REST_API_URL and KV_REST_API_TOKEN environment variables must point to an Upstash Redis instance. The README does not document any alternative Redis provider.

### Can vercel/platforms run outside Vercel?

The proxy.ts subdomain detection logic handles Vercel-specific hostname formats explicitly, and the README only documents Vercel deployment. Adapting it to another platform requires modifying proxy.ts and potentially replacing the @upstash/redis client if the target platform does not support HTTP-based Redis.

### How does vercel/platforms handle local subdomain development?

Tenant subdomains in local development use the pattern [tenant-name].localhost:3000. The proxy.ts detects the localhost environment and routes requests accordingly. The main admin interface runs at http://localhost:3000/admin.

## Sources

- [Issues](https://github.com/vercel/platforms/issues)
- [Project website](https://vercel.pub)
- [README](https://github.com/vercel/platforms/blob/main/README.md)
- [vercel/platforms on GitHub](https://github.com/vercel/platforms)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/vercel-platforms
