bantam.hostbantam.host
CLI Guide

Deploy Next.js with Bantam CLI

Deploy Next.js static exports for blazing-fast performance

Install Bantam CLI:

npm install -g @bantamhq/cli

View on NPM: @bantamhq/cli

Quick start:

# Build your Next.js app
npm run build

# Deploy it
bantam deploy ./out

Quick Start

1

Configure for static export

Enable static HTML export in your Next.js config

// next.config.js
const nextConfig = {
  output: 'export',
  // Optional: Add a base path if deploying to a subdirectory
  // basePath: '/my-app',
}

module.exports = nextConfig
2

Build and export

Next.js 13+ automatically exports when output: 'export' is set

npm run build
Creates static files in the ./out directory
3

Deploy with Bantam

Your Next.js static site is now live!

bantam deploy ./out
Get a permanent URL with bantam deploy ./out --permanent

Framework Details

Static Export Requirements

Next.js static export has some limitations. These features require a Node.js server:

  • Image Optimization (next/image with default loader)
  • Internationalized Routing (i18n)
  • API Routes
  • Rewrites, Redirects, and Headers
  • ISR (Incremental Static Regeneration)
  • Middleware
Use getStaticProps and getStaticPaths for data fetching.getServerSideProps is not supported in static exports.

Image Optimization Workarounds

To use Next.js Image component with static export:

Option 1: Use a custom loader:

// next.config.js
const nextConfig = {
  output: 'export',
  images: {
    loader: 'custom',
    loaderFile: './my-loader.js',
  },
}

// my-loader.js
export default function cloudinaryLoader({ src, width, quality }) {
  const params = ['f_auto', 'c_limit', `w_${width}`, `q_${quality || 'auto'}`]
  return `https://res.cloudinary.com/demo/image/upload/${params.join(',')}/${src}`
}

Option 2: Use unoptimized images:

const nextConfig = {
  output: 'export',
  images: {
    unoptimized: true,
  },
}

Trailing Slashes & Clean URLs

Configure how Next.js handles trailing slashes for better static hosting:

Enable trailing slashes for folder-based routing:

const nextConfig = {
  output: 'export',
  trailingSlash: true,
}
With trailingSlash: true, /about becomes /about/index.html, ensuring clean URLs work correctly on static hosts.

Advanced Usage

Custom Domain

Deploy directly to your own domain

bantam deploy ./dist --domain myapp.com

Permanent Deployment

Keep your site online forever (requires login)

bantam deploy ./dist --permanent

Custom Expiry

Set how long your deployment stays online

bantam deploy ./dist --expiry-days 7

Custom Subdomain

Choose your own subdomain on bantam.site

bantam deploy ./dist --subdomain my-project

CI/CD Integration

Add Bantam to your continuous deployment pipeline:

# GitHub Actions example
- name: Deploy to Bantam
  run: |
    npm install -g @bantamhq/cli
    bantam login --token ${{ secrets.BANTAM_TOKEN }}
    bantam deploy ./dist --domain myapp.com --permanent
Troubleshooting

Common Issues & Solutions

Quick fixes for deployment challenges

01.Error: 'next export' is no longer needed

In Next.js 13.3+, use the output: 'export' configuration instead of running next export:

// next.config.js
const nextConfig = {
  output: 'export',
}

// Then just run:
npm run build

02.Dynamic routes return 404

Ensure you're using generateStaticParams (App Router) or getStaticPaths (Pages Router):

// app/blog/[slug]/page.js
export async function generateStaticParams() {
  const posts = await getPosts()
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

// pages/blog/[slug].js
export async function getStaticPaths() {
  const posts = await getPosts()
  return {
    paths: posts.map((post) => ({
      params: { slug: post.slug },
    })),
    fallback: false,
  }
}

03.API routes not working after deployment

API routes require a Node.js server and don't work with static exports. Options:

  • Move API logic to an external service (recommended)
  • Use client-side data fetching instead
  • Deploy to a platform that supports Next.js server features
// Instead of API route, fetch directly from client
useEffect(() => {
  fetch('https://api.example.com/data')
    .then(res => res.json())
    .then(setData)
}, [])

04.Environment variables not working

For static exports, use NEXT_PUBLIC_ prefix for client-side variables:

# .env.local
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_GA_ID=UA-123456789

// Usage in components
const apiUrl = process.env.NEXT_PUBLIC_API_URL

Build-time only variables don't need the prefix but aren't accessible in the browser.

05.How to handle authentication with static export?

Static sites can still have authentication using client-side approaches:

  • Use JWT tokens stored in localStorage/cookies
  • Implement client-side route guards
  • Fetch user data on mount
  • Consider services like Auth0, Firebase Auth, or Clerk
// Client-side auth check
useEffect(() => {
  const token = localStorage.getItem('token')
  if (!token) {
    router.push('/login')
  }
}, [])