We are living in a new era of Fast development using GenAI. We Call it “Vibe Coding.”

I don’t write every line of code from scratch anymore. For my latest product, I used a Figma-to-Code plugin to generate a pixel-perfect Landing Page. I used an AI agent to build a complex User Dashboard. And I grabbed an open-source template for the Admin Panel.

The result? I had three separate zip files. Three separate tech stacks. Three separate package.json files.

The Problem: The Merge Nightmare

My goal was to present this to the user as One Single Product.

Naturally, I tried to combine them. I dragged the Landing Page code into the Dashboard’s src folder.

It was a catastrophe.

  • CSS Bleed: The Landing Page had a global body { margin: 0; font-family: 'Inter'; } that completely destroyed the layout of my Dashboard.

  • Dependency Hell: The Dashboard needed React 18. The Admin template was stuck on React 16. The Landing Page used a version of Tailwind that conflicted with everything.

  • The “Frankenstein” Monolith: I spent three days fixing webpack errors and zero days building features.

I realized that merging the code was impossible. But I didn’t need to merge the code. I just needed to merge the experience.

The Solution: The Gateway Architecture

I decided to stop fighting the tools. If the AI gives me three separate projects, I will keep them as three separate projects.

I architected a solution where the projects live in complete isolation on the server, never touching each other. To the user, however, they look like a single, seamless application.

I achieved this using Nginx as a Layer 7 Gateway. It acts as a traffic controller, routing the user to the correct independent application based on the URL path.

  • rohanprajapati.dev/ serves the Landing Page

  • rohanprajapati.dev/dashboard serves the User App

  • rohanprajapati.dev/admin serves the Admin Panel

Here is the detailed breakdown of how I implemented this.

Phase 1: File Isolation

I abandoned the idea of a “monorepo.” Instead, I treated my server filesystem like a filing cabinet. Each AI-generated project gets its own dedicated folder in /var/www/.

Bash

/var/www/
├── marketing-site/      # The Figma Export (HTML/CSS)
├── user-dashboard/      # The AI Agent's React App (Dist folder)
└── admin-panel/         # The Template (Dist folder)

This immediately solved the conflict issues. I could delete the entire Marketing Site and upload a new design without risking a single line of code in my Dashboard.

Phase 2: The Nginx “Stitch”

The magic happens in the Nginx configuration. I used the alias directive to map specific URL paths to these isolated folders.

This is the exact configuration I used to glue them together:

Nginx

server {
    listen 80;
    server_name rohanprajapati.dev;
 
    # 1. The Landing Page (Root)
    # This handles the main domain traffic
    location / {
        root /var/www/marketing-site;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
 
    # 2. The User Dashboard
    # Seamlessly loads the React app when user visits /dashboard
    location /dashboard {
        alias /var/www/user-dashboard/dist;
        index index.html;
        try_files $uri $uri/ /dashboard/index.html;
    }
 
    # 3. The Admin Panel
    # Loads the admin tools only on /admin
    location /admin {
        alias /var/www/admin-panel/dist;
        index index.html;
        try_files $uri $uri/ /admin/index.html;
    }
}

Note: The try_files line in the sub-locations is critical. It ensures that when a user refreshes the page while inside the React app, Nginx knows to serve the index.html file so React Router can take over.

Phase 3: The “White Screen” Fix

When I first deployed this, everything seemed perfect. The Landing Page loaded. But when I clicked “Login” to go to /dashboard, I was greeted by a blank white screen.

The Console was screaming 404 errors.

The Issue:

Modern frontend tools (like Vite, Next.js, or Webpack) assume your app is hosting at the root domain (/). The generated HTML was trying to load scripts from /assets/main.js.

But my dashboard wasn’t at the root. It was at /dashboard. Nginx was looking for those scripts in the Marketing folder!

The Fix:

I had to explicitly tell the build tool where the app would live.

For Vite Projects (Most AI Agents use this):

I opened vite.config.ts and added the base property:

TypeScript

export default defineConfig({
  base: '/dashboard/',  // This must match the Nginx location exactly
  plugins: [react()],
})

For Next.js Projects:

I opened next.config.js:

JavaScript

module.exports = {
  basePath: '/dashboard',
}

Once I rebuilt the projects with this setting, the apps knew to ask for /dashboard/assets/main.js. Nginx found the file, and the application sprang to life.

Phase 4: The Visual Stitch (The “Shell” Strategy)

Even with Nginx routing, navigating from /dashboard to /admin felt like a “hard” jump. The browser would refresh, and the UI would flicker. It felt like two different websites.

I wanted a “Super App” feel, where a single sidebar controls everything.

The Solution: Use the User Dashboard as a “Shell”

I turned the User Dashboard into the main container. I added a sidebar navigation that persists. When I click the “Admin Panel” link in the sidebar, I don’t redirect the browser. Instead, I load the Admin route inside an iframe.

Why this works:

  1. CSS Isolation: The messy Admin template CSS cannot leak out of the iframe to break my clean Dashboard sidebar.

  2. Seamless Nav: The sidebar never reloads. The user feels like they stayed in the same app.

  3. Authentication: Since both run on rohanprajapati.dev, they share the same cookies. Being logged into the Dashboard automatically logs me into the Admin panel.

Here is the React component I used in the Dashboard to “embed” the Admin panel:

TypeScript

// Inside User Dashboard Code
const AdminView = () => {
  return (
    <div className="w-full h-screen flex">
      <Sidebar /> {/* Persist this! */}
      <div className="flex-1">
        <iframe 
          src="/admin" 
          title="Admin Panel"
          className="w-full h-full border-none"
        />
      </div>
    </div>
  );
};

By setting the iframe src to /admin, Nginx routes that specific request to the Admin folder, while the outer “shell” remains the User Dashboard.

The Result

I now have a system that perfectly supports the “Vibe Coding” workflow.

I can generate a crazy new landing page design, zip it, and drop it into the marketing-site folder. The Dashboard remains untouched. The Admin Panel is securely contained inside an iframe, unable to break my layout.

To the user, it feels like one massive, cohesive product. To me, it’s three tiny, manageable, and isolated sandboxes. This is how you survive the era of AI-generated code.