Skip to content

Overview

This guide covers integrating Vibenv with Next.js 15 applications. The integration uses a custom server approach with the following key components:

  • SWC Plugin - Injects component metadata (data-component-file, data-component-name) at build time
  • Custom Server - Mounts vibe-server routes for AI agent communication
  • Script Injection - Loads the vibe-agent UI during development

Prerequisites

  • Node.js: Version 20.18.0 or higher
  • Next.js: Version 15.x
  • React: Version 19.x
  • TypeScript: Version 5.x

1) Configure .npmrc for Vibenv packages

Vibenv ships as scoped packages from the Vibenv registry. Add this to your project-level .npmrc:

ini
@vibenv:registry=https://npm.vibenv.net/
legacy-peer-deps = true

Then authenticate with the Vibenv registry:

bash
npm login --scope @vibenv

Login details will be provided by the Vibenv team.

2) Install Vibenv dependencies

Install the required Vibenv packages:

bash
npm install @vibenv/vibe-agent @vibenv/framework-plugins @vibenv/logger @vibenv/platform @vibenv/constants @vibenv/swc-plugin @vibenv/transpiler-plugins @vibenv/vibe-server @vibenv/vite-plugins @vibenv/webpack-plugins

3) Configure SWC Plugin

Create or update your next.config.ts to include the SWC plugin:

typescript
import type { NextConfig } from 'next';
import path from 'path';

const nextConfig: NextConfig = {
    output: 'standalone',
    experimental: {
        swcPlugins: [[path.resolve(__dirname, '../../src/packages/swc-plugin/vibenv_swc_plugin.wasm'), {}]] as any
    }
};

export default nextConfig;

Important Notes:

  • The SWC plugin path should be adjusted based on your monorepo structure
  • SWC plugins are NOT supported in Turbopack yet
    • Use npm run dev (webpack) to enable SWC plugin functionality
    • Use npm run dev:turbo for faster builds without SWC plugins
  • The plugin adds data-component-file and data-component-name attributes to JSX elements

4) Create Custom Server with Vibe-Server Integration

Create server.mjs at your project root:

javascript
import { createServer } from 'http';
import { parse } from 'url';
import next from 'next';
import express from 'express';

// Vibe-server imports (conditionally loaded in development)
let routes = null;
let vibeServerAvailable = false;

// Try to load vibe-server (gracefully fail if not installed)
try {
    const vibeServerModule = await import('@vibenv/vibe-server/routes.js');
    routes = vibeServerModule.routes;
    vibeServerAvailable = true;
} catch (error) {
    console.warn(
        '@vibenv/vibe-server not installed. Running without vibe-agent integration.'
    );
    console.warn('   To enable: npm install @vibenv/vibe-server @vibenv/constants @vibenv/cli-agents');
}

// Environment configuration
const dev = process.env.NODE_ENV !== 'production';
const hostname = process.env.HOSTNAME || 'localhost';
const port = parseInt(process.env.PORT || '3000', 10);
const enableVibeServer = dev && vibeServerAvailable && process.env.DISABLE_VIBE_SERVER !== 'true';

// Initialize Next.js app
const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();

/**
 * Logger factory for vibe-server
 * Creates a factory function that produces loggers with prefixes
 */
const createLoggerFactory = (basePrefix) => {
    return (subPrefix) => ({
        log: (...args) => console.log(`${basePrefix}${subPrefix}`, ...args),
        error: (...args) => console.error(`${basePrefix}${subPrefix}`, ...args),
        warn: (...args) => console.warn(`${basePrefix}${subPrefix}`, ...args)
    });
};

/**
 * Start the server
 */
