Next.js Static Export: Statik Site Oluşturma

Next.js Static Export: Statik Site Oluşturma
Next.js, dinamik sunucu taraflı render'ın yanı sıra tamamen statik HTML dosyaları olarak export edilebilir. Bu, sunucu gerektirmeden herhangi bir statik hosting servisinde (GitHub Pages, Netlify, S3, Cloudflare Pages) barındırılabilen hızlı ve güvenli siteler oluşturmanızı sağlar. Bu yazıda Next.js Static Export yapılandırmasını, sınırlamalarını ve deployment stratejilerini adım adım inceleyeceğiz.
Static Export Nedir?
Static Export, Next.js uygulamanızı tamamen statik HTML, CSS ve JavaScript dosyalarına dönüştürür. Build sırasında tüm sayfalar pre-render edilir ve out klasörüne çıktı olarak verilir. Sunucu tarafında çalışan hiçbir kod yoktur.
Yapılandırma
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
// Statik export'ta image optimization sunucu gerektirir
images: {
unoptimized: true,
},
// Opsiyonel: Trailing slash ekle
trailingSlash: true,
// Opsiyonel: Base path
basePath: '/my-app',
// Opsiyonel: Asset prefix (CDN kullanıyorsanız)
assetPrefix: 'https://cdn.example.com',
};
module.exports = nextConfig;
Build ve Export
# Build komutu - out/ klasörüne static dosyalar oluşturur
next build
# Lokal test
npx serve out
# Klasör yapısı
out/
├── index.html
├── about.html
├── blog/
│ ├── index.html
│ ├── react-rehberi.html
│ └── nextjs-rehberi.html
├── _next/
│ └── static/
├── favicon.ico
└── robots.txt
Dinamik Route'lar ile Static Export
Dinamik route'lar ([slug]) için generateStaticParams fonksiyonu zorunludur:
// app/blog/[slug]/page.js
import { getAllPosts, getPostBySlug } from '@/lib/posts';
// Build sırasında hangi sayfaların oluşturulacağını belirle
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export async function generateMetadata({ params }) {
const { slug } = await params;
const post = await getPostBySlug(slug);
return {
title: post.title,
description: post.excerpt,
};
}
export default async function BlogPost({ params }) {
const { slug } = await params;
const post = await getPostBySlug(slug);
return (
<article>
<h1>{post.title}</h1>
<time>{post.date}</time>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
Markdown/MDX ile Blog
// lib/posts.js
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';
import { remark } from 'remark';
import html from 'remark-html';
const postsDirectory = path.join(process.cwd(), 'content/posts');
export function getAllPosts() {
const fileNames = fs.readdirSync(postsDirectory);
return fileNames
.filter((name) => name.endsWith('.md'))
.map((fileName) => {
const slug = fileName.replace(/\.md$/, '');
const fullPath = path.join(postsDirectory, fileName);
const fileContents = fs.readFileSync(fullPath, 'utf8');
const { data, content } = matter(fileContents);
return {
slug,
title: data.title,
date: data.date,
excerpt: data.excerpt || '',
content,
};
})
.sort((a, b) => new Date(b.date) - new Date(a.date));
}
export async function getPostBySlug(slug) {
const fullPath = path.join(postsDirectory, `${slug}.md`);
const fileContents = fs.readFileSync(fullPath, 'utf8');
const { data, content } = matter(fileContents);
const processedContent = await remark().use(html).process(content);
return {
slug,
title: data.title,
date: data.date,
content: processedContent.toString(),
};
}
Desteklenmeyen Özellikler
Static Export modunda aşağıdaki özellikler kullanılamaz:
| Özellik | Durum | Alternatif |
|---|---|---|
| Server Components (dinamik) | Sınırlı | Build-time data fetching |
| Route Handlers (dinamik) | Kullanılamaz | Harici API servisi |
| Server Actions | Kullanılamaz | Client-side API çağrıları |
| Middleware | Kullanılamaz | CDN edge rules |
| ISR / Revalidation | Kullanılamaz | Rebuild tetikleme |
| Image Optimization | Sunucu yok | unoptimized: true veya harici loader |
| Draft Mode | Kullanılamaz | Ayrı preview ortamı |
cookies(), headers(), searchParams (dinamik) ve redirect() (server-side) gibi dinamik fonksiyonlar static export'ta kullanılamaz. Bu fonksiyonları kullanan sayfalar build sırasında hata verir.
GitHub Pages Deployment
# .github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/upload-pages-artifact@v3
with:
path: out
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
steps:
- uses: actions/deploy-pages@v4
id: deployment
// next.config.js - GitHub Pages için
const nextConfig = {
output: 'export',
basePath: '/repo-name',
images: { unoptimized: true },
};
Cloudflare Pages Deployment
# Cloudflare Pages yapılandırması
# Build command: next build
# Output directory: out
# Node.js version: 20
# veya wrangler CLI ile
npx wrangler pages deploy out --project-name=my-site
Custom Image Loader
// lib/image-loader.js
export default function customLoader({ src, width, quality }) {
// Cloudinary örneği
return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`;
}
// next.config.js
const nextConfig = {
output: 'export',
images: {
loader: 'custom',
loaderFile: './lib/image-loader.js',
},
};
Client-Side Data Fetching
Static export'ta dinamik veri için client-side fetching kullanılır:
'use client';
import { useState, useEffect } from 'react';
export function DynamicComments({ postId }) {
const [comments, setComments] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`https://api.example.com/posts/${postId}/comments`)
.then((res) => res.json())
.then((data) => {
setComments(data);
setLoading(false);
});
}, [postId]);
if (loading) return <p>Yorumlar yükleniyor...</p>;
return (
<ul>
{comments.map((comment) => (
<li key={comment.id}>{comment.text}</li>
))}
</ul>
);
}
Sonuç
Next.js Static Export, sunucu gerektirmeyen, hızlı ve güvenli statik siteler oluşturmanın güçlü bir yoludur. output: 'export' yapılandırması ile tüm sayfalarınız build sırasında pre-render edilir. generateStaticParams ile dinamik route'ları, Markdown/MDX ile blog içeriklerini ve client-side fetching ile dinamik verileri yönetebilirsiniz. GitHub Pages, Cloudflare Pages, Netlify gibi ücretsiz hosting servislerinde barındırarak maliyet etkin çözümler üretebilirsiniz.