O que é Remix?
Remix é um framework React construído sobre padrões web. Enquanto muitos frameworks abstraem a plataforma, o Remix se apoia nela: requisições e respostas HTTP, formulários HTML, links, headers e cookies são a base, e o framework os aprimora em vez de substituí-los.
O resultado é uma aplicação que funciona antes mesmo do JavaScript carregar. Formulários são enviados, links navegam e os dados aparecem, porque o servidor está realizando o trabalho. Quando o JavaScript finalmente chega, as mesmas interações tornam-se instantâneas e parciais. Essa propriedade é chamada de progressive enhancement (aprimoramento progressivo), e é a ideia central do Remix.
Em 2024, a equipe do Remix integrou o framework ao React Router v7, onde ele continua existindo como “framework mode”. Os conceitos e APIs permanecem inalterados, portanto, tudo aqui se aplica a ambos.
Rotas aninhadas
As rotas são arquivos dentro de app/routes, e os pontos nos nomes dos arquivos mapeiam para segmentos da URL.
app/routes/
├── _index.tsx # /
├── posts._index.tsx # /posts
├── posts.$slug.tsx # /posts/:slug
├── posts.$slug.edit.tsx # /posts/:slug/edit
└── api.health.tsx # /api/health
As rotas são aninhadas para corresponder à UI. Uma rota pai renderiza um layout compartilhado e um <Outlet />, e as rotas filhas são renderizadas dentro dele. Como o Remix conhece toda a árvore de rotas, ele consegue carregar os dados de uma página e de todas as suas rotas aninhadas em paralelo, em vez de em cascata (waterfall).
// app/routes/posts.tsx
import { Outlet } from "@remix-run/react";
export default function PostsLayout() {
return (
<div className="posts">
<h1>Posts</h1>
<Outlet />
</div>
);
}
Loaders
Um loader é uma função de servidor que fornece dados para uma rota antes que ela seja renderizada. Ele é executado no servidor, portanto, pode se comunicar diretamente com um banco de dados.
// app/routes/posts.$slug.tsx
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
export async function loader({ params }: LoaderFunctionArgs) {
const post = await getPost(params.slug);
if (!post) throw json({ message: "Post not found" }, { status: 404 });
return json({ post });
}
export default function Post() {
const { post } = useLoaderData<typeof loader>();
return <article>{post.title}</article>;
}
Não há estado de carregamento (loading state) para gerenciar nem requisições no lado do cliente. O Remix chama o loader no servidor para a renderização inicial e novamente durante a navegação, e o componente recebe os dados de forma síncrona.
Actions e formulários
As mutações são manipuladas por actions, que são funções de servidor vinculadas a uma rota e disparadas por envios de formulários.
// app/routes/posts.new.tsx
import { redirect } from "@remix-run/node";
import { Form } from "@remix-run/react";
export async function action({ request }: ActionFunctionArgs) {
const data = await request.formData();
const title = String(data.get("title") ?? "").trim();
if (!title) return json({ error: "Title is required" }, { status: 400 });
await createPost(title);
return redirect("/posts");
}
export default function NewPost() {
return (
<Form method="post">
<input name="title" required />
<button type="submit">Create</button>
</Form>
);
}
Como este é um formulário real, ele funciona sem JavaScript. O Remix intercepta o envio quando o cliente está pronto e revalida automaticamente os loaders afetados, fazendo com que a UI reflita os novos dados sem a necessidade de atualizações manuais de cache.
Error boundaries
Cada rota pode exportar um ErrorBoundary que captura erros de seu loader, action ou componente.
// app/routes/posts.$slug.tsx
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error) && error.status === 404) {
return <h1>Post not found</h1>;
}
return <h1>Something went wrong</h1>;
}
As boundaries são aninhadas, portanto, um erro em uma rota filha não deixa a página inteira em branco. O layout ao redor permanece funcional, o que proporciona uma experiência muito melhor do que uma tela branca.
Rotas de recurso e padrões web
Uma rota sem um export default é uma resource route: ela retorna dados em vez de UI, o que é ideal para APIs e webhooks.
// app/routes/api.health.tsx
export async function loader() {
return json({ status: "ok", time: Date.now() });
}
Como o Remix utiliza as Request e Response da plataforma, você pode ler headers, definir cookies, fazer stream de respostas e retornar qualquer código de status sem precisar aprender uma API específica de um framework. Esse conhecimento é transferível diretamente para outros runtimes.
Por que o modelo parece diferente
A maioria dos apps React gerencia dados em effects e os mantém no estado do cliente, sincronizando esse estado de volta para o servidor através de rotas de API. O Remix colapsa essas camadas:
- A rota é dona de seus dados através de um loader.
- A rota é dona de suas mutações através de uma action.
- O servidor detém a fonte da verdade, e o cliente permanece leve.
Menos peças móveis significam menos bugs, menos estado para sincronizar e menos código para escrever. A contrapartida é uma opinião mais forte sobre como os dados fluem, que é exatamente o motivo pelo qual as equipes que o adotam tendem a gostar.
Melhores práticas
- Busque dados em loaders, não em effects.
- Realize mutações através de actions e forms, e deixe o Remix revalidar.
- Use
FormeLinkdo Remix para que o enhancement funcione automaticamente. - Exporte um
ErrorBoundaryem rotas que possam falhar. - Mantenha as rotas aninhadas alinhadas com a hierarquia da UI.
- Use resource routes para APIs e webhooks.
- Retorne status codes e headers adequados em loaders e actions.
Erros comuns
- Fazer fetch em
useEffectem vez de usar um loader, perdendo a renderização no servidor. - Usar
fetchpara mutações em vez de uma action e revalidar os dados manualmente. - Esquecer o
<Outlet />em uma rota de layout pai. - Silenciar erros em vez de deixar que as boundaries os manipulem.
- Fazer over-fetching em um loader pai quando a rota filha deveria ser a dona dos dados.
- Tratar o Remix como uma SPA client-side e lutar contra o seu modelo de servidor.
Próximos passos
O Remix é a expressão mais forte do conceito “use the platform” no mundo React. Compare-o com o Next.js para conhecer a outra principal abordagem de React, e com o SvelteKit para ver uma filosofia semelhante em outro ecossistema. Reforce seus fundamentos de React, TypeScript e Node.js e, em seguida, construa um pequeno app com um loader, uma action e um error boundary.