How to Create a Custom Checkbox Form in Vue
You have a problem. The browser’s default checkbox form is ugly and outdated, but importing an entire library just to change the styling seems like overkill. Instead, let’s build a custom checkbox form component from scratch. It will take 15 minutes to type up, or just 3 if you just copy and paste my boilerplate code.
Before we dive into how it works, you can see a live demo here, or you can see how we are using it in production on the signup flow of boot.dev’s coding courses platform.
The HTML
<template>
<div>
<div class="checkbox-form">
<div class="answers">
<label v-for="(option, i) of options" :key="i" class="item">
<span :for="option">{{ option }}</span>
<input
:id="option"
v-model="checked"
type="checkbox"
:value="option"
@change="onChange"
/>
<span class="checkmark" />
</label>
</div>
</div>
</div>
</template>
As you can see, the important thing here is that we are creating an input and span element for each option provided in the component’s props. We’ll styling these and add functionality in the next steps.
The JavaScript
export default {
props: {
options: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
checked: [],
};
},
methods: {
onChange() {
this.$emit("input", this.checked);
},
},
};
We only require one prop: options, which represent all of the options available to the user. As boxes are checked, we add them to the checked state variable, and each time that variable changes we emit it so that the parent can stay reactive.
The CSS
.checkbox-form {
display: flex;
flex-direction: column;
align-items: center;
}
.checkbox-form .answers {
display: flex;
flex-direction: column;
align-items: left;
width: 100%;
}
.checkbox-form label {
margin-left: 1em;
}
.checkbox-form .item {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 1em;
height: 25px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
display: flex;
align-items: center;
}
.checkbox-form .item input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.checkbox-form .checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
background-color: #c2c2c2;
}
.checkbox-form .item:hover input ~ .checkmark {
background-color: #949494;
}
.checkbox-form .item input:checked ~ .checkmark {
background-color: #d8a22e;
}
.checkbox-form .checkmark:after {
content: "";
position: absolute;
display: none;
}
.checkbox-form .item input:checked ~ .checkmark:after {
display: block;
}
.checkbox-form .item .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
Nothing too crazy is happening here, but the main takeaway is that we’re hiding the default input completely and styling our span element to be the actual checkmark.
Putting it All Together
<template>
<div>
<div class="checkbox-form">
<div class="answers">
<label v-for="(option, i) of options" :key="i" class="item">
<span :for="option">{{ option }}</span>
<input
:id="option"
v-model="checked"
type="checkbox"
:value="option"
@change="onChange"
/>
<span class="checkmark" />
</label>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
options: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
checked: [],
};
},
methods: {
onChange() {
this.$emit("input", this.checked);
},
},
};
</script>
<style scoped>
.checkbox-form {
display: flex;
flex-direction: column;
align-items: center;
}
.checkbox-form .answers {
display: flex;
flex-direction: column;
align-items: left;
width: 100%;
}
.checkbox-form label {
margin-left: 1em;
}
.checkbox-form .item {
display: block;
position: relative;
padding-left: 35px;
margin-bottom: 12px;
cursor: pointer;
font-size: 1em;
height: 25px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
display: flex;
align-items: center;
}
.checkbox-form .item input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.checkbox-form .checkmark {
position: absolute;
top: 0;
left: 0;
height: 25px;
width: 25px;
background-color: #c2c2c2;
}
.checkbox-form .item:hover input ~ .checkmark {
background-color: #949494;
}
.checkbox-form .item input:checked ~ .checkmark {
background-color: #d8a22e;
}
.checkbox-form .checkmark:after {
content: "";
position: absolute;
display: none;
}
.checkbox-form .item input:checked ~ .checkmark:after {
display: block;
}
.checkbox-form .item .checkmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
</style>
Related Articles
How to Make a Custom Slider Component in Vue
Nov 24, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
Creating a custom slider component can be tricky, especially if you want to create a lean standalone Vue component. In this quick article, you’ll learn how to build a fully customizable slider component in Vue. Feel free to swap out the majority of the CSS to get the styling you want, but I’ll give you a good jumping-off point.
How to Make a Simple Vue Custom Select Component
Sep 25, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
Creating a custom select tag with its own styling is notoriously difficult. Sometimes it’s impossible to build from scratch without a combination of styled divs and custom JavaScript. In this article, you’ll learn how to create a custom select component in Vue that can be easily styled with your own CSS. In fact, it’s the same component we use in production on boot.dev, and you can see it in action on our JavaScript playground.
Creating a Custom Tooltip Component in Vue
Aug 28, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
There are plenty of libraries out there that will have you up and running with a good tooltip solution in minutes. However, if you are like me, you are sick and tired of giant dependency trees that have the distinct possibility of breaking at any time. For that reason, we’re going to build a custom single file tooltip component that you can build yourself and tweak to your heart’s content. It might take 15 minutes instead of 3… sorry about that.
How to Create a Custom Toggle Switch Component in Vue.js
Jul 21, 2020 by Lane Wagner - Boot.dev co-founder and backend engineer
Custom toggle switches are a pain to code from scratch. So many lines for such a simple UI widget! In this quick tutorial, we will learn how to build a fully encapsulated toggle switch component in Vue.js. The component we’re building is used currently on boot.dev’s login page. Go take a look to see a live demo.