From Pages to App

Bu kılavuz size yardımcı olacaktır:

Upgrading

Node.js Version

Minimum Node.js sürümü artık v18.17'dir. Daha fazla bilgi için adresindeki Node.js belgelerine bakın.

Next.js Version

Next.js sürüm 13'e güncellemek için tercih ettiğiniz paket yöneticisini kullanarak aşağıdaki komutu çalıştırın:

Terminal
npm install next@latest react@latest react-dom@latest

ESLint Version

ESLint kullanıyorsanız, ESLint sürümünüzü yükseltmeniz gerekir:

Terminal
npm install -D eslint-config-next@latest

Bilmekte fayda var: ESLint değişikliklerinin etkili olması için VS Code'da ESLint sunucusunu yeniden başlatmanız gerekebilir. Komut Paletini açın (Mac'tecmd+shift+p; Windows'ta ctrl+shift+p ) ve ESLint: Restart ESLint Server adresini arayın.

Next Steps

Güncellemeyi yaptıktan sonra, sonraki adımlar için aşağıdaki bölümlere bakın:

Upgrading New Features

Next.js 13, yeni özellikler ve kurallar içeren yeni Uygulama Yönlendiricisini tanıttı. Yeni Yönlendirici app dizininde bulunur ve pages dizini ile birlikte var olur.

Next.js 13'e yükseltmek için yeni Uygulama Yönlendiricisini kullanmanız gerekmez. Güncellenen Image bileşeni, Link bileşeni, Script bileşeni ve Font optimizasyonu gibi her iki dizinde de çalışan yeni özelliklerle pages adresini kullanmaya devam edebilirsiniz.

<Image/> Component

Next.js 12, geçici bir içe aktarma ile Görüntü Bileşeninde yeni iyileştirmeler sundu: next/future/image. Bu iyileştirmeler arasında daha az istemci tarafı JavaScript, görüntüleri genişletmek ve şekillendirmek için daha kolay yollar, daha iyi erişilebilirlik ve yerel tarayıcı tembel yüklemesi yer alıyordu.

Sürüm 13'te bu yeni davranış artık next/image için varsayılandır.

Yeni Görüntü Bileşenine geçiş yapmanıza yardımcı olacak iki kodmod vardır:

<Link> Bileşeni artık bir <a> etiketinin alt öğe olarak manuel olarak eklenmesini gerektirmiyor. Bu davranış 12.2 sürümünde deneysel bir seçenek olarak eklenmişti ve artık varsayılandır. Next.js 13'te, <Link> her zaman <a> adresini oluşturur ve destekleri altta yatan etikete yönlendirmenize olanak tanır.

Örneğin:

import Link from 'next/link'
 
// Next.js 12: `<a>` has to be nested otherwise it's excluded
<Link href="/about">
  <a>About</a>
</Link>
 
// Next.js 13: `<Link>` always renders `<a>` under the hood
<Link href="/about">
  About
</Link>

Bağlantılarınızı Next.js 13'e yükseltmek için new-link codemod'u kullanabilirsiniz.

<Script> Component

Davranışları next/script hem pages hem de app adreslerini destekleyecek şekilde güncellenmiştir, ancak sorunsuz bir geçiş sağlamak için bazı değişikliklerin yapılması gerekmektedir:

Font Optimization

Daha önce Next.js, yazı tipi CSS'sini inlining yaparak yazı tiplerini optimize etmenize yardımcı oluyordu. Sürüm 13 yeni next/font modülü size yazı tipi yükleme deneyiminizi kişiselleştirme olanağı verirken aynı zamanda mükemmel performans ve gizlilik sağlar. next/font hem pages hem de app dizinlerinde desteklenir.

CSS inlining pages'da hala çalışırken, app'da çalışmaz. next/font onun yerine.

next/font adresini nasıl kullanacağınızı öğrenmek için Yazı Tipi Optimizasyonu sayfasına bakın.

Migrating from pages to app

🎥 İzleyin: Uygulama Yönlendiricisini aşamalı olarak nasıl benimseyeceğinizi öğrenin → YouTube (16 dakika).

