There are a few different ways to approach creating a React dynamic button component. When I say dynamic button, I am not just talking about the color, size or typography, also the responsiveness to breakpoints, and being able to control it from one component. Let’s give this a test ride.

The Idea

The way I approach to creating a reusable component is to list every use case and state for it. Use case and state of the component might be different per device. With a few tweaks, we can make it responsive and easily customizable.

Let’s identify the different types for our button (I usually use Excel for a task like this.)

I want my button component to:

  • Cover all my main colors, such as primary, secondary, accent 1, accent 2, accent 3.
  • Have three sizes: Small, default(medium) and large.
  • Have the feature of being rounded (100% Radius).
  • Have the feature of being squared.
  • Being able to add an icon to either left, right or center of the button with or with label/text.

So these are the requirements for my button component. Let’s visualize and map them all using Google Sheets 🙂

I am have created a simple visual for templating the buttons below.

Let’s start mapping them all using the Google Sheet.

Lolz! If you looked at the Google Sheet, there are 144 possible combinations of button styles with my requirements.

Run Forest run!!! 😀

I want to make it easier for myself, and create few functions to, in a way, automate the process of it. Let’s see what kind of functionalities we will need, so Forest won’t have to run as much as he does. 😛

  • Button Type
  • Button Size
  • Rounded Button?
  • Squared Button?
  • Icon?
  • Position of the Icon

So what I will do next is to create SASS/CSS classes for different states of the button component.

Let’s start by creating our variables for the styling with SASS.

