Deploy Next.js with Bantam CLI
Deploy Next.js static exports for blazing-fast performance
Quick start:
# Build your Next.js app
npm run build
# Deploy it
bantam deploy ./out
Quick Start
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
Build and export
Next.js 13+ automatically exports when output: 'export' is set
npm run build
Deploy with Bantam
Your Next.js static site is now live!
bantam deploy ./out
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
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,
}
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
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')
}
}, [])