App Router'a geçiş, Next.js'nin üzerine inşa edildiği Sunucu Bileşenleri, Suspense ve daha fazlası gibi React özelliklerini ilk kez kullanıyor olabilirsiniz. Özel dosyalar ve düzenler gibi yeni Next.js özellikleriyle birleştirildiğinde, geçiş, öğrenilmesi gereken yeni kavramlar, zihinsel modeller ve davranış değişiklikleri anlamına gelir.

Geçişinizi daha küçük adımlara bölerek bu güncellemelerin birleşik karmaşıklığını azaltmanızı öneririz. app dizini, sayfa sayfa geçişe izin vermek için pages dizini ile eşzamanlı olarak çalışmak üzere kasıtlı olarak tasarlanmıştır.

Step 1: Creating the app directory

En son Next.js sürümüne güncelleyin (13.4 veya üstü gerekir):

npm install next@latest

Ardından, projenizin kök dizininde (veya src/ dizininde) yeni bir app dizini oluşturun.

Step 2: Creating a Root Layout

app dizini içinde yeni bir app/layout.tsx dosyası oluşturun. Bu, app içindeki tüm rotalar için geçerli olacak bir kök düzenidir.

app/layout.tsx
export default function RootLayout({
  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

<head> HTML öğelerini yönetmek için yerleşik SEO desteğini kullanabilirsiniz:

app/layout.tsx
import { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',
}

Migrating _document.js and _app.js

Mevcut bir _app veya _document dosyanız varsa, içeriği (örneğin global stiller) kök düzene (app/layout.tsx) kopyalayabilirsiniz. app/layout.tsx 'daki stiller pages/* için geçerli olmayacaktır. pages/* rotalarınızın bozulmasını önlemek için geçiş sırasında _app/_document 'u tutmalısınız. Tamamen taşındıktan sonra bunları güvenle silebilirsiniz.

Herhangi bir React Context sağlayıcısı kullanıyorsanız, bunların bir İstemci Bileşenine taşınması gerekecektir.

Migrating the getLayout() pattern to Layouts (Optional)

Next.js, pages dizininde sayfa başına düzenler elde etmek için Sayfa bileşenlerine bir özellik eklemeyi önerdi. Bu model, app dizinindeki iç içe düzenler için yerel destek ile değiştirilebilir.

See before and after example

Önce

components/DashboardLayout.js
export default function DashboardLayout({ children }) {
  return (
    <div>
      <h2>My Dashboard</h2>
      {children}
    </div>
  )
}
pages/dashboard/index.js
import DashboardLayout from '../components/DashboardLayout'
 
export default function Page() {
  return <p>My Page</p>
}
 
Page.getLayout = function getLayout(page) {
  return <DashboardLayout>{page}</DashboardLayout>
}

Sonra

  • Page.getLayout özelliğini pages/dashboard/index.js adresinden kaldırın ve sayfaları app dizinine taşıma adımlarını izleyin.

    app/dashboard/page.js
    export default function Page() {
      return <p>My Page</p>
    }
  • pages dizin davranışını korumak için DashboardLayout içeriğini yeni bir İstemci Bileşenine taşıyın.

    app/dashboard/DashboardLayout.js
    'use client' // this directive should be at top of the file, before any imports.
     
    // This is a Client Component
    export default function DashboardLayout({ children }) {
      return (
        <div>
          <h2>My Dashboard</h2>
          {children}
        </div>
      )
    }
  • DashboardLayout adresini app dizini içinde yeni bir layout.js dosyasına aktarın.

    app/dashboard/layout.js
    import DashboardLayout from './DashboardLayout'
     
    // This is a Server Component
    export default function Layout({ children }) {
      return <DashboardLayout>{children}</DashboardLayout>
    }
  • İstemciye gönderdiğiniz JavaScript bileşeni miktarını azaltmak için DashboardLayout.js (İstemci Bileşeni) adresinin etkileşimli olmayan kısımlarını aşamalı olarak layout.js (Sunucu Bileşeni) adresine taşıyabilirsiniz.

Step 3: Migrating next/head

pages dizininde, next/head React bileşeni title ve meta gibi <head> HTML öğelerini yönetmek için kullanılır. app dizininde, next/head yeni yerleşik SEO desteği ile değiştirilmiştir.

Daha önce:

pages/index.tsx
import Head from 'next/head'
 
export default function Page() {
  return (
    <>
      <Head>
        <title>My page title</title>
      </Head>
    </>
  )
}

Sonra:

app/page.tsx
import { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'My Page Title',
}
 
export default function Page() {
  return '...'
}

Tüm meta veri seçeneklerine bakın.

Step 4: Migrating Pages

Bir sayfanın taşınmasını iki ana adıma ayırmanızı öneririz:

Bilmekte fayda var: Bu en kolay geçiş yoludur çünkü pages dizini ile en benzer davranışa sahiptir.

Adım 1: Yeni bir İstemci Bileşeni Oluşturun

app/home-page.tsx
'use client'
 
// This is a Client Component. It receives data as props and
// has access to state and effects just like Page components
// in the `pages` directory.
export default function HomePage({ recentPosts }) {
  return (
    <div>
      {recentPosts.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

Adım 2: Yeni bir sayfa oluşturun

Step 5: Migrating Routing Hooks

app dizinindeki yeni davranışı desteklemek için yeni bir yönlendirici eklendi.

app adresinde, next/navigation adresinden içe aktarılan üç yeni kancayı kullanmalısınız: useRouter(), usePathname()ve useSearchParams().

app/example-client-component.tsx
'use client'
 
import { useRouter, usePathname, useSearchParams } from 'next/navigation'
 
export default function ExampleClientComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const searchParams = useSearchParams()
 
  // ...
}

Buna ek olarak, yeni useRouter kancası aşağıdaki değişikliklere sahiptir:

useRouter() API referansını görüntüleyin.

Step 6: Migrating Data Fetching Methods

pages dizini, sayfalar için veri almak üzere getServerSideProps ve getStaticProps adreslerini kullanır. app dizini içinde, bu önceki veri getirme işlevleri, fetch() ve async React Sunucu Bileşenleri üzerine inşa edilmiş daha basit bir API ile değiştirilir.

app/page.tsx
export default async function Page() {
  // This request should be cached until manually invalidated.
  // Similar to `getStaticProps`.
  // `force-cache` is the default and can be omitted.
  const staticData = await fetch(`https://...`, { cache: 'force-cache' })
 
  // This request should be refetched on every request.
  // Similar to `getServerSideProps`.
  const dynamicData = await fetch(`https://...`, { cache: 'no-store' })
 
  // This request should be cached with a lifetime of 10 seconds.
  // Similar to `getStaticProps` with the `revalidate` option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })
 
  return <div>...</div>
}

Server-side Rendering (getServerSideProps)

pages dizininde, getServerSideProps sunucudan veri almak ve propları dosyadaki varsayılan dışa aktarılan React bileşenine iletmek için kullanılır. Sayfa için ilk HTML sunucudan önceden oluşturulur, ardından sayfa tarayıcıda "hidratlanır" (etkileşimli hale getirilir).

pages/dashboard.js
// `pages` directory
 
export async function getServerSideProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return { props: { projects } }
}
 
export default function Dashboard({ projects }) {
  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

app dizininde, Sunucu Bileşenlerini kullanarak veri getirme işlemimizi React bileşenlerimizin içine yerleştirebiliriz. Bu, sunucudan işlenen HTML'yi korurken istemciye daha az JavaScript göndermemizi sağlar.

cache seçeneğini no-store olarak ayarlayarak, getirilen verilerin asla önbelleğe alınmaması gerektiğini belirtebiliriz. Bu, pages dizinindeki getServerSideProps seçeneğine benzer.

app/dashboard/page.tsx
// `app` directory
 
// This function can be named anything
async function getProjects() {
  const res = await fetch(`https://...`, { cache: 'no-store' })
  const projects = await res.json()
 
  return projects
}
 
export default async function Dashboard() {
  const projects = await getProjects()
 
  return (
    <ul>
      {projects.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}

Accessing Request Object

pages dizininde, Node.js HTTP API'sine dayalı olarak istek tabanlı verileri alabilirsiniz.

Örneğin, req nesnesini getServerSideProps adresinden alabilir ve isteğin çerezlerini ve başlıklarını almak için kullanabilirsiniz.

pages/index.js
// `pages` directory
 
export async function getServerSideProps({ req, query }) {
  const authHeader = req.getHeaders()['authorization'];
  const theme = req.cookies['theme'];
 
  return { props: { ... }}
}
 
export default function Page(props) {
  return ...
}

app dizini, istek verilerini almak için yeni salt okunur işlevler sunar:

app/page.tsx
// `app` directory
import { cookies, headers } from 'next/headers'
 
async function getData() {
  const authHeader = headers().get('authorization')
 
  return '...'
}
 
export default async function Page() {
  // You can use `cookies()` or `headers()` inside Server Components
  // directly or in your data fetching function
  const theme = cookies().get('theme')
  const data = await getData()
  return '...'
}

Static Site Generation (getStaticProps)

pages dizininde, getStaticProps işlevi derleme zamanında bir sayfayı önceden işlemek için kullanılır. Bu fonksiyon, harici bir API'den veya doğrudan bir veritabanından veri almak ve bu verileri derleme sırasında oluşturulurken tüm sayfaya aktarmak için kullanılabilir.

pages/index.js
// `pages` directory
 
export async function getStaticProps() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return { props: { projects } }
}
 
export default function Index({ projects }) {
  return projects.map((project) => <div>{project.name}</div>)
}

app dizininde, veri getirme ile fetch() varsayılan olarak cache: 'force-cache' adresini kullanacaktır ve bu adres elle geçersiz kılınana kadar istek verilerini önbelleğe alacaktır. Bu, pages dizinindeki getStaticProps adresine benzer.

app/page.js
// `app` directory
 
// This function can be named anything
async function getProjects() {
  const res = await fetch(`https://...`)
  const projects = await res.json()
 
  return projects
}
 
export default async function Index() {
  const projects = await getProjects()
 
  return projects.map((project) => <div>{project.name}</div>)
}

Dynamic paths (getStaticPaths)

pages dizininde, getStaticPaths işlevi derleme zamanında önceden oluşturulması gereken dinamik yolları tanımlamak için kullanılır.

pages/posts/[id].js
// `pages` directory
import PostLayout from '@/components/post-layout'
 
export async function getStaticPaths() {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
  }
}
 
export async function getStaticProps({ params }) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  return { props: { post } }
}
 
export default function Post({ post }) {
  return <PostLayout post={post} />
}

app dizininde, getStaticPaths ile değiştirilir generateStaticParams.

generateStaticParamsgetStaticPaths ile benzer şekilde davranır, ancak rota parametrelerini döndürmek için basitleştirilmiş bir API'ye sahiptir ve düzenlerin içinde kullanılabilir. generateStaticParams 'un dönüş şekli, iç içe geçmiş param nesneleri dizisi veya çözümlenmiş yollar dizisi yerine bir segmentler dizisidir.

app/posts/[id]/page.js
// `app` directory
import PostLayout from '@/components/post-layout'
 
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }]
}
 
