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

Estilizando componentes

React não possui sistema de estilo próprio. Você escreve CSS comum, e JSX oferece duas formas de anexá-lo a um elemento: um nome de classe, que cobre quase tudo, e uma prop style para os poucos valores que uma folha de estilo não consegue conhecer antecipadamente. 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 conforme seus dados mudam.

Nomes de classe em JSX

class é uma palavra reservada em JavaScript, então JSX chama o atributo de 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 do jeito que sempre foi.

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

Dois nomes de classe em uma string, separados por espaço, igual a HTML. A regra camelCase do JSX se aplica aos nomes de atributo na sua marcação; os nomes de classe em si são seus, então kebab-case, BEM, ou qualquer convenção que sua folha de estilo já use passa intacta.

Importando uma folha de estilo

Um arquivo de componente puxa o CSS que precisa com uma importação simples no topo:

jsx
import './App.css'

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

Não há variável à esquerda dessa importação, 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. Vite injeta as regras na página durante o desenvolvimento e emite um arquivo .css real em um build de produção. Configurando um projeto React descreve o mesmo mecanismo que lida com imagens.

Onde colocar a importação é uma questão de organização. Importar Button.css no topo de Button.jsx mantém os estilos perto do componente que os usa. As regras em si permanecem globais: um seletor .title corresponde a cada elemento com essa classe em qualquer lugar da página, seja qual for o arquivo que o importou. Convenções de nomenclatura como BEM existem para manter esse namespace compartilhado gerenciável, e CSS Modules e bibliotecas CSS-in-JS delimitam as regras para você uma vez que um app ultrapassa a disciplina de nomenclatura sozinha.

Nota de versão

React 19 adicionou suporte para renderizar <link rel="stylesheet"> e <style> de dentro de um componente, com uma prop precedence que deixa React elevá-los para o head do documento e ordená-los de forma previsível. Versões anteriores precisavam de uma biblioteca como react-helmet. A importação da ferramenta de build acima se comporta da mesma forma em todas as versões.

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. Pegue um jogo de dados onde cada dado pode ser retido entre lançamentos. Se um dado é retido vive em estado mais acima e chega aqui como uma prop:

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

Cada dado recebe die. Um retido também recebe die--held. A folha de estilo guarda as cores e bordas reais, e o único trabalho do componente é decidir qual classe aplica. Quando isHeld muda, React atualiza o atributo de classe e o navegador redesenha. Nada dentro do componente sabe como "retido" parece, então essa aparência pode mudar sem este arquivo mudar de jeito nenhum.

Um detalhe: quando isHeld é false, o template literal produz "die " com um espaço à direita. Navegadores ignoram isso, embora pareça desorganizado no devtools. Um array lida com isso de forma mais limpa e escala além de uma condição:

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

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

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

A prop style

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

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

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

A maioria do estilo pertence a uma folha de estilo, 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 como 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 tudo em .progress__fill. O único valor que a folha de estilo não consegue saber é a largura, porque vem de dados, então esse valor fica 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 dimensionamento, posicionamento e sobreposição ficam em .hero, e apenas a URL vem pela prop.

Estilos inline também têm um limite duro. Um objeto de estilo descreve as declarações de um elemento, então não há forma de expressar um estado hover, uma media query, ou uma animação keyframe nele. Essas coisas pertencem ao CSS independentemente de quão dinâmico o resto do estilo 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 pontos de interrogação que ninguém quer editar. Uma vez que um componente tenha mais de uma condição, mova a computação acima do return e deixe a forma de 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 permanece legível porque o atributo é um único identificador.

Quando várias classes são mutuamente exclusivas, 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 recai na 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 algumas centenas de bytes, com uma forma de objeto para as partes condicionais: clsx('btn', { 'btn--disabled': isDisabled }). 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 mapeia diretamente para como o DOM expõe estilo. Todo elemento tem uma propriedade style, uma CSSStyleDeclaration, cujos membros são os nomes de propriedade CSS camelCase: node.style.backgroundColor. React atribui esses membros individualmente. Uma string significaria escrever cssText, que reanálisa e substitui cada declaração no elemento toda vez que alguma delas muda.

Atribuir por propriedade é o que torna as atualizações de estilo diff-áveis. React compara o objeto de estilo 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 fornecedor seguem a mesma regra camelCase com uma primeira letra capitalizada, como em WebkitLineClamp, com ms como a exceção que permanece minúscula (msOverflowStyle). React não adiciona prefixos por sua conta.

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 roteia pelo setProperty, passando o valor verbatim. Nenhum px é anexado, então as unidades são suas para fornecer. Esta é uma ponte útil entre as duas abordagens: defina uma propriedade customizada inline a partir de dados e deixe a folha de estilo consumir isso em tantas regras, pseudo-classes e media queries quanto quiser.

O custo de identidade vale a pena conhecer. Um objeto de estilo inline escrito direto no JSX, como nos exemplos acima, aloca um objeto fresco a cada renderização. Em um elemento host isso 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 estilo recém-construído para um filho envolto em memo, e a comparação de prop superficial 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.

As correções são ordinárias: eleve objetos de estilo estáticos para 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 optar por um nome de classe remove a questão totalmente. Classes escalam melhor no navegador de qualquer forma, já que um conjunto de regra correspondida é compartilhado entre todo elemento carregando a classe, enquanto declarações inline são repetidas em cada elemento e inflam HTML renderizado no servidor correspondientemente.

JunoOpte por uma classe primeiro, estilo inline em segundo Estilo em React é o CSS que você já escreve, anexado através de className porque class é uma palavra reservada em JavaScript. Importe sua folha de estilo no topo do arquivo, coloque suas regras nela, e deixe o componente decidir qual classe aplica construindo a string a partir de props ou estado. Guarde a prop style para valores que seu arquivo CSS não consegue saber antecipadamente, como uma largura que vem de dados.
JunoOpte por uma classe primeiro, estilo inline em segundoclassName é um slot de expressão, então um template literal cobre uma condição e um array com filter(Boolean).join(' ') cobre o resto. Compute acima do return para que o JSX permaneça escaneável, e use um objeto de lookup quando as classes são mutuamente exclusivas. A prop style recebe chaves camelCase, anexa px a números simples para propriedades de comprimento, e deixa os sem unidade como opacity sozinhos. Ganha seu lugar para valores em tempo de execução, e não consegue expressar estados hover ou media queries de jeito nenhum.
JunoOpte por uma classe primeiro, estilo inline em segundo O objeto style existe porque mapeia para CSSStyleDeclaration, o 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 estilo. A armadilha é identidade: um objeto literal fresco a cada renderização é invisível em um elemento host e fatal para um limite de memo, então eleve objetos estáticos para escopo de módulo, useMemo os dinâmicos, e prefira um nome de classe onde um funcionará.

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