In this guide, you'll learn how to build a full-stack multi-tenant application by using the Platforms Starter Kit and the following technologies:
- Next.js as the React framework
- Tailwind for CSS styling
- Prisma as the ORM for database access
- PlanetScale as the database (MySQL)
- NextAuth.js for authentication
- Vercel for deployment
If you already have an existing project and only want to see the steps for multi-tenancy, skip ahead to steps 4 and 5. Also, the code for this app can be found here.
1. Set up your Next.js project
We’ll be using the Platforms Starter Kit to kickstart our Next.js project.
First, open up your terminal and navigate and run the following:
1npx create-next-app --example https://github.com/vercel/platforms/tree/main platforms
This will create a new folder in your current directory called
platforms. Then, you can navigate into the folder, install the dependencies, and launch the app:
1cd platforms && npm i && npm run dev
The new application has the following structure:
Aside from the
/api folder, there are 3 main folders in the
/app: All routes for the app subdomain (
app.example.com), where users can customize their individual content pages.
/home: All routes for the landing page (
/_sites: All routes for all user content pages (e.g.
These folders contain the basic app structure for a multi-tenant app. However, only the
/home route works for now. Let’s continue by adding our database.
Note: Don't forget to convert the
.env.example file that’s located at the root of the repo into a
.env file – it'll come in handy later.
2. Set up your MySQL database
1. Prerequisite: You need to have the PlanetScale CLI installed
2. Create a new account with PlanetScale.
3. Using the PlanetScale CLI, create a new database called
1pscale db create platforms
4. Next, connect to the database branch:
1pscale connect platforms main --port 3309
5. In a different terminal window, use the
db push command to push the schema defined in
1npx prisma db push
6. Now that the initial schema has been added, promote your
main branch to production:
1pscale branch promote platforms main
7. You've just provisioned your database! Now, when you go to app.localhost:3000, you should see the following screen:
8. To push additional schema changes to your database, follow the instructions from PlanetScale on Prisma Migrations.
9. Don't forget to get your production MySQL
DATABASE_URL from your Planetscale database – you'll need it for when you deploy your app to Vercel later. You can do that by following this guide. Note that your
DATABASE_URL should be in the following format:
3. Set up user authentication with next-auth
Now, let’s add authentication to allow users to create accounts, add new sites, and add custom domains.
We will use the
next-auth library for authentication. This example is preconfigured to use GitHub OAuth. All user data is stored in your PlanetScale database, based on the Prisma schema defined.
To set up GitHub for authentication:
1. Go to Developer Settings on GitHub.
2. Click on "New GitHub App".
3. Name your GitHub App. In our example, we'll call it "Platforms Starter Kit (dev)".
4. Add your homepage URL (or a placeholder, if you don't have a website yet).
5. For the "Callback URL" field, put
http://app.localhost:3000. Since GitHub only allows one callback URL per app, we have to create separate apps for
localhost and production (hence the "dev" name in step 3).
6. If the "Active" field under "Webhook" is checked, uncheck it. Now, click on "Create Github App".
7. Once your app is created, you should see the following screen. Click on "Generate a new client secret":
8. Copy the client secret you generated and paste it under the
GITHUB_SECRET value in your
9. Copy the Client ID and paste it under the
GITHUB_ID value in your
You're all set! You can now go back to the app login page and log in with GitHub.
4. Configure rewrites for multi-tenancy
Vercel Edge Functions give you the benefits of static with the power of dynamic. Inside this template, we use Middleware to create flexible rewrite rules.
First, navigate to the
_middleware.js file at the root of your
/pages folder. Inside this file, we have set up rewrite rules to map each subdomain/custom domain to their dynamic routes in
You’ll need to replace all instances of
vercel.pub with your own domain to configure rewrites correctly. If you don't have a custom domain, you can add the
.vercel.app domain that your project was assigned and use that as your custom domain.
5. Deploy to Vercel
Next, we’ll be deploying the repo to Vercel. Note that this is not the final step since we’ll still need to configure the feature for adding custom domains in the next step.
1. First, create a new Github repository and push your local changes.
2. Deploy it to Vercel. Ensure you add all Environment Variables in your
.env file to Vercel during the import process.
- Ensure you create another GitHub App for production environment as well, and then add the production callback URL as an Environment Variable. It should be in the following format:
- Fill in the
SECRETtoken in the
.envfile and add that as an Environment Variable.
- Add the production
DATABASE_URLyou retrieved in step 2 as an Environment Variable.
3. In your Vercel project, add your root domain & wildcard domain.
- When adding your custom domain, ignore the recommended step to "add the
www.version of your domain and redirect your root domain to it" – just add the root domain.
- To set up wildcard domains, you'll need to add the domain using the Nameservers method (as opposed to the recommended A records method).
6. Add custom domains with the Vercel API
We’ll use the Vercel API to add custom domains to your project and assign it to the user's account:
1. Get your
AUTH_BEARER_TOKEN env var from your Vercel Accounts Settings page under Tokens. Add that value to your
.env file under the
2. Get the
PROJECT_ID_VERCEL env var for your project from
https://vercel.com/<TEAM_OR_USER_NAME>/<PROJECT_SLUG>/settings. It should be in the format
3. If you're deploying your project under a team account, you'll also need to get your
TEAM_ID_VERCEL env var – this can be found at
We’ve set up a few API routes to help you handle custom domains:
POST): Add domains to your Vercel project using this endpoint whenever a user adds it on your platform. This returns 3 possible outcomes:
- Status code
403: The domain is already owned by another team but you can still request delegation from the team and add it.
- Status code
409: The domain is already being used by a different project. You can’t add it unless the domain is removed from the project.
- Status code
200: The domain is successfully added.
/api/domain/check: Checks if a domain has been successfully configured for your project. Returns two possible values:
true: domain has been configured successfully
false: domain has not been configured correctly
/api/request-delegation: If a domain is owned by another team, you can use this endpoint to request delegation from the team and add it. Caveat: we are actively working on improving our domain delegation process and it’s likely that there won’t be the need for this endpoint in the future
DELETE): Removes domains from your Vercel project using this endpoint whenever a user removes it from your platform.
POST endpoint only adds the root domain. If you want to add the
www. subdomain and redirect it to the root, you'll have to perform an additional API call.
7. Additional Features
Here are some supplementary code snippets that might be required to build Platforms on Vercel:
Avoid Cumulative Layout Shift (CLS) from the native Twitter embed by using our static tweets implementation (supports image, video, gif, poll, retweets, quote retweets, and more).
You'll need a Twitter auth bearer token, which you'll paste into the
TWITTER_AUTH_TOKEN field in your
.env file. Here's how you can get a Twitter auth bearer token.
Image Uploads with Cloudinary
Cloudinary is used to handle image uploads. Here’s the reusable component we created and here’s the code we used to generate a blurhash from the uploaded images.
You'll need a Cloudinary cloud account (
cloudName) and a Cloudinary upload preset (
uploadPreset). Here's how you can get those variables set up.
In this guide, you learned how to build a full-stack multi-tenant application by using the Platforms Starter Kit. From blogging platforms to low-code tools, this starter kit can be a starter kit for a number of different types of applications, we’re excited to see what you build!
If you run into any issues or have any questions about this guide, feel free to raise them on GitHub or drop them in the Next.js Discord.