Creating a Reusable Button Component in React, Typescript and Tailwind

In this tutorial we are going to build a custom button component using react, typescript and tailwind, so let’s get started:

First let’s setup our react typescript app, create a new app using the command :

$ yarn create react-app nexter-button --template typescript

This will create a project with react and typescript starter app.

Let’s run our App to test :

$ yarn start

Cool our app is up, Let’s install Tailwind head over to the tailwind create-react-app installation docs: https://tailwindcss.com/docs/guides/create-react-app

To Install Tailwind do the following steps below, since we are using create-react-app, setting up tailwind is easy :

// install the following packages for Tailwind
$ npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9


// Then install craco package
$ npm install @craco/craco
 

Once it’s installed, update your scripts in your package.json file to use craco instead of react-scripts for all scripts except eject:

  // package.json
{
    // ...
    "scripts": {
     "start": "craco start",
     "build": "craco build",
     "test": "craco test",
     "eject": "react-scripts eject"
    },
  }

Next, create a craco.config.js at the root of our project and add the tailwindcss and autoprefixer as PostCSS plugins:

// craco.config.js
module.exports = {
  style: {
    postcss: {
      plugins: [
        require('tailwindcss'),
        require('autoprefixer'),
      ],
    },
  },
}

// run the following commad. This will create a file called tailwind.config.js

$ npx tailwindcss-cli@latest init

This will create a minimal tailwind.config.js file at the root of your project :

// tailwind.config.js

module.exports = {
  purge: [],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}

Configure Tailwind to remove unused styles in production

In your tailwind.config.js file, configure the purge option with the paths to all of your components so Tailwind can tree-shake unused styles in production builds:

// tailwind.config.js 
// extented with few colors 

const colors = require('tailwindcss/colors')
module.exports = {
   purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
    darkMode: false, // or 'media' or 'class'
    theme: {
      colors: {
        transparent: 'transparent',
        current: 'currentColor',
        black: colors.black,
        white: colors.white,
        gray: colors.coolGray,
        red: colors.red,
        yellow: colors.amber,
        blue: colors.lightBlue,
        green: colors.green
      },
      extend: {
        colors: {
          'regal-blue': '#243c5a',
          'bleed-blue': '#3B82F6',
        }
      },
    },
    variants: {
      extend: {},
    },
    plugins: [],
  }

Include Tailwind in your CSS

Open the ./src/index.css file that Create React App generates for you by default and use the @tailwind directive to include Tailwind’s base, components, and utilities styles, replacing the original file contents:


/* ./src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

Tailwind will swap these directives out at build-time with all of the styles it generates based on our configured design system.

Finally, let’s import our css in ./src/index.ts file:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Now when you run npm run start, Tailwind CSS will be ready to use in our project.

Creating the button component

okay, so now our react app is setup with typescript and tailwind, we can go-ahead on deciding our props for our Button component:

  • children: Optional property to allow developers to include another ReactNode or text on top of the button.
  • onClick – An event handler that can define the result of a button when clicked.
  • variant – (string type) – button variant can be (default, primary, info, warning, success, danger, dark)
  • size – (string type) size of the button can be (sm, md, lg, xs)
  • disabled – (boolean type) whether the button will be disabled or not

Ok, Let’s create our Button component at src/components/Button.tsx


import React from 'react';

// interface to declare all our prop types
interface Props {
  children: React.ReactNode;
  onClick: () => void;
  variant?: string, // default, primary, info, success, warning, danger, dark
  size?: string, // sm, md, lg
  disabled?: boolean;
}

// button component, consuming props
const Button: React.FC<Props> = ({
  children,
  onClick,
  variant = 'default',
  size = 'md',
  disabled,
  ...rest
}) => {
  return (
    <button
      className={`btn ${variant} ${size}` + (disabled ? ' disabled' : '')}
      onClick={onClick}
      disabled={disabled}
      {...rest}
    >
      {children}
    </button>
  );
};

export default Button;

and the tailwind css can be added in to be used in this component using @layer-components so the classes get populated automatically from the global css. We can’t use @apply or @layer comps inside our component or using buttons.module.css because of postcss not able to recognize the base tailwind imports..

Let’s add our css to render our buttons :


/* ./src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;


@layer components {
  .btn {
   @apply font-bold py-2 px-4 m-2 rounded;
  }
  .sm {
    @apply px-2;
  }

  .md {
    @apply px-4;
  }

  .lg {
    @apply px-8;
  }

  .xl {
    @apply px-16;
  }

  /*button colors: default, primary, info, success, warning, danger, dark */
  .default {
    @apply bg-gray-100 text-gray-800;
  }
  .default:hover {
    @apply bg-gray-200 text-gray-800;
  }

  .primary {
    @apply bg-bleed-blue text-white;
  }
  .primary:hover {
    @apply bg-blue-600 text-white;
  }

  .success {
    @apply bg-green-500 text-white;
  }
  .success:hover {
    @apply bg-green-600 text-white;
  }

  .info {
    @apply bg-blue-500 text-white;
  }
  .info:hover {
    @apply bg-blue-600 text-white;
  }

  .warning {
    @apply bg-yellow-400 text-white;
  }
  .warning:hover {
    @apply bg-yellow-500 text-white;
  }
  
  .danger {
    @apply bg-red-500 text-white;
  }
  .danger:hover {
    @apply bg-red-600 text-white;
  }

  .dark {
    @apply bg-gray-800 text-white;
  }
  .dark:hover {
    @apply bg-gray-900 text-white;
  }

  .disabled {
    @apply opacity-50 cursor-not-allowed;
  }
  .disabled:hover {
    @apply opacity-50 cursor-not-allowed;
  }
}

Cool, Let’s use our Button Component !

In App.tsx add this :

import Button from './components/Button';

function App() {
  return (
    <div> 

      <h1> Size: sm </h1>
      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="default"
      size="sm"
      > Default </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="primary"
      size="sm"
      > Primary </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="info"
      size="sm"
      > Info </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="warning"
      size="sm"
      > Warning </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="success"
      size="sm"
      > Success </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="danger"
      size="sm"
      > Danger </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="dark"
      size="sm"
      > Dark </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="default"
      size="sm"
      disabled={true}
      > Disabled </Button>

      <hr />

      <h1> Size : md </h1>
      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="default"
      size="md"
      > Default </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="primary"
      size="md"
      > Primary </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="info"
      size="md"
      > Info </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="warning"
      size="md"
      > Warning </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="success"
      size="md"
      > Success </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="danger"
      size="md"
      > Danger </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="dark"
      size="md"
      > Dark </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="default"
      size="md"
      disabled={true}
      > Disabled </Button>

      <hr />

      <h1> Size : lg </h1>
      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="default"
      size="lg"
      > Default </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="primary"
      size="lg"
      > Primary </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="info"
      size="lg"
      > Info </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="warning"
      size="lg"
      > Warning </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="success"
      size="lg"
      > Success </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="danger"
      size="lg"
      > Danger </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="dark"
      size="lg"
      > Dark </Button>

      <Button 
      onClick={()=> alert('Button 1 is clicked !')}
      variant="default"
      size="lg"
      disabled={true}
      > Disabled </Button>
    </div>
  );
}

export default App;

Great ! Let’s now check by running our App , So onclick I have added a alert to test our clicks as of now, you can use any function with that..


Posted

in

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *