Paulund

How To Create A Toggle VueJS Component

In this tutorial we're going to build a form component for a checkbox but styled as a toggle.

The functionality for this toggle will be just like a checkbox but will move the toggle when the checkbox is checked. We will also need to style this differently depending on the state of the checkbox.

<form-toggle v-model="checked" id="checkbox" label="Toggle" />
<template>
    <div class="my-2">
        <div class="toggle">
            <input type="checkbox" :id="id" :name="id" value="1" v-on:change="updateValue($event.target.value)" :checked="isChecked" />
            <label :for="id"></label>
        </div>
        <p class="inline-block">{{ label }}</p>
    </div>
</template>

The important element of the above code is the checkbox element.

<input type="checkbox" :id="id" :name="id" value="1" v-on:change="updateValue($event.target.value)" :checked="isChecked" />

It has a number of attributes that will apply the logic to the component so it's important we talk through what these do.

Type is the normal HTML attributes that will define this as a checkbox.

:id is a way of us to use VueJS variable as the value for a HTML attribute therefore :id="id" will output the value of this.id inside on a id attribute. :name is used the same way as id we just display the id variable inside the attribute.

The value of the checkbox is 1.

Then we use v-on:change which will run a method on the change event of the checkbox. In this example we'll call the updateValue method and pass in the current value of the checkbox.

:checked will decide if the checkbox is in a checked state or not.

<script>
  export default {
    props: ['id', 'label', 'value'],

    methods: {
      updateValue: function () {
        let value = 0

        if(this.value === 0) {
          value = 1
        }

        this.$emit('input', value)
      }
    },

    computed: {
      isChecked() {
        return this.value === 1
      }
    }
  }
</script>

The first data point we need to define is the props for the component, we're going to use ID, label and value.

props: ['id', 'label', 'value'],

The methods we need to create is updateValue which will emit the current value to the parent component.

    methods: {
      updateValue: function () {
        let value = 0

        if(this.value === 0) {
          value = 1
        }

        this.$emit('input', value)
      }
    },

Then we can use a computed method to decide if we show the checkbox as checked or unchecked depending on the current value of the checkbox. This will be used on the checked attribute of the checkbox.

    computed: {
      isChecked() {
        return this.value === 1
      }
    }

The id is to populate the id and name attribute on the checkbox. The value prop will allow us to pass in a default checked/unchecked state.

<style lang="scss">
    .toggle {
        display: inline-block;
        position: relative;
        cursor: pointer;
        height: 2rem;
        width: 4rem;
        background: #3490dc;
        border-radius: 9999px;
        margin-bottom: 1rem;
        input[type=checkbox] {
            visibility: hidden;
        }
        label {
            display: block;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            transition: all .5s ease;
            cursor: pointer;
            position: absolute;
            top: 5px;
            z-index: 1;
            left: 7px;
            background: #ddd;
        }
        input[type=checkbox]:checked + label {
            left: 35px;
            background: #FFF;
        }
    }
</style>

This will create a pill like toggle and changes the colour of the dot from grey to white depending on if the checkbox is checked/unchecked.

Below is the full code you can use as for your VueJS Toggle component.

<template>
    <div class="my-2">
        <div class="toggle">
            <input type="checkbox" :id="id" :name="id" value="1" v-on:change="updateValue($event.target.value)" :checked="isChecked" />
            <label :for="id"></label>
        </div>
        <p class="inline-block">{{ label }}</p>
    </div>
</template>

<style lang="scss">
    .toggle {
        display: inline-block;
        position: relative;
        cursor: pointer;
        height: 2rem;
        width: 4rem;
        background: #3490dc;
        border-radius: 9999px;
        margin-bottom: 1rem;
        input[type=checkbox] {
            visibility: hidden;
        }
        label {
            display: block;
            width: 22px;
            height: 22px;
            border-radius: 50%;
            transition: all .5s ease;
            cursor: pointer;
            position: absolute;
            top: 5px;
            z-index: 1;
            left: 7px;
            background: #ddd;
        }
        input[type=checkbox]:checked + label {
            left: 35px;
            background: #FFF;
        }
    }
</style>

<script>
  export default {
    props: ['id', 'label', 'value'],
    methods: {
      updateValue: function () {
        let value = 0
        if(this.value === 0) {
          value = 1
        }
        this.$emit('input', value)
      }
    },
    computed: {
      isChecked() {
        return this.value === 1
      }
    }
  }
</script>