app.prepare()
    .then(() => {
        const expressApp = express();
        const server = createServer(expressApp);

        // Vibe-Server Integration (Development Only)
        if (enableVibeServer && routes) {
            const loggerFactory = createLoggerFactory('[VIBENV]');

            // Add JSON body parser for vibe-server routes
            expressApp.use('/_vibe-agent', express.json());

            // Mount vibe-server routes
            expressApp.use('/_vibe-agent', routes(loggerFactory));

            console.log('Vibe-server enabled');
            console.log('  Routes available at: http://' + hostname + ':' + port + '/_vibe-agent');
        }

        // Next.js Request Handler (All Routes)
        expressApp.all('*', (req, res) => {
            const parsedUrl = parse(req.url, true);
            return handle(req, res, parsedUrl);
        });

        // Start Server
        server.listen(port, hostname, (err) => {
            if (err) throw err;
            console.log(`> Ready on http://${hostname}:${port}`);
        });

        // Graceful Shutdown
        const gracefulShutdown = (signal) => {
            console.log(`Received ${signal}, closing server gracefully...`);
            server.close(() => {
                console.log('Server closed');
                process.exit(0);
            });
            setTimeout(() => {
                console.error('Forcing server close after timeout');
                process.exit(1);
            }, 10000);
        };

        process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
        process.on('SIGINT', () => gracefulShutdown('SIGINT'));
    })
    .catch((err) => {
        console.error('Error starting server:', err);
        process.exit(1);
    });

Update your package.json scripts:

json
{
  "scripts": {
    "dev": "node server.mjs",
    "dev:next": "next dev",
    "dev:turbo": "next dev --turbopack",
    "build": "next build",
    "start": "NODE_ENV=production node server.mjs"
  }
}

Vibe-Server Routes Available:

  • POST /_vibe-agent/prompt - Submit AI prompts
  • GET /_vibe-agent/sse - Real-time updates via Server-Sent Events
  • GET /_vibe-agent/session-response - Get session state
  • GET /_vibe-agent/workspace/accept - Commit and push changes
  • GET /_vibe-agent/workspace/drop - Discard changes
  • GET /_vibe-agent/workspace/reset - Reset to main branch
  • GET /_vibe-agent/kill - Kill agent process

5) Create VibeAgentScript Component

Create src/components/VibeAgentScript.tsx:

tsx
'use client';

import Script from 'next/script';

/**
 * VibeAgentScript Component
 *
 * Injects the vibe-agent UI script into the application.
 * The vibe-agent provides AI-powered development tools in the browser.
 */
export const VibeAgentScript = () => {
    const isDevelopment = process.env.NODE_ENV === 'development';
    const vibeAgentUrl = process.env.NEXT_PUBLIC_VIBE_AGENT_URL || 'http://localhost:5173/src/main.ts';
    const disableVibeAgent = process.env.NEXT_PUBLIC_DISABLE_VIBE_AGENT === 'true';

    // Don't load in production or if explicitly disabled
    if (!isDevelopment || disableVibeAgent) {
        return null;
    }

    return (
        <Script
            src={vibeAgentUrl}
            type="module"
            strategy="afterInteractive"
            onError={(e) => {
                console.warn(
                    '[VibeAgent] Failed to load vibe-agent script. ' +
                    'Make sure the vibe-agent dev server is running: ' +
                    'cd packages/vibe-agent && npm run dev'
                );
            }}
        />
    );
};

6) Update Root Layout

Update src/app/layout.tsx to include the VibeAgentScript component:

tsx
import type { ReactNode } from 'react';
import type { Metadata } from 'next';
import { VibeAgentScript } from '@/components/VibeAgentScript';
// ... other imports

export const metadata: Metadata = {
    title: 'Create Next App',
    description: 'Generated by create next app'
};

const Layout = ({ children }: Readonly<{ children: ReactNode }>) => {
    return (
        <html suppressHydrationWarning lang='en'>
            <body className="...">
                {/* Your app content */}
                {children}
                <VibeAgentScript />
            </body>
        </html>
    );
};

export default Layout;

7) Environment Variables (Optional)

Create a .env file in your project root for local configuration:

bash
# Vibe-Agent Configuration
NEXT_PUBLIC_VIBE_AGENT_URL=http://localhost:5173/src/main.ts
NEXT_PUBLIC_DISABLE_VIBE_AGENT=false

