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.
- Connect the repository.
- Build command
npm run build, publish directory.next. - Add the
NEXT_PUBLIC_*variables under Site settings → Environment variables. - Leave
BUILD_STANDALONEunset — 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.