Skip to content
This page has been auto-translated and may contain errors.View in English

Estilizando componentes

React não tem um sistema de estilos próprio. Você escreve CSS comum, e JSX oferece duas formas de anexá-lo a um elemento: um nome de classe, que funciona na maioria dos casos, e uma prop style para os poucos valores que uma folha de estilos não consegue saber com antecedência. Este capítulo cobre ambos, junto com o padrão que fica entre eles: construir um nome de classe a partir de props ou estado, para que um elemento se restyle automaticamente quando seus dados mudam.

Nomes de classe em JSX

class é uma palavra reservada em JavaScript, então JSX nomeia o atributo className. O valor é uma string comum, e React a define como o atributo class do elemento no DOM, o que significa que o CSS do outro lado é escrito exatamente como sempre foi.

jsx
function Avatar() {
  return <img className="avatar avatar--large" src={photo} alt="Brasília" />
}

Dois nomes de classe em uma string, separados por um espaço, assim como em HTML. A regra de camelCase de JSX se aplica aos nomes dos atributos no seu markup; os nomes de classe em si são seus, então kebab-case, BEM ou qualquer convenção que sua folha de estilos já usa continua funcionando normalmente.

Importando uma folha de estilos

Um arquivo de componente puxa o CSS que precisa com um import simples no topo:

jsx
import './App.css'

export default function App() {
  return <h1 className="title">React Facts</h1>
}

Não há variável à esquerda desse import, e o componente nunca lê o arquivo. É uma instrução para a ferramenta de build: este módulo depende daquele CSS, então inclua-o. O Vite injeta as regras na página durante o desenvolvimento e emite um arquivo .css de verdade em um build de produção. Configurando um projeto React descreve o mesmo mecanismo que trata imagens.

Onde você coloca o import é uma questão de organização. Importar Button.css no topo de Button.jsx mantém os estilos junto do componente que os usa. As regras em si continuam globais: um seletor .title combina com todo elemento que tenha essa classe em qualquer lugar da página, não importa qual arquivo a tenha importado. Convenções de nomenclatura como BEM existem para manter esse namespace compartilhado gerenciável, e bibliotecas como CSS Modules e CSS-in-JS fazem o escopo das regras por você quando uma aplicação fica grande demais para contar apenas com disciplina de nomes.

Version note

React 19 added support for rendering <link rel="stylesheet"> and <style> from inside a component, with a precedence prop that lets React hoist them into the document head and order them predictably. Earlier versions needed a library such as react-helmet. The build-tool import above behaves the same way in every version.

Nomes de classe que dependem de dados

className fica dentro de chaves como qualquer outra expressão JSX, então a string pode ser computada. Imagine um jogo de dados onde cada dado pode ser retido entre lançamentos. Se um dado está retido fica no estado mais acima e chega aqui como uma prop:

jsx
function Die({ value, isHeld }) {
  return (
    <button className={`die ${isHeld ? 'die--held' : ''}`}>
      {value}
    </button>
  )
}

Todo dado recebe die. Um que está retido também recebe die--held. A folha de estilos contém as cores e bordas de verdade, e o único trabalho do componente é decidir qual classe se aplica. Quando isHeld muda, React atualiza o atributo de classe e o navegador repainta. Nada dentro do componente sabe como "retido" aparece, então essa aparência pode mudar sem que este arquivo mude de forma alguma.

Uma observação: quando isHeld é false, o template literal produz "die " com um espaço à direita. Os navegadores o ignoram, embora fique desajeitado no devtools. Um array funciona de forma mais limpa e funciona bem com mais de uma condição:

jsx
const classes = ['die', isHeld && 'die--held'].filter(Boolean).join(' ')

return <button className={classes}>{value}</button>

isHeld && 'die--held' avalia para false quando a condição falha, filter(Boolean) remove isso, e join(' ') coloca um único espaço entre o que sobrou.

A prop style