async function getPost(params) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()
 
  return post
}
 
export default async function Post({ params }) {
  const post = await getPost(params)
 
  return <PostLayout post={post} />
}

app dizinindeki yeni model için getStaticPaths yerine generateStaticParams adının kullanılması daha uygundur. get öneki, getStaticProps ve getServerSideProps artık gerekli olmadığı için tek başına daha iyi oturan daha açıklayıcı bir generate ile değiştirilmiştir. Paths son eki, birden fazla dinamik segmente sahip iç içe yönlendirme için daha uygun olan Params ile değiştirilmiştir.


Replacing fallback

pages dizininde, getStaticPaths adresinden döndürülen fallback özelliği, derleme zamanında önceden oluşturulmamış bir sayfanın davranışını tanımlamak için kullanılır. Bu özellik, sayfa oluşturulurken bir geri dönüş sayfası göstermek için true, 404 sayfası göstermek için false veya sayfayı istek zamanında oluşturmak için blocking olarak ayarlanabilir.

pages/posts/[id].js
// `pages` directory
 
export async function getStaticPaths() {
  return {
    paths: [],
    fallback: 'blocking'
  };
}
 
export async function getStaticProps({ params }) {
  ...
}
 
export default function Post({ post }) {
  return ...
}

app dizinindeconfig.dynamicParams özelliği, dışındaki paramların nasıl generateStaticParams ele alınır:

Bu, pages dizinindeki getStaticPaths adresinin fallback: true | false | 'blocking' seçeneğinin yerini alır. fallback: 'blocking' seçeneği dynamicParams 'a dahil edilmemiştir çünkü 'blocking' ve true arasındaki fark akış ile ihmal edilebilir düzeydedir.

app/posts/[id]/page.js
// `app` directory
 
export const dynamicParams = true;
 
export async function generateStaticParams() {
  return [...]
}
 
async function getPost(params) {
  ...
}
 
export default async function Post({ params }) {
  const post = await getPost(params);
 
  return ...
}

ile dynamicParamstrue (varsayılan) olarak ayarlandığında, oluşturulmamış bir rota segmenti istendiğinde, sunucu tarafından oluşturulacak ve önbelleğe alınacaktır.

Incremental Static Regeneration (getStaticProps with revalidate)

pages dizininde, getStaticProps işlevi, bir sayfayı belirli bir süre sonra otomatik olarak yeniden oluşturmak için bir revalidate alanı eklemenize olanak tanır.

pages/index.js
// `pages` directory
 
export async function getStaticProps() {
  const res = await fetch(`https://.../posts`)
  const posts = await res.json()
 
  return {
    props: { posts },
    revalidate: 60,
  }
}
 
export default function Index({ posts }) {
  return (
    <Layout>
      <PostList posts={posts} />
    </Layout>
  )
}

