DEV Community

renan leonel
renan leonel

Posted on

How to Customize SVGs using Next.js and Tailwind CSS

This article aims to demonstrate how to customize SVG images using Next.js and Tailwind, without the need for external configurations or the use of auxiliary libraries like svgr/webpack.

For this tutorial, we're using the following image: https://www.flaticon.com/free-icon-font/checkbox_3917076?related_id=3917076

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512" height="512">
  <g>
    <path d="M405.333,0H106.667C47.786,0.071,0.071,47.786,0,106.667v298.667C0.071,464.214,47.786,511.93,106.667,512h298.667   C464.214,511.93,511.93,464.214,512,405.333V106.667C511.93,47.786,464.214,0.071,405.333,0z M426.667,172.352L229.248,369.771   c-16.659,16.666-43.674,16.671-60.34,0.012c-0.004-0.004-0.008-0.008-0.012-0.012l-83.563-83.541   c-8.348-8.348-8.348-21.882,0-30.229s21.882-8.348,30.229,0l83.541,83.541l197.44-197.419c8.348-8.318,21.858-8.294,30.176,0.053   C435.038,150.524,435.014,164.034,426.667,172.352z" />
  </g>
</svg>
Enter fullscreen mode Exit fullscreen mode

The default behavior of Next.js with the <Image> tag does not allow changing the fill property of the SVG. This becomes an issue when we need to change the color of an SVG image during mouse hover, for example, using Tailwind CSS classes like fill-white or bg-white. This approach doesn't work because we're not directly changing the fill property of the <svg>, and as of the writing of this article (Next 13.5.4), the <Image/> tag doesn't support changes to the fill of SVGs.

import Image from 'next/image';

import checkbox from '../../public/icons/checkbox.svg';

const Home = () => {
  return (
    <main className='h-screen bg-zinc-800 flex items-center justify-center'>
      <Image
        src={checkbox}
        alt='checkbox'
        className='w-20 h-20 fill-white'
      />
    </main>
  );
}

export default Home;
Enter fullscreen mode Exit fullscreen mode

Image description

To solve this issue, we need to create a component for the SVG, removing style properties from the SVG tag, such as width, height, and primarily fill. These properties will be passed to the SVG through the className property of the component. Here's how to do it:

import { twMerge } from 'tailwind-merge';

interface CheckboxProps {
  className?: string;
}

const Checkbox = ({ className }: CheckboxProps) => {
  return (
    <svg
      xmlns='http://www.w3.org/2000/svg'
      version='1.1'
      id='Capa_1'
      x='0px'
      y='0px'
      viewBox='0 0 512 512'
      width='512'
      height='512'
      className={twMerge('w-20 h-20', className)}
    >
      <g>
        <path d='M405.333,0H106.667C47.786,0.071,0.071,47.786,0,106.667v298.667C0.071,464.214,47.786,511.93,106.667,512h298.667   C464.214,511.93,511.93,464.214,512,405.333V106.667C511.93,47.786,464.214,0.071,405.333,0z M426.667,172.352L229.248,369.771   c-16.659,16.666-43.674,16.671-60.34,0.012c-0.004-0.004-0.008-0.008-0.012-0.012l-83.563-83.541   c-8.348-8.348-8.348-21.882,0-30.229s21.882-8.348,30.229,0l83.541,83.541l197.44-197.419c8.348-8.318,21.858-8.294,30.176,0.053   C435.038,150.524,435.014,164.034,426.667,172.352z' />
      </g>
    </svg>
  );
};

export default Checkbox;
Enter fullscreen mode Exit fullscreen mode

Finally, our page.tsx file will look like this:

import Image from 'next/image';
import Checkbox from '@/components/svg/Checkbox';
import checkbox from '../../public/icons/checkbox.svg';

const Home = () => {
  return (
    <main className='h-screen bg-zinc-800 flex items-center justify-center gap-6'>
      <Image
        src={checkbox}
        alt='checkbox'
        className='w-20 h-20 fill-white'
      />
      <Checkbox className='fill-white hover:fill-red-600' />
    </main>
  );
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

Image description

Now, we can customize our SVG according to external application states and user actions, such as hover. It's a simple solution, but not very intuitive, as Next.js' <Image/> tag doesn't handle this kind of scenario effectively.

Top comments (0)