Nivelando o conhecimento
Se você esta chegando agora ao mundo do Next.js - ou ainda não entendeu o App Router - é muito importante saber o funcionamento e as diferenças entre o App Router e o Pages Router, e para isso é crucial entendermos as estratégias de renderização e como elas são “representadas” em cada Router.
Antes de prosseguir precisamos relembrar alguns conceitos para um entendimento pleno das estratégias de renderização. Quando falamos de React e Next.js diversas vezes nos deparamos com os termos renderização (rendering), pré-renderização (pre-rendering) e hidratação (hydration):
Renderização: É o processo que o React realiza para converter o código em HTML, tornando assim possível que o navegador consiga "desenhar a interface" na tela.
Pré-renderização: É o processo que o Next.js realiza para converter o código em HTML, porém, executa este processo no lado do servidor.
Hidratação: É o processo que o React realiza para "injetar" os valores do JSON e as instruções JavaScript no HTML renderizado, tornando assim a página interativa.
Você pode entender renderização e pré-renderização como o mesmo processo, sendo "pré-renderização" uma nomenclatura adotada pelo Next.js. Em resumo, enquanto o React só tem a capacidade de executar a renderização no lado do cliente, o Next.js executa este mesmo processo no lado do servidor, deixando a cargo do lado do cliente apenas a hidratação.
Ao adotar essa estratégia o Next.js oferece duas grandes vantagens em relação a um projeto "React puro" criado utilizando o Create React App:
- Melhor performance, pois uma parte do processamento para "criar a página" é realizado no lado do servidor.
- Melhor experiência para o usuário, pois ao invés de exibir uma "tela branca" enquanto a página é renderizada no lado do cliente, é imediatamente exibido o HTML pré-renderizado no lado servidor, fazendo com que o lado do cliente tenha apenas que aguardar o processo de hidratação para que a página se torne interativa. É interessante ressaltar que ao desativar o JavaScript do navegador o HTML continua sendo exibido, fazendo com que apenas a parte interativa não funcione.
Importante saber
Uma página gerada estaticamente e salva em uma CDN significa que quando requisitada já possui seu HTML pré-renderizado salvo em um storage e apenas precisa enviá-lo para o cliente, não necessitando nenhum processamento no lado do servidor para pré-renderizar a página.
Entendidos esses conceitos, vamos falar sobre as estratégias de renderização disponíveis em cada Router do Next.js!
Pages Router
-
Static Site Generation (SSG): O HTML é pré-renderizado no lado do servidor durante o processo de build, o resultado é salvo em uma CDN, enviado e hidratado no lado do cliente. Uma página usa esta estratégia quando:
- Não exporta nenhuma função.
- Exporta a função
getStaticProps
com a propriedaderevalidate: false
(Padrão). - Não é uma Dynamic Route.
-
Incremental Static Regeneration (ISR): O HTML é pré-renderizado no lado do servidor durante o processo de build de acordo com as regras de revalidação de cache estabelecidas, o resultado é salvo em uma CDN, enviado e hidratado no lado do cliente. Uma página usa esta estratégia quando:
- Exporta a função
getStaticProps
com a propriedaderevalidate
com o valor maior que0
. - Invoca a função
res.revalidate(‘/path-to-revalidate’)
. - É uma Dynamic Route e exporta as funções
getStaticProps
egetStaticPaths
.
- Exporta a função
-
Server-side Rendering (SSR): O HTML é pré-renderizado no lado do servidor a cada requisição, enviado e hidratado no lado do cliente. Uma página usa esta estratégia quando:
- Exporta a função
getServerSideProps
. - Exporta a função
getInitialProps
(Legado).
- Exporta a função
-
Client-side Rendering (CSR): O HTML é pré-renderizado no lado do servidor a cada requisição, enviado e hidratado no lado do cliente. Uma página usa esta estratégia quando:
- Não exporta as funções
getServerSideProps
,getInitialProps
(Legado) ougetStaticProps
. - Busca os dados necessário e atualiza a tela no lado do cliente através de Effects ou utilizando bibliotecas especializadas como SWR ou TanStack Query (Recomendado).
- Não exporta as funções
Pontos importantes
- Independente da estratégia utilizada todas as páginas são reativas, já que o processo de hidratação sempre acontece no lado do cliente, isto quer dizer que você pode utilizar States, Events, Effects e APIs do navegador.
- Quando uma página gerada estaticamente é requisitada mas ainda não foi gerada, não necessariamente retornará uma página de
Not Found
, sendo possível gerá-la pela primeira vez definindo a propriedadefallback
da funçãogetStaticPaths
paratrue
ou'blocking'
.
App Router
A primeira grande mudança em relação ao Pages Router é que agora não temos mais o conceito de páginas. Enquanto no Pages Router páginas são “componentes especiais” que podem exportar funções como getServerSideProps
, getStaticProps
, etc. e componentes são as partes que compõem essas páginas, essas funções deixam de existir e no App Router tudo são componentes. Dito isto, precisamos ter em mente que existem dois tipos de componentes:
- Client Components: O componente é pré-renderizado no lado do servidor e enviado para ser hidratado no lado do cliente.
- Server Components: O componente é pré-renderizado e hidratado no lado do servidor, enviando um componente totalmente renderizado que não requer nenhum processamento no lado do cliente.
É importante que você entenda que Client Components funcionam essencialmente como páginas funcionam no Pages Router, isto quer dizer que independente da estratégia de renderização que uma página usa ela sempre vai ser pré-renderizada no lado do servidor e hidratada no lado do cliente, e é justamente aqui que ocorre a grande mudança implicada pelos Server Components.
Importante saber
O Server Components não é uma tecnologia desenvolvida ou exclusiva do Next.js, e sim uma tecnologia desenvolvida pelo time do React que foi integrada ao Next.js através da criação do App Router.
Antes de traçar um paralelo entre as estratégias de renderização é necessário entender o novo sistema de Data Fetching do App Router. Enquanto no Pages Router podemos definir as regras de revalidação de cache de uma página gerada estaticamente através da propriedade revalidate
da função getStaticProps
ou invocando a função res.revalidate('/path-to-revalidate')
, no App Router podemos simplesmente invocar a função fetch(url, options)
dentro de um Server Component e definir as regras de revalidação de cache através das propriedades options.cache
e options.next.revalidate
. A grande mudança aqui é que agora o mesmo componente pode possuir diversas requisições com regras de revalidação de cache diferentes, proporcionando muito mais granularidade em relação as páginas geradas estaticamente no Pages Router.
Outra mudança considerável foi a adição das Dynamic Functions. Como Server Components são pré-renderizados e hidratados no lado do servidor eles não tem acesso a informações do lado do cliente como cookies, headers e parâmetros de url. Para isso foram adicionadas "funções especiais" chamadas de Dynamic Functions que conseguem acessar essas informações.
Tendo tudo isso em mente, vamos agora traçar um paralelo entre as estratégias de renderização do Pages Router e do App Router:
-
SSG → Static Rendering: O HTML é pré-renderizado e hidratado no lado do servidor como um Server Component durante o processo de build, o resultado é salvo em uma CDN, enviado e exibido no lado do cliente. Um componente usa esta estratégia quando:
- Não invoca a função
fetch
. - Invoca uma ou mais funções
fetch
com a propriedadeoptions.cache: 'force-cache'
(Padrão). - Invoca uma ou mais funções
fetch
com a propriedadeoptions.next.revalidate: false
. - Não invoca uma Dynamic Function.
- Não é uma Dynamic Route.
- Exporta a variável
dynamic
com um valor diferente de'force-dynamic'
. - Exporta a variável
revalidate: false
(Padrão).
- Não invoca a função
-
ISR → Static Data Fetching: O HTML é pré-renderizado e hidratado no lado do servidor como um Server Component durante o processo de build de acordo com as regras de revalidação de cache, o resultado é salvo em uma CDN, enviado e exibido no lado do cliente. Um componente usa esta estratégia quando:
- Invoca uma ou mais funções
fetch
com a propriedadeoptions.next.revalidate
com um valor maior que0
. - Não invoca uma Dynamic Function.
- Exporta a variável
dynamic
com um valor diferente de'force-dynamic'
. - É uma Dynamic Route, exporta a função
generateStaticParams
e exporta a variáveldynamicParams: true
(Padrão). - Exporta a variável
revalidate
com um valor maior que0
. - Invoca a função
revalidatePath
. - Invoca a função
revalidateTag
.
- Invoca uma ou mais funções
-
SSR → Dynamic Rendering e Dynamic Data Fetching: O HTML é pré-renderizado e hidratado no lado do servidor como um Server Component a cada requisição, enviado e exibido no lado do cliente. Um componente usa estas estratégias quando:
- Invoca uma ou mais funções
fetch
com a propriedadeoptions.cache: 'no-store'
. - Invoca uma ou mais funções
fetch
com a propriedadeoptions.next.revalidate: 0
. - Invoca uma Dynamic Function.
- Exporta a variável
dynamic: 'force-dynamic'
. - Exporta a variável
revalidate: 0
.
- Invoca uma ou mais funções
-
CSR → Client Components: O HTML é pré-renderizado no lado do servidor como um Client Component a cada requisição, enviado e hidratado no lado do cliente. Um componente usa esta estratégia quando:
- Possui a diretiva
use client
no topo do arquivo antes de qualquerimport
.
- Possui a diretiva
Pontos importantes
- Diferente do Pages Router em que todas as páginas são reativas, no App Router apenas Client Components são reativos, já que é a única estratégia de renderização em que o processo de hidratação acontece no lado do cliente, isto quer dizer que você não pode utilizar States, Events, Effects e APIs do navegador em Server Components, limitando-se as Dynamic Functions.
- Todas as funções e variáveis citadas no App Router são exclusivas dos Server Components, isto quer dizer que você não pode usá-las em Client Components. A busca de dados e atualização da tela em Client Components continua sendo feita no lado do cliente assim como no Pages Router, através de Effects ou utilizando bibliotecas especializadas como SWR ou TanStack Query (Recomendado).
- Exportar a variável
dynamic: 'force-dynamic'
é equivalente a invocar todas as funçõesfetch
de um componente com as propriedadesoptions.next.revalidate: 0
ouoptions.cache: 'no-store'
no App Router, ou exportar a funçãogetServerSideProps
no Pages Router. - Exportar a função
generateStaticParams
e a variáveldynamicParams
no App Router é equivalente a exportar a funçãogetStaticPaths
com a propriedadefallback
no Pages Router. - Exportar a variável
revalidate: false
(Padrão) também pode ser representado comorevalidate: 'force-cache'
. - A variável
fetchCache
não está sendo considerado aqui pois é recomendado evitar utilizá-la, visto que ela sobrepõe o comportamento padrão de revalidação de cache.
Críticas e sugestões são muito bem vindas, vlw!
Top comments (3)
Artigo incrível e muito esclarecedor.
Procurando recursos pro meu trampo achei esse texto, e to triste pq eles são gringos e não tem uma versão em inglês disso! É o melhor artigo até agora sobre o assunto, parabéns.
Eu tenho um problema num project meu, sera que me podes ajudar a perceber o pk dele nao fazer build