A prop style recebe um objeto JavaScript. Os nomes das propriedades são as versões camelCase das do CSS, e as chaves duplas são o slot de expressão comum com um objeto literal dentro:

jsx
<div style={{ backgroundColor: 'darkslateblue', paddingTop: 12 }}>
  Estilizado inline
</div>

background-color vira backgroundColor e padding-top vira paddingTop. Os valores geralmente são strings. Passe um número simples e React acrescenta px para propriedades que aceitam comprimento, então paddingTop: 12 renderiza como padding-top: 12px. Propriedades que CSS trata como sem unidade mantêm o número bruto: lineHeight, opacity, flexGrow, zIndex e fontWeight passam direto. Qualquer outra unidade tem que ser escrita como string, como em width: '60%' ou margin: '2rem'.

A maioria dos estilos pertence a uma folha de estilos, onde uma classe pode carregar uma dúzia de declarações e ser compartilhada por uma dúzia de elementos. A prop style ganha seu lugar quando um valor é conhecido apenas em tempo de execução e CSS não tem forma de alcançá-lo:

jsx
function ProgressBar({ percent }) {
  return (
    <div className="progress">
      <div className="progress__fill" style={{ width: `${percent}%` }} />
    </div>
  )
}

A cor do preenchimento, altura, transição e raio de canto vivem em .progress__fill. O único valor que a folha de estilos não consegue saber é a largura, porque vem de dados, então esse valor vai inline. Uma imagem de fundo cuja URL chega de uma API tem a mesma forma:

jsx
<div className="hero" style={{ backgroundImage: `url(${photo.url})` }} />

O tamanho, posicionamento e overlay ficam em .hero, e apenas a URL chega através da prop.

Estilos inline também têm um limite bem claro. Um objeto de style descreve as declarações de um único elemento, então não há jeito de expressar um estado de hover, uma media query ou uma animação keyframe nele. Esses pertencem a CSS não importa o quão dinâmico o resto da estilização fica.

Um único ternário dentro de um template literal lê bem. Dois ou três deles empilhados na mesma string viram uma parede de backticks e interrogações que ninguém quer editar. Quando um componente tem mais de uma condição, mova a computação acima do return e deixe o array fazer o trabalho:

jsx
function Button({ variant, size, isDisabled, isLoading, children }) {
  const classes = [
    'btn',
    `btn--${variant}`,
    `btn--${size}`,
    isDisabled && 'btn--disabled',
    isLoading && 'btn--loading',
  ]
    .filter(Boolean)
    .join(' ')

  return (
    <button className={classes} disabled={isDisabled}>
      {children}
    </button>
  )
}

Cada linha é uma decisão, então adicionar uma quarta condição significa adicionar uma linha em vez de reestruturar uma expressão, e o JSX continua legível porque o atributo é um único identificador.

Quando várias classes são mutuamente excludentes, um objeto de lookup vence uma corrente de ternários:

jsx
const statusClasses = {
  idle: 'card--idle',
  loading: 'card--loading',
  error: 'card--error',
}

const classes = ['card', statusClasses[status]].filter(Boolean).join(' ')

Um status não reconhecido produz undefined, que filter(Boolean) remove, então o elemento volta para a classe base card em vez de renderizar o texto literal "undefined" no atributo.

Uma vez que você está escrevendo isso em todo componente, os pacotes clsx e classnames fazem o mesmo trabalho em poucos bytes, com um formato de objeto para as partes condicionais: clsx('btn', { 'btn--disabled': isDisabled }). Eles são uma conveniência sobre a versão de array acima, que vale a pena entender primeiro.

A prop style recebe um objeto porque isso mapeia diretamente para como o DOM expõe estilos. Todo elemento tem uma propriedade style, um CSSStyleDeclaration, cujos membros são os nomes de propriedade CSS em camelCase: node.style.backgroundColor. React atribui esses membros individualmente. Uma string significaria escrever cssText, que reanalisa e substitui cada declaração no elemento cada vez que qualquer uma delas muda.

