qr-code-styling
qr-code-styling copied to clipboard
Importing QRCodeStyling into NextJS immediately fails
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"); }
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.
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,
});
@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
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
});
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'
.
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
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
Wow, it's working! Thank you so much. I wasted a lot of time. You are my hero 💙💛
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?
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
Thanks, @rtorcato
thanks @rtorcato it works
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);
});