Skip to main content
This guide covers deploying the KeyBox frontend dashboard, built with Next.js 16, React 19, and TypeScript.

Prerequisites

Before deploying the frontend, ensure you have:
  • Node.js 20.x or higher
  • pnpm 9.0.0 or higher (recommended package manager)
  • Backend server deployed and accessible (see Backend Setup)

Installation

1

Navigate to web directory

cd apps/web
2

Install dependencies

The project uses pnpm as the package manager:
pnpm install
If you don’t have pnpm installed:
npm install -g [email protected]

Key Dependencies

The frontend uses these major technologies:
  • Next.js 16.1.1 - React framework with App Router
  • React 19.2.3 - UI library
  • TypeScript - Type safety
  • TanStack Query - Data fetching and caching
  • Axios - HTTP client
  • Radix UI - Accessible component primitives
  • Tailwind CSS - Utility-first styling
  • Framer Motion - Animations
  • Lucide React - Icon library
  • Sonner - Toast notifications
3

Configure environment variables

Create a .env.local file in the apps/web directory:
touch .env.local
Add the required environment variable:
NEXT_PUBLIC_API_URL=http://localhost:5000/
The NEXT_PUBLIC_ prefix is required for environment variables that need to be accessible in the browser.
See Environment Variables for production configuration.

Development Mode

Run the development server with hot-reloading:
pnpm dev
The application will be available at http://localhost:3000.

Development Features

  • Hot Module Replacement (HMR)
  • Fast Refresh for React components
  • TanStack Query DevTools (automatically enabled in development)
  • TypeScript error checking
  • ESLint integration

Production Build

1

Build the application

Create an optimized production build:
pnpm build
This will:
  • Compile TypeScript
  • Bundle JavaScript and CSS
  • Optimize images and assets
  • Generate static pages where possible
  • Create server-side rendering bundles
2

Test production build locally

Start the production server:
pnpm start
Visit http://localhost:3000 to test the production build.

Deployment Options

Vercel is the recommended platform for Next.js applications.
1

Install Vercel CLI

npm i -g vercel
2

Deploy

From the apps/web directory:
vercel
For production deployment:
vercel --prod
3

Configure environment variables

In the Vercel dashboard:
  1. Go to Project Settings → Environment Variables
  2. Add NEXT_PUBLIC_API_URL with your backend URL
  3. Set it for Production, Preview, and Development environments
  4. Redeploy for changes to take effect
Vercel automatically detects Next.js and configures optimal build settings.

Deploy to Netlify

1

Install Netlify CLI

npm install -g netlify-cli
2

Build and deploy

netlify deploy --prod
Or connect your GitHub repository through the Netlify dashboard.
3

Configure build settings

In netlify.toml:
[build]
  command = "pnpm build"
  publish = ".next"

[[plugins]]
  package = "@netlify/plugin-nextjs"

Deploy with Docker

Create a Dockerfile in the apps/web directory:
FROM node:20-alpine AS base

# Install pnpm
RUN npm install -g [email protected]

# Dependencies
FROM base AS deps
WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

# Builder
FROM base AS builder
WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

RUN pnpm build

# Runner
FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]
Update next.config.ts to enable standalone output:
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
};

export default nextConfig;
Build and run:
docker build -t keybox-web --build-arg NEXT_PUBLIC_API_URL=https://api.yourdomain.com/ .
docker run -p 3000:3000 keybox-web

Deploy to Static Hosting

For static export (if your app doesn’t require SSR):
1

Configure for static export

Update next.config.ts:
const nextConfig: NextConfig = {
  output: "export",
  images: {
    unoptimized: true,
  },
};
2

Build static files

pnpm build
This creates an out directory with static HTML/CSS/JS.
3

Deploy to hosting

Upload the out directory to:
  • AWS S3 + CloudFront
  • GitHub Pages
  • Cloudflare Pages
  • Any static hosting provider
Static export disables Next.js features like:
  • Server-side rendering (SSR)
  • API routes
  • Dynamic routes without generateStaticParams
  • Image optimization (unless using a custom loader)

Configuration

API Base URL

The frontend communicates with the backend via the NEXT_PUBLIC_API_URL environment variable. Development:
NEXT_PUBLIC_API_URL=http://localhost:5000/
Production:
NEXT_PUBLIC_API_URL=https://api.yourdomain.com/
Ensure the URL ends with a trailing slash /

Authentication Flow

The frontend uses JWT tokens stored in cookies:
  1. User logs in via /login or /signup
  2. OAuth redirect to backend (/auth/google)
  3. Backend redirects back with JWT in URL
  4. Frontend stores JWT in cookie
  5. All API requests include JWT in Authorization header

Axios Configuration

The app uses a custom Axios instance (lib/api.ts):
import axios from "axios";
import Cookies from "js-cookie";

export const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
});

api.interceptors.request.use((config) => {
  const jwt = Cookies.get("jwt");
  if (jwt) {
    config.headers.Authorization = `Bearer ${jwt}`;
  }
  return config;
});

api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      Cookies.remove("jwt");
      window.location.href = "/login";
    }
    return Promise.reject(error);
  }
);

Performance Optimization

Image Optimization

Use Next.js <Image> component for automatic optimization:
import Image from "next/image";

<Image
  src="/logo.png"
  alt="KeyBox"
  width={200}
  height={50}
  priority // for above-the-fold images
/>

Code Splitting

Next.js automatically code-splits by route. For additional splitting:
import dynamic from "next/dynamic";

const HeavyComponent = dynamic(() => import("./HeavyComponent"), {
  loading: () => <p>Loading...</p>,
  ssr: false, // disable SSR if needed
});

Caching Strategy

TanStack Query handles data caching:
const { data } = useQuery({
  queryKey: ["licenses"],
  queryFn: fetchLicenses,
  staleTime: 5 * 60 * 1000, // 5 minutes
  cacheTime: 10 * 60 * 1000, // 10 minutes
});

Security Best Practices

Production security checklist:
  • Set NODE_ENV=production in production
  • Use HTTPS for all API requests
  • Implement Content Security Policy (CSP)
  • Enable HTTP Strict Transport Security (HSTS)
  • Sanitize user inputs
  • Keep dependencies updated: pnpm audit
  • Never expose API keys in client-side code

Content Security Policy

Add CSP headers in next.config.ts:
const nextConfig: NextConfig = {
  async headers() {
    return [
      {
        source: "/(.*)",
        headers: [
          {
            key: "Content-Security-Policy",
            value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';",
          },
        ],
      },
    ];
  },
};

Troubleshooting

Build Errors

TypeScript errors:
pnpm build
# Check for type errors in output
ESLint errors:
pnpm lint

Runtime Issues

API connection fails:
  • Verify NEXT_PUBLIC_API_URL is set correctly
  • Check CORS configuration on backend
  • Ensure backend is running and accessible
Authentication not working:
  • Check JWT cookie is being set
  • Verify backend OAuth configuration
  • Check browser console for errors

Performance Issues

Slow page loads:
  • Enable Next.js Analytics (Vercel)
  • Use React DevTools Profiler
  • Check TanStack Query DevTools for excessive requests
  • Optimize images and reduce bundle size

Monitoring

Analytics

Add Vercel Analytics:
import { Analytics } from "@vercel/analytics/react";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  );
}

Error Tracking

Integrate Sentry or similar:
pnpm add @sentry/nextjs

Next Steps

Build docs developers (and LLMs) love