Kirill Igumenshchev

Developing with Firebase Emulators and WSL Across Two Machines: A Complete Guide

Developing with the Firebase Emulator Suite is fantastic for local development, but what if you want to use a second machine as an extended display for your UI and emulator dashboards? This is especially useful for React development, where you might want to see your UI updates in real-time on a separate screen. This guide will walk you through setting up a robust development environment using Windows 11, WSL (Windows Subsystem for Linux), the Firebase Emulator Suite, and a React frontend (built with Vite), with the ability to access your application and emulators from a second machine on your local network.

Why This Setup?

This setup offers several benefits:

Prerequisites

Step 1: Set up Your Firebase Project (Inside WSL)

  1. Initialize Firebase: Inside your WSL terminal, navigate to your project directory and initialize Firebase:

    firebase init

    Select the Firebase features you need (Firestore, Functions, Hosting, Storage, etc.). Choose to use the emulators when prompted.

  2. Configure firebase.json: Open your firebase.json file. Crucially, set the host property for each emulator you want to use to "0.0.0.0". This tells the emulators to listen on all network interfaces, not just localhost within WSL. This is essential for port forwarding to work.

    {
      "emulators": {
        "auth": {
          "port": 9099,
          "host": "0.0.0.0"
        },
        "functions": {
          "port": 5001,
          "host": "0.0.0.0"
        },
        "firestore": {
          "port": 8080,
          "host": "0.0.0.0"
        },
        "hosting": {
          "port": 5000,
          "host": "0.0.0.0"
        },
        "storage": {
          "port": 9199,
          "host": "0.0.0.0"
        },
        "ui": {
          "enabled": true,
          "port": 4000,
          "host": "0.0.0.0" // Even though the UI *says* 127.0.0.1, this setting is key
        },
        "hub": {
          "port": 4400,
          "host": "0.0.0.0"
        },
        "singleProjectMode": true
      }
    }

    You might also be using custom ports, so include those as well.

  3. Firebase Login: Run firebase login inside WSL. This is important, do this before starting the emulators. Because of the 0.0.0.0 binding, the CLI tools might have trouble connecting to the emulators during the login process if you don’t do this first.

  4. Start the emulators:

    firebase emulators:start --import=./emulator-data --export-on-exit=./emulator-data

