Table of Contents
Have you ever wanted to create pure React form? Have you ever wished to use React with Material design? Imagine creating something functional and also beautiful! I have a great news for you … This is your lucky day! Today, in this tutorial, we will take React and Material design and create beautiful form. I hope this tutorial will help you learn how to create simple UI such as form. I also hope it will give you a better understanding of React. Let’s begin.
[sociallocker]Download PDF version[/sociallocker]
Live demo on Codepen.
Required knowledge
Let’s start this React form tutorial by briefly addressing the prerequisites. First, you should have at least basic knowledge of React. We discussed the basics of React in the previous tutorial on To-do app with React. Next great place for learning the fundamentals of React is React website. This website contains a lot of information with many examples to get you started. Another prerequisite for this tutorial is a good knowledge of JavaScript.
Does this mean that you can’t finish this React form tutorial without this knowledge? Well, no. You can follow this React form tutorial without knowing anything about React or JS. However, there is one problem. You may not know why we did certain things the way we did. It is similar to reading a book in foreign language. You can get through it without a problem. Still, you will not be any smarter when you finish it. Therefore, I would suggest learning the fundamentals first.
Prerequisites
Anyway, this decision is up to you. There are a few more things you will need to work on this React form tutorial. You will need to use the React library. This library contains two parts. The first part is React itself. The second part is React DOM. You can either use hosted versions of libraries on CDN or download it on your computer. Aside from React we will use three other external resources. The first external resource will be font Roboto hosted on Google Fonts. We will use only one weight, which will be 400 (regular).
The second resource is Normalize stylesheet. This will help us unite appearance of form elements across different browsers. You can also use Reset stylesheet if you want. However, I prefer Normalize stylesheet because I don’t believe that removing all styling from every element is necessary. The last resource is jQuery. We will use this JavaScript library only for submitting our React form with AJAX. This is all for external resources. Well, not exactly. I also used autoprefixer plugin to take care about vendor prefixes.
Next, you will need some text editor to write the code. You can use just a plain text editor like a notepad. Popular and free solution for Windows is Notepad++ and Atom. On Mac it is TextMate. My favorite text editor is Sublime Text 3. It is also a good practice is to use Git for version control. It is great for keeping backups of your code. There are a number of options you can choose from such as Github, Bitbucket and Gitlab.
HTML
When it comes to HTML, the code will be very simple. We will only need one div.
We will use this div
as a container for our form. The rest of our code for React form will be in JavaScript. React will basically take our JavaScript code and render it into the div
container. For this React form tutorial, I decided to not to use any HTML or CSS framework. Therefore, we will have complete control over the code and styling. This will reduce the majority of possibilities that your version of code will look different from mine, at least I hope.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" description="width=device-width, initial-scale=1" />
<title>Insanely Easy React Form in Material Design</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.2.0/normalize.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet" />
</head>
<body>
<div class="react-form-container"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</body>
</html>
Sass
When it comes to styling, I have to mention one thing. In this React form tutorial, we will use Sass. To be more specific, we will use the newer syntax (SCSS). If you are not familiar with Sass, don’t worry. I will include the whole code also in CSS in the end. In the first part of styling we will set a couple of variables for colors and radius. We will use three colors. The first one will be for background-color
of the body
. This color will be very light grey (#f5f5f5). The second color will be darker grey (#eee). We will use this color for input
borders.
The third and last color will be amber, or mostly pure orange (#ffab00). We will use this color for form button
and input
borders on focus
state. The last variable will be for border-radius
I previously mentioned. This variable will be equal to 3 pixels. Next, I created one function for converting pixels to rem units. The last Sass-related thing is mixin for transition.
// Variables
$color-grey: #eee;
$color-grey-light: #f5f5f5;
$color-primary: #ffab00;
$radius: 3px;
// Function
@function remy($value, $base: 16px) {
@return ($value / $base) * 1rem;
}
Styling the html and body
After we created the setup for this React form tutorial, it’s time to tackle the styling. First, let’ take care about html
and body
. In the terms of html
, this means setting font-size
to 16 pixels
and box-sizing
to border-box
. In the terms of body
, we will set also font-size
now to 100%
and background-color
to $color-grey-light
variable. Let’s also not forget on setting box-sizing
to inherit
for all elements using universal selector (*). This basically means that elements will inherit it from html
.
html {
box-sizing: border-box;
font-size: 16px;
}
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
font: 100% 'Roboto', arial, sans-serif;
background: $color-grey-light;
}
Making the React form pretty
Next element ready for styling is also the most important piece of this React form tutorial – the form
. First, let’s center the form and add some white space above it by using margin-left
, margin-right
and margin-top
properties. Set margin-right
and margin-left
to auto
and margin-top
to approximately 2 rems
. We should also add some inner white space by using padding
and setting it to 32 pixels
. The background-color
of form will be #fff
(white).
Did you know that people usually don’t like sharp edges or shapes? Seriously. We prefer oval shapes over the sharp ones. Let’s use $radius
variable and apply use it for border-radius
property. Lastly, let’s add some depth by using box-shadow
.
form {
padding: remy(32px);
margin-top: 2rem;
margin-right: auto;
margin-left: auto;
max-width: remy(380px);
background-color: #fff;
border-radius: $radius;
box-shadow: 0 15px 35px rgba(50,50,93,.1),0 5px 15px rgba(0,0,0,.07);
}
Styles for miscellaneous content of the React form
Now it is time to style the elements inside the React form. The first element in the line is heading. We will use h1
element. Styling of this element will require four lines of code. Set the margin-top
to 0
, margin-bottom
to approximately 3.236rem
, text-align
to center
and font-size
to 1.618rem
.
h1 {
margin-top: 0;
margin-bottom: 3.236rem;
text-align: center;
font-size: 1.618rem;
}
After heading, we have to take care about fieldsets
I used to group form inputs
and labels
. Normalize stylesheet creates some padding
and border
for this element. Let’s get rid of these styles by setting both of them to 0
. One thing, I used .form-group
for this element. We should also use margin-top
to create some space between two consecutive fieldsets
. I think that setting it to 1rem
will be sufficient.
.form-group {
padding: 0;
border: 0;
& + & {
margin-top: 1rem;
}
}
Taking care of labels
Next on the list are labels
. For this element, the styles will be following. We will set display
to inline-block
, margin-bottom
to .5rem
, font-size
to .75rem
, text-transform
to uppercase
and touch-action
to manipulation
. The last one will cause browser to consider touches that begin on the label
only for the purposes of scrolling and continuous zooming.
label {
display: inline-block;
margin-bottom: .5rem;
font-size: .75rem;
text-transform: uppercase;
touch-action: manipulation;
}
Styling form inputs and textarea
After labels
, we have to deal with inputs
and textarea.
For both these elements we will set display
to block
, padding
to .5rem .75rem
, width
to 100%
, font-size
to 1rem
, line-height
to 1.25
, color
to #55595c
, background-color
to #fff
, background-image
to none
, background-clip
to padding-box
, border-top
to 0
, border-right
to 0
, border-bottom
to 1px solid $color-grey
, border-left
to 0
and border-radius
to $radius
. For focus
state, we will remove the outline
by setting it to 0
. We will also change the border-bottom-color
by setting it to $color-primary
and use our transition
mixin. Lastly, we will allow only vertical resizing of the textarea
element.
input,
textarea {
display: block;
padding: .5rem .75rem;
width: 100%;
font-size: 1rem;
line-height: 1.25;
color: #55595c;
background-color: #fff;
background-image: none;
background-clip: padding-box;
border-top: 0;
border-right: 0;
border-bottom: 1px solid $color-grey;
border-left: 0;
border-radius: $radius;
@include transition;
&:focus {
outline: 0;
border-bottom-color: $color-primary;
}
}
textarea {
resize: vertical;
}
The last piece of this React form is the button. Styles for button will be a little bit more complex, so we should get right into it. Let’s begin by setting display
to inline-block
, padding
to .75rem 1rem
and margin-top
to 1.618rem
. Next, let’s take care about typography settings. Set the font-weight
to 400
, text-align
to center
, text-transform
to uppercase
, color
to #fff
, vertical-align
to middle
and white-space
to nowrap
. After that, let’s continue with visuals. Set the background-color
to $color-primary
, border
to 1px solid transparent
and box-shadow
to 0 15px 35px rgba(50,50,93,.1),0 5px 15px rgba(0,0,0,.07)
Next, set cursor
to pointer
and user-select
to none
. Finally, let’s add the last pieces of the puzzle, or button – hover
and focus
state. In case of both, hover
and focus
, set background-color
to lighten($color-primary, 13%)
and box-shadow
to 0 18px 35px rgba(50,50,93,.1),0 8px 15px rgba(0,0,0,.07)
. In case of only focus
state, remove the outline
by setting it to 0
.
.btn {
display: inline-block;
padding: .75rem 1rem;
margin-top: 1.618rem;
font-weight: 400;
text-align: center;
text-transform: uppercase;
color: #fff;
vertical-align: middle;
white-space: nowrap;
background-color: $color-primary;
border: 1px solid transparent;
box-shadow: 0 15px 35px rgba(50,50,93,.1),0 5px 15px rgba(0,0,0,.07);
cursor: pointer;
user-select: none;
@include transition;
&:focus,
&:hover {
background-color: lighten($color-primary, 13%);
box-shadow: 0 18px 35px rgba(50,50,93,.1),0 8px 15px rgba(0,0,0,.07);
}
&:focus {
outline: 0;
}
}
The whole Sass puzzle put together
Now, when we tackled all the individual parts of our React form, it is time to put them together. First, I will share with you the whole code in Sass.
Whole Sass code:
// Variables
$color-grey: #eee;
$color-grey-light: #f5f5f5;
$color-primary: #ffab00;
$radius: 3px;
// Function
@function remy($value, $base: 16px) {
@return ($value / $base) * 1rem;
}
// Mixins
@mixin transition($prop: all, $duration: .25s, $timing: cubic-bezier(.4,0,1,1)) {
transition: $prop $duration $timing;
}
html {
box-sizing: border-box;
font-size: 16px;
}
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
font: 100% 'Roboto', arial, sans-serif;
background: $color-grey-light;
}
form {
padding: remy(32px);
margin-top: 2rem;
margin-right: auto;
margin-left: auto;
max-width: remy(380px);
background-color: #fff;
border-radius: $radius;
box-shadow: 0 15px 35px rgba(50,50,93,.1),0 5px 15px rgba(0,0,0,.07);
}
h1 {
margin-top: 0;
margin-bottom: 3.236rem;
text-align: center;
font-size: 1.618rem;
}
.form-group {
padding: 0;
border: 0;
& + & {
margin-top: 1rem;
}
}
label {
display: inline-block;
margin-bottom: .5rem;
font-size: .75rem;
text-transform: uppercase;
touch-action: manipulation;
}
input,
textarea {
display: block;
padding: .5rem .75rem;
width: 100%;
font-size: 1rem;
line-height: 1.25;
color: #55595c;
background-color: #fff;
background-image: none;
background-clip: padding-box;
border-top: 0;
border-right: 0;
border-bottom: 1px solid $color-grey;
border-left: 0;
border-radius: $radius;
@include transition;
&:focus {
outline: 0;
border-bottom-color: $color-primary;
}
}
textarea {
resize: vertical;
}
.btn {
display: inline-block;
padding: .75rem 1rem;
margin-top: 1.618rem;
font-weight: 400;
text-align: center;
text-transform: uppercase;
color: #fff;
vertical-align: middle;
white-space: nowrap;
background-color: $color-primary;
border: 1px solid transparent;
box-shadow: 0 15px 35px rgba(50,50,93,.1),0 5px 15px rgba(0,0,0,.07);
cursor: pointer;
user-select: none;
@include transition;
&:focus,
&:hover {
background-color: lighten($color-primary, 13%);
box-shadow: 0 18px 35px rgba(50,50,93,.1),0 8px 15px rgba(0,0,0,.07);
}
&:focus {
outline: 0;
}
}
Sass puzzle converted to CSS
As I promised, here it is. This is the previous code in nice and pure CSS. Now it is up to you to decide whatever version do you want to use.
Whole CSS code:
html {
box-sizing: border-box;
font-size: 16px;
}
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
font: 100% 'Roboto', arial, sans-serif;
background: #f5f5f5;
}
form {
padding: 2rem;
margin-top: 2rem;
margin-right: auto;
margin-left: auto;
max-width: 23.75rem;
background-color: #fff;
border-radius: 3px;
box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);
}
h1 {
margin-top: 0;
margin-bottom: 3.236rem;
text-align: center;
font-size: 1.618rem;
}
.form-group {
padding: 0;
border: 0;
}
.form-group + .form-group {
margin-top: 1rem;
}
label {
display: inline-block;
margin-bottom: .5rem;
font-size: .75rem;
text-transform: uppercase;
-ms-touch-action: manipulation;
touch-action: manipulation;
}
input,
textarea {
display: block;
padding: .5rem .75rem;
width: 100%;
font-size: 1rem;
line-height: 1.25;
color: #55595c;
background-color: #fff;
background-image: none;
background-clip: padding-box;
border-top: 0;
border-right: 0;
border-bottom: 1px solid #eee;
border-left: 0;
border-radius: 3px;
-webkit-transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
}
input:focus,
textarea:focus {
outline: 0;
border-bottom-color: #ffab00;
}
textarea {
resize: vertical;
}
.btn {
display: inline-block;
padding: .75rem 1rem;
margin-top: 1.618rem;
font-weight: 400;
text-align: center;
text-transform: uppercase;
color: #fff;
vertical-align: middle;
white-space: nowrap;
background-color: #ffab00;
border: 1px solid transparent;
box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
transition: all 0.25s cubic-bezier(0.4, 0, 1, 1);
}
.btn:focus, .btn:hover {
background-color: #ffc142;
box-shadow: 0 18px 35px rgba(50, 50, 93, 0.1), 0 8px 15px rgba(0, 0, 0, 0.07);
}
.btn:focus {
outline: 0;
}
React
Okay, we put together the HTML and Sass, or CSS. Now, we should take care about the last and probable toughest part of this React form tutorial. Let’s now put together the React code! Before we begin, there is one thing you have to know. I wrote this React form tutorial in new JavaScript syntax – ES6. This also means that you will probably use some compiler such as babel. Otherwise, it is possible that it may not work on older browsers.
Let’ start with storing the form container div
inside a variable. In the end, we will use this variable to render our React form. Since we are not going to change this variable in this tutorial, we can save it as constant – use const
.
const reactFormContainer = document.querySelector('.react-form-container')
Labels as the first component for our React form
Now, let’s create the first component for our React form. This component will be for label
elements. We will create new ReactFormLabel
class that will extend React.Component
. In this class, instead of providing a separate getInitialState
method, we will use constructor
. Here, we will call super(props)
. Two things here. First, you don’t have to call super()
for every React component you create. Calling super()
is necessary only if you need to have a constructor
. Therefore, if you use constructor
, you have to call super()
.
The second thing is if you have to call super()
or super(props)
. Here you have the answer. Call super(props)
when you want to access this.props
object inside the constructor
. React automatically set it for you if you want to access it anywhere else. This is also why we will not call super(props)
in this React form tutorial.
The second part of our ReactFormLabel
component is render
method that comes right after constructor
. This method will return the html
code for label
element. We will need to set htmlFor
attribute (for
attribute in React) to {this.props.htmlFor}
. Anything we use as value for htmlFor
attribute on the component will be rendered as value for for
attribute. We will also set the text of the label
to {this.props.title}
. Then, value of title
attribute will be rendered as the text of the label.
class ReactFormLabel extends React.Component {
constructor() {
super()
}
render() {
return(
<label htmlFor={this.props.htmlFor}>{this.props.title}</label>
)
}
}
Component for our form
The second and last component we will create in this React form tutorial will be the form. First, we will create ReactForm
class to extend React.Component
. Second, we will again create constructor
and call super()
inside it. When we are inside the constructor
, we will also define default state. We will create this.state
with a number of items in it. These items will be used for storing the values
from input
elements. For keys
, I used name, email, subject and message keywords. For values, just empty strings.
class ReactForm extends React.Component {
constructor() {
super()
this.state = {
name: '',
email: '',
subject: '',
message: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
}
Handling changes
Now, we have the basic code for React form component. Let’s add first method that will help us handle changes. These changes will occur when user will input anything into input
elements. This method, let’s call it handleChange
, will take these data and save it inside the state
object we created inside constructor
. In order to make this method re-usable, we will use name
attribute of target input
to find the right key
inside the state
object. Then, we will assign new value
of the input
to this key
. This final step is done by line with this.setState(newState)
.
handleChange = (e) => {
let newState = {}
newState[e.target.name] = e.target.value
this.setState(newState)
}
Handling submit
The second method we will use is for submitting our React form called handleSubmit
. This method contains few parts. In the first one, we will prevent the form from submitting by calling e.preventDefault()
. Next, we will take data from state
we created inside constructor
and store them as an object inside new variable called formData
. After this, I used very simple if
statement to check that all input
elements contain some data. If not, we will terminate handleSubmit
method.
Next, we will use a bit of AJAX to take the data inside formData
variable and send them somewhere. This place is defined as value of url
. The type of data (dataType
) we want to send are json
. Type of request (type
) will be POST
. Finally, the data
will be previously mention content of the formData
variable. After this, we will create method for success
and error
. These methods will contain the code for both cases, if form submitting fails and if it succeeds.
Lastly, we will reset the state
of our React form component (ReactForm
). We will do this by using this.setState()
and setting values
for all keys
to empty strings.
handleSubmit = (e, message) => {
e.preventDefault()
let formData = {
formSender: this.state.name,
formEmail: this.state.email,
formSubject: this.state.subject,
formMessage: this.state.message
}
if (formData.formSender.length < 1 || formData.formEmail.length < 1 || formData.formSubject.length < 1 || formData.formMessage.length < 1) {
return false
}
$.ajax({
url: '/some/url',
dataType: 'json',
type: 'POST',
data: formData,
success: function(data) {
if (confirm('Thank you for your message. Can I erase the form?')) {
document.querySelector('.form-input').val('')
}
},
error: function(xhr, status, err) {
console.error(status, err.toString())
alert('There was some problem with sending your message.')
}
})
this.setState({
firstName: '',
lastName: '',
email: '',
subject: '',
message: ''
})
}
It’s time for rendering
The last missing piece of our React form component (ReactForm
) is render method. This method will return the HTML code for our form. We will use the ReactFormLabel
component with plain HTML to get this done. Let’s make this shorter. All inputs will contain className
, id
, type
, name
, required
, onChange
and value
attributes. Value
attribute will be set to {this.state.name}
and onChange to {this.handleChange}
. Label
components will have htmlFor
attribute. The last two elements will be textarea
and button
.
render() {
return(
<form className='react-form' onSubmit={this.handleSubmit}>
<h1>Say Hi!</h1>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formName' title='Full Name:' />
<input id='formName' className='form-input' name='name' type='text' required onChange={this.handleChange} value={this.state.name} />
</fieldset>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formEmail' title='Email:' />
<input id='formEmail' className='form-input' name='email' type='email' required onChange={this.handleChange} value={this.state.email} />
</fieldset>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formSubject' title='Subject:'/>
<input id='formSubject' className='form-input' name='subject' type='text' required onChange={this.handleChange} value={this.state.subject} />
</fieldset>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formMessage' title='Message:' />
<textarea id='formMessage' className='form-textarea' name='message' required onChange={this.handleChange}></textarea>
</fieldset>
<div className='form-group'>
<input id='formButton' className='btn' type='submit' placeholder='Send message' />
</div>
</form>
)
}
The last piece of this tutorial for our React form will be using ReactDOM
and calling render()
with two parameters. First parameter stands for what we want to render. This will be the <ReactForm />
component. The second parameter stands for the container where our React form should be rendered. This will be reactFormContainer
.
ReactDOM.render(<ReactForm />, reactFormContainer)
Putting it all together
This is all we need to render our React form in HTML. Let me share with you all the JavaScript code in one piece.
Whole JavaScript code:
const reactFormContainer = document.querySelector('.react-form-container')
class ReactFormLabel extends React.Component {
constructor(props) {
super(props)
}
render() {
return(
<label htmlFor={this.props.htmlFor}>{this.props.title}</label>
)
}
}
class ReactForm extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
email: '',
subject: '',
message: ''
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange = (e) => {
let newState = {}
newState[e.target.name] = e.target.value
this.setState(newState)
}
handleSubmit = (e, message) => {
e.preventDefault()
let formData = {
formSender: this.state.name,
formEmail: this.state.email,
formSubject: this.state.subject,
formMessage: this.state.message
}
if (formData.formSender.length < 1 || formData.formEmail.length < 1 || formData.formSubject.length < 1 || formData.formMessage.length < 1) {
return false
}
$.ajax({
url: '/some/url',
dataType: 'json',
type: 'POST',
data: formData,
success: function(data) {
if (confirm('Thank you for your message. Can I erase the form?')) {
this.setState({
firstName: '',
lastName: '',
email: '',
subject: '',
message: ''
})
}
},
error: function(xhr, status, err) {
console.error(status, err.toString())
alert('There was some problem with sending your message.')
}
})
this.setState({
firstName: '',
lastName: '',
email: '',
subject: '',
message: ''
})
}
render() {
return(
<form className='react-form' onSubmit={this.handleSubmit}>
<h1>Say Hi!</h1>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formName' title='Full Name:' />
<input id='formName' className='form-input' name='name' type='text' required onChange={this.handleChange} value={this.state.name} />
</fieldset>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formEmail' title='Email:' />
<input id='formEmail' className='form-input' name='email' type='email' required onChange={this.handleChange} value={this.state.email} />
</fieldset>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formSubject' title='Subject:'/>
<input id='formSubject' className='form-input' name='subject' type='text' required onChange={this.handleChange} value={this.state.subject} />
</fieldset>
<fieldset className='form-group'>
<ReactFormLabel htmlFor='formMessage' title='Message:' />
<textarea id='formMessage' className='form-textarea' name='message' required onChange={this.handleChange}></textarea>
</fieldset>
<div className='form-group'>
<input id='formButton' className='btn' type='submit' placeholder='Send message' />
</div>
</form>
)
}
}
ReactDOM.render(<ReactForm />, reactFormContainer)
Closing thoughts on react form tutorial
Congrats! You just finished this React form tutorial. For some of you this may be the second tutorial using React library. If so, I hope it helped you get a better understanding of React. If not, I hope you still enjoyed it. Tutorials such as this one are a little bit hard to summarize. So, I will skip that. Instead, I will only ask you for one thing. If you liked this tutorial, please share it.
If you liked this article, please subscribe so you don't miss any future post.
If you'd like to support me and this blog, you can become a patron, or you can buy me a coffee 🙂