CSS no React: módulos, Tailwind ou styled-components
As quatro formas de estilizar componente no React, o que cada uma custa no bundle final e a tabela de quando escolher cada uma delas.
Você monta o componente, abre o navegador e surge a primeira dúvida: devo usar
style, importar um arquivo CSS ou instalar alguma biblioteca? Ao final desta
lição, você saberá aplicar cada opção, reconhecer os erros silenciosos mais
comuns e escolher um caminho sem depender de torcida por ferramenta.
O React não cria um novo tipo de CSS. Ele renderiza elementos do navegador e
permite ligar as regras a esses elementos principalmente por className ou pela
prop style. Em projetos reais também aparecem CSS Modules, Tailwind e
styled-components; eles mudam onde você escreve e processa as regras, não a
função básica do CSS.
Pense no uniforme de uma equipe. O inline leva a instrução costurada na própria peça; CSS Modules guarda cada molde com uma etiqueta local; Tailwind oferece um catálogo de etiquetas prontas; styled-components leva uma máquina que monta regras durante a execução. A comparação ajuda a localizar o estilo, mas tem um limite: no navegador, cascade, herança e especificidade continuam sendo CSS de verdade.
Para enxergar a diferença sem trocar também o problema, vamos vestir o mesmo
CardLivro da Livraria Sete Ventos de quatro maneiras. Depois compararemos os
builds medidos na mesma máquina; primeiro, porém, vamos entender o mínimo que
aparece na tela.
Como o CSS chega a um componente React?
Ele chega diretamente pelo objeto da prop style ou indiretamente por uma
string em className que aponta para regras CSS. O conteúdo do card abaixo é o
mesmo nas quatro versões; só o caminho do estilo muda:
export default function CardLivro({ titulo, autor, preco, esgotado }) {
return (
<article>
<span>{esgotado ? 'Esgotado' : 'Em estoque'}</span>
<h3>{titulo}</h3>
<p>{autor}</p>
<strong>R$ {preco.toFixed(2)}</strong>
<button disabled={esgotado}>Adicionar à sacola</button>
</article>
);
}O card recebe dados por props e usa um ternário para
decidir o texto do selo. O termo markup significa a estrutura de elementos
que será convertida em HTML. Para isolar apenas a forma de estilizar, o próximo
teste renderiza o mesmo selo nas quatro abordagens. Como ele já importa
styled-components, abra o terminal na pasta do projeto e rode
npm install styled-components antes de copiá-lo; CSS Modules já é processado
pelo Vite e não pede outro pacote.
import { renderToStaticMarkup as html } from 'react-dom/server';
import styled from 'styled-components';
import estilos from './Selo.module.css';
const SeloStyled = styled.span`
font-size: 12px;
text-transform: uppercase;
color: #a12d2d;
`;
console.log('inline ', html(
<span style={{ fontSize: 12, textTransform: 'uppercase', color: '#a12d2d' }}>Esgotado</span>
));
console.log('modules ', html(<span className={estilos.selo}>Esgotado</span>));
console.log('tailwind', html(<span className="text-xs uppercase text-red-800">Esgotado</span>));
console.log('styled ', html(<SeloStyled>Esgotado</SeloStyled>));A primeira linha coloca declarações dentro do atributo style. CSS Modules
entrega uma classe transformada pelo build; Tailwind mantém no HTML os nomes das
utilidades escritas; styled-components produz classes próprias. A aparência
pode ser igual, mas o lugar onde você procura uma regra depois é diferente.
Leia o diagrama da esquerda para a direita: CSS Modules e Tailwind preparam uma folha no build; o inline escreve no elemento; styled-components precisa de código da biblioteca em execução para criar e inserir regras. “Build” é a etapa que transforma o projeto em arquivos de produção. Esse mapa será nossa referência quando compararmos tamanho e manutenção.
O desenho representa especificamente este laboratório Vite, renderizado no
cliente. styled-components também oferece ServerStyleSheet para coletar CSS
no servidor e reidratar as regras no navegador; portanto, “injeta no navegador”
não descreve toda arquitetura possível com a biblioteca.
Quando vale usar style inline no React?
Use style principalmente quando um valor nasce no JavaScript durante a
execução, como a largura de um progresso ou uma posição calculada. No JSX, essa
prop recebe um objeto, não a string usada no HTML. Os nomes viram camelCase:
background-color passa a ser backgroundColor.
const estilos = {
card: {
border: '1px solid #d8d2c4',
borderRadius: '12px',
padding: '16px',
background: '#fffdf7',
},
botao: { width: '100%', background: '#1f6f43', color: '#fff' },
};
export default function CardLivro({ titulo }) {
return (
<article style={estilos.card}>
<h3>{titulo}</h3>
<button style={estilos.botao}>Adicionar à sacola</button>
</article>
);
}O objeto está fora do componente porque esses valores são fixos. Criá-lo dentro da função produz uma nova referência em cada re-render. Isso não é um erro por si só, mas pode invalidar uma otimização se o objeto for passado a um filho memoizado. Comece simples e só trate essa referência quando houver uma necessidade medida.
Valores numéricos recebem px nas propriedades que esperam comprimento; as
propriedades definidas como sem unidade continuam sem sufixo. Veja a conversão:
console.log(
html(
<p style={{ fontSize: 18, marginTop: 8, lineHeight: 1.4, zIndex: 3, opacity: 0.8 }}>
Vidas Secas
</p>
)
);Na saída, fontSize: 18 virou font-size:18px, enquanto lineHeight: 1.4,
zIndex: 3 e opacity: 0.8 permaneceram sem unidade. Essa diferença vem da
lista de propriedades sem unidade reconhecida pelo React.
O limite aparece quando a regra depende de um seletor. O atributo style não
expressa sozinho :hover, ::before nem media query. Tentar colocar uma
pseudo-classe como propriedade do objeto gera esta saída:
const botao = {
background: '#1f6f43',
color: '#fff',
':hover': { background: '#175734' },
};
console.log(html(<button style={botao}>Adicionar à sacola</button>));O trecho :hover:[object Object] é a pista do problema: o objeto aninhado virou
texto, a declaração ficou inválida e o navegador a ignorou. A correção é manter
o valor dinâmico no inline e mover estados de seletor para uma classe CSS.
Experimente você mesmo
Antes de executar, preveja duas coisas: qual valor receberá px e se o botão
mudará de cor ao passar o mouse. Coloque o componente inline no App.jsx e
renderize-o como <CardLivro titulo="Torto Arado" />. Rode npm run dev e
troque padding: '16px' por padding: 24. Depois teste o objeto com :hover e
inspecione o atributo style no DevTools. Você deve ver 24px e confirmar que a
pseudo-classe não funciona ali.
Como o CSS Modules evita conflito entre nomes?
O CSS Modules dá escopo local às classes ao transformar seus nomes durante o
build. No Vite, basta terminar o arquivo com .module.css; importar esse arquivo
devolve um objeto que liga o nome legível à classe gerada.
É como guardar duas chaves chamadas “card” em chaveiros identificados: você pode
usar .card em componentes diferentes sem depender de um nome global único. A
analogia termina no nome, pois propriedades herdadas e variáveis CSS ainda podem
atravessar a árvore normalmente.
.card {
border: 1px solid #d8d2c4;
border-radius: 12px;
padding: 16px;
background: #fffdf7;
font-family: system-ui, sans-serif;
}
.selo {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #2f6f3e;
}
.seloEsgotado {
color: #a12d2d;
}
.botao:disabled {
background: #b7b1a4;
cursor: not-allowed;
}As regras omitidas seguem o mesmo CSS conhecido: .titulo, .autor, .preco,
.botao e .botao:hover. Pseudo-classes e media queries continuam válidas.
Agora o componente usa o objeto importado para preencher className:
import estilos from './CardLivro.module.css';
export default function CardLivro({ titulo, autor, preco, esgotado }) {
return (
<article className={estilos.card}>
<span className={`${estilos.selo} ${esgotado ? estilos.seloEsgotado : ''}`}>
{esgotado ? 'Esgotado' : 'Em estoque'}
</span>
<h3 className={estilos.titulo}>{titulo}</h3>
<p className={estilos.autor}>{autor}</p>
<strong className={estilos.preco}>R$ {preco.toFixed(2)}</strong>
<button className={estilos.botao} disabled={esgotado}>
Adicionar à sacola
</button>
</article>
);
}No build deste laboratório, o CSS de produção saiu minificado e com os nomes transformados:
Na saída, .card aparece como ._card_14rzk_1, e o componente recebe esse nome
por estilos.card. O formato exato é detalhe do empacotador: não escreva nem
procure esse nome gerado à mão. Continue usando o objeto importado.
Um import como import './global.css' é diferente: o Vite injeta aquelas regras
na página e elas continuam globais. Já import estilos from './CardLivro.module.css' retorna o mapa local. Você pode usar variáveis CSS dos
temas globais dentro do módulo e, quando realmente houver uma regra-base local,
o recurso composes do CSS Modules permite composição. Para iniciantes, uma
classe explícita e variáveis CSS costumam ser mais fáceis de rastrear.
Por que uma classe do CSS Module pode desaparecer?
Ela desaparece quando a chave consultada no objeto não existe. Um nome em kebab-case não corresponde automaticamente ao acesso camelCase na configuração padrão deste exemplo:
import estilos from './CardLivro.module.css'; // tem .selo-esgotado
console.log(Object.keys(estilos));
console.log(html(<span className={estilos.seloEsgotado}>Esgotado</span>));A lista mostra 'selo-esgotado', mas o código procurou seloEsgotado. O valor
virou undefined; por isso a segunda linha é apenas <span>Esgotado</span>, sem
atributo class. Corrija escrevendo a classe em camelCase, usando
estilos['selo-esgotado'] ou configurando a convenção de nomes no Vite.
Experimente você mesmo
Preveja o resultado antes de salvar: o selo ficará verde ou vermelho? Crie
CardLivro.module.css com as classes do exemplo, importe-o no componente e rode
o card no App.jsx como <CardLivro titulo="Torto Arado" autor="Itamar Vieira Junior" preco={54.9} esgotado={true} />.
Execute npm run dev. Depois renomeie
.seloEsgotado para .selo-esgotado sem alterar o JSX. Abra o console, confira
Object.keys(estilos) e aplique uma das três correções acima. O exercício
termina quando a classe gerada de esgotado reaparece no atributo class e o
selo volta a ficar vermelho.
Como usar Tailwind no Vite sem perder classes?
No Tailwind 4, você pode usar o plugin oficial do Vite e importar o framework no CSS. As classes precisam aparecer completas nos arquivos que o Tailwind examina; montar apenas pedaços do nome em JavaScript pode fazer a regra sumir do build.
Na pasta do projeto Vite, rode
npm install tailwindcss @tailwindcss/vite. Depois adicione o plugin ao
vite.config.js, como no primeiro bloco abaixo. No arquivo de entrada
src/index.css, coloque a importação do segundo bloco e confirme que
src/main.jsx ainda contém import './index.css'; sem esse último import, o CSS
é criado, mas não entra na página.
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({ plugins: [react(), tailwindcss()] });@import "tailwindcss";Depois da configuração, você combina utilidades dentro de className. Cada nome
representa uma regra pequena: p-4 adiciona espaçamento, rounded-xl arredonda
as bordas e disabled:bg-stone-400 vale no estado desabilitado.
export default function CardLivro({ titulo, autor, preco, esgotado }) {
return (
<article className="rounded-xl border border-stone-300 bg-stone-50 p-4 font-sans">
<span
className={`text-xs uppercase tracking-wider ${
esgotado ? 'text-red-800' : 'text-green-800'
}`}
>
{esgotado ? 'Esgotado' : 'Em estoque'}
</span>
<h3 className="mt-2 mb-0.5 text-lg">{titulo}</h3>
<p className="text-sm text-stone-500">{autor}</p>
<strong className="mt-3 block text-xl">R$ {preco.toFixed(2)}</strong>
<button
className="mt-3 w-full cursor-pointer rounded-lg bg-green-800 p-2.5 text-white hover:bg-green-900 disabled:cursor-not-allowed disabled:bg-stone-400"
disabled={esgotado}
>
Adicionar à sacola
</button>
</article>
);
}Aqui mora o erro mais comum: o Tailwind lê os arquivos como texto para detectar
classes, sem executar o componente nem compreender a interpolação. O teste
abaixo troca nomes completos por text-${cor}-800 e compara os builds:
export default function CardLivro({ titulo, esgotado }) {
const cor = esgotado ? 'red' : 'green';
return (
<article className="rounded-xl border border-stone-300 p-4">
<span className={`text-xs uppercase text-${cor}-800`}>
{esgotado ? 'Esgotado' : 'Em estoque'}
</span>
<h3 className="mt-2 text-lg">{titulo}</h3>
</article>
);
}grep -o "\.text-[a-z]*-800" 03-tailwind/dist/assets/*.css
grep -o "\.text-[a-z]*-800" 06-tw-dinamico/dist/assets/*.cssO primeiro grep encontrou .text-green-800 e .text-red-800. O segundo
terminou com (exit 1), que nesse comando significa “nenhuma correspondência”.
Não é o ternário que resolve: são os dois nomes completos visíveis no arquivo.
A correção é mapear cada estado para uma string completa. Se nenhuma utilidade
for gerada, revise antes a
configuração do projeto com Vite.
Experimente você mesmo
Antes de rodar, preveja qual versão deixará o selo colorido. Execute os dois
componentes renderizando, por exemplo,
<CardLivro titulo="Torto Arado" autor="Itamar Vieira Junior" preco={54.9} esgotado={false} />.
Faça npm run build e procure text-red-800 no CSS de
dist. Em seguida, substitua a interpolação por um objeto como const cores = { red: 'text-red-800', green: 'text-green-800' } e use cores[cor]. O teste está
certo quando as duas classes completas voltarem ao arquivo gerado.
Quando styled-components pode fazer sentido?
Ele pode fazer sentido quando o projeto já usa CSS-in-JS ou precisa ligar temas
e props às regras com a API da biblioteca. Em vez de aplicar apenas uma classe a
uma tag, você cria um componente estilizado, como Card ou Selo:
Se você chegou direto nesta seção e ainda não instalou a biblioteca, rode
npm install styled-components na pasta do projeto antes de salvar o componente.
import styled from 'styled-components';
const Card = styled.article`
border: 1px solid #d8d2c4;
border-radius: 12px;
padding: 16px;
background: #fffdf7;
`;
const Selo = styled.span`
font-size: 12px;
text-transform: uppercase;
color: ${(props) => (props.$esgotado ? '#a12d2d' : '#2f6f3e')};
`;
const Titulo = styled.h3`
font-size: 18px;
margin: 8px 0 2px;
`;
const Preco = styled.strong`
display: block;
margin-top: 12px;
font-size: 20px;
`;
export default function CardLivro({ titulo, preco, esgotado }) {
return (
<Card>
<Selo $esgotado={esgotado}>{esgotado ? 'Esgotado' : 'Em estoque'}</Selo>
<Titulo>{titulo}</Titulo>
<Preco>R$ {preco.toFixed(2)}</Preco>
</Card>
);
}Card e Selo são componentes React e recebem props. A interpolação lê
$esgotado para escolher a cor. O prefixo $ marca uma prop transiente:
ela participa do estilo, mas não é encaminhada ao elemento HTML. Sem o prefixo,
o teste reproduz este aviso:
const Selo = styled.span`
color: ${(props) => (props.esgotado ? '#a12d2d' : '#2f6f3e')};
`;
console.log(html(<Selo esgotado={true}>Esgotado</Selo>));If you want to write it to the DOM, pass a string instead: esgotado=“true” or esgotado={value.toString()}. <span class=“sc-bdvwhi iEISLD”>Esgotado</span>
O aviso diz que true foi enviado para um atributo não booleano chamado
esgotado. O <span> ainda foi renderizado com classes, mas o console denuncia
que uma prop interna vazou para o DOM. Neste caso, volte a $esgotado; em
composições mais avançadas, a biblioteca também oferece shouldForwardProp.
Essa API traz código adicional no benchmark abaixo, feito especificamente com styled-components 6.5.3. O custo e a estratégia podem mudar conforme versão, SSR, compilação e arquitetura; portanto, o número do laboratório não é uma lei para todo projeto.
Como montar classes condicionais sem criar lixo no HTML?
Produza uma única string contendo apenas os nomes ativos. Em CSS Modules e
Tailwind, um template literal funciona para casos pequenos, mas misturar && e
valores booleanos pode inserir texto indesejado:
import estilos from './Selo.module.css'; // tem .selo, .esgotado e .novo
function Selo({ esgotado, novo }) {
return (
<span className={`${estilos.selo} ${esgotado && estilos.esgotado} ${novo ? estilos.novo : ''}`}>
Em estoque
</span>
);
}
console.log(html(<Selo esgotado={false} novo={false} />));A saída confirma a armadilha: o booleano false virou literalmente a classe
false, e o valor vazio deixou um espaço extra. Isso não costuma mudar a
aparência, mas suja o HTML e dificulta o diagnóstico. Uma função como clsx
descarta valores falsos e junta só as classes presentes:
Instale essa função auxiliar uma vez com npm install clsx; depois o import do
próximo bloco passa a resolver normalmente.
import clsx from 'clsx';
import estilos from './Selo.module.css';
function Selo({ esgotado, novo }) {
return (
<span className={clsx(estilos.selo, esgotado && estilos.esgotado, novo && estilos.novo)}>
Em estoque
</span>
);
}
console.log(html(<Selo esgotado={false} novo={false} />));Agora a saída contém apenas _selo_1ultd_1: sem false e sem espaço final.
Você pode fazer um ternário explícito sem biblioteca ou usar clsx quando a
lista crescer. Com Tailwind, tailwind-merge também pode eliminar utilidades que
o pacote reconhece como conflitantes.
Essas funções organizam a string, mas não desligam a cascade. Se duas regras CSS válidas competirem, origem, camada, importância, especificidade e ordem ainda decidem a vencedora. Até dois arquivos globais importados em pontos diferentes podem mudar o resultado pela ordem de carregamento. Revise a base de JSX no React e, para conflitos reais, consulte a especificidade do CSS.
Quanto cada abordagem adicionou a este bundle?
Neste laboratório, CSS Modules ficou próximo do inline, Tailwind acrescentou CSS e styled-components acrescentou mais JavaScript. Essa é uma comparação controlada, não uma previsão para qualquer aplicação.
Os quatro projetos têm o mesmo index.html, o mesmo App.jsx e três livros na
tela, com React 19.2.8 e Vite 8.2.2. O comando abaixo gera o build de produção de
cada versão:
Esse comando pressupõe quatro pastas irmãs chamadas 01-inline, 02-modules,
03-tailwind e 04-styled; ele é o registro do laboratório executado para esta
comparação. Para repetir, duplique seu projeto Vite quatro vezes, aplique em cada
cópia a versão ensinada na seção correspondente e rode o comando a partir da
pasta que contém as quatro. Se preferir trabalhar em uma única cópia, rode
npm run build antes de trocar de abordagem e anote cada resultado.
for d in 01-inline 02-modules 03-tailwind 04-styled; do
echo "===== $d ====="
(cd $d && npx vite build)
doneCada grupo da saída lista o HTML e os arquivos JS ou CSS gerados. A coluna
gzip aproxima o tamanho transferido quando o servidor usa essa compressão; o
tempo em milissegundos mede apenas aquela execução. Organizando os valores:
| abordagem | JS (gzip) | CSS (gzip) | total gzip | diferença |
|---|---|---|---|---|
style inline |
60,57 kB | — | 60,57 kB | referência |
| CSS Modules | 60,38 kB | 0,37 kB | 60,75 kB | +0,18 kB |
| Tailwind | 60,45 kB | 2,19 kB | 62,64 kB | +2,07 kB |
| styled-components | 71,28 kB | — | 71,28 kB | +10,71 kB |
Leia sempre a diferença contra a primeira linha:
- CSS Modules somou 0,18 kB gzip neste card; o escopo foi resolvido no build.
- Tailwind somou 2,07 kB gzip. Um teste separado só com
p-4gerou 1,44 kB gzip entre preflight e variáveis; o restante depende das utilidades usadas aqui. - styled-components somou 10,71 kB gzip ao JS nesta configuração e versão.
Os tempos de build não sustentam uma escolha neste exemplo pequeno. Em cinco execuções após aquecimento, no MacBook usado no teste, as médias foram 45,6 ms, 48,8 ms, 53,4 ms e 49,4 ms. A diferença ficou próxima da variação entre rodadas; para um projeto real, repita a medição na própria base.
O que aconteceu quando o laboratório ganhou seis componentes?
O CSS dos módulos cresceu mais em bytes brutos, enquanto as utilidades do Tailwind foram mais reaproveitadas neste desenho específico. Isso mostra a curva deste experimento, não uma regra universal sobre projetos grandes.
O card foi duplicado como CardLivro, CardAutor, CardColecao, CardEditora,
CardSelo e CardResenha. Cada um recebeu raio entre 10 px e 18 px e padding
entre 14 px e 22 px. Nos módulos, o laboratório repetiu o conjunto de regras em
seis arquivos; no Tailwind, trocou utilidades como rounded-xl p-4 por valores
de raio e espaço diferentes. O novo build produziu:
Na saída, o CSS dos módulos foi de 0,65 kB para 3,28 kB, pois esta implementação repetiu as regras em cada arquivo. O CSS do Tailwind foi de 6,69 kB para 7,03 kB; o build reutilizou utilidades já existentes e acrescentou oito combinações. Em gzip, porém, os módulos ainda ficaram menores: 0,57 kB contra 2,25 kB.
Há outras arquiteturas possíveis. Os módulos poderiam compartilhar uma classe
base com composes, um arquivo comum ou variáveis CSS; o Tailwind poderia ganhar
muitas combinações únicas. Portanto, compare também legibilidade, cache,
manutenção e requisitos do time. Independentemente da ferramenta, regras que
atingem o mesmo elemento continuam sujeitas à cascade.
Qual abordagem escolher para começar?
Para o primeiro projeto, CSS Modules é um ponto de partida seguro porque mantém o CSS reconhecível e reduz colisões de nomes. Depois, deixe os requisitos — e não a popularidade — indicarem se vale usar inline, Tailwind ou styled-components.
A tabela resume hipóteses iniciais com base neste laboratório. Leia “escolha” como a primeira opção a avaliar, não como resposta automática:
| situação | escolha | por quê |
|---|---|---|
| primeiro projeto React, estudando | CSS Modules | é CSS que você já sabe, com escopo de graça e zero instalação |
| valor calculado em tempo de execução | style inline |
largura de barra, posição de tooltip, cor vinda da API |
| time com mais de duas pessoas no mesmo layout | Tailwind | ninguém precisa inventar nome de classe nem caçar arquivo |
| design system com tema trocável no clique | styled-components | prop e tema entram no CSS sem duplicar regra |
| menor bundle com hover e media queries neste laboratório | CSS Modules | +0,18 kB gzip, sem abrir mão de seletores CSS |
Guarde quatro sinais de diagnóstico: inline não representa pseudo-classes;
CSS Modules pode devolver undefined quando a chave está errada; Tailwind exige
nomes completos detectáveis; props internas do styled-components devem ser
filtradas. Nenhum deles transforma uma ferramenta em vencedora universal.
Como transformar esta comparação em prática?
Implemente o mesmo estado esgotado em duas abordagens e compare o resultado.
Você terminará com evidência de manutenção e bundle, em vez de apenas uma
preferência.
Se o projeto ainda não existe, comece por
criar um projeto React com Vite. Escreva o
card da Livraria Sete Ventos sem copiar, altere a cor do selo nas duas versões e
rode vite build. Anote JS e CSS em gzip e marque onde faria a próxima mudança.
Se className ainda parecer estranho, revise
JSX no React.
Este artigo fecha a sequência atual da trilha de React; React Router apareceu no passo anterior. A ponte agora é um projeto pequeno: monte uma estante com três cards, uma página de detalhes e um tema próprio. A melhor abordagem para esse projeto é a que cumpre o requisito com um lugar previsível para encontrar e corrigir cada regra.
Prefere aprender em vídeo?
Tem aula sobre este assunto no nosso canal.
DevClub no YouTubeComo utilizar Styled Components no React | CSS no React | Criando App com ReactAssistir a aula
DevClub no YouTubeClone do X(Twitter) com React e Tailwind CSS (Passo a Passo)Assistir a aula
Perguntas frequentes
Dá para misturar mais de uma abordagem no mesmo projeto?
CSS Modules funciona fora do Vite?
Preciso de arquivo de configuração para o Tailwind 4?
styled-components ainda faz sentido em 2026?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, Vite 8.2.2, React 19.2.8, Tailwind 4.3.3, styled-components 6.5.3, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- React — Common components: the style prop — react.dev
- Vite — CSS Modules — vite.dev
- Tailwind CSS — Detecting classes in source files — tailwindcss.com
- styled-components — Transient props — styled-components.com
- styled-components — Server-side rendering — styled-components.com