app dizininde, veri getirme ile fetch() isteği belirtilen süre boyunca önbelleğe alacak olan revalidate adresini kullanabilir.

app/page.js
// `app` directory
 
async function getPosts() {
  const res = await fetch(`https://.../posts`, { next: { revalidate: 60 } })
  const data = await res.json()
 
  return data.posts
}
 
export default async function PostList() {
  const posts = await getPosts()
 
  return posts.map((post) => <div>{post.name}</div>)
}

API Routes

API Rotaları pages/api dizininde herhangi bir değişiklik olmadan çalışmaya devam eder. Ancak, bunların yerini app dizinindeki Rota İşleyicileri almıştır.

Rota İşleyicileri, Web İsteği ve Yanıt API'lerini kullanarak belirli bir rota için özel istek işleyicileri oluşturmanıza olanak tanır.

app/api/route.ts
export async function GET(request: Request) {}

Bilmekte fayda var: Daha önce istemciden harici bir API çağırmak için API rotalarını kullandıysanız, artık verileri güvenli bir şekilde almak için bunun yerine Sunucu Bileşenlerini kullanabilirsiniz. Veri getirme hakkında daha fazla bilgi edinin.

Step 7: Styling

pages dizininde, global stil sayfaları yalnızca pages/_app.js ile sınırlandırılmıştır. app dizini ile bu sınırlama kaldırılmıştır. Global stiller herhangi bir düzene, sayfaya veya bileşene eklenebilir.

Tailwind CSS

Tailwind CSS kullanıyorsanız, app dizinini tailwind.config.js dosyanıza eklemeniz gerekir:

tailwind.config.js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx,mdx}', // <-- Add this line
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
  ],
}

Ayrıca app/layout.js dosyanızdaki global stillerinizi de içe aktarmanız gerekir:

app/layout.js
import '../styles/globals.css'
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

Tailwind CSS ile stil oluşturma hakkında daha fazla bilgi edinin

Codemods

Next.js, bir özellik kullanımdan kaldırıldığında kod tabanınızı yükseltmenize yardımcı olmak için Codemod dönüşümleri sağlar. Daha fazla bilgi için Codemods bölümüne bakın.