Atribuir por propriedade é o que torna atualizações de style diffable. React compara o objeto de style anterior com o próximo chave por chave: chaves cujos valores correspondem são deixadas em paz, chaves alteradas são escritas e chaves que desapareceram são resetadas para uma string vazia. Apenas as propriedades que realmente mudaram tocam o DOM. Uma string CSS não oferece forma de expressar essa atualização parcial.

Propriedades com prefixo de vendor seguem a mesma regra de camelCase com uma primeira letra maiúscula, como em WebkitLineClamp, com ms como a exceção que permanece em minúscula (msOverflowStyle). React não adiciona prefixos por você.

Propriedades customizadas são o único caso onde a chave permanece exatamente como CSS escreve:

jsx
<div className="card" style={{ '--accent': team.color }}>

React vê o -- inicial e o direciona através de setProperty, passando o valor literalmente. Nenhum px é acrescentado, então as unidades são suas para fornecer. Essa é uma ponte útil entre as duas abordagens: defina uma propriedade customizada inline a partir de dados e deixe a folha de estilos consumi-la em quantas regras, pseudo-classes e media queries quiser.

O custo de identidade vale a pena conhecer. Um objeto de style inline escrito direto no JSX, como nos exemplos acima, aloca um objeto novo a cada renderização. Em um elemento host isso não custa nada mensurável, já que React compara os valores em vez da referência e não escreve nada quando correspondem. Começa a importar quando o objeto cruza um limite de componente: passe um objeto de style construído na hora para um filho envolvido em memo, e a comparação rasa de props vê uma nova referência a cada renderização e re-renderiza o filho mesmo assim, derrotando a memoização. Uma string className não tem esse problema, porque strings comparam por valor.

Os consertos são ordinários: coloque objetos de style estáticos em escopo de módulo para que a referência seja criada uma vez, e envolva os dinâmicos em useMemo com chave nos valores de que derivam (veja Hooks). Ambos valem a pena fazer apenas onde um profile mostra a re-renderização custando algo, e recorrer a um nome de classe remove a questão completamente. Classes escalam melhor no navegador mesmo assim, já que um conjunto de regra combinada é compartilhado entre todo elemento que carrega a classe, enquanto declarações inline são repetidas em cada elemento e incham o HTML renderizado no servidor de acordo.

JunoRecorra a uma classe primeiro, estilo inline em segundo Estilização em React é o CSS que você já escreve, anexado através de className porque class é uma palavra reservada em JavaScript.

Importe sua folha de estilos no topo do arquivo, coloque suas regras nela e deixe o componente decidir qual classe se aplica construindo a string a partir de props ou estado.

Guarde a prop style para valores que seu arquivo CSS não consegue saber com antecedência, como uma largura que vem de dados.

JunoRecorra a uma classe primeiro, estilo inline em segundoclassName é um slot de expressão, então um template literal funciona para uma condição e um array com filter(Boolean).join(' ') cobre o resto. Compute acima do return para que o JSX continue escaneável e use um objeto de lookup quando as classes são mutuamente excludentes.

A prop style recebe chaves em camelCase, acrescenta px a números simples para propriedades de comprimento e deixa os sem unidade como opacity sozinhos. Ela ganha seu lugar para valores em tempo de execução, e não consegue expressar estados de hover ou media queries de forma alguma.

JunoRecorra a uma classe primeiro, estilo inline em segundo O objeto style existe porque mapeia para CSSStyleDeclaration, que deixa React fazer diff e escrever propriedades individuais em vez de reanalisar uma string cssText. Chaves começando com -- passam por setProperty intocadas, o que torna uma propriedade customizada a ponte mais limpa de dados em tempo de execução para uma folha de estilos.

A armadilha é identidade: um objeto literal novo a cada renderização é invisível em um elemento host e fatal para um limite memo, então coloque objetos estáticos em escopo de módulo, envolva os dinâmicos em useMemo e prefira um nome de classe quando um funcionar.

Próximo: Props, onde componentes começam a receber dados de seu pai.