Back

Generating QR Codes with Vue

Generating QR Codes with Vue

xxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx ABSTRACT TEXT GOES HERE xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

QR codes are 2D barcodes that represent text, originally designed for inventory tracking. Why should you use them in the first place? QR codes have a few shining points:

  • They’re efficient ways to represent text that might otherwise take up much more space.
  • QR codes are more durable than plain text. Unlike plain text, they fully preserve their meaning even when slightly damaged or obscured.

This article will briefly cover the structure of a QR code and show you how to generate them in Vue apps by building a QR code generator. Here’s what you need to follow along:

  • Working knowledge of HTML, CSS, and JavaScript
  • Working knowledge of Vue3 and the Composition API
  • Working knowledge of npm and the command line

Understanding the structure of a QR code

A QR code is a square made of dots where each dot can be one of two colors. A dot in one color represents a bit set to zero, and a dot in the other color represents a bit set to one. You can use any pair of colors you want as long as there’s sufficient contrast between them.

Not all QR codes are the same size, and the size of a QR code is determined by its version, which can range from 1 to 40. A Version 1 code is 21 by 21 pixels, and a Version 40 code is 177 by 177 pixels.

A QR code can also be visually split into several parts, each of which serves a different function:

chart of the structure of a QR code

  • Finder or Position Patterns: These square-shaped patterns are located at three corners of the QR code, and they assist in identifying the code and recognizing the rotation of the code.

  • Alignment Patterns: These small square-shaped patterns found within larger QR codes serve as additional reference points for aligning the scanning device.

  • Timing Patterns: Thin lines on the QR code’s sides form timing patterns. These lines provide the scanning device with timing information to accurately interpret the code’s content.

  • Format Information: A small section within the QR code contains format information, such as the error correction level and mask pattern used. QR codes have four error correction levels:

    • Low (L), which can restore 7% of the code’s data
    • Medium (M), which can restore 15%,
    • Quartile (Q), which can restore 25%,
    • and High (H), which can restore 30% of the data
  • Version Information: In larger QR codes, a designated area holds version information, specifying the size and capacity of the QR code.

  • Data Region: The data region holds the encoded information. Several data types can be encoded with a QR code, including alphanumeric characters, numeric values, or even binary data.

  • Quiet Zone: The quiet zone is a margin of white space surrounding the QR code, providing a buffer between the code and its surroundings. This margin ensures accurate scanning by preventing interference from nearby visual elements.

QR codes are built by taking the attributes of the code (error correction, mask patterns, and version information) and the data of the code, encoding it all to binary, and then filling out the various parts of it as appropriate.

Thankfully, you don’t have to do this by hand. Several Vue.js libraries simplify the process, like vue-qrcode and qrcode.vue.

The library you’ll be using to build the generator is vue-qrcode.

Building the QR code generator

Here’s a sneak peek at the finished generator:

-

Your first step is to build a new Vite project. Open your terminal and run the following command:

npm create vite@latest

Then follow the prompts like this:

prompts

Run the following commands:

 cd qr-generator-demo
 npm install
 npm run dev

If done correctly, your project folder should look like this:

-

Your next step is to delete the boilerplate you won’t need. Delete the src/assets folder, as well as the src/components folder, and replace the contents of App.vue with an empty template tag.

Now, you can start building the UI for the generator. Write all the styling for the project by replacing the contents of the style.css file with this:

body {
  background-color: #f1f3f4;
  margin: 0;
  padding: 0;
}

button {
  all: unset;
}

header {
  background-color: #fff;
  padding: 1rem 2rem;
  font-size: 2rem;
  font-weight: 600;
}

.container {
  display: flex;
  background-color: white;
  width: 1000px;
  box-shadow: 0px 0px 5px #e5e7e8;
  margin: 3rem auto;
  min-height: 350px;
  border-radius: 5px;
  padding: 2rem 0;
}

.left {
  width: 50%;
  border-right: 3px dashed black;
  padding: 0 2rem;
}

.right {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  padding: 0 2rem;
}

.user-input-field {
  display: block;
  border: none;
  font-size: 1.5rem;
  height: 100%;
  width: 100%;
  box-sizing: border-box;
}

.qrcode-box {
  flex-grow: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}

.qrcode {
  display: inline-block;
  font-size: 0;
  margin-bottom: 0;
  position: relative;
}

