Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Importing QRCodeStyling into NextJS immediately fails #38

Open
Ali762 opened this issue Feb 12, 2021 · 13 comments · May be fixed by #64
Open

Importing QRCodeStyling into NextJS immediately fails #38

Ali762 opened this issue Feb 12, 2021 · 13 comments · May be fixed by #64

Comments

@Ali762
Copy link

Ali762 commented Feb 12, 2021

Using version 1.3.4

Simply adding
import QRCodeStyling from "qr-code-styling";

causes the package to immediately fail with server error:
ReferenceError: self is not defined

The library does not check to verify it's running client side.

Workaround for now is to replace the import statement with
if (typeof window !== "undefined") { console.log("i am client"); const QRCodeStyling = require("qr-code-styling"); }

@Jmg-21
Copy link

Jmg-21 commented Apr 1, 2021

same issue encountered! @Ali762 can you share how to use the code inside the component after that import
am getting "self is not defined at Object. (/.../qr-code-styling/lib/qr-code-styling.js:1:208)"

@Ali762
Copy link
Author

Ali762 commented Apr 1, 2021

I got it working like this. I'm not sure why I used qrCode update - you might be able to use your data directly in the options.

export function QR( mydata) {
  let qrCode;
  if (typeof window !== "undefined") {           //Only do this on the client
    const QRCodeStyling = require("qr-code-styling");
    qrCode = new QRCodeStyling({
      width: 600,
      height: 600,
      data: "yourdata",
     ...etc

then

 qrCode.update({
    data: mydata,
  });

@Jmg-21
Copy link

Jmg-21 commented Apr 2, 2021

@Ali762 thank you! it works with no delay. another note is when you use it with ssr:false it will have delay creating 1 and if more than 1 lets say 100 qr at the same time will only create 2 qr's. but with this way. it solve the issue

@jromero
Copy link

jromero commented Jun 6, 2021

FWIW, I ran into this issue "self is not defined" on Node JS because this library has number of dependencies on the browser.

I eventually got it to work by emulating various components. Here's what worked at this point in time:

// emulate browser dependencies
const Blob = require("cross-blob");
const Canvas = require("canvas");
const { JSDOM } = require("jsdom");
global.Blob = Blob;
global.window = new JSDOM().window;
global.self = global.window;
global.document = global.window.document;
global.Image = Canvas.Image;
global.XMLSerializer = global.window.XMLSerializer;

// swallow not implemented location changes (https://github.com/jsdom/jsdom/issues/2112#issuecomment-673540137)
const listeners = window._virtualConsole.listeners('jsdomError');
const originalListener = listeners && listeners[0];
window._virtualConsole.removeAllListeners('jsdomError');
window._virtualConsole.addListener('jsdomError', error => {
  if (
    error.type !== 'not implemented' &&
    error.message !== 'Not implemented: navigation (except hash changes)' &&
    originalListener
  ) {
    originalListener(error);
  }
  // swallow error
});

@jasonbarry
Copy link

Should be solved by webpack's output.globalObject config setting:

To make UMD build available on both browsers and Node.js, set output.globalObject option to 'this'.

@jasonbarry jasonbarry linked a pull request Jul 7, 2021 that will close this issue
@SamSamskies
Copy link

I just ran into this issue and I solved it by using dynamic imports with the ssr config option set to false. https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr

@rtorcato
Copy link

rtorcato commented Apr 1, 2022

I created a react hook to use QRCodeStyling in NextJS. The following code should help it also uses typescript.

import React, { useEffect, useRef, useState } from 'react'

import DashboardLayout from '@app/layouts/DashboardLayout'
import { IPage } from '@app/ts/nextJS.types'
import PageContainer from '@app/components/containers/PageContainer'
import QRCodeStyling, { Options as QRCodeStylingOptions, FileExtension } from 'qr-code-styling'
import CardContainer from '@app/components/containers/CardContainer'
import Button from '@app/components/buttons/Button'

const styles = {
  inputWrapper: {
    margin: '20px 0',
    display: 'flex',
    justifyContent: 'space-between',
    width: '100%',
  },
  inputBox: {
    flexGrow: 1,
    marginRight: 20,
  },
}

const qrOptions: QRCodeStylingOptions = {
  width: 300,
  height: 300,
  image: 'https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg',
  dotsOptions: {
    color: '#4267b2',
    type: 'rounded',
  },
  imageOptions: {
    crossOrigin: 'anonymous',
    margin: 20,
  },
}

const useQRCodeStyling = (options: QRCodeStylingOptions): QRCodeStyling | null => {
  //Only do this on the client
  if (typeof window !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const QRCodeStylingLib = require('qr-code-styling')
    const qrCodeStyling: QRCodeStyling = new QRCodeStylingLib(options)
    return qrCodeStyling
  }
  return null
}

const QrCodePage: IPage = () => {
  const [url, setUrl] = useState('https://cardstore.matrixdigital.com')
  const [fileExt, setFileExt] = useState<FileExtension | undefined>('png')
  const qrCode = useQRCodeStyling(qrOptions)
  const ref = useRef<any>(null)

  useEffect(() => {
    qrCode?.append(ref.current)
  }, [ref, qrCode])

  useEffect(() => {
    qrCode?.update({ data: url })
  }, [url, qrCode])

  const onUrlChange: React.ChangeEventHandler<HTMLInputElement> | undefined = (event) => {
    event.preventDefault()
    setUrl(event.target.value)
  }

  const onExtensionChange: React.ChangeEventHandler<HTMLSelectElement> | undefined = (event) => {
    setFileExt(event.target.value as FileExtension)
  }

  const onDownloadClick = () => {
    qrCode?.download({ extension: fileExt })
  }
  return (
    <PageContainer>
      <CardContainer>
        <div className="m-3">
          <h1 className="text-lg text-medium">QR Codes</h1>
          <div className="my-5">
            <div style={styles.inputWrapper}>
              <input value={url} onChange={onUrlChange} style={styles.inputBox} />
              <select onChange={onExtensionChange} value={fileExt}>
                <option value="png">PNG</option>
                <option value="jpeg">JPEG</option>
                <option value="webp">WEBP</option>
              </select>
              <Button theme="primary" onClick={onDownloadClick}>
                Download
              </Button>
            </div>
            <div ref={ref} />
          </div>
        </div>
      </CardContainer>
    </PageContainer>
  )
}
QrCodePage.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout>

export default QrCodePage

@zhen1asemen1uk
Copy link

Wow, it's working! Thank you so much. I wasted a lot of time. You are my hero 💙💛

@multikitty
Copy link

hello, @rtorcato . it's working for me. Thanks.
But I have an issue now.
When I build my next app, the following error shows me.

const QRCodeStylingLib = require('qr-code-styling');

Error: Unexpected require(). global-require

How to fix it?

@rtorcato
Copy link

hello, @rtorcato . it's working for me. Thanks. But I have an issue now. When I build my next app, the following error shows me.

const QRCodeStylingLib = require('qr-code-styling');

Error: Unexpected require(). global-require

How to fix it?

this should solve it. It should just be eslint error

https://stackoverflow.com/a/37487154/1213956

@multikitty
Copy link

Thanks, @rtorcato

@minikdev
Copy link

minikdev commented Dec 7, 2023

thanks @rtorcato it works

@madhusudanbabar
Copy link

madhusudanbabar commented Jun 2, 2024

I created a react hook to use QRCodeStyling in NextJS. The following code should help it also uses typescript.

import React, { useEffect, useRef, useState } from 'react'

import DashboardLayout from '@app/layouts/DashboardLayout'
import { IPage } from '@app/ts/nextJS.types'
import PageContainer from '@app/components/containers/PageContainer'
import QRCodeStyling, { Options as QRCodeStylingOptions, FileExtension } from 'qr-code-styling'
import CardContainer from '@app/components/containers/CardContainer'
import Button from '@app/components/buttons/Button'

const styles = {
  inputWrapper: {
    margin: '20px 0',
    display: 'flex',
    justifyContent: 'space-between',
    width: '100%',
  },
  inputBox: {
    flexGrow: 1,
    marginRight: 20,
  },
}

const qrOptions: QRCodeStylingOptions = {
  width: 300,
  height: 300,
  image: 'https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg',
  dotsOptions: {
    color: '#4267b2',
    type: 'rounded',
  },
  imageOptions: {
    crossOrigin: 'anonymous',
    margin: 20,
  },
}

const useQRCodeStyling = (options: QRCodeStylingOptions): QRCodeStyling | null => {
  //Only do this on the client
  if (typeof window !== 'undefined') {
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    const QRCodeStylingLib = require('qr-code-styling')
    const qrCodeStyling: QRCodeStyling = new QRCodeStylingLib(options)
    return qrCodeStyling
  }
  return null
}

const QrCodePage: IPage = () => {
  const [url, setUrl] = useState('https://cardstore.matrixdigital.com')
  const [fileExt, setFileExt] = useState<FileExtension | undefined>('png')
  const qrCode = useQRCodeStyling(qrOptions)
  const ref = useRef<any>(null)

  useEffect(() => {
    qrCode?.append(ref.current)
  }, [ref, qrCode])

  useEffect(() => {
    qrCode?.update({ data: url })
  }, [url, qrCode])

  const onUrlChange: React.ChangeEventHandler<HTMLInputElement> | undefined = (event) => {
    event.preventDefault()
    setUrl(event.target.value)
  }

  const onExtensionChange: React.ChangeEventHandler<HTMLSelectElement> | undefined = (event) => {
    setFileExt(event.target.value as FileExtension)
  }

  const onDownloadClick = () => {
    qrCode?.download({ extension: fileExt })
  }
  return (
    <PageContainer>
      <CardContainer>
        <div className="m-3">
          <h1 className="text-lg text-medium">QR Codes</h1>
          <div className="my-5">
            <div style={styles.inputWrapper}>
              <input value={url} onChange={onUrlChange} style={styles.inputBox} />
              <select onChange={onExtensionChange} value={fileExt}>
                <option value="png">PNG</option>
                <option value="jpeg">JPEG</option>
                <option value="webp">WEBP</option>
              </select>
              <Button theme="primary" onClick={onDownloadClick}>
                Download
              </Button>
            </div>
            <div ref={ref} />
          </div>
        </div>
      </CardContainer>
    </PageContainer>
  )
}
QrCodePage.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout>

export default QrCodePage

Hey, Thanks for this, I'm having similar issue in Nuxt 3, I took reference from your dynamic import thing and it worked well.

Here's the snippet for Nuxt 3

let qrCode;

onMounted(async () => {
  if (!window) return;
  const qrCodeLib = (await import("qr-code-styling")).default;
  qrCode = new qrCodeLib(options);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants