> ## Documentation Index
> Fetch the complete documentation index at: https://docs.declaw.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# web-dev — Next.js + Tailwind build

> Scaffold a Next.js app with Tailwind, write a page, build it, and inspect the output — the web-dev template's happy path.

The `web-dev` template ships `Node 20`, `npm`, `yarn`, `pnpm`, plus the
Next.js, Vite, Prisma, Drizzle, Tailwind, ESLint, and Prettier CLIs
pre-installed globally. Pick it when you want an agent to bootstrap,
edit, and build a React / Next frontend without a multi-minute cold
`npm install` on every run.

## What you'll learn

* Picking `template="web-dev"` to skip Node + tooling installs
* Creating a new Next.js app with `npx create-next-app` offline-cached
* Writing a page and running `next build` against it

## Prerequisites

<Snippet file="snippets/env-setup.mdx" />

## Code

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import textwrap

    from declaw import Sandbox


    PAGE = textwrap.dedent("""
        export default function Home() {
          const stats = { orders: 42, revenue: 1337.5, uptime: "99.9%" };
          return (
            <main className="min-h-screen bg-slate-900 text-slate-50 p-8">
              <h1 className="text-3xl font-bold">Declaw demo</h1>
              <ul className="mt-4 space-y-2">
                {Object.entries(stats).map(([k, v]) => (
                  <li key={k}>
                    <span className="text-slate-400">{k}: </span>
                    <span className="font-mono">{String(v)}</span>
                  </li>
                ))}
              </ul>
            </main>
          );
        }
    """)


    def main() -> None:
        sbx = Sandbox.create(template="web-dev", timeout=240)
        try:
            for tool in ("node --version", "next --version", "tsc --version"):
                r = sbx.commands.run(tool, timeout=15)
                print(f"{tool:<35} => {(r.stdout or r.stderr).splitlines()[0]}")

            # Scaffold a Next.js app. Flags make it non-interactive.
            r = sbx.commands.run(
                "cd /tmp && npx --yes create-next-app@latest app "
                "--ts --tailwind --eslint --app --no-src-dir "
                "--import-alias '@/*' --use-npm --skip-install",
                timeout=120,
            )
            if r.exit_code != 0:
                print("scaffold failed:", r.stderr[:800])
                return

            # Overwrite the default home page with our own content.
            sbx.files.write("/tmp/app/app/page.tsx", PAGE)

            # Install deps + build.
            r = sbx.commands.run(
                "cd /tmp/app && npm install --silent && npx next build",
                timeout=300,
            )
            if r.exit_code != 0:
                print("build failed:", r.stderr[-1200:])
                return
            print("build succeeded — last 12 lines:")
            print("\n".join(r.stdout.strip().splitlines()[-12:]))

            # Inspect the output directory structure.
            r = sbx.commands.run("ls -1 /tmp/app/.next/server/app 2>/dev/null | head")
            print("\nrendered routes:")
            print(r.stdout)
        finally:
            sbx.kill()


    if __name__ == "__main__":
        main()
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={null}
    import "dotenv/config";
    import { Sandbox } from "@declaw/sdk";

    const PAGE = `
    export default function Home() {
      const stats = { orders: 42, revenue: 1337.5, uptime: "99.9%" };
      return (
        <main className="min-h-screen bg-slate-900 text-slate-50 p-8">
          <h1 className="text-3xl font-bold">Declaw demo</h1>
          <ul className="mt-4 space-y-2">
            {Object.entries(stats).map(([k, v]) => (
              <li key={k}>
                <span className="text-slate-400">{k}: </span>
                <span className="font-mono">{String(v)}</span>
              </li>
            ))}
          </ul>
        </main>
      );
    }
    `;

    async function main(): Promise<void> {
      const sbx = await Sandbox.create({ template: "web-dev", timeout: 240 });
      try {
        for (const cmd of ["node --version", "next --version", "tsc --version"]) {
          const r = await sbx.commands.run(cmd, { timeout: 15 });
          const line = (r.stdout || r.stderr).split("\n")[0];
          console.log(`${cmd.padEnd(35)} => ${line}`);
        }

        let r = await sbx.commands.run(
          "cd /tmp && npx --yes create-next-app@latest app " +
          "--ts --tailwind --eslint --app --no-src-dir " +
          "--import-alias '@/*' --use-npm --skip-install",
          { timeout: 120 },
        );
        if (r.exitCode !== 0) {
          console.log("scaffold failed:", r.stderr.slice(0, 800));
          return;
        }

        await sbx.files.write("/tmp/app/app/page.tsx", PAGE);

        r = await sbx.commands.run(
          "cd /tmp/app && npm install --silent && npx next build",
          { timeout: 300 },
        );
        if (r.exitCode !== 0) {
          console.log("build failed:", r.stderr.slice(-1200));
          return;
        }
        const tail = r.stdout.trim().split("\n").slice(-12).join("\n");
        console.log("build succeeded — last 12 lines:");
        console.log(tail);

        r = await sbx.commands.run("ls -1 /tmp/app/.next/server/app 2>/dev/null | head");
        console.log("\nrendered routes:");
        console.log(r.stdout);
      } finally {
        await sbx.kill();
      }
    }

    main().catch(console.error);
    ```
  </Tab>
</Tabs>

## Expected output

```
node --version                      => v20.x
next --version                      => 14.x
tsc --version                       => Version 5.x

build succeeded — last 12 lines:
   ▲ Next.js 14.x
   - Environments: .env.local
   ...
   Route (app)                              Size     First Load JS
   ┌ ○ /                                    ...      ...
   ...

rendered routes:
page.js
```

<Note>
  `create-next-app` pulls packages from the npm registry — add a
  `SecurityPolicy` with domains `registry.npmjs.org` and
  `registry.yarnpkg.com` if you have a domain allowlist configured.
</Note>
