Olha só o que acabou de sair do forno 🔥

E aí, dev! Se você acompanha o canal experimental do React desde o ano passado, já sabe os dois nomes que estavam na fila: View Transitions e Fragment Refs. Pois é — no React 19.3, os dois finalmente ficaram estáveis e já estão no npm.

Mas tem mais coisa boa nesse release. A gente ganhou uma API browser() de primeira classe pra sair do SSR quando precisar, integração com Trusted Types (aquela camada de segurança contra XSS), e agora Server Components podem renderizar Context direto, sem aquele Provider wrapper chato.

Bora passar por cada feature com código que você pode jogar direto no projeto. Essa análise é baseada no anúncio oficial do React 19.3.

Resumo rápido: View Transitions e Fragment Refs estão estáveis. use(browser()) substitui o hack do useEffect com setMounted. Trusted Types agora funcionam out-of-the-box.

Frontend developer reviewing React 19.3 View Transitions documentation on a bright monitor with component tree diagram Algorithm Concept Visual

1. View Transitions: animação sem pagar o pedágio do Framer Motion

O novo componente <ViewTransition> deixa você animar elementos quando entram, saem, se movem ou redimensionam, usando a View Transition API nativa do browser.

import { ViewTransition, useState, startTransition } from 'react';
import { Video } from './Video';
import videos from './data';

export default function Component() {
  const [showItem, setShowItem] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          // Importante: só anima se a atualização estiver dentro de startTransition
          startTransition(() => {
            setShowItem((prev) => !prev);
          });
        }}
      >
        {showItem ? '➖' : '➕'}
      </button>
      {showItem && (
        <ViewTransition>
          <Video />
        </ViewTransition>
      )}
    </>
  );
}

Regra de ouro: só atualizações dentro de startTransition, useDeferredValue ou um reveal do <ViewTransition> vão animar. Atualizações urgentes (tipo digitar num input) pulam a animação de propósito.

addTransitionType — mesmo estado, animação diferente

Navegar um carrossel pra frente ou pra trás muda o mesmo currentSlide — mas deveria animar em direções opostas. O addTransitionType resolve isso:

function nextSlide() {
  startTransition(() => {
    addTransitionType('next');
    setCurrentSlide(c => c + 1);
  });
}

Depois você escopa a animação no CSS com :active-view-transition-type(next).

Suspense + View Transitions: do jeito certo

Envolver um Suspense boundary em <ViewTransition> deixa o React animar a transição fallback → conteúdo. Mas cuidado — o próprio release notes avisa pra não animar UI cacheada. Os princípios recomendados:

  • Fallbacks aparecem imediatamente (sem animação)
  • Fallback → conteúdo final deve animar
  • Filhos que não suspendem aparecem na hora
<Suspense fallback={<VideoPlaceholder />}>
  <ViewTransition update="auto" enter="none" exit="none">
    <LazyVideo />
  </ViewTransition>
</Suspense>

Você também pode envolver <img> ou fontes em <ViewTransition> pra forçar Suspense enquanto carregam — ou seja, o componente espera imagem, fonte e dados juntos, em vez de cada um aparecer piscando quando termina.

Code editor showing React 19.3 Fragment Refs example with useRef and focus() call on a list of posts Coding Session Visual

2. Fragment Refs — controle de DOM sem wrapper

O problema clássico: você renderiza uma lista de irmãos sem pai único, e precisa anexar um event listener ou mover o foco. Antes, ou você colocava um <div> wrapper (quebrando o layout) ou modificava um componente de terceiros (impossível).

Fragment Refs resolvem isso te dando um FragmentInstance:

function Component() {
  const fragmentRef = useRef(null);

  useEffect(() => {
    const fragmentInstance = fragmentRef.current;
    fragmentInstance.focus(); // Move o foco pro primeiro filho
  }, []);

  return (
    <Fragment ref={fragmentRef}>
      {posts.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </Fragment>
  );
}

O FragmentInstance expõe um conjunto curado de métodos DOM: addEventListener, focus, focusLast, blur, observeUsing (IntersectionObserver/ResizeObserver), getClientRects, scrollIntoView e mais. É uma API restrita de propósito — o React não tá te entregando o DOM inteiro.

3. use(browser()) — o escape hatch de SSR que a gente merecia

O padrão do useEffect com flag mounted morreu. O React 19.3 traz browser() do react-dom:

import { use } from 'react';
import { browser } from 'react-dom';

function TimeZone() {
  use(browser()); // Suspende no servidor, renderiza direto no cliente
  const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone;
  return <span>{timeZone}</span>;
}

No servidor, use(browser()) dispara Suspense — o fallback aparece no HTML inicial. No cliente, não suspende, então o componente renderiza normal depois da hidratação.

Detalhe importante: diferente de outros hooks, use() pode ser chamado condicionalmente ou depois de um early return. Ou seja:

function useBrowserQuery(query, options) {
  if (options.initialData === undefined) {
    use(browser()); // Só sai do SSR quando não tem dado inicial
  }
  return useQuery(query, options);
}

4. Suporte a Trusted Types (segurança)

Se seu site usa Content-Security-Policy: require-trusted-types-for 'script', o React antes quebrava Trusted Types forçando '' + value. Agora o React 19.3 passa objetos TrustedHTML / TrustedScript / TrustedScriptURL sem coerção. É uma vitória silenciosa de segurança — sem migração necessária.

5. Server Components podem renderizar Context direto

Chega de Provider wrapper de mentira. Server Components agora podem importar e renderizar Context de um módulo 'use client' direto:

// server-component.js
import { UserContext } from './user-context';

export async function Layout({ children }) {
  const currentUser = await getCurrentUser();
  return (
    <UserContext value={currentUser}>
      {children}
    </UserContext>
  );
}

⚠️ Limitações e avisos

  • View Transitions são só DOM. Suporte a React Native tá em andamento, mas não saiu.
  • Animar UI cacheada de Suspense é anti-pattern. O release notes avisa explicitamente — você vai ver flicker em cada re-render.
  • use(browser()) não é ferramenta de data fetching. É pra optar por sair do SSR em componentes que realmente não conseguem renderizar no servidor (localStorage, timezone, APIs só do browser).
  • Fragment Refs não são um ref de DOM completo. Você ganha um conjunto curado de métodos, não o node cru.
  • Breaking change no Strict Mode: Effects agora são invocados em dobro durante a hidratação pra bater com client-rendered roots. Se sua lógica de hidratação tem side effects, audita isso.

📚 Próximos passos

  1. Roda npm install react@19.3 react-dom@19.3 e confere o Changelog completo.
  2. Começa pelo use(browser()) — é a migração de menor risco e remove mais boilerplate.
  3. Prototipa View Transitions numa rota de baixo tráfego antes de soltar no app todo.
  4. Se você usa CSP com Trusted Types, valida agora se suas policies de sanitização funcionam end-to-end.

Se você tá construindo apps React com IA, a mesma cadência de release importa no backend também — dá uma olhada no nosso breakdown sobre rodar agentes de IA prontos pra produção com Gemini 3 e frameworks open-source. E se você tá pesando a ética de shippar comportamentos autônomos de UI, esse texto sobre design de whistleblower de IA e segurança contra insider-threat vale a leitura.

Developer laptop running npm install react@19.3 with terminal output showing stable release notes Technical Structure Concept

Conclusão

O React 19.3 é um release de estabilidade disfarçado de feature drop. As features principais — View Transitions e Fragment Refs — já estavam nos canais experimentais tempo suficiente pra maioria dos times conhecer o formato. O que importa no 19.3 é que agora dá pra shippar com segurança, com caveats documentados e comportamento previsível.

As duas features silenciosas — use(browser()) e suporte a Trusted Types — vão deletar bastante boilerplate e fechar uma lacuna real de segurança. Se você fizer uma coisa essa semana: procura no seu codebase por useEffect(() => setMounted(true), []) e troca por use(browser()).

Começa pequeno, shippa incremental, e lê o changelog oficial antes de subir pra produção. Valeu, dev! 🚀

Este conteúdo foi elaborado com o auxílio de ferramentas de IA, com base em fontes confiáveis, e revisado pela nossa equipe editorial antes da publicação. Não substitui o aconselhamento de um profissional especializado.