Step 2: Set up Your React Project (Inside WSL)

  1. Create or Use Existing Project: If you don’t already have a React project set up with Vite, create one:

    yarn create vite my-react-app --template react-ts # Or react for JavaScript
    cd my-react-app
  2. Install Firebase SDK:

    yarn add firebase  # Or npm install firebase
  3. Environment Variables (.env.local): Create a file named .env.local in the root of your React project (where package.json is). Do not commit this file to version control. Add the following, replacing 192.168.6.93 with your Windows host machine’s IP address (more on finding this below):

    VITE_AUTH_EMULATOR_HOST=[http://192.168.6.93:9099](http://192.168.6.93:9099)
    VITE_FIRESTORE_EMULATOR_HOST=192.168.6.93
    VITE_FIRESTORE_EMULATOR_PORT=8080
    VITE_FUNCTIONS_EMULATOR_HOST=192.168.6.93
    VITE_FUNCTIONS_EMULATOR_PORT=5001
    
    # Your other Firebase config variables
    VITE_FIREBASE_API_KEY=...
    VITE_FIREBASE_AUTH_DOMAIN=...
    VITE_FIREBASE_PROJECT_ID=...
    VITE_FIREBASE_STORAGE_BUCKET=...
    VITE_MESSAGING_SENDER_ID=...
    VITE_APP_ID=...
    VITE_MEASUREMENT_ID=...

    This is crucial: your React app, running on the client machine, needs to connect to the emulators using the Windows host’s IP address, not localhost.

  4. Firebase Initialization (firebaseConfig.ts or similar): Create or modify your Firebase initialization file to use the environment variables:

    // src/firebaseConfig.ts
    import { initializeApp } from "firebase/app";
    import { getFirestore, connectFirestoreEmulator } from "firebase/firestore";
    import { getAuth, connectAuthEmulator } from "firebase/auth";
    import { getFunctions, connectFunctionsEmulator } from "firebase/functions";
    import { getStorage } from "firebase/storage";
    
    const firebaseConfig = {
      apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
      authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
      projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
      storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
      messagingSenderId: import.meta.env.VITE_MESSAGING_SENDER_ID,
      appId: import.meta.env.VITE_APP_ID,
      measurementId: import.meta.env.VITE_MEASUREMENT_ID,
    };
    
    const app = initializeApp(firebaseConfig);
    export const db = getFirestore(app);
    export const auth = getAuth(app);
    export const functions = getFunctions(app);
    export const storage = getStorage(app);
    
    
    if (import.meta.env.DEV) {
      connectAuthEmulator(auth, import.meta.env.VITE_AUTH_EMULATOR_HOST);
      connectFirestoreEmulator(
        db,
        import.meta.env.VITE_FIRESTORE_EMULATOR_HOST,
        parseInt(import.meta.env.VITE_FIRESTORE_EMULATOR_PORT),
      );
      connectFunctionsEmulator(
        functions,
        import.meta.env.VITE_FUNCTIONS_EMULATOR_HOST,
        parseInt(import.meta.env.VITE_FUNCTIONS_EMULATOR_PORT),
      );
      console.log("Connected to Firebase Emulators");
    }
  5. Start Vite with --host: This makes your React app accessible from other devices on your network.

    yarn dev --host

Step 3: Port Forwarding (Windows Host - PowerShell)**

This is the magic that connects your client machine to the WSL instance running the emulators. We’ll use a PowerShell script to create firewall rules and set up port forwarding using netsh.

  1. Create a PowerShell Script: Create a new file (e.g., wsl-port-forward.ps1) and paste the following code into it:

    # Run this in PowerShell on the *Windows* host (not inside WSL) - AS ADMINISTRATOR!
    
    # Get the WSL IP address dynamically
    $remoteport = bash.exe -c "ifconfig eth0 | grep 'inet '"
    $found = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
    
    if( $found ){
      $remoteport = $matches[0];
    } else{
      echo "The Script Exited, the ip address of WSL 2 cannot be found";
      exit;
    }
    
    # --- All Ports for Firebase Emulators (and Vite) ---
    $ports = @(4000, 9099, 5001, 8080, 5000, 9199, 4400, 4500, 9150, 5173, 8852);  # Include Vite's port too!
    $addr = '0.0.0.0'; # Listen on all interfaces
    
    # --- Firewall Rules and Port Forwarding (Combined) ---
    # We'll use a single loop to create firewall rules and port forwarding for all ports
    
    foreach ($port in $ports) {
        $firewallRuleName = "WSL 2 Firebase Emulator - Port $port"
    
        # Remove any existing firewall rule (clean up)
        Remove-NetFireWallRule -DisplayName $firewallRuleName -ErrorAction SilentlyContinue
    
        # Add firewall rules (inbound and outbound)
        New-NetFireWallRule -DisplayName $firewallRuleName -Direction Outbound -LocalPort $port -Action Allow -Protocol TCP
        New-NetFireWallRule -DisplayName $firewallRuleName -Direction Inbound -LocalPort $port -Action Allow -Protocol TCP
    
        # Delete any existing portproxy rule
        netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr
    
        # Add the portproxy rule
        netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport
    }
    
    Write-Host "Port forwarding rules created/updated for ports: $($ports -join ', ')" -ForegroundColor Green
    • This script handles both the Windows Firewall rules and the netsh port forwarding.
    • It dynamically gets the WSL IP address.
    • It includes all the necessary ports for the Firebase Emulator Suite and Vite.
    • It includes a custom port that my firebase function uses, 8852
  2. Run as Administrator: Open PowerShell as an administrator (right-click -> “Run as administrator”). Navigate to the directory where you saved the script (using cd) and run it:

    .\wsl-port-forward.ps1

    You should see output indicating that the firewall rules and port forwarding rules were created. If you get any errors, double-check that you’re running PowerShell as administrator and that the script is saved correctly as a .ps1 file.

Step 4: Find Your Windows Host IP Address

You need the local IP address of your Windows host machine (the one running WSL). Do not use 127.0.0.1 or localhost.

Step 5: Access from Your Client Machine

On your client machine (the second laptop):

  1. Open a Web Browser: Open Chrome, Firefox, Edge, or any other browser.
  2. Enter the URLs:
    • React App: http://<Windows_Host_IP>:5173 (e.g., http://192.168.6.93:5173)
    • Firebase Emulator UI: http://<Windows_Host_IP>:4000 (e.g., http://192.168.6.93:4000)
    • Individual Emulators: Use the appropriate ports (e.g., http://192.168.6.93:8080 for Firestore).

Troubleshooting

Making it Persistent (Optional)

The main thing that can disrupt this setup is the WSL IP address changing. Here are some ways to mitigate that:

Conclusion

This setup provides a powerful and flexible development environment for Firebase and React applications. By using WSL, port forwarding, and environment variables, you can create a seamless workflow that allows you to leverage multiple machines for a more productive and comfortable development experience. Remember to re-run the port forwarding script and update your environment variables if the WSL IP address changes. Happy coding!