Max Schmitt

September 4 2021

React: Implementing Material Design's Floating Labels

In this article we will look at how we can implement Material Design's popular floating input labels with React.

Typing inside a text input with a floating label

It's a fun little exercise and it's not actually too difficult.

If you'd like to skip ahead, feel free to check out the CodeSandbox.

Floating Labels Anatomy

To make floating labels work, we need to work with three elements:

  • The <input> element
  • The floating <label>
  • The container <div>

Empty State

When the input is empty, the label will float over the actual text input, exactly in the same position where any user input would appear.

A visualization of the different layers/elements of the component we're looking to build

Filled State

When the text input has a value, we will move the label up and out of the way while also scaling it down a bit.

A visualization of the label being translated upwards and scaled down

Floating Labels React Component

Keeping in mind the two distinct states (filled vs. empty) and our three elements, we can go ahead and implement the React part of our component:

JS

function TextField({ id, label, style, value, onChange, ...rest }) {
const isEmpty = String(value || '').length === 0
const isFilled = !isEmpty
return (
<div
className={['TextField', isEmpty ? 'TextField--isEmpty' : '', isFilled ? 'TextField--isFilled' : ''].join(
' '
)}
style={style}
>
<label className="TextField-label" htmlFor={id}>
{label}
</label>
<input {...rest} className="TextField-input" id={id} value={value} onChange={onChange} />
</div>
)
}
export default TextField

To keep things simple, we're assuming our component is always controlled (not optionally uncontrolled).

Floating Labels CSS

When implementing the CSS for the Material Design floating labels, pay special attention that the label's text is aligned exactly with the input's text.

Especially getting this to work across browsers is a bit tricky, but I've left some tips in the comments below:

CSS

.TextField {
position: relative;
line-height: 0;
}
.TextField-label {
position: absolute;
font: inherit;
transition: transform 200ms;
transform-origin: top left;
color: gray;
pointer-events: none;
line-height: 1;
display: block;
}
.TextField--isFilled .TextField-label {
transform: translate(0, -1em) scale(0.8);
}
.TextField--isEmpty .TextField-label {
transform: translate(0, 0.5em) scale(1);
}
.TextField-input {
/* 💡 Make sure that the input's text styles are the same as the label's: */
font: inherit;
/* 💡 Setting 'box-sizing: content-box;' really helps making
the height of the input predictable and consistent across
browsers: */
box-sizing: content-box;
height: 2em;
line-height: 1;
margin: 0;
padding: 0;
border: none;
border-bottom: 2px solid gray;
outline: none;
width: 100%;
}

Testing Text Alignment of Label and Input

To test the text alignment of the label with the input, it can help to disable the styling for the filled state:

JS

/* .TextField--isEmpty */ .TextField-label {
transform: translate(0, 0.5em) scale(1);
opacity: 0.5;
}
/*
.TextField--isFilled .TextField-label {
transform: translate(0, -1em) scale(0.8);
}
*/
Testing the text alignment of the label and the input in Safari, Chrome and Firefox browsers

Conclusion

That's the basics of implementing Material Design's floating labels with React.

Thank you for reading, I hope this article was helpful to you.

Check out the CodeSandbox for the complete implementation.