Deferred rendering
Sections abaixo da dobra hidratam ao rolar a página, não no carregamento inicial.
Deferred rendering é como a v2 mantém pequeno o payload inicial. Sections marcadas como deferred renderizam shells de placeholder no servidor e hidratam quando entram no viewport. O mecanismo é nativo do framework — você decide o que fazer deferred, não como.
Por que diferir
Uma PLP ou PDP típica tem 8-15 sections. Se todo loader rodar imediatamente:
- Cada loader faz 1+ chamadas para APIs upstream.
- O tempo total de resposta vira o do loader mais lento.
- O HTML incha com conteúdo que o usuário talvez nunca veja.
Diferir sections abaixo da dobra colapsa isso: as primeiras 1-3 sections renderizam por completo, o resto renderiza skeletons e o trabalho pesado acontece depois do load .
Duas maneiras de diferir
1. Automático — foldThreshold
Defina um fold threshold no setup; sections além desse índice renderizam lazy:
// src/setup.ts
import { setAsyncRenderingConfig } from "@decocms/start/cms";
setAsyncRenderingConfig({
foldThreshold: 3,
respectCmsLazy: true,
});
Um valor comum em produção é foldThreshold: 3 — as 3 primeiras sections renderizam eager, tudo abaixo da dobra defere.
2. Controlado pelo CMS — respectCmsLazy
Com respectCmsLazy ligado, o CMS pode marcar sections como lazy no editor. O autor decide por página quais sections estão acima da dobra.
Em .deco/blocks/ , a flag fica:
{
"__resolveType": "$live/pages/Page.ts",
"sections": [
{ "__resolveType": "site/sections/Hero/Hero.tsx", "alwaysEager": true },
{ "__resolveType": "site/sections/Shelf/Shelf.tsx" }
]
}
Sections com alwaysEager: true ignoram o fold threshold.
O componente LazySection
Por baixo, sections deferred são embrulhadas em <LazySection> de @decocms/start/hooks . Ele:
- Renderiza um placeholder
<LoadingFallback />no server. - Configura um
IntersectionObserverno placeholder. - Quando o placeholder entra no viewport, chama
loadDeferredSection({ pageId, sectionIndex })(uma server function do TanStack Start). - Substitui o placeholder pela section renderizada.
Você normalmente não instancia LazySection na mão — DecoPageRenderer faz isso conforme a flag de lazy da section.
Loading fallbacks
Cada section pode declarar seu próprio skeleton. Default: shimmer genérico. Sobrescreva via LoadingFallback :
// src/sections/Shelf/Shelf.tsx
export const LoadingFallback = () => (
<div class="grid grid-cols-4 gap-4 animate-pulse">
{[...Array(8)].map((_, i) => <div key={i} class="aspect-square bg-gray-200" />)}
</div>
);
export default function Shelf({ products }: Props) { /* ... */ }
O framework pega LoadingFallback automaticamente.
Detecção de bot
Bots (Googlebot, Bingbot etc.) não disparam IntersectionObserver , então o framework detecta o user-agent e renderiza sections deferred eager para bots. SEO preservado.
Navegação SPA e re-fetch
Quando o usuário navega entre páginas client-side (patch do TanStack Router), sections deferred já carregadas para a página anterior não são reaproveitadas — cada load de rota começa do zero. É intencional: estado por página é o modelo mental mais limpo.
O framework dedup chamadas concorrentes a loadDeferredSection , então navegações rápidas não desperdiçam requests.
Pegadinhas em modo dev
Modo dev do Vite Cloudflare e sections deferred. O plugin Vite do Cloudflare força um único contexto de I/O por request. Chamadas a loadDeferredSection que acontecem após a resposta inicial podem lançar um erro “I/O across requests”. As opções:
- Marque as sections afetadas com
alwaysEager: trueno dev local. - Adicione
no_handle_cross_request_promise_resolutionemwrangler.jsonccompatibility_flags.
Deploys em produção não têm esse problema.
Referência de configuração
setAsyncRenderingConfig({
foldThreshold: 3, // sections [0..2] renderizam eager, [3..] diferem
respectCmsLazy: true, // honra flag de lazy por section vinda do CMS
alwaysEagerKinds: [], // nomes de section que sempre renderizam eager
intersectionMargin: "200px", // rootMargin do IntersectionObserver
});
Quando marcar uma section como alwaysEager
- Conteúdo SEO-crítico acima da dobra — Hero, breadcrumbs, imagem principal do produto.
- Primeiro card de produto em prateleiras onde você quer a imagem LCP pré-carregada.
- Triggers de drawer do carrinho que precisam estar interativos imediatamente.
Na dúvida, defira. O framework lida com bots corretamente, e sections eager desnecessariamente são a regressão de performance mais comum que vemos.
Veja também
Found an error or want to improve this page?
Edit this page