Deployment

Pre-flight

npm run lint && npm run typecheck && npm test && npm run build

Set production environment variables before building — NEXT_PUBLIC_* values are baked into the client bundle at build time.

Vercel

Import the repository, set the environment variables, deploy. No configuration needed; leave BUILD_STANDALONE unset.

Netlify

Netlify runs Next.js through the official adapter, which it applies automatically for App Router projects.

  1. Connect the repository.
  2. Build command npm run build, publish directory .next.
  3. Add the NEXT_PUBLIC_* variables under Site settings → Environment variables.
  4. Leave BUILD_STANDALONE unset — the adapter handles the server.

The demo API routes under src/app/api/ deploy as Netlify Functions. If you have connected your own backend and deleted them, nothing server-side remains and the build is purely static assets plus client rendering.

If a build fails on Netlify but succeeds locally, the usual cause is a missing environment variable: NEXT_PUBLIC_* values are read at build time, and an absent NEXT_PUBLIC_API_URL silently falls back to demo mode.

Docker

The bundled Dockerfile is a three-stage build producing a non-root image from Next.js standalone output.

docker build -t numeralens \
  --build-arg NEXT_PUBLIC_API_URL=https://api.yourcompany.com/v1 \
  --build-arg NEXT_PUBLIC_ENABLE_DEMO_MODE=false .

docker run -p 3000:3000 numeralens

The build stage sets BUILD_STANDALONE=true, which switches next.config.ts to output: "standalone". The runtime stage copies .next/standalone, .next/static and public, then runs node server.js as uid 1001.

Node server

npm ci
npm run build
npm run start          # listens on PORT, default 3000

Put it behind nginx or a load balancer for TLS and compression. For the standalone variant:

BUILD_STANDALONE=true npm run build
node .next/standalone/server.js

Static export

Not supported as-is. The bundled demo API routes need a server. If you connect a real backend and delete src/app/api/, every remaining route is client-rendered and output: "export" becomes viable — but the dynamic /customers/[id] and /vendors/[id] routes would then need generateStaticParams or a client-side lookup.

Reverse proxy note

If your API sits on another origin, either enable CORS there (including Access-Control-Allow-Credentials when using cookie auth) or proxy it under the same origin — for example, rewrite /api/finance/* to your backend in next.config.ts and set NEXT_PUBLIC_API_URL=/api/finance. Same-origin proxying avoids CORS entirely and keeps cookies simple.

Performance notes

  • Server-render is shell-only; data loads client-side through TanStack Query with keepPreviousData, so filter changes never flash empty layouts.
  • Large tables use server-side pagination where the dataset warrants it (general ledger, transactions).
  • The demo dataset is generated once per browser session and cached in memory.