1---
2title: Dev servers
3description: Run a framework dev server as a Command.Dev resource — started by alchemy dev, a no-op on alchemy deploy, with the server's URL surfaced back into your stack.
4---
5
6`Command.Dev` is a long-lived process scoped to the stack instance:
7started during [`alchemy dev`](/cli/dev), restarted when its inputs
8change, and a literal no-op during `alchemy deploy` — the deploy
9provider returns `{ url: undefined }` without spawning anything.
10This page assumes the [Command provider](/command) is registered.
11
12## Declare a dev server
13
14Pass the command that starts your framework's dev server:
15
16```typescript
17import * as Command from "alchemy/Command";
18
19const dev = yield* Command.Dev("Frontend", {
20 command: "npm run dev",
21});
22yield* Console.log(dev.url); // e.g. "http://localhost:5173"
23```
24
25Like every Command resource, `Dev` accepts `cwd` (for monorepo
26packages), `env` (merged on top of `process.env`, with `Redacted`
27values kept out of logs and state), and `shell` — without it the
28command string is whitespace-split into binary and arguments, with
29no quoting support.
30
31## Where the process lives
32
33The child process runs inside the dev sidecar (see
34`Command/Local.ts`) rather than in your Alchemy program's own
35process, so it survives user-code HMR — Alchemy's user process can
36restart without killing your `npm run dev` server. Its stdout and
37stderr are mirrored to the terminal, preserving colored output.
38
39## The url attribute
40
41`dev.url` is best-effort: Alchemy scans both stdout and stderr for
42the first `http(s)://…` URL within a hard 5-second window, stripping
43ANSI escape sequences first so Vite- and Next-style colored output
44still matches. If nothing matches within the window, `url` is
45`undefined` and the process keeps running.
46
47## Lifecycle
48
49A `Dev` resource restarts its process when props change, keeps the
50same PID across unchanged redeploys, and kills the process on
51destroy. If the command exits within the 5-second startup window,
52the deploy fails with a typed `CommandError` whose reason is
53`UnexpectedExit` — match it with the exported guard, as the test
54suite does:
55
56```typescript
57assert(Command.isCommandError(error));
58assert(error.reason._tag === "UnexpectedExit");
59expect(error.reason.exitCode).toBe(1);
60```
61
62## With StaticSite
63
64You rarely declare `Command.Dev` by hand for a frontend:
65`StaticSite`'s `dev` prop declares one for you during `alchemy dev`
66and skips the build entirely, letting your framework's own dev
67server serve the site. See
68[Static sites](/cloudflare/frontend/static-site) for the full story.
69
70## Where next
71
72- [`alchemy dev`](/cli/dev) — the CLI mode that activates `Dev`
73 resources.
74- [Memoized builds](/command/memoization) — `Command.Build` and
75 `Command.Exec`, the deploy-time counterparts.
76
77Reference:
78
79- [Dev API reference](/providers/command/dev)