/ Docs

Next.js Guide

Run a full Next.js development server in the browser with App Router, Pages Router, HMR, CSS Modules, and API routes.

Setup

The Next.js dev server needs three things: a VirtualFS to store your files, a NextDevServer to process requests, and a ServerBridge to serve them in an iframe.

import { VirtualFS } from 'almostnode';
import { NextDevServer } from 'almostnode/next';
import { getServerBridge } from 'almostnode';

const vfs = new VirtualFS();
const server = new NextDevServer(vfs);
const bridge = getServerBridge();

// Initialize the service worker
await bridge.initServiceWorker();

// Register the server on a port
bridge.registerServer(server, 3000);

// Point an iframe to the dev server
iframe.src = bridge.getServerUrl(3000);

App Router

The App Router uses file-based routing with /app directory conventions. Each directory can contain page.tsx, layout.tsx, loading.tsx, and error.tsx.

Basic Page

// Create a root layout
vfs.writeFileSync('/app/layout.tsx', `
  export default function RootLayout({ children }) {
    return (
      <html>
        <body>{children}</body>
      </html>
    );
  }
`);

// Create the home page
vfs.writeFileSync('/app/page.tsx', `
  export default function Home() {
    return <h1>Welcome!</h1>;
  }
`);

// Create a nested route: /about
vfs.writeFileSync('/app/about/page.tsx', `
  export default function About() {
    return <h1>About us</h1>;
  }
`);

Dynamic Routes

Use bracket notation for dynamic segments:

// /app/posts/[id]/page.tsx → matches /posts/123
vfs.writeFileSync('/app/posts/[id]/page.tsx', `
  export default function Post({ params }) {
    return <h1>Post {params.id}</h1>;
  }
`);

Route Groups

Route groups use (groupName) directories. They're transparent in the URL — useful for organizing code without affecting the path:

// /app/(marketing)/pricing/page.tsx → /pricing
vfs.writeFileSync('/app/(marketing)/pricing/page.tsx', `
  export default function Pricing() {
    return <h1>Plans</h1>;
  }
`);

Pages Router

The Pages Router uses the /pages directory. Each file becomes a route:

// /pages/index.jsx → /
vfs.writeFileSync('/pages/index.jsx', `
  export default function Home() {
    return <h1>Pages Router home</h1>;
  }
`);

// /pages/about.jsx → /about
vfs.writeFileSync('/pages/about.jsx', `
  export default function About() {
    return <h1>About</h1>;
  }
`);

API Routes

Files in /pages/api/ (Pages Router) or /app/api/.../route.ts (App Router) create API endpoints:

// Pages Router API route
vfs.writeFileSync('/pages/api/hello.js', `
  export default function handler(req, res) {
    res.status(200).json({ message: 'Hello!' });
  }
`);

// App Router route handler
vfs.writeFileSync('/app/api/hello/route.ts', `
  export async function GET(request) {
    return Response.json({ message: 'Hello!' });
  }
`);

CSS Modules

Import .module.css files to get scoped class names:

vfs.writeFileSync('/app/styles.module.css', `
  .title {
    color: blue;
    font-size: 2rem;
  }
  .subtitle {
    color: gray;
  }
`);

vfs.writeFileSync('/app/page.tsx', `
  import styles from './styles.module.css';

  export default function Home() {
    return (
      <div>
        <h1 className={styles.title}>Hello</h1>
        <p className={styles.subtitle}>World</p>
      </div>
    );
  }
`);

Classes are scoped with a hash to prevent collisions between components.

Hot Module Replacement

The dev server supports HMR through React Refresh. When you change a file in the VFS, the server detects it and pushes updates to the browser without a full page reload:

// Initial page
vfs.writeFileSync('/app/page.tsx', `
  export default function Home() {
    return <h1>Version 1</h1>;
  }
`);

// Later, update the file — HMR kicks in automatically
vfs.writeFileSync('/app/page.tsx', `
  export default function Home() {
    return <h1>Version 2</h1>;
  }
`);

Configuration

You can provide a next.config.js for path aliases and other options:

vfs.writeFileSync('/next.config.js', `
  module.exports = {
    experimental: {
      paths: {
        '@/*': ['./src/*'],
        '@components/*': ['./src/components/*']
      }
    }
  };
`);