Building Multi-Tenancy in Laravel with React.js: A Complete Guide

Author

Kritim Yantra

Jun 09, 2025

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_id column)
  • 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

  1. Create two tenants:
    Tenant::create(['name' => 'Tenant A', 'domain' => 'tenant-a.local']);
    Tenant::create(['name' => 'Tenant B', 'domain' => 'tenant-b.local']);
    
  2. Register users under each tenant.
  3. 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.

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)

Tags

Comments

No comments yet. Be the first to comment!

Please log in to post a comment:

Sign in with Google

Related Posts

Understanding Laravel 12 Routes
Web Development
Understanding Laravel 12 Routes
#Laravel #Php
Kritim Yantra Kritim Yantra
Apr 15, 2025
What Is Model Context Protocol (MCP)?
Machine Learning
What Is Model Context Protocol (MCP)?
#Python #AI #LLM
Kritim Yantra Kritim Yantra
Apr 21, 2025