.qrcode__image {
  background-color: #fff;
  border: 0.25rem solid #fff;
  border-radius: 0.25rem;
  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.25);
  height: 15%;
  left: 50%;
  overflow: hidden;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 15%;
}

.download-button {
  text-transform: capitalize;
  width: fit-content;
  border: 1px solid black;
  border-radius: 5px;
  margin: 1rem 0;
  padding: 0.5rem 1rem;
  cursor: pointer;
  text-align: center;
}

.placeholder {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
  width: 100%;
}

.spinner {
  width: 20px;
  height: 20px;
  border: 3px solid black;
  border-top: 3px solid white;
  border-radius: 50%;
  display: inline-block;
  animation: spin linear 1s infinite;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

Next, you’ll write the markup for the application’s shell. Replace the contents of App.vue with this code:

<script setup>
import { ref } from "vue";

const userInput = ref("");
</script>

<template>
  <header>
    <span>QR Code Generator</span>
  </header>

  <div class="container">
    <div class="left">
      <textarea
        class="user-input-field"
        placeholder="Enter your text here"
        v-model="userInput"
      />
    </div>

    <div class="right">
      <template v-if="userInput && userInput.length > 0">
        <div class="qrcode-box">
          <div
            v-if="userInput && userInput.length > 0"
            class="qrcode"
            ref="templateReference"
          ></div>
        </div>
      </template>

      <div v-else class="placeholder">
        <p>Enter some text on the left to generate a code</p>
      </div>
    </div>
  </div>
</template>

The code above does a few things:

  • declares a ref called userInput
  • Creates a header and a container for the rest of the elements and splits it in two
  • the left side contains the textarea the user will type data into
  • the right side contains the container for the rendered QR code and renders a placeholder if the user hasn’t supplied any input.

If you visit it in your browser, your application should now look like this:

-

And you’re done with the shell of the UI.

Integrating vue-qrcode

Add vue-qrcode to the project by running this command:

npm install qrcode@1 @chenfengyuan/vue-qrcode@2

Before we go on, we need to understand the fundamentals of the library. The Vue-qrcode library offers all its functionality by providing you with a single component called VueQrcode. Vueqrcode receives the data to encode and the width of the QR code as props, then renders a valid QR code to the screen.

Import it at the top of App.vue by adding this line to the top of your <script> tag:

import VueQrcode from '@chenfengyuan/vue-qrcode';

Now, you only need to use the component in your template. Inside your <template> tag, add this code inside the <div> with a class of qrcode:

<vue-qrcode :value="userInput" :options="{ width: 200 }"></vue-qrcode>

The value prop accepts the text that will be encoded, and the options prop receives an object that allows you to specify the width. When you type into the textarea, you should see a QR code generated.

Downloading the QR code

The generator isn’t very useful if people have to take screenshots of the code to use it, so let’s implement a feature that will let users download the generated QR code.

The approach we’ll take is simple: convert the rendered QR code to an image, then download the image to the user’s device. But first, we need a library to handle the HTML-to-image conversion.

We’ll use the html-to-image library for this. Install it by running the following in the terminal:

npm install --save html-to-image

We’ll need a reference to the HTML element we will convert to use the library. Create one by adding a template ref to the <div> with a class of qrcode like so:

<div
  v-if="userInput && userInput.length > 0"
  class="qrcode"
  ref="templateReference"
>
  <vue-qrcode
    :value="userInput"
    :options="{ width: 200 }"
  ></vue-qrcode>
</div>

Next, enter the <script> tag and import the toPng function from html-to-image like this:

import { toPng } from "html-to-image";

Now you can write the function that will download the QR code:

const downloading = ref(false);

const templateReference = ref(null);

const downloadQRCode = () => {
  if (templateReference.value === null || !userInput.value) {
    return;
  }

  downloading.value = true;

  toPng(templateReference.value)
    .then((dataUrl) => {
      const link = document.createElement("a");
      link.download = "my-qrcode.png";
      link.href = dataUrl;
      link.click();
      downloading.value = false;
    })
    .catch((err) => {
      console.log(err, templateReference.value);
      downloading.value = false;
    });
};

This is how the downloadQRcode function works:

  • It checks that the template reference is valid and that the user has provided input
  • It then calls toPng using the reference. toPng returns a promise that contains the dataUrl of the image if it resolves, and an error if it rejects.
  • If the promise resolves, it creates an <a> tag whose href is set to the dataUrl and uses the download attribute of the <a> tag to download the image.

The last piece of the feature is a download button that calls downloadQRcode when clicked. Add this markup after the <div> with a class qrcode-box like this:

<template v-if="userInput && userInput.length > 0">
  <div class="qrcode-box">
    <!-- content unchanged -->
  </div>

  <button @click="downloadQRCode" class="download-button">
    <span v-if="!downloading">download code as png</span>
    <span v-else class="spinner"></span>
  </button>
</template>

And you’ve created a QR code generator! In the browser, your web application should now look like this:

screenshot

Testing the QR code

There are several ways to test your QR codes. Many mobile phones come with inbuilt scanners, but if those aren’t an option for you, you can use a service like ZXing to parse the downloaded code and make sure it returns the correct content.

Customizing the QR code

The VueQrcode component is flexible and allows you to customize the generated code. Modifying the options object allows you to specify aspects of the code like the version, error correction level, and mask pattern. For example, here’s how you would set a version of 25 and an error correction of H for your code:

<vue-qrcode
  :value="userInput"
  :options="{ width: 200, version: 25, errorCorrectionLevel: 'H' }"
></vue-qrcode>

It even allows you to specify custom colors for the code other than black and white, but you have to specify the colors as hex codes. For example, this QR code:

<vue-qrcode
  :value="userInput"
  :options="{
    width: 200,
    color: { dark: '#b00', light: '#ffffff' },
  }"
