How to Build a Barcode and QR Code Scanner in Next.js with SSR
Next.js is a flexible React framework that gives you building blocks to create fast web applications. It can pre-render your React app so that it is SEO-friendly. It supports several types of pre-rendering including server-side rendering or static generation, and updating or creating content at runtime with incremental static regeneration.
In this article, we are going to build a barcode and QR code scanner using Next.js. Dynamsoft Camera Enhancer is used to access the camera in browsers and Dynamsoft Barcode Reader is used to read barcodes from the camera video frames.
Build a Barcode and QR Code Scanner with Next.js in Steps
Let’s do this in steps.
New Project
Create a new Next.js project named barcode-scanner
:
npx create-next-app@latest barcode-scanner
Then, we can run the following to test it:
cd barcode-scanner
npm run dev
Install Dependencies
Install Dynamsoft Barcode Reader and Dynamsoft Camera Enhancer.
npm install dynamsoft-javascript-barcode@9.6.11 dynamsoft-camera-enhancer@3.3.1
Create a Barcode Scanner React Component
Next, let’s create a barcode scanner component under src/components/BarcodeScanner.tsx
that can open the camera and scan barcodes from the camera video frames.
-
Write the basic content of the component:
import { CameraEnhancer } from "dynamsoft-camera-enhancer"; import { PlayCallbackInfo } from "dynamsoft-camera-enhancer/dist/types/interface/playcallbackinfo"; import { TextResult,BarcodeReader } from "dynamsoft-javascript-barcode"; import React from "react"; import { ReactNode } from "react"; export interface ScannerProps{ isActive?: boolean; children?: ReactNode; interval?: number; license?: string; onInitialized?: (enhancer:CameraEnhancer,reader:BarcodeReader) => void; onScanned?: (results:TextResult[]) => void; onPlayed?: (playCallbackInfo: PlayCallbackInfo) => void; onClosed?: () => void; } const BarcodeScanner = (props:ScannerProps): React.ReactElement => { return ( ) } export default BarcodeScanner;
The component has several props to control the scanner and get the barcode results.
-
Define the container for the scanner with a container for the camera using a fixed class name used by the Dynamsoft Camera Enhancer. Here, we use the relative position so that we can customize its position in the parent component.
const container = React.useRef(null); return ( <div ref={container} style={{ position:"relative", width:"100%", height:"100%" }}> <div className="dce-video-container"></div> </div> )
-
Initialize Barcode Reader and Camera Enhancer when the component is mounted. Several events are registered.
React.useEffect(()=>{ const init = async () => { if (BarcodeReader.isWasmLoaded() === false) { if (props.license) { BarcodeReader.license = props.license; }else{ BarcodeReader.license = "LICENSE-KEY"; license } BarcodeReader.engineResourcePath = "https://cdn.jsdelivr.net/npm/dynamsoft-javascript-barcode@9.6.11/dist/"; } reader.current = await BarcodeReader.createInstance(); enhancer.current = await CameraEnhancer.createInstance(); await enhancer.current.setUIElement(container.current!); enhancer.current.on("played", (playCallbackInfo: PlayCallbackInfo) => { if (props.onPlayed) { props.onPlayed(playCallbackInfo); } }); enhancer.current.on("cameraClose", () => { if (props.onClosed) { props.onClosed(); } }); enhancer.current.setVideoFit("cover"); if (props.onInitialized) { props.onInitialized(enhancer.current,reader.current); } } if (mounted.current === false) { init(); } mounted.current = true; },[])
-
Monitor the
isActive
props. Start the camera if it is set to true. Otherwise, stop the camera.const toggleCamera = () => { if (mounted.current === true) { if (props.isActive === true) { enhancer.current?.open(true); }else{ stopScanning(); enhancer.current?.close(); } } } React.useEffect(()=>{ toggleCamera(); },[props.isActive])
-
Define functions related to scanning the barcodes. We start an interval to get frames from the camera and read barcodes.
const interval = React.useRef<any>(null); const decoding = React.useRef(false); const startScanning = () => { const decode = async () => { if (decoding.current === false && reader.current && enhancer.current) { decoding.current = true; const results = await reader.current.decode(enhancer.current.getFrame()); if (props.onScanned) { props.onScanned(results); } decoding.current = false; } } if (props.interval) { interval.current = setInterval(decode,props.interval); }else{ interval.current = setInterval(decode,40); } } const stopScanning = () => { clearInterval(interval.current); }
-
Trigger scanning if the camera is opened.
enhancer.current.on("played", (playCallbackInfo: PlayCallbackInfo) => { if (props.onPlayed) { props.onPlayed(playCallbackInfo); } startScanning(); });
-
Try to toggle the camera after initialization as well.
const init = async () => { toggleCamera(); }
Use the Barcode Scanner Component in the App
Switch to pages/index.tsx
. Let’s use the scanner component in the app.
-
Import the scanner component and bind an
isActive
state to it so that we can use a button to control its scanning status.export default function Home(props:any) { const [isActive,setIsActive] = React.useState(false); const toggleScanning = () => { setIsActive(!isActive); } return ( <> <Head> <title>Next.js Barcode Reader</title> <meta name="description" content="Generated by create next app" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="icon" href="/favicon.ico" /> </Head> <main> <div className={homeStyles.app}> <h2>Next.js Barcode Scanner</h2> <button onClick={toggleScanning}>{isActive ? "Stop Scanning" : "Start Scanning"}</button> <div className={homeStyles.barcodeScanner}> <BarcodeScanner isActive={isActive} ></BarcodeScanner> </div> </div> </main> </> ) }
-
Bind the
initialization
event. Show anInitializing...
text if the scanner has not been initialized. If the initialization has been done, display thestart scanning
button.JSX:
{initialized ? ( <button onClick={toggleScanning}>{isActive ? "Stop Scanning" : "Start Scanning"}</button> ) : ( <div>Initializing...</div> )} <BarcodeScanner onInitialized={() => setInitialized(true)} isActive={isActive} ></BarcodeScanner>
JavaScript:
const [initialized,setInitialized] = React.useState(false);
-
Bind the
onScanned
event. Stop scanning and display the barcode results if barcodes are found.JSX:
<BarcodeScanner onInitialized={() => setInitialized(true)} isActive={isActive} onScanned={(results) => onScanned(results)} ></BarcodeScanner>
JavaScript:
const onScanned = (results:TextResult[]) => { if (results.length>0) { let text = ""; for (let index = 0; index < results.length; index++) { const result = results[index]; text = text + result.barcodeFormatString + ": " + result.barcodeText + "\n"; } alert(text); setIsActive(false); } }
Read License from Environment Variables
We need to set the license for Dynamsoft Barcode Reader to use it. You can apply for a license here.
We can directly store the license in the code and use it:
BarcodeReader.license = "<license>";
But with Next.js’ server-side pre-rendering option, we can read the license from the system’s environment variable for better flexibility.
-
Create a new
getServerSideProps
function in the index page:export async function getServerSideProps() { let license:string|undefined = process.env.DBRLicense; return { props: { license:license } }; }
The function tries to read the license from the environment variables and pass it as props to the page.
-
In the page, we can pass the license props to the barcode scanner component to use it.
<BarcodeScanner license={props.license} onInitialized={() => setInitialized(true)} isActive={isActive} onScanned={(results) => onScanned(results)} ></BarcodeScanner>
All right, we’ve now finished the barcode and QR code scanner with Next.js. You can check out the online demo to have a try.