Construindo componentes reutilizáveis do React usando o Tailwind
Publicados: 2022-03-10Neste post, veremos várias maneiras diferentes de criar componentes React reutilizáveis que aproveitam o Tailwind sob o capô enquanto expõem uma interface agradável para outros componentes. Isso melhorará seu código, passando de longas listas de nomes de classes para props semânticos que são mais fáceis de ler e manter.
Você precisará ter trabalhado com React para entender bem este post.
Tailwind é uma estrutura CSS muito popular que fornece classes de utilitários de baixo nível para ajudar os desenvolvedores a criar designs personalizados. Ele cresceu em popularidade nos últimos anos porque resolve dois problemas muito bem:
- O Tailwind facilita fazer alterações iterativas no HTML sem vasculhar as folhas de estilo para encontrar seletores CSS correspondentes.
- Tailwind tem convenções e padrões sensatos. Isso torna mais fácil para as pessoas começarem sem escrever CSS do zero.
Adicione a documentação abrangente e não é nenhuma surpresa por que o Tailwind é tão popular.
Esses métodos ajudarão você a transformar o código que se parece com isso:
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> Enable </button>
Para codificar assim:
<Button size="sm" textColor="white" bgColor="blue-500"> Enable </Button>
A diferença entre os dois snippets é que no primeiro usamos uma tag de botão HTML padrão, enquanto no segundo usamos um componente <Button>
. O componente <Button>
foi construído para reutilização e é mais fácil de ler, pois possui uma semântica melhor. Em vez de uma longa lista de nomes de classes, ele usa propriedades para definir vários atributos, como size
, textColor
e bgColor
.
Vamos começar.
Método 1: Controlando classes com o módulo Classnames
Uma maneira simples de adaptar o Tailwind em um aplicativo React é adotar os nomes das classes e alterná-los programaticamente.
O módulo classnames npm facilita a alternância de classes no React. Para demonstrar como você pode usar isso, vamos pegar um caso de uso onde você tem componentes <Button>
em seu aplicativo React.
// This could be hard to read. <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Enable</button> // This is more conventional React. <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Vamos ver como separar as classes do Tailwind para que as pessoas que usam este componente <Button>
possam usar props React como size
, textColor
e bgColor
.
- Passe adereços como
bgColor
etextColor
diretamente no modelo de string de nome de classe. - Use objetos para alternar nomes de classes programaticamente (como fizemos com a prop
size
)
No código de exemplo abaixo, veremos as duas abordagens.
// Button.jsx import classnames from 'classnames'; function Button ({size, bgColor, textColor, children}) { return ( <button className={classnames("bg-${bgColor} text-${textColor} font-bold py-2 px-4 rounded", { "text-xs": size === 'sm' "text-xl": size === 'lg', })}> {children} </button> ) }; export default Button;
No código acima, definimos um componente Button
que recebe as seguintes props:
-
size
Define o tamanho do botão e aplica as classes Tailwindtext-xs
outext-xl
-
bgColor
Define a cor de fundo do botão e aplica as classes Tailwindbg-*
. -
textColor
Define a cor do texto do botão e aplica astext-* classes
Tailwind . -
children
Quaisquer subcomponentes serão passados por aqui. Geralmente conterá o texto dentro do<Button>
.
Ao definir Button.jsx
, agora podemos importá-lo e usar props React em vez de nomes de classe. Isso torna nosso código mais fácil de ler e reutilizar.
import Button from './Button'; <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Usando nomes de classe para componentes interativos
Um Button é um caso de uso muito simples. Que tal algo mais complicado? Bem, você pode levar isso adiante para criar componentes interativos.
Por exemplo, vamos ver uma lista suspensa que é feita usando o Tailwind.
Uma lista suspensa interativa criada usando Tailwind e alternância de nome de classe.
Para este exemplo, criamos o componente HTML usando nomes de classe CSS Tailwind, mas expomos um componente React que se parece com isso:
<Dropdown options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} onOptionSelect={(option) => { console.log("Selected Option", option)} } />
Observando o código acima, você notará que não temos nenhuma classe Tailwind. Eles estão todos ocultos dentro do código de implementação de <Dropdown/>
. O usuário deste componente Dropdown
só precisa fornecer uma lista de options
e um manipulador de cliques, onOptionSelect
quando uma option
é clicada.
Vamos ver como esse componente pode ser construído usando o Tailwind.
Removendo parte do código não relacionado, aqui está o cerne da lógica. Você pode ver este Codepen para um exemplo completo.
import classNames from 'classnames'; function Dropdown({ options, onOptionSelect }) { // Keep track of whether the dropdown is open or not. const [isActive, setActive] = useState(false); const buttonClasses = `inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-blue-500 active:text-gray-200 transition ease-in-out duration-150`; return ( // Toggle the dropdown if the button is clicked <button onClick={() => setActive(!isActive)} className={buttonClasses}> Options </button> // Use the classnames module to toggle the Tailwind .block and .hidden classes <div class={classNames("origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg", { block: isActive, hidden: !isActive })}> // List items are rendered here. {options.map((option) => <div key={option} onClick={(e) => onOptionSelect(option)}>{option}</div>)} </div> ) } export default Dropdown;
A lista suspensa se torna interativa mostrando ou ocultando-a seletivamente usando as classes .hidden
e .block
. Sempre que o <button>
é pressionado, acionamos o manipulador onClick
que alterna o estado isActive
. Se o botão estiver ativo ( isActive === true
), definimos a classe do block
. Caso contrário, definimos a classe hidden
. Ambas são classes Tailwind para alternar o comportamento de exibição.

Em resumo, o módulo classnames é uma maneira simples e eficaz de controlar programaticamente nomes de classe para o Tailwind. Torna mais fácil separar a lógica em props React, o que facilita a reutilização de seus componentes. Funciona para componentes simples e interativos.
Método 2: Usando constantes para definir um sistema de design
Outra maneira de usar o Tailwind e o React juntos é usando constantes e adereços de mapeamento para uma constante específica. Isso é eficaz para sistemas de design de edifícios. Vamos demonstrar com um exemplo.
Comece com um arquivo theme.js
onde você lista seu sistema de design.
// theme.js (you can call it whatever you want) export const ButtonType = { primary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", secondary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", basic: "bg-white hover:bg-gray-700 text-gray-700 font-bold rounded", delete: "bg-red-300 hover:bg-red-500 text-white font-bold rounded" }; export const ButtonSize = { sm: "py-2 px-4 text-xs", lg: "py-3 px-6 text-lg" }
Neste caso, temos dois conjuntos de constantes:
-
ButtonType
define como os botões são estilizados em nosso aplicativo. -
ButtonSizes
define os tamanhos dos botões em nosso aplicativo.
Agora, vamos escrever nosso componente <Button>
:
import {ButtonType, ButtonSize} from './theme'; function Button({size, type, children}) { // This can be improved. I'm keeping it simple here by joining two strings. const classNames = ButtonType[type] + " " + ButtonSize[size]; return ( <button className={classNames}>{children}</button> ) } export default Button;
Usamos as constantes ButtonType
e ButtonSize
para criar uma lista de nomes de classes. Isso torna a interface do nosso <Button>
muito mais agradável. Ele nos permite usar props de size
e type
em vez de colocar tudo em uma string de nome de classe.
// Cleaner and well defined props. <Button size="xs" type="primary">Enable</Button>
Contra a abordagem anterior:
// Exposing class names <button className="py-2 px-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Enable</button>
Se você precisar redefinir a aparência dos botões em seu aplicativo, basta editar o arquivo theme.js
e todos os botões em seu aplicativo serão atualizados automaticamente. Isso pode ser mais fácil do que pesquisar nomes de classes em vários componentes.
Método 3: Compondo Utilitários com @apply
Uma terceira maneira de melhorar a legibilidade de seus componentes React é usar CSS e o padrão @apply
disponível no PostCSS para extrair classes repetidas. Esse padrão envolve o uso de folhas de estilo e pós-processadores.
Vamos demonstrar como isso funciona através de um exemplo. Suponha que você tenha um grupo de botões que tenha um botão primário e um secundário.

<button className="py-2 px-4 mr-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Update Now</button> <button className="py-2 px-4 text-xs mr-4 hover:bg-gray-100 text-gray-700 border-gray-300 border font-bold rounded">Later</button>
Usando o padrão @apply
, você pode escrever este HTML como:
<button className="btn btn-primary btn-xs">Update Now</button> <button className="btn btn-secondary btn-xs">Later</button>
Que pode então ser adotado para React para se tornar:
import classnames from "classnames"; function Button ({size, type, children}) { const bSize = "btn-" + size; const bType = "btn-" + type; return ( <button className={classnames("btn", bSize, bType)}>{children}</button> ) } Button.propTypes = { size: PropTypes.oneOf(['xs, xl']), type: PropTypes.oneOf(['primary', 'secondary']) }; // Using the Button component. <Button type="primary" size="xs">Update Now</Button> <Button type="secondary" size="xs">Later</Button>
Veja como você criaria esses nomes de classe no estilo BEM, como .btn
, .btn-primary
e outros. Comece criando um arquivo button.css
:
/\* button.css \*/ @tailwind base; @tailwind components; .btn { @apply py-2 px-4 mr-4 font-bold rounded; } .btn-primary { @apply bg-blue-500 hover:bg-blue-700 text-white; } .btn-secondary { @apply hover:bg-gray-700 text-gray-700 border-gray-300 border; } .btn-xs { @apply text-xs; } .btn-xl { @apply text-xl; } @tailwind utilities;
O código acima não é CSS real , mas será compilado pelo PostCSS. Há um repositório GitHub disponível aqui que mostra como configurar PostCSS e Tailwind para um projeto JavaScript.
Há também um pequeno vídeo que demonstra como configurá-lo aqui.
Desvantagens de usar @apply
O conceito de extrair classes de utilitário Tailwind em classes CSS de nível superior parece fazer sentido, mas tem algumas desvantagens que você deve estar ciente. Vamos destacá-los com outro exemplo.
Primeiro, ao extrair esses nomes de classes, perdemos algumas informações. Por exemplo, precisamos estar cientes de que .btn-primary
deve ser adicionado a um componente que já tenha .btn
aplicado a ele. Além disso, .btn-primary
e .btn-secondary
não podem ser aplicados juntos. Esta informação não é evidente apenas olhando para as classes.
Se esse componente fosse algo mais complicado, você também precisaria entender a relação pai-filho entre as classes. De certa forma, esse é o problema que o Tailwind foi projetado para resolver e, usando @apply
, estamos trazendo os problemas de volta, de uma maneira diferente.
Aqui está um vídeo em que Adam Wathan - o criador do Tailwind - mergulha nos prós e contras de usar @apply
.
Resumo
Neste artigo, examinamos três maneiras de integrar o Tailwind em um aplicativo React para criar componentes reutilizáveis. Esses métodos ajudam você a construir componentes React que possuem uma interface mais limpa usando props
.
- Use o módulo classnames para alternar as classes programaticamente.
- Defina um arquivo de constantes onde você define uma lista de classes por estado de componente.
- Use
@apply
para extrair classes CSS de nível superior.
Se você tiver alguma dúvida, envie-me uma mensagem no Twitter em @tilomitra.
Leitura recomendada no SmashingMag:
- Configurando Tailwind CSS em um projeto React
- Criando tabelas classificáveis com React
- Um guia para CSS DevTools novo e experimental no Firefox
- Faça seus próprios painéis de conteúdo de expansão e contratação