# How to preview a Next.js app an AI agent built Run `next dev` in the agent's sandbox with spawn, allow `*.runtimehost.com` in `allowedDevOrigins`, and share port 3000 as a preview. **On Runtime each agent's app gets its own microVM and a private HTTPS address in one call.** The app never runs on your servers, the address needs a token unless you make it public, and hot reload works because previews carry WebSockets. The sandbox has Node.js 24.21.0; Next.js 16.3.6, current on 25 September 2026, needs 20.9 or later. ## Run the agent's app Upload what the agent wrote, or scaffold a fresh project, then start the dev server and share it: ```ts check import { Sandbox } from "withruntime"; const nextConfig = `import type { NextConfig } from "next"; const nextConfig: NextConfig = { allowedDevOrigins: ["*.runtimehost.com"] }; export default nextConfig; `; const sbx = await Sandbox.create({ timeoutSeconds: 3600 }); // keeps running after this script ends await sbx.exec("npx create-next-app@16.3.6 site --yes --use-npm --disable-git", { check: true, timeoutMs: 600_000, env: { NEXT_TELEMETRY_DISABLED: "1" }, }); await sbx.files.write("/workspace/site/next.config.ts", nextConfig); await sbx.spawn("npx next dev -p 3000", { cwd: "/workspace/site", env: { NEXT_TELEMETRY_DISABLED: "1" }, }); await sbx.exec("npx wait-on@9.1.0 http://127.0.0.1:3000", { check: true, timeoutMs: 180_000 }); const preview = await sbx.previews.create(3000); console.log(preview.urlWithToken); // a one-time link for your browser ``` ```python check from withruntime import Sandbox NEXT_CONFIG = """import type { NextConfig } from "next"; const nextConfig: NextConfig = { allowedDevOrigins: ["*.runtimehost.com"] }; export default nextConfig; """ sbx = Sandbox.create(timeout_seconds=3600) sbx.exec("npx create-next-app@16.3.6 site --yes --use-npm --disable-git", check=True, timeout_ms=600_000, env={"NEXT_TELEMETRY_DISABLED": "1"}) sbx.files.write("/workspace/site/next.config.ts", NEXT_CONFIG) sbx.spawn("npx next dev -p 3000", cwd="/workspace/site", env={"NEXT_TELEMETRY_DISABLED": "1"}) sbx.exec("npx wait-on@9.1.0 http://127.0.0.1:3000", check=True, timeout_ms=180_000) print(sbx.previews.create(3000)["urlWithToken"]) ``` For a project the agent already wrote, replace the scaffold with `sbx.files.upload("./site", "/workspace/site")` and `npm ci`. Upload the source without its `node_modules`: packages installed on a Mac carry native binaries for the wrong system. ## Why `allowedDevOrigins` matters In development, Next.js blocks requests for dev-only assets and endpoints that come from an origin other than `localhost`, its subdomains or the hostname the server started with. A preview's hostname is a name under `runtimehost.com`, so without an entry the page loads but the dev assets it asks for are refused with 403, and the dev server logs "Blocked cross-origin request to Next.js dev resource". Hot reload then never connects. | Setting | Effect | | ------------------------------------------ | ------------------------------------------------------------------------------ | | No entry | Next.js blocks the preview's requests for dev assets | | `allowedDevOrigins: ["*.runtimehost.com"]` | Every preview address works; `*` covers one label, which is all a preview uses | | `next start` (production) | No dev endpoints, so the setting does not apply | Next.js matches only the hostname of the request's `Origin`, so write the entry without `https://` and without a port. ## Dev server or production build | Question | `next dev` | `next build` then `next start` | | ----------------------------------- | ---------------------------------- | --------------------------------- | | Does an edit show at once? | Yes, over hot reload | No; rebuild first | | Does it match what users would get? | Close, but compiled on request | Yes | | Who should open it? | The person watching the agent work | A reviewer, a client, a demo | | Preview visibility | Private, with the one-time link | Public, or private for a reviewer | To share the finished app: ```ts check import { Sandbox } from "withruntime"; const sbx = await Sandbox.connect(process.env.SANDBOX_ID ?? ""); const build = await sbx.exec("cd site && npx next build", { timeoutMs: 900_000, onStdout: (text) => process.stdout.write(text), }); if (build.exitCode !== 0) throw new Error(build.stdout + build.stderr); await sbx.spawn("npx next start -p 3001", { cwd: "/workspace/site" }); await sbx.exec("npx wait-on@9.1.0 http://127.0.0.1:3001", { check: true, timeoutMs: 120_000 }); const demo = await sbx.previews.create(3001, { visibility: "public" }); console.log(demo.url); ``` A failed build is useful to the agent too: `next build` checks types, so send its output back as the next message and the agent sees each error with its file and line. ## How much disk a Next.js app takes A fresh `create-next-app@16.3.6` project, measured on a Linux x86-64 machine on 25 September 2026: | Part | Size | | ------------------------------ | ------ | | `node_modules` | 476 MB | | `.next` after `next build` | 39 MB | | npm's download cache, `~/.npm` | 769 MB | That fits the default 4 GiB disk, which has about 2.5 GiB free, with room to spare. An agent that adds many packages, or a monorepo, needs more: ask for `diskMiB: 8192` when you create the sandbox. The trial allows up to 10 GiB. ## Start from an image with the dependencies installed Build the project's dependencies into a [custom image](/docs/images) with a Dockerfile. The lockfile comes first, so a rebuild after a code change reuses the installed packages: ```dockerfile no-run FROM runtime ENV NEXT_TELEMETRY_DISABLED=1 WORKDIR /workspace/site COPY package.json package-lock.json ./ RUN npm ci --no-fund --no-audit COPY . . RUN chown -R 1000:1000 /workspace/site ``` ```python check from withruntime import Runtime runtime = Runtime() with open("site/Dockerfile") as handle: runtime.images.build(name="site", dockerfile=handle.read(), context_dir="site", on_log=lambda line: print(line["text"])) sbx = runtime.sandboxes.create(image="site") sbx.spawn("npx next dev -p 3000", cwd="/workspace/site") print(sbx.previews.create(3000)["urlWithToken"]) ``` Keep `node_modules` and `.next` in `.dockerignore`, so the build context holds only the source. ## Related - [Preview apps an AI agent builds](/use-cases/preview-agent-built-apps) - [Preview a Vite dev server](/integrations/vite) - [Test the app with Playwright](/integrations/playwright) - [Per-user dev environments](/use-cases/per-user-dev-environments) - [Custom domains for a finished app](/docs/networking) ## Sources Checked 25 September 2026. - [allowedDevOrigins](https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins): what Next.js blocks in development, the `localhost` default, wildcard rules and hostname-only matching - [create-next-app](https://nextjs.org/docs/app/api-reference/cli/create-next-app): `--yes`, `--use-npm`, `--disable-git` - [next CLI](https://nextjs.org/docs/app/api-reference/cli/next): `-p`, default port 3000, Turbopack by default - [Next.js installation](https://nextjs.org/docs/app/getting-started/installation): Node.js 20.9 or later - [Next.js telemetry](https://nextjs.org/telemetry): `NEXT_TELEMETRY_DISABLED=1` - [next on npm](https://www.npmjs.com/package/next): version 16.3.6 Facts on this page were checked on 25 September 2026.