Ícones SVG em React/Preact com webpack

Mantendo seus ícones, sem perder a cabeça!

Image for post
Image for post
Diferentes formas e tamanhos, porém, consistentes com sua UI!

Os ícones sempre foram um problema no mundo de front-end. Toda vez que pensamos que o problema está finalmente resolvido, a indústria faz outro balanço. Requisitos e possibilidades variam, então outra solução aparece. Imagem com sprites, fontes, SVG sprites … você o nomeia.

Recentemente eu publiquei alguns artigos em que eu mostrei como importar CSS e qualquer outro recurso como componente React/Preact. Isso me inspirou a explorar o tema ainda mais e encontrar uma forma eficiente e concisa de importar SVGs como componentes.

Image for post
Image for post
Mágica? Nope, apenas webpack!

SVG Inline

No momento, SVG inline é a melhor abordagem para ícones na web. Ele não só resolve o problema com os monitores HiDPI, mas, ao contrário do SVG-como-imagem, ele permite controlar suas propriedades com JavaScript: alterar cores, animar, etc. Para mais detalhes sobre isso, recomendo assistir um vídeo no Front End Center, “Why Inline SVG is Best SVG”.

Carregando SVG com webpack

Com o webpack, é tão fácil quanto importar um arquivo:

import svgSource from './image.svg'

Existem algumas soluções para isso:

Aqui vamos usar o último, devido ao fato de que ele foi construído com a API dangerouslySetInnerHTML em mente, então, o objeto retornado não contém o elemento SVG raiz.

O dilema

Inicialmente, eu simplesmente colocava os SVG importados em um componente e passava o nome do ícone como uma prop.

Essa solução tem uma desvantagem óbvia: o tamanho do seu arquivo final cresce com o número de ícones, independentemente de você usar todos ou não:

import Icon from 'app/UI/_lib/Icon'<Icon type='danger' size='big' color='small' />

Por exemplo, a coleção de SVG do Font Awesome 5 pesa 6.3MB, que claramente não é uma opção.

A solução para isso é importar o arquivo de componente e ícone separadamente e combiná-los manualmente:

import Icon from 'app/UI/_lib/Icon'
import dangerIcon from 'app/UI/_lib/danger.svg'
<Icon source={dangerIcon} size='big' color='small' />

Algumas pessoas iriam parar por aqui. Porém, nós podemos fazer melhor!

Importando SVG como componentes React/Preact

Para alcançar meu objetivo, primeiro criei um loader que converte SVGs em componentes React/Preact:

import DangerIcon from 'app/UI/_lib/danger.svg'<DangerIcon fill='#f00' width='1rem' />

Envolvendo ícones em uma HOC

No mundo real, nosso ícone teria mais propriedades (ex: estilos, tamanho, brilho) e, para manter a UI consistente, muitas vezes limitamos o tamanho ou cor, baseado no design definido.

A solução para isso seria um HOC, porém, estamos de volta a nossa solução inicial:

import Icon from 'app/UI/_lib/Icon'
import DangerIcon from 'app/UI/_lib/danger.svg'
<Icon component={DangerIcon} size='big' color='red' />

Comecei a me perguntar, e se pudermos automatizar esse processo utilizando loaders do webpack?

Depois de um tempo, encontrei um caminho e criei um loader que me permitiu escrever o seguinte:

import DangerIcon from 'app/UI/_lib/danger.svg'<DangerIcon size='big' color='red' />//=> <svg fill='#f00' width='1rem'>...</svg>

Segue a configuração do webpack do meu projeto:

const iconsPath = path.resolve(process.cwd(), 'app/UI/_lib/Icon')...{
test: /\.svg$/,
use: [
{
loader: 'hoc',
options: {
useDefault: true,
path: path.join(iconsPath, 'index.jsx') // 👈 Nossa HOC
}
},
'desvg/react', // 👈 Convertendo SVG para componentes
'svg'
],
include: iconsPath
},
...

E finalmente, para manter nossa UI consistente, nossa HOC:

import React from 'react'
import classNames from 'classnames'
export default function (Svg) {
return function Icon ({
size = 'medium',
color = 'black',
style = 'solid'
}) {
return (
<div
className={classNames(
'Icon',
`is-${size}`,
`is-${color}`,
`is-${style}`
)}
>
<Svg fill='currentColor' />
</div>
)
}
}

Dessa maneira, eu tenho um modo consistente de importar ícones, sem precisar me preocupar com o tamanho do arquivo final, deixando meu código mais simples e organizado.

Para obter mais informações sobre uso e configuração, você pode visitar o repositório do desvg e hoc-loader.

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store