# Vibe-Server Configuration
DISABLE_VIBE_SERVER=false

# Server Configuration
HOSTNAME=localhost
PORT=3000
NODE_ENV=development

8) Optional: Set the CLI agent via .vibenv.js

Create .vibenv.js in your project root to control which CLI agent Vibenv uses:

javascript
module.exports = {
    "cli-agent": "claude" // or "copilot"
};

This file is read from the current working directory when Vibenv starts.

9) Start the Development Server

First, ensure the vibe-agent UI is running (in a separate terminal):

bash
cd packages/vibe-agent
npm run dev

Then start your Next.js application:

bash
npm run dev

You should see:

  • Vibenv logs in the console during compilation
  • The Vibe-server routes available at http://localhost:3000/_vibe-agent
  • Press Shift+V in the browser to toggle the Vibe-Agent UI

How Integration Works

  1. SWC Plugin: During build, the SWC plugin injects data-component-file and data-component-name attributes into JSX elements, enabling the vibe-agent to identify components.

  2. Custom Server: The Express server in server.mjs:

    • Mounts vibe-server routes at /_vibe-agent/*
    • Works with both webpack and Turbopack
    • Provides full Express middleware control
    • Handles graceful shutdown
  3. Script Injection: The VibeAgentScript component:

    • Loads the vibe-agent UI from the Vite dev server (HMR enabled)
    • Only runs in development
    • Uses Next.js Script component for optimal loading

Troubleshooting

SWC Plugin Not Working

Issue: Component metadata attributes not appearing

Solutions:

  • Ensure you're using npm run dev (webpack), not npm run dev:turbo
  • Verify the SWC plugin path is correct in next.config.ts
  • Check that the WASM file exists at the specified path

Vibe-Server Routes Not Available

Issue: /_vibe-agent/* routes return 404

Solutions:

  • Ensure @vibenv/vibe-server is installed
  • Check that you're using the custom server (npm run dev, not npm run dev:next)
  • Verify DISABLE_VIBE_SERVER is not set to true

Vibe-Agent UI Not Loading

Issue: Script fails to load or UI doesn't appear

Solutions:

  • Ensure vibe-agent dev server is running: cd packages/vibe-agent && npm run dev
  • Check NEXT_PUBLIC_VIBE_AGENT_URL in .env
  • Verify NEXT_PUBLIC_DISABLE_VIBE_AGENT is not true
  • Check browser console for script loading errors

Port Conflicts

Issue: Port 3000 already in use

Solutions:

  • Change the port in .env: PORT=3001
  • Or kill the process using port 3000: lsof -ti:3000 | xargs kill

Turbopack Limitations

Issue: SWC plugin doesn't work with Turbopack

Solution:

  • This is a known limitation. Use npm run dev for full Vibenv functionality
  • Use npm run dev:turbo only for faster development without component metadata

TypeScript Configuration

Ensure your tsconfig.json includes the following:

json
{
    "compilerOptions": {
        "lib": ["dom", "dom.iterable", "esnext"],
        "allowJs": true,
        "skipLibCheck": true,
        "strict": true,
        "noEmit": true,
        "esModuleInterop": true,
        "module": "esnext",
        "moduleResolution": "bundler",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "jsx": "preserve",
        "incremental": true,
        "plugins": [{ "name": "next" }],
        "paths": {
            "@/*": ["./src/*"]
        },
        "target": "ES2023"
    }
}

Next.js Configuration Options

The Next.js configuration in this integration uses:

  • output: 'standalone' - Enables standalone output for Docker deployment
  • experimental.swcPlugins - Injects the Vibenv SWC plugin for component metadata

Production Considerations

  • The vibe-agent and vibe-server are development-only tools
  • In production, the VibeAgentScript component returns null
  • The custom server can still be used in production (without vibe-server routes)
  • For deployment, ensure NODE_ENV=production is set