Polotno Docs
Demos

QR Codes

Generate and edit QR codes using SVG elements and custom data

QR code shapes are not a built-in feature of Polotno. But you can easily render them via svg elements. You can also use the custom property of an element to save additional information, such as QR code content for future reference.

You can use qrcode (or another similar library) to generate the content of a QR code.

The tutorial below helps you create an initial version that you can later modify to fit your needs.

Step 1. Create a side section to generate and edit QR codes

In a separate file add this code:

import React from 'react';
import { observer } from 'mobx-react-lite';
import { SectionTab } from 'polotno/side-panel';
import QRCode from 'qrcode';
import * as svg from 'polotno/utils/svg';
import ImQrcode from '@meronex/icons/im/ImQrcode';
import { Button, InputGroup } from '@blueprintjs/core';

// create svg image for QR code for input text
export async function getQR(text: string) {
  return new Promise<string>((resolve) => {
    QRCode.toString(
      text || 'no-data',
      {
        type: 'svg',
        color: {
          dark: '#00F', // Blue dots
          light: '#0000', // Transparent background
        },
      },
      (err, string) => {
        resolve(svg.svgToURL(string));
      }
    );
  });
}

// define the new custom section
export const QrSection = {
  name: 'qr',
  Tab: (props) => (
    <SectionTab name="Qr" {...props}>
      <ImQrcode />
    </SectionTab>
  ),
  // we need observer to update component automatically on any store changes
  Panel: observer(({ store }) => {
    const [val, setVal] = React.useState('');

    const el = store.selectedElements[0];
    const isQR = el?.name === 'qr';

    // if selection is changed we need to update input value
    React.useEffect(() => {
      if (el?.custom?.value) {
        setVal(el?.custom.value);
      }
    }, [isQR, el]);

    // update image src when we change input data
    React.useEffect(() => {
      if (isQR) {
        getQR(val).then((src) => {
          el.set({
            src,
            custom: {
              value: val,
            },
          });
        });
      }
    }, [el, val, isQR]);

    return (
      <div>
        {isQR && <p>Update select QR code:</p>}
        {!isQR && <p>Create new QR code:</p>}
        <InputGroup
          onChange={(e: any) => {
            setVal(e.target.value);
          }}
          placeholder="Type qr code content"
          value={val}
          style={{ width: '100%' }}
        />
        <div
          style={{
            display: 'flex',
            justifyContent: 'space-between',
            paddingTop: '20px',
          }}
        >
          <Button
            style={{
              display: isQR ? '' : 'none',
            }}
            onClick={() => {
              store.selectElements([]);
              setVal('');
            }}
          >
            Cancel
          </Button>
          <Button
            style={{
              display: isQR ? 'none' : '',
            }}
            onClick={async () => {
              const src = await getQR(val);

              store.activePage.addElement({
                type: 'svg',
                name: 'qr',
                x: 50,
                y: 50,
                width: 200,
                height: 200,
                src,
                custom: {
                  value: val,
                },
              });
            }}
          >
            Add new QR code
          </Button>
        </div>
      </div>
    );
  }),
};

Step 2. Add the created section into the Polotno side panel

import { SidePanel, DEFAULT_SECTIONS } from 'polotno/side-panel';
import { QrSection } from './qr-section'; // this file is created on step 1

// add a new custom section
const sections = [QrSection, ...DEFAULT_SECTIONS];

// then in render of side panel:
<SidePanel store={store} sections={sections} defaultSection="qr" />

Live demo