~3 min read • Updated Oct 28, 2025
Introduction
Next.js lets you start as a SPA or static site and progressively add server features. When you run next build, each route is rendered into a separate HTML file, reducing JavaScript load and improving performance.
Static Export Configuration
In next.config.js, enable static export:
const nextConfig = {
output: 'export',
trailingSlash: true, // optional
distDir: 'dist', // optional
}
module.exports = nextConfigAfter running next build, the out folder will contain your HTML/CSS/JS files.
Server Components Support
Server Components run during build and produce static HTML:
export default async function Page() {
const res = await fetch('https://api.example.com/...')
const data = await res.json()
return <main>...</main>
}Client Components and SWR
To fetch data on the client:
'use client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((r) => r.json())
export default function Page() {
const { data, error } = useSWR('/api/posts/1', fetcher)
if (error) return 'Failed to load'
if (!data) return 'Loading...'
return data.title
}Client-Side Navigation Between Pages
Example index page:
import Link from 'next/link'
export default function Page() {
return (
<>
<h1>Index Page</h1>
<ul>
<li><Link href="/post/1">Post 1</Link></li>
<li><Link href="/post/2">Post 2</Link></li>
</ul>
</>
)
}Image Optimization with Cloudinary
In next.config.js:
images: {
loader: 'custom',
loaderFile: './my-loader.ts',
}In my-loader.ts:
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}`
}Using next/image
import Image from 'next/image'
export default function Page() {
return <Image alt="turtles" src="/turtles.jpg" width={300} height={300} />
}Using Route Handlers for JSON Output
// app/data.json/route.ts
export async function GET() {
return Response.json({ name: 'Lee' })
}This will generate a static data.json file during build.
Accessing Browser APIs
In a Client Component:
'use client'
import { useEffect } from 'react'
useEffect(() => {
console.log(window.innerHeight)
}, [])Unsupported Features in Static Export
- Dynamic routes without
generateStaticParams() - Cookies, redirects, rewrites, Proxy, ISR, Server Actions
- Image optimization with default loader
Hosting on Static Servers
After build, Next.js generates files like:
/out/index.html
/out/404.html
/out/blog/post-1.html
/out/blog/post-2.htmlNginx configuration:
server {
listen 80;
server_name acme.com;
root /var/www/out;
location / {
try_files $uri $uri.html $uri/ =404;
}
location /blog/ {
rewrite ^/blog/(.*)$ /blog/$1.html break;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}Conclusion
With static export in Next.js, you can deploy your app without a Node.js server to any HTML/CSS/JS host. It’s ideal for SPAs, documentation sites, and lightweight projects.
Written & researched by Dr. Shahin Siami