qr-local-dev
Version:
Print a QR code for your local dev server in the terminal
296 lines (225 loc) • 8.12 kB
Markdown
# qr-local-dev
Display a QR code in your terminal that points to your machine's local network URL, so you can open your dev server on a phone or another computer instantly.
## Install
```bash
npm i -D qr-local-dev
```
Or use it globally:
```bash
npm i -g qr-local-dev
```
## CLI
```bash
qr-local -p 3000
```
Options:
- `-p, --port <number>`: Port to include in the URL (e.g., 3000)
- `--ports <list>`: Comma list of ports to try (e.g., 3000,5173,8080)
- `--http`/`--https` or `--protocol <p>`: `http` | `https` | `auto`
- `--path <path>`: Optional path to append (e.g., /app)
- `-H, --host <ip>`: Override detected host/IP
- `-s, --small`: Use smaller terminal QR variant
- `-d, --detect`: Try common ports and pick a running dev server
- `-c, --copy`: Copy the resolved URL to your clipboard
- `--mdns`: Broadcast via mDNS/Bonjour (if available)
- `--mdns-name <n>`: Custom mDNS service name
- `--force-qr`: Print QR even if not a TTY
- `-w, --wait`: Wait for the server to become available
- `--wait-timeout <ms>`: Max wait time in ms (default 15000)
- `--wait-interval <ms>`: Probe interval in ms (default 500)
- `--square`: Render square-style QR (uses qrcode-terminal)
## API
```js
const { printLocalhostQR } = require("qr-local-dev");
await printLocalhostQR({ port: 3000 });
// Auto-detect server and protocol (useful across frameworks)
await printLocalhostQR({
protocol: "auto",
detect: true,
small: true,
square: true,
copy: true,
mdns: true,
wait: true,
waitTimeout: 15000,
waitInterval: 500,
});
```
TypeScript users: this package ships `.d.ts` types. Import as:
```ts
import { printLocalhostQR } from "qr-local-dev";
```
## Express example
```js
const express = require("express");
const { printLocalhostQR } = require("qr-local-dev");
const app = express();
const port = process.env.PORT || 3000;
app.get("/", (_req, res) => {
res.send("Hello from Express");
});
app.listen(port, async () => {
await printLocalhostQR({ port });
});
```
## Next.js: auto-print QR on dev start (no extra scripts)
Add an instrumentation file so the QR shows right after `npm run dev`:
1. Create `instrumentation.js` or `instrumentation.ts` at your project root:
```ts
// instrumentation.(js|ts) (Next.js App Router)
export async function register() {
// Only run in Node.js runtime (skip Edge) and only in dev
if (process.env.NEXT_RUNTIME !== "nodejs") return;
if (process.env.NODE_ENV === "production") return;
// Prevent duplicate prints during Fast Refresh
const shownKey = "__qr_local_dev_shown__";
if ((globalThis as any)[shownKey]) return;
(globalThis as any)[shownKey] = true;
const { printLocalhostQR } = await import("qr-local-dev");
const envBool = (v?: string) => v === "1" || v === "true";
const portsEnv = process.env.QR_LOCAL_PORTS;
const ports = portsEnv
? portsEnv
.split(",")
.map((p) => Number(p.trim()))
.filter(Boolean)
: undefined;
const delay = Number(process.env.QR_LOCAL_DELAY ?? 300);
setTimeout(() => {
printLocalhostQR({
protocol: process.env.QR_LOCAL_PROTOCOL || "auto",
detect: envBool(process.env.QR_LOCAL_DETECT ?? "1"),
wait: envBool(process.env.QR_LOCAL_WAIT ?? "1"),
waitTimeout: Number(process.env.QR_LOCAL_WAIT_TIMEOUT ?? 15000),
waitInterval: Number(process.env.QR_LOCAL_WAIT_INTERVAL ?? 500),
small: envBool(process.env.QR_LOCAL_SMALL ?? "1"),
square: envBool(process.env.QR_LOCAL_SQUARE ?? "0"),
copy: envBool(process.env.QR_LOCAL_COPY ?? "0"),
mdns: envBool(process.env.QR_LOCAL_MDNS ?? "0"),
mdnsName: process.env.QR_LOCAL_MDNS_NAME,
forceQr: envBool(process.env.QR_LOCAL_FORCE_QR ?? "0"),
host: process.env.QR_LOCAL_HOST,
path: process.env.QR_LOCAL_PATH || "",
port: process.env.PORT ? Number(process.env.PORT) : undefined,
ports,
}).catch(() => {});
}, delay);
}
```
2. Pin your dev port so the QR matches:
```json
{
"scripts": {
"dev": "next dev -p 3000"
}
}
```
3. Run your app as usual:
```bash
npm run dev
```
Optional HTTPS:
```bash
next dev -p 3000 --experimental-https
```
If you use HTTPS, set `protocol: "https"` in `printLocalhostQR`.
Troubleshooting:
- Requires App Router (instrumentation file convention). For Pages Router-only apps, use the CLI approach.
- Ensure the file name is exactly `instrumentation.js` or `instrumentation.ts` in your project root (or `app/`).
- If the port can change, use `--wait`/`detect` or pin the port with `-p`.
## Framework tips
- Vite (React/Vue/Svelte/Solid): default ports 5173/4173; use `--protocol auto --detect --ports 5173,4173`
- Next.js: default 3000; use instrumentation above or `concurrently` with `next dev -p 3000`
- Nuxt: default 3000; prefer `--protocol auto --detect --ports 3000,4000`
- Astro: default 4321; use `--protocol auto --detect --ports 4321`
- SvelteKit: default 5173; use `--protocol auto --detect --ports 5173`
- Angular: default 4200; use `--protocol auto --detect --ports 4200`
- Vue CLI: default 8080; use `--protocol auto --detect --ports 8080`
General advice:
- Pin your dev port to keep the QR stable (avoid auto-increment ports).
- If your dev server switches to HTTPS, `--protocol auto` will probe and pick it.
- Add `-c/--copy` so you can paste the URL quickly on your device.
## Framework recipes (copy/paste)
Install helpers once:
```bash
npm i -D concurrently wait-on
```
Add one of these to your app's `package.json` scripts.
1. Next.js (Pages Router only):
```json
{
"scripts": {
"dev": "concurrently -k -s first \"next dev -p 3000\" \"wait-on http://localhost:3000 && qr-local --protocol auto --detect --wait\""
}
}
```
2. Vite (React/Vue/Svelte/Solid):
```json
{
"scripts": {
"dev": "concurrently -k -s first \"vite\" \"wait-on http://localhost:5173 && qr-local --protocol auto --detect --ports 5173,4173 --wait\""
}
}
```
3. Nuxt:
```json
{
"scripts": {
"dev": "concurrently -k -s first \"nuxi dev -p 3000\" \"wait-on http://localhost:3000 && qr-local --protocol auto --detect --wait\""
}
}
```
4. Astro:
```json
{
"scripts": {
"dev": "concurrently -k -s first \"astro dev\" \"wait-on http://localhost:4321 && qr-local --protocol auto --detect --ports 4321 --wait\""
}
}
```
5. SvelteKit:
```json
{
"scripts": {
"dev": "concurrently -k -s first \"vite\" \"wait-on http://localhost:5173 && qr-local --protocol auto --detect --ports 5173 --wait\""
}
}
```
6. Angular:
```json
{
"scripts": {
"dev": "concurrently -k -s first \"ng serve --host 0.0.0.0 --port 4200\" \"wait-on http://localhost:4200 && qr-local --protocol auto --detect --ports 4200 --wait\""
}
}
```
7. Vue CLI:
```json
{
"scripts": {
"dev": "concurrently -k -s first \"vue-cli-service serve --host 0.0.0.0 --port 8080\" \"wait-on http://localhost:8080 && qr-local --protocol auto --detect --ports 8080 --wait\""
}
}
```
8. Remix:
```json
{
"scripts": {
"dev": "concurrently -k -s first \"remix dev\" \"wait-on http://localhost:3000 && qr-local --protocol auto --detect --ports 3000 --wait\""
}
}
```
Notes:
- Use `--square` for a square-looking QR: append `--square` to the QR command.
- Add `--copy` to copy URL to clipboard automatically.
- For HTTPS, prefer `--protocol auto` (it will probe) or pass `--https`.
### WSL (Windows Subsystem for Linux)
- Your LAN IP might differ between Windows and WSL. This tool uses `os.networkInterfaces()` inside the current runtime.
- If mobile cannot reach the URL, try overriding host with the Windows LAN IP: `qr-local -H 192.168.x.y -p 3000`.
- If the port is forwarded or bound to 127.0.0.1 only, ensure your dev server binds to `0.0.0.0`.
### Docker/containers
- On Linux, `--network host` allows containers to use the host network (QR will point to the host IP).
- Otherwise, expose the port and run the QR command on the host, not inside the container.
### TTY/CI
- Some environments pipe output; the QR may be hidden. Use `--force-qr` to print anyway or rely on the URL line.
- You can disable QR (URL only) by omitting `--force-qr` in non-TTY contexts.