></vue-qrcode>

It will render like this:

-

You can see a full list of customization in the documentation.

Adding a logo to the QR code

While this isn’t a component feature, you can further customize the code by allowing users to add a logo on top of its center. You can do this any way you please, but in this tutorial, we’ll center the logo image on top of the code with position: absolute.

A note of warning: adding a logo to a QR code reduces the amount of data the error correction can recover because the logo covers up some of the QR code’s data. A scanner will still be able to read the code as long as the amount of data covered up is below the threshold of the error correction level, but it’s best to keep logos small relative to the size of the code.

Let’s modify our generator to allow a user to upload a logo. First, add a checkbox that lets the user decide if they want a logo and an input that lets them upload it. In your <template>, put this code right after the <div> with a class of qrcode-box:

<div>
  <input type="checkbox" id="toggle-logo" v-model="useLogo" />
  <label for="toggle-logo"> Add logo to QR code </label>
</div>

<input v-if="useLogo" type="file" @change="handleLogoUpload" accept="image/*" />

The <input> element calls a handleLogoUpload function that we haven’t defined yet to process the user’s selection of a new file, and the <input> only accepts image files.

Then add an image tag right after the vue-qrcode component like this:

<div
  v-if="userInput && userInput.length > 0"
  class="qrcode"
  ref="templateReference"
>
  <vue-qrcode
    :value="userInput"
    :options="{ width: 200 }"
  ></vue-qrcode>
  <img
    v-if="useLogo && uploadedImgUrl && uploadedImgUrl.length != 0"
    class="qrcode__image"
    :src="uploadedImgUrl"
    alt="business logo"
  />
</div>

The image will only render if useLogo is true and the dataUrl of the uploaded image isn’t valid.

Now, add the javascript that handles the upload in your <script> section:

const useLogo = ref(false);
const uploadedImgs = ref([]);
const uploadedImgUrl = ref("");

const handleLogoUpload = ($event) => {
  const input = $event.target;
  uploadedImgs.value = input.files;

  if (uploadedImgs.value.length == 0) {
    return;
  }

  const reader = new FileReader();

  reader.addEventListener(
    "load",
    () => {
      uploadedImgUrl.value = reader.result;
    },
    false
  );

  reader.readAsDataURL(uploadedImgs.value[0]);
};

The handleLogoUpload function takes the event that triggered it as an argument, checks that a file was actually uploaded, and then uses the FileReader API to convert the uploaded file to a base64 dataUrl that becomes the source of the img tag in the template. And that’s it. If you save and then try it out, you should see a small logo in the middle of your QR code like this:

-

You can find the complete code for the tutorial here.

Conclusion

This article explored creating a QR code generator in Vue.js using the vue-qrcode library. We discussed the utility and anatomy of QR codes and explored the capabilities of vue-qrcode. I hope you enjoyed the article, and happy coding!

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay