Next 13!
Postado em: 25/10/2022
Oi pessoal! Como várias pessoas devem saber, hoje rolou a Next Conf 2022, e nela apresentaram features sensacionais e nesse post vou trazer para vocês alguns dos pontos que mudaram da versão 12 para a versão 13 do Next! Então pega o cafézinho e vamos direto ao ponto.
app
Agora além diretório pages
temos um novo chamado app
. As pastas dentro desse diretório definirão as rotas da aplicação e não menos importante, agora utilizamos um arquivo page.tsx
dentro de cada uma dessas pastas para definir a UI da página. Por exemplo, antes a estrutura era: pages/hello.tsx
ou pages/blog/[slug].tsx
. Agora ficam assim respectivamente: app/hello/page.tsx
ou app/blog/[slug]/page.tsx
.
// app/blog/[slug]/page.tsx
async function getPost(slug) {
const res = await fetch(`https://api.example.com/posts/${slug}`)
return await res.json()
}
export default async function PostPage({ params: { slug } }) {
const post = await getPost(slug)
return (
<article>
<h1>{post.title}</h1>
</article>
)
}
Uma nova forma de organizar a UI
No Next 13 os diretórios dentro de app
são para as rotas e os arquivos são para a interface, além disso podemos criar arquivos específicos para construir partes específicas da interface que funcionam da mesma forma que o page.tsx
ficando dentro do diretório de uma rota, por exemplo: app/hello/layout.tsx
, app/hello/loading.tsx
, app/hello/error.tsx
ou app/hello/layout.tsx
.
- layout.tsx: usamos esse arquivo para definir um componente "wrapper". Dentro dele também podemos fazer um fetch para requisitar dados.
export default function Layout({ children }) {
return <section>{children}</section>
}
- loading.tsx: podemos agora definir um componente que mostra um "loader" durante a requisição de uma rota.
export default function Loading() {
return <MyAwesomeLoaderComponent />
}
getStaticProps
Agora extraímos dados de páginas estáticas dentro de uma função customizada e podemos chamar essa função diretamente dentro do nosso componente. Os dados retornados por essa função são armazenados em cache até serem revalidados manualmente.
Antes:
export async function getStaticProps() {
const res = await fetch('https://example.url')
const data = await res.json()
return {
props: {
data
}
}
}
Depois:
async function fetchData() {
const res = await fetch('https://example.url')
const data = await res.json()
return data
}
export default async function Page() {
const data = await fetchData()
return <div>{data}</div>
}
getServerSideProps
Nossa forma de criar páginas SSR também mudou, agora fica da mesma forma que o getStaticProps
. A única diferença é que precisamos passar { cache: "no-store" }
para o fetch da função.
Antes:
export async function getServerSideProps() {
const res = await fetch('https://example.url')
const data = await res.json()
return {
props: {
data
}
}
}
Depois:
async function fetchData() {
const res = await fetch('https://example.url', { cache: 'no-store' })
const data = await res.json()
return data
}
export default async function Page() {
const data = await fetchData()
return <div>{data}</div>
}
getStaticPaths
A função getStaticPaths
foi renomeada para generateStaticParams
e a chave paths.params
não existe mais.
Antes:
export async function getStaticPaths() {
return {
paths: [{ params: { id: 1 } }, { params: { id: 2 } }]
}
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/post/${params.id}`)
const post = await res.json()
return {
props: {
post
}
}
}
export default function Post({ post }) {
return <Post post={post} />
}
Depois:
export async function generateStaticParams() {
return [{ id: 1 }, { id: 2 }]
}
async function fetchPost(params) {
const res = await fetch(`https://api.example.com/post/${params.id}`)
const post = await res.json()
return post
}
export default async function({ params }) {
const post = await fetchPost(params)
return <Post post={post} />
}
ISR
Nossa forma de regenerar páginas estáticas de forma incremental (Incremental Static Regeneration ou somente ISR) também está mais simples. Agora apenas passamos a opção { next: { revalidate: <number> } }
no fetch da função.
Antes:
export async function getStaticProps({ params }) {
const res = await fetch(`https://api.example.com/post/${params.id}`)
const post = await res.json()
return {
props: {
post
},
revalidate: 60
}
}
Depois:
async function fetchPost(params) {
const res = await fetch(`https://api.example.com/post/${params.id}`, {
next: {
revalidate: 60
}
})
const post = await res.json()
return post
}
export default async function({ params }) {
const post = await fetchPost(params)
return <Post post={post} />
}
Se estiver utilizando um client de API customizado ou não pode utilizar o fetch, é possível fazer dessa forma.
export const revalidate = 60
Migrando o _document.tsx e _app.tsx
Agora os arquivos _document.tsx
e _app.tsx
foram movidos para o arquivo layout.tsx
que fica na raiz da pasta app
. Precisamos adicionar as tags html
e body
. A tag head
é opicional e os estilos globais são importados nesse arquivo, e não menos importante é que esse layout é obrigatório pois ele serve de "wrapper" para todas as páginas.
Antes:
// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
// pages/_app.tsx
import type { AppProps } from 'next/app'
import '@/styles/global.css'
export default function App({ Component, pageProps }: AppProps) {
return <Component { ...pageProps }>
}
Depois:
// app/layout.tsx
import '@/styles/global.css'
export default function Layout({ children }) {
return (
<html lang='en'>
<head>
<title>My Website in Next.JS!</title>
</head>
<body>{children}</body>
</html>
)
}
pages/404.tsx
A forma de retornar uma página customizada quando uma página não é entrada também mudou. Agora o arquivo se chama not-found.tsx
, para mostrar essa página retornamos o componente chamando uma função notFound
dentro de um arquivo page.tsx
.
Antes:
// pages/404.tsx
export default function NotFound() {
return <p>Page not found!</p>
}
Depois:
// app/blog/[slug]/not-found.tsx
export default function NotFound() {
return <p>Post not found :(</p>
}
// app/blog/[slug]/page.tsx
import { notFound } from 'next/dist/client/components/not-found'
export default async function PostPage({ params }) {
const post = await getPost(params.slug)
if (!post) {
return notFound()
}
return (
<article>
<h1>{post.title}</h1>
</article>
)
}
next/head
next/head
agora é um arquivo que fica dentro de cada pasta de rota. O head.tsx
é um server component, então podemos fazer requisições de dados dentro desse arquivo. Um detalhe é que as tags permitidas nesse arquivo são title
, meta
, link
e script
.
Antes:
// pages/index.tsx
import Head from 'next/head'
export default function Index() {
return (
<div>
<Head>
<title>Index page!</title>
</Head>
<p>Hello!</p>
</div>
)
}
Depois:
// pages/blog/head.tsx
async function getData() {
const res = await fetch('https://example.url')
const data = await res.json()
return data
}
export default async function Head() {
const data = await getData()
return <title>{data.title}</title>
}
Bom pessoal, foi um post bem rapidinho com apenas algumas das mudanças. Para mais detalhes estou deixando o link do post oficial no blog do Next que é bem mais completo. Espero que tenham gostado e até a próxima!