Building Multi-Tenancy in Laravel with React.js: A Complete Guide
1. What is Multi-Tenancy?
Multi-tenancy allows a single application to serve multiple customers (tenants) while keeping their data isolated. Common approaches:
- Database per Tenant (Most secure, complex)
- Shared Database with Tenant ID (Easier, uses
tenant_idcolumn) - Schema per Tenant (PostgreSQL-only)
In this guide, we’ll use Shared Database with Tenant ID for simplicity.
2. Project Setup
2.1. Install Laravel & React
composer create-project laravel/laravel:^12.0 multi-tenant-app
cd multi-tenant-app
npm install react react-dom @vitejs/plugin-react
2.2. Configure Vite for React
Update vite.config.js:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
laravel(['resources/js/app.jsx']),
react(),
],
});
2.3. Set Up Multi-Tenancy
We’ll use Laravel’s global scopes and middleware to isolate tenant data.
3. Backend: Multi-Tenancy in Laravel
3.1. Add tenant_id to Users & Tenant-Specific Models
php artisan make:migration add_tenant_id_to_users_table
// Migration
public function up() {
Schema::table('users', function (Blueprint $table) {
$table->foreignId('tenant_id')->constrained('tenants')->onDelete('cascade');
});
}
3.2. Create a Tenant Model
php artisan make:model Tenant -m
// Migration
Schema::create('tenants', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('domain')->unique();
$table->timestamps();
});
3.3. Set Up Global Tenant Scope
// app/Models/Tenant.php
use Illuminate\Database\Eloquent\Builder;
protected static function booted() {
static::addGlobalScope('tenant', function (Builder $builder) {
if (auth()->check()) {
$builder->where('tenant_id', auth()->user()->tenant_id);
}
});
}
3.4. Create Tenant Middleware
php artisan make:middleware TenantMiddleware
// app/Http/Middleware/TenantMiddleware.php
public function handle($request, Closure $next) {
if ($tenant = Tenant::where('domain', $request->getHost())->first()) {
config(['tenant' => $tenant]);
return $next($request);
}
abort(404, 'Tenant not found.');
}
Register in app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\TenantMiddleware::class,
],
];
3.5. Tenant-Aware Authentication
Modify LoginController to ensure users log in only to their tenant:
protected function attemptLogin(Request $request) {
return Auth::attempt([
'email' => $request->email,
'password' => $request->password,
'tenant_id' => Tenant::where('domain', $request->getHost())->first()->id,
]);
}
4. Frontend: React.js Multi-Tenant UI
4.1. Fetch Tenant-Specific Data
// resources/js/components/TenantDashboard.jsx
import { useEffect, useState } from 'react';
import axios from 'axios';
export default function TenantDashboard() {
const [data, setData] = useState([]);
useEffect(() => {
axios.get('/api/tenant-data').then((res) => {
setData(res.data);
});
}, []);
return (
<div>
<h1>Tenant Dashboard</h1>
{data.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
4.2. Dynamic Tenant Routing
Use React Router to handle tenant-specific routes:
// resources/js/routes/AppRouter.jsx
import { Routes, Route } from 'react-router-dom';
export default function AppRouter() {
return (
<Routes>
<Route path="/dashboard" element={<TenantDashboard />} />
<Route path="/settings" element={<TenantSettings />} />
</Routes>
);
}
4.3. Tenant Switching (If Allowed)
// resources/js/components/TenantSwitcher.jsx
export default function TenantSwitcher() {
const [tenants, setTenants] = useState([]);
useEffect(() => {
axios.get('/api/user/tenants').then((res) => {
setTenants(res.data);
});
}, []);
return (
<select onChange={(e) => window.location.href = `https://${e.target.value}.yourdomain.com`}>
{tenants.map((tenant) => (
<option key={tenant.id} value={tenant.domain}>{tenant.name}</option>
))}
</select>
);
}
5. Testing Multi-Tenancy
- Create two tenants:
Tenant::create(['name' => 'Tenant A', 'domain' => 'tenant-a.local']); Tenant::create(['name' => 'Tenant B', 'domain' => 'tenant-b.local']); - Register users under each tenant.
- Verify data isolation:
- Log in to
tenant-a.local→ Only see Tenant A’s data. - Log in to
tenant-b.local→ Only see Tenant B’s data.
- Log in to
6. Best Practices
✅ Use Subdomains (tenant1.yourdomain.com) for easy routing.
✅ Cache Tenant Data to avoid repeated DB queries.
✅ Backup Tenant Databases Separately if using DB-per-tenant.
✅ Use Queues for Tenant-Aware Jobs (Laravel’s Queue::tenant()).
7. Conclusion
You’ve built a multi-tenant SaaS app with:
- Laravel (Backend tenant isolation)
- React.js (Dynamic frontend per tenant)
- Subdomain routing (For tenant separation)
Projects to explore next
Source-code projects from the KritiMyantra catalogue. Check each project's details before buying.
Flask Authentication System (Free Version) - Kritim Yantra
🛠 Features✔ Google & GitHub Authentication✔ User Registration & Login✔ Profile/Dashboard Access✔ Secure & Mi...
Flask Blog CMS with Admin Dashboard — Python Source Code
A Flask-based blogging website with a built-in admin dashboard. Manage articles, images, categories, tags and featured p...
Laravel 12 + ReactJS Starter Kit: Add Multi-Language Support (Arabic, English, Spanish) with Blog CRUD
🚀 Installation & Setup Download & Extract Get the ZIP from kritimyantra.com and extract it. Includes preconfigu...
Comments
No comments yet. Be the first to comment.
Sign in to join the discussion.