DEV Community

Rafael Domingues
Rafael Domingues

Posted on

Creating Floating Label/Placeholder for Input with ReactJS

- What is a float label?

These days I was working in a project that the inputs had a different behavior, while empty they had a normal placeholder but when filled, the placeholder moved above the typed text, something that looks very simple but that would make a difference in the page layout.

Example:

After some research I decided to share my learning and help you put that finishing touch on your projects.

- How to do?

Creating a new project

First of all I will create a new project in React with yarn:

$ yarn create react-app float-input

After cleaning up the code that the command did for us, I created a div that contains an input and a label like this:

<div id="float-label">
  <input type="email" />

  <label htmlFor="email">
    E-mail
  </label>
</div>

Now let's stylize with css, feel free to style it the way you prefer, in my case I did the following:

#float-label {
  display: flex;
  flex-direction: column;
  min-width: 350px;
}

#float-label input {
  width: 100%;
  height: 56px;
  padding: 14px 16px 0 10px;
  outline: 0;
  border: 1px solid #ddd;
  border-radius: 4px;
  background: #fff;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 16px;
}

#float-label label {
  font-size: 16px;
  font-family: Arial, Helvetica, sans-serif;
  padding: 0 12px;
  color: #999;
  pointer-events: none;
}

Our input now looks like this:

Doing animation with CSS

Now we start with the "magic", which consists of a transition and the behavior of position absolute, which has the axes referring to the parent element, if it is position relative.

First we add position relative to the css of our div:

#float-label {
  ...

  position: relative;
}

Now we add position absolute to our label and a transform to center our label, as if it were a placeholder for our input:

#float-label label {
  ...

  position: absolute;
  transform: translate(0, 26px) scale(1);
}

We have the following result:

Now we are going to do the animation with a transition and use the focus-within to apply the translate effect to change the position and scale in ourlabel:

#float-label label {
  ...

  transform-origin: top left;
  transition: all 0.2s ease-out;
}

#float-label:focus-within label {
  transform: translate(0, 12px) scale(0.75);
}

We now have the following result but still with a small problem that we are going to fix:

Doings changes in ReactJS

First we add a className to our label, which will depend directly on a variable that we will create in the state of our React component through the useState hook:

const [isActive, setIsActive] = useState(false);

return(
...
  <label className={ isActive ? "Active" : ""} htmlFor="email" >
  E-mail
  </label>
...
);

To change our state variable we will create a function that handles what was typed in our input:

const [value, setValue] = useState('');

function handleTextChange(text) {
  setValue(text);

  if (text !== '') {
    setIsActive(true);
  } else {
    setIsActive(false);
  }
}

return(
...
<input
  type="email"
  value={value}
  onChange={(e) => handleTextChange(e.target.value)}
/>
);

The function is called whenever we type something in the input and is responsible for changing our value and checking if the text in the input is actually a word.

Finally, we stylized our Active class in our css with the same code used before to make our label change position:

#float-label .Active {
  transform: translate(0, 12px) scale(0.75);
}

And our animation is ready! Tell me below if you liked and if you managed to do it :)

Remembering that if you know a better way to do this same functionality, feel free to share too!

Based on: https://velhobit.com.br/design/como-fazer-efeito-float-label-animado-com-css3-puro.html

Discussion (7)

Collapse
andrewandopen profile image
andrew-andopen • Edited on

Really useful, thank you! I've replicated everything from above and have run in to a bit of trouble when I have multiple inputs fields.

Typing in to one field sets the isActive state to true, as a result adding the label styling to all inputs, not just the single input I have populated. Any suggestions on how to work around this?

Thanks

Collapse
rafacdomin profile image
Rafael Domingues Author

Separate this input into a component and use that component instead so that each input will have a different isActive state, or manually create a different property isActive for each input you will use

Collapse
luaneaquino profile image
Luane

Thank you, great example and helped me to understand how to do floating label in React

Collapse
idiglove profile image
Faith Morante

You're a great help!! Cheers

Collapse
ljmerza profile image
Leonardo Merza

one small issue with this. transform translate is based on the label width so the label will be offset differently based on the label text length

Collapse
ltsaiete profile image
Luis Saiete

Thank you. I was actually trying to make the transform effect manipulating the input effect directly from JS.

This worked perfectly for me, thank you!!!!

Collapse
josemiguelo profile image
José Miguel Ochoa

Thanks for the example. Great stuff!