Simple QRCode Generator Using SPFx

In this article. I have explained how to develop QRCode Generator in SharePoint using SPFx

What is QR Code?

  • It is a machine-scannable image that can instantly be read using a Smartphone camera
  • QR code consists of a number of black squares and dots which represent certain pieces of information. 

Webpart Features

  • Generate QRCode based on information like Plain Text and URL.
  • Download the generated QRCode to your local computer.
  • If needed you can store the generated QRCode information to SharePoint list.

Create a new project folder project name "QRCodeGeneratorApp" from the command window

Provide the solution name "QR Code Generator” and choose the client side to component to create as “Webpart”

Provide the webpart name and choose “React” as a project template

After successful creation of SharePoint project install necessary NPM packages

Open the terminal enter "npm install --save qrcode” “npm install @pnp/sp”

Open the “QRCodeGenerator.tsx” from src/webparts/components Include the below imports for QRCode & PNP JS to connect SharePoint REST API

Create SharePoint custom list to store the QRCode Information

Next open Visual studio code IDE open the file “QRCodeGenerator.tsx”

Create the Interface to pass the parameters for store the QRCode data into SharePoint lists

Create a constant variable to store the List Name and initialize the SP using spfi

export interface IQRCodeApp {
  Title: string,
  QRInformation: string,
}
const ListName = "QRCodeApp"
const sp = spfi().using(SPBrowser({ baseUrl: "<!—Sharepoint site URL" }));

Declare the necessary State Variables

//Declare all the necessary State variables
//Store the Base64 URL from canvas element
const [baseURL, SetbaseURL] = useState(String);
//Store the QRCode data information simple text
const [title, SetTitle] = useState(String);
//Handle disabled event for save/download/generate/clear input controls
const [saveDisabled, setSaveDisabled] = useState(true);
const [downloadDisabled, setDownloadDisabled] = useState(true);
const [generateDisabled, setGenerateDisabled] = useState(true);
const [clearDisabled, setClearDisabled] = useState(true);
//Manage success / Error Message
const [successMessage, setSuccessMessage] = useState(false);
const [errorMessage, setErrorMessage] = useState(false);

Create a function “GenerateQRCode” to generate QRCode based on the input,

//Generate the QR Code
const GenerateQRCode = () => {
    QRCode.toCanvas(document.getElementById('myCanvas'), title).then(res => {
        let canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
        let dataURL = canvas.toDataURL();
        SetbaseURL(dataURL);
        setSaveDisabled(false);
        setDownloadDisabled(false);
        setGenerateDisabled(true);
        console.log(res);
    }).catch(err => {
        console.log(err);
    })
}

Create a function “Download” to download the generate QRCode information locally on “Download” onclick event

//Handles download generated QRCode
const Download = () => {
    let canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
    const dataURL = canvas.toDataURL();
    let a = document.createElement("a"); //Create <a>
    a.href = "" + dataURL; //Image Base64 Goes here
    a.download = "QRCode.png"; //File name Here
    a.click();
    setDownloadDisabled(true);
}

Create a function for handling success/error message

/* Success Messsage Handler */
  const SuccessMessage = () => (
    <MessageBar
      messageBarType={MessageBarType.success}
      isMultiline={false}
    >
      QRCode Saved Successfully !..
    </MessageBar>
  );
/* Error Messsage Handler */
  const ErrorMessage = () => (
    <MessageBar
      messageBarType={MessageBarType.error}
      isMultiline={false}
    >
      Oops Something went wrong !..
    </MessageBar>
);

Create a function to clear the input & canvas element using Onclick event on “Clear” button

 //Clear all the inputs
 const clearValues = () => {
     let titleValue: string = '';
     SetTitle(titleValue);
     setGenerateDisabled(false);
     setClearDisabled(true);
     setDownloadDisabled(true);
     setSaveDisabled(true);
     clearCanvas();
 }
 //Clear the canvas element
 const clearCanvas = () => {
     let canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
     let context = canvas.getContext('2d');
     context.clearRect(0, 0, canvas.width, canvas.height);
     var w = canvas.width;
     canvas.width = 1;
     canvas.width = w;
 }

Create a function to store the QRCode information into SharePoint list on “Save” onclick event

const SaveItem = async () => {
    if (!_.isEmpty(title)) {
        try {
            let params: IQRCodeApp = {
                Title: title,
                QRInformation: baseURL
            }
            console.log(params);
            const saveItems: any = await sp.web.lists.getByTitle(ListName).items.add(params);
            if (saveItems) {
                setSuccessMessage(true);
                setSaveDisabled(true);
            } else {
                setErrorMessage(true);
            }
        } catch (error) {
            setErrorMessage(true);
        }
    } else {
        ErrorMessage()
        setErrorMessage(true);
    }
}

Use a Stack control to render the input element like below.

Below style variable to manage the width of the stack and handles padding of stack element

/* Styles For Stack Controls */
  const stackItemStyles: IStackItemStyles = {
    root: {
      width: 600
    },
  };

  const innerStackTokens: IStackTokens = {
    childrenGap: 5,
    padding: 10,
  };
  return (
    <Stack>
      <Stack horizontal tokens={innerStackTokens}>
        <Stack.Item styles={stackItemStyles}>
          <TextField multiline rows={3} value={title} onChange={onChangeInput} />
        </Stack.Item>
      </Stack>
      <Stack horizontal tokens={innerStackTokens}>
        <Stack.Item >
          <PrimaryButton text="Generate" disabled={generateDisabled} onClick={GenerateQRCode} />
        </Stack.Item>
        <Stack.Item>
          <PrimaryButton text="Clear" disabled={clearDisabled} onClick={clearValues} />
        </Stack.Item>
      </Stack>
      <Stack horizontal >
        <Stack.Item> <canvas id="myCanvas"></canvas> </Stack.Item>
      </Stack>
      <Stack horizontal >
        <StackItem><PrimaryButton text="Download" disabled={downloadDisabled} onClick={Download} />  <PrimaryButton text="Save" disabled={saveDisabled} onClick={SaveItem} /></StackItem>
      </Stack>

      <Stack horizontal tokens={innerStackTokens}>
        {(successMessage) && <SuccessMessage></SuccessMessage>}
        {(errorMessage) && <ErrorMessage></ErrorMessage>}
      </Stack>
  </Stack>
)

Full Source Code of "QRCodeGenerator.tsx"

import React, { useState, useEffect, FC } from 'react';
import styles from './QrCodeGenerator.module.scss';
import { IQrCodeGeneratorProps } from './IQrCodeGeneratorProps';
import { escape, isElement } from '@microsoft/sp-lodash-subset';
import { IStackItemStyles, IStackTokens, PrimaryButton, Stack, StackItem, TextField, Dialog, MessageBar, MessageBarButton, MessageBarType, Link } from 'office-ui-fabric-react';

import * as QRCode from 'qrcode';
import { spfi, SPBrowser } from '@pnp/sp';
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

import _ from 'lodash';

export interface IQRCodeApp {
  Title: string,
  QRInformation: string,
}

const ListName = "QRCodeApp"
const sp = spfi().using(SPBrowser({ baseUrl: "https://wh1r2.sharepoint.com/" }));

export const QrCodeGenerator: FC<IQrCodeGeneratorProps> = (props) => {

  //Declare all the necessary State variables
  const [baseURL, SetbaseURL] = useState(String);
  const [title, SetTitle] = useState(String);
  const [saveDisabled, setSaveDisabled] = useState(true);
  const [downloadDisabled, setDownloadDisabled] = useState(true);
  const [generateDisabled, setGenerateDisabled] = useState(true);
  const [clearDisabled, setClearDisabled] = useState(true);
  const [successMessage, setSuccessMessage] = useState(false);
  const [errorMessage, setErrorMessage] = useState(false);
 
  //OnChange handler for Multiline input 
  const onChangeInput = (ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue: string) => {    
    SetTitle(newValue);
    setGenerateDisabled(false);
    setClearDisabled(false);
    if (newValue == '') {
      setGenerateDisabled(true);
      setClearDisabled(true);
    }
  }

//Generate the QR Code
  const GenerateQRCode = () => {
    QRCode.toCanvas(document.getElementById('myCanvas'), title).then(res => {
      let canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
      let dataURL = canvas.toDataURL();
      SetbaseURL(dataURL);
      setSaveDisabled(false);
      setDownloadDisabled(false);
      setGenerateDisabled(true);
      console.log(res);
    }).catch(err => {
      console.log(err);
    })
  }

  //Handles download generated QRCode
  const Download = () => {
    let canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
    const dataURL = canvas.toDataURL();
    let a = document.createElement("a"); //Create <a>
    a.href = "" + dataURL; //Image Base64 Goes here
    a.download = "QRCode.png"; //File name Here
    a.click();
    setDownloadDisabled(true);
    //SetbaseURL(baseURL);
  }

  /* Success Messsage Handler */
  const SuccessMessage = () => (
    <MessageBar
      messageBarType={MessageBarType.success}
      isMultiline={false}
    >
      QRCode Saved Successfully !..
    </MessageBar>
  );

/* Error Messsage Handler */
  const ErrorMessage = () => (
    <MessageBar
      messageBarType={MessageBarType.error}
      isMultiline={false}
    >
      Oops Something went wrong !..
    </MessageBar>
  );

  //Save QRCode Information to SharePoint List
  const SaveItem = async () => {
    if(!_.isEmpty(title)){
      try {
        let params: IQRCodeApp = {
          Title: title,
          QRInformation: baseURL
        }
        console.log(params);
        const saveItems: any = await sp.web.lists.getByTitle(ListName).items.add(params);
        if (saveItems) {
          setSuccessMessage(true);
          setSaveDisabled(true);
        } else {
          setErrorMessage(true);
        }
      } catch (error) {
        setErrorMessage(true);
      }
    } else{
       ErrorMessage()
       setErrorMessage(true);
    }   
  }

  //Clear all the inputs
  const clearValues = () => {
    let titleValue: string = '';
    SetTitle(titleValue);
    setGenerateDisabled(false);
    setClearDisabled(true);
    setDownloadDisabled(true);
    setSaveDisabled(true);
    clearCanvas();
  }

  //Clear the canvas element
  const clearCanvas = () => {
    let canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
    let context = canvas.getContext('2d');
    context.clearRect(0, 0, canvas.width, canvas.height);
    var w = canvas.width;
    canvas.width = 1;
    canvas.width = w;
  }

/* Styles For Stack Controls */
  const stackItemStyles: IStackItemStyles = {
    root: {
      width: 600
    },
  };

  const innerStackTokens: IStackTokens = {
    childrenGap: 5,
    padding: 10,
  };

  return (
    <Stack>
      <Stack horizontal tokens={innerStackTokens}>
        <Stack.Item styles={stackItemStyles}>
          <TextField multiline rows={3} value={title} onChange={onChangeInput} />
        </Stack.Item>
      </Stack>
      <Stack horizontal tokens={innerStackTokens}>
        <Stack.Item >
          <PrimaryButton text="Generate" disabled={generateDisabled} onClick={GenerateQRCode} />
        </Stack.Item>
        <Stack.Item>
          <PrimaryButton text="Clear" disabled={clearDisabled} onClick={clearValues} />
        </Stack.Item>
      </Stack>
      <Stack horizontal >
        <Stack.Item> <canvas id="myCanvas"></canvas> </Stack.Item>
      </Stack>
      <Stack horizontal >
        <StackItem><PrimaryButton text="Download" disabled={downloadDisabled} onClick={Download} />  <PrimaryButton text="Save" disabled={saveDisabled} onClick={SaveItem} /></StackItem>
      </Stack>

      <Stack horizontal tokens={innerStackTokens}>
        {(successMessage) && <SuccessMessage></SuccessMessage>}
        {(errorMessage) && <ErrorMessage></ErrorMessage>}
      </Stack>
    </Stack>
  )

}

export default QrCodeGenerator;

Hit “gulp serve” to run the SPFx component in workbench