//Variables
$font-family: 'Arial',Helvetica, "sans";
$default: #d1d1d1;
$default-text-color: #555555;
$primary: #3498db;
$primary-text-color: #ffffff;
$secondary: tomato;
$secondary-text-color: #ffffff;
$accent1: #8e44ad;
$accent1-text-color: #ffffff;
$accent2: linear-gradient(135deg, #5763E7 0%, #FF6347 100%);
$accent2-text-color: #ffffff;
$accent3: linear-gradient(135deg, #57E787 0%, #477AFF 100%);
$accent3-hover: linear-gradient(150deg, #57E787 29%, #477AFF 100%);
$accent3-text-color: #ffffff;

Let’s add iconography to our style. I will use Material Icons for the button component.

//Icons

@import url(//fonts.googleapis.com/icon?family=Material+Icons);
.material-icons {
  font-family: 'Material Icons';
  font-weight: normal;
  font-style: normal;
  font-size: 24px;  /* Preferred icon size */
  display: inline-block;
  line-height: 1;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: normal;
  white-space: nowrap;
  direction: ltr;

  /* Support for all WebKit browsers. */
  -webkit-font-smoothing: antialiased;
  /* Support for Safari and Chrome. */
  text-rendering: optimizeLegibility;

  /* Support for Firefox. */
  -moz-osx-font-smoothing: grayscale;

  /* Support for IE. */
  font-feature-settings: 'liga';
}

I have added some basic styling for the body, app id, and row for layout purposes. I like using flex for the layout!

body {
	font-family: $font-family;
	font-size: 16px;
	background: #353C89;
	h1,h2,h3,h4,h5,h6 {
		color: #ffffff;
	}
}
#app {
	display:block;
	width: 100%;
}
.capitalize {
	text-transform: capitalize;
}

//d-block
.d-block {
	display:block;
    -webkit-box-flex: 0;
    -ms-flex: 0 0 100%;
    flex: 0 0 100%;
    max-width: 100%;
}
.d-flex {
	display: flex;
}
.align-items {
	align-items: center;
}
//Row Block
.row {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	button.button {
		margin-right: 14px;
		&:last-child{
			margin-right: 0;
		}
	}
	@media only screen and (max-width: 600px) {
		display: block;
	}
}

Now, we will start adding style for the different states for the button component. We will add classes for colors, sizes, icon, its positions (left, center, right), rounded or squared?

//Button Styles
.button {
  padding: 0 14px;
  border: none;
  font-size: 1em;
  border-radius: 2px;
  transition: all .2s ease-in-out;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  line-height: 1;
  height: 36px;
  span.label {
    font-size: 0.875em;
  }
  i.icon {
    margin-right: 5px;
    font-size: 1.5em;
    line-height: 0;
    &.icon-position-right{
      order:1;
      margin-right: unset;
      margin-left: 5px;
    }
  }
  &:hover {
    cursor: pointer;
    opacity: .9;
    transform: scale(1.075);
  }
  &.button-rounded {
    border-radius: 30px;
  }
  &.squared {
    height: 36px;
    width: 36px;
    padding: 0;
    justify-content: center;
    i.icon {
      margin-right: 0;
    }
  }
  &.button-large {
    padding: 0 20px;
    height: 46px;
    .label {
      font-size: 1.125em;
    }
    .icon {
      font-size: 1.75em;
    }
    &.squared {
      height: 46px;
      width: 46px;
      padding: 0;
      .icon {
        margin-right:unset;
        margin-left:unset;
      }
    }
  }
  &.button-small {
    padding: 0 8px;
    height: 24px;
    .label {
      font-size: .775em;
      line-height: .9;
    }
    .icon {
      font-size: .9em;
    }
    &.squared {
      height: 24px;
      width: 24px;
      padding: 0;
    }
  }
  &.button-default {
    background: $default;
    color: $default-text-color;
  }
  &.button-primary {
    background-color: $primary;
    color: $primary-text-color;
  }
  &.button-secondary {
    background-color: $secondary;
    color: $secondary-text-color;
  }
  &.button-accent1 {
    background-color: $accent1;
    color: $accent1-text-color;
  }
  &.button-accent2 {
    background-image: $accent2;
    color: $accent2-text-color;
  }
  &.button-accent3 {
    background-image: $accent3;
    color: $accent3-text-color;
    &:hover{
      background-image: $accent3-hover;
    }
  }
}

Next, we are going to start building the actual React component portion for the button. I am going to create conditional states for each requirement of the button. Now it is time to make use of the CSS classes I have created for all the needs. I am going to add the conditionals within the class=”” attribute for the button, className=”” for React syntax. It would look something like this:

button${btnType ? ' button-'+btnType : ' button-default'}

So when we call our Button component, we will have the option to pick which type of button we want to display. If we do not select a type, the function will automatically display the default button.

What will we do next is to use this method and add the other requirements. Everything for that functionality should look something like:

function Button (props) {
  let {btnType, btnSize, btnClass, rounded, squared, children, iconName, iconPosition} = props;
  return (
    <button className={`
      //button type, is it primary, secondary, accent1, accent2, accent3. If nothing selected, default it button-default. 
      button${btnType ? ' button-'+btnType : ' button-default'}
      //button size, is it large or small, default would be empty string and system would know it is default/medium size
      ${btnSize ? ' button-'+btnSize : ''}
      //is it rounded?
      ${rounded ? ' button-rounded': ''}
      //is it squared?
      ${squared ? ' squared': ''}
      //additional button class for custom classes
      ${btnClass ? ` ${btnClass}`: ''}`}
      >			
      {
        //Icon? 
        iconName ? 
          //Icon Position?			
          <i className={`icon material-icons${iconPosition?` icon-position-${iconPosition}`:''}`}>
            {iconName}
          </i> : 
        ''
      }
      {
        //Button Label
        children ? <span className="label">{children}</span> : ''
      }
    </button>
  );
}

Let’s reuse the Button component with different types, shapes, and types.

function App (props) {
  return (
    <div>
      <div>
        <h2>Default Button</h2>
        <div className="row">
          <Button btnSize="small" rounded>Small</Button>
          <Button btnSize="small" iconName="add_circle_outline" iconPosition="left" rounded>Small</Button>
          <Button btnSize="small" iconName="add_circle_outline" iconPosition="right" rounded>Small</Button>
          <Button btnSize="small" iconName="add_circle_outline" squared rounded/>
          <Button rounded>Default</Button>
          <Button iconName="add_circle_outline" iconPosition="left" rounded>Default</Button>
          <Button iconName="add_circle_outline" iconPosition="right" rounded>Default</Button>
          <Button iconName="add_circle_outline" squared rounded/>
          <Button btnSize="large" btnClass="button-block-sm" rounded>Large</Button>
          <Button btnSize="large" iconName="add_circle_outline" iconPosition="left" rounded>Large</Button>
          <Button btnSize="large" iconName="add_circle_outline" iconPosition="right" rounded>Large</Button>
          <Button btnSize="large" iconName="add_circle_outline" squared rounded/>
        </div>
      </div>
    </div>
  )
}

const app = (
    <App/>
)

ReactDOM.render(app, document.querySelector('#app'))

I just custom coded the default display of the button component to show you how many custom lines of code it would take to copy paste and edit. :/  pfft. lot’s of copy-paste coding, which I do not like. So let’s create a function that loops through all the possible button combinations and spits it out. The function itself also should be flexible enough for future updates, such as the responsive portion of the component. I will save that portion for Part II of this post. I will link it here when it is done. Stay tuned 😉

Another reason why I love programming is, it enables me to automate the process of the task and make it easier for me to test if needed 🙂 DRY (Don’t Repeat Yourself) method.

See the Pen Dynamic Button Component - React 16 by Timurtek Bizel (@Timurtek) on CodePen.

I couldn’t resist and here is the full list of every reusable button type . 🙂

Things to Consider

There is still room for expanding the functionality here. We can add font-sizes and family variables to the variables section. You do not have to use React to adapt to this concept. Take the logic and the structure of it, then apply it to your project to make your life easier 🙂