├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.jsx ├── App.test.js ├── HowToUse.jsx ├── Html5QrcodePlugin.jsx ├── ResultContainerPlugin.jsx ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js └── setupTests.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # html5-qrcode with React 2 |
3 | [reactjs.org](https://reactjs.org/) | `Support Level` = `Strong` 4 | 5 | Example of using [mebjas/html5-qrcode](https://github.com/mebjas/html5-qrcode) in React project with example, source and demo. 6 | 7 | ## How to build a `React Plugin / Component` using [html5-qrcode](https://github.com/mebjas/html5-qrcode) 8 | We shall be using React's recommendation on [Integrating with Other Libraries](https://reactjs.org/docs/integrating-with-other-libraries.html) to create a plugin for `React`. 9 | 10 | ### Download the latest library 11 | You can download this from [Github release page](https://github.com/mebjas/html5-qrcode/releases) or [npm](https://www.npmjs.com/package/html5-qrcode). And include this in `index.html`. 12 | 13 | ```html 14 | 15 | ``` 16 | 17 | ### Create a new component `Html5QrcodeScannerPlugin` 18 | You can write a custom plugin like this (see [src/Html5QrcodePlugin.jsx](./src/Html5QrcodePlugin.jsx) for reference) 19 | 20 | ```jsx 21 | // file = Html5QrcodePlugin.jsx 22 | import { Html5QrcodeScanner } from 'html5-qrcode'; 23 | import { useEffect } from 'react'; 24 | 25 | const qrcodeRegionId = "html5qr-code-full-region"; 26 | 27 | // Creates the configuration object for Html5QrcodeScanner. 28 | const createConfig = (props) => { 29 | let config = {}; 30 | if (props.fps) { 31 | config.fps = props.fps; 32 | } 33 | if (props.qrbox) { 34 | config.qrbox = props.qrbox; 35 | } 36 | if (props.aspectRatio) { 37 | config.aspectRatio = props.aspectRatio; 38 | } 39 | if (props.disableFlip !== undefined) { 40 | config.disableFlip = props.disableFlip; 41 | } 42 | return config; 43 | }; 44 | 45 | const Html5QrcodePlugin = (props) => { 46 | 47 | useEffect(() => { 48 | // when component mounts 49 | const config = createConfig(props); 50 | const verbose = props.verbose === true; 51 | // Suceess callback is required. 52 | if (!(props.qrCodeSuccessCallback)) { 53 | throw "qrCodeSuccessCallback is required callback."; 54 | } 55 | const html5QrcodeScanner = new Html5QrcodeScanner(qrcodeRegionId, config, verbose); 56 | html5QrcodeScanner.render(props.qrCodeSuccessCallback, props.qrCodeErrorCallback); 57 | 58 | // cleanup function when component will unmount 59 | return () => { 60 | html5QrcodeScanner.clear().catch(error => { 61 | console.error("Failed to clear html5QrcodeScanner. ", error); 62 | }); 63 | }; 64 | }, []); 65 | 66 | return ( 67 |
68 | ); 69 | }; 70 | 71 | export default Html5QrcodePlugin; 72 | ``` 73 | 74 | ### Use this new component in your React app 75 | A very crude example would be to 76 | ```jsx 77 | const App = (props) => { 78 | 79 | const onNewScanResult = (decodedText, decodedResult) => { 80 | // handle decoded results here 81 | }; 82 | 83 | return ( 84 |
85 | 91 |
92 | ); 93 | }; 94 | ``` 95 | 96 | ### Example implementation 97 | You can find an example implementation at [example.html](./example.html). 98 | 99 | ### Additional Contributors 100 | | Name | Profile| 101 | | ----- | ------ | 102 | | Andy Tenholder| [@AndyTenholder](https://github.com/AndyTenholder) | 103 | | Minhaz | [@mebjas](https://github.com/mebjas) | 104 | | Mohit Tank| [@tankmohit](https://github.com/tankmohit) | 105 | 106 | ### Credits 107 | - [scanapp.org](https://scanapp.org) - Free online barcode and qrcode scanner - scan directly on your web browser using camera or images saved on your device. Works well on smart phones as well as PC or Mac. 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html5-qrcode-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.15.0", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "html5-qrcode": "^2.1.3", 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2", 12 | "react-scripts": "4.0.3", 13 | "web-vitals": "^1.1.2" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scanapp-org/html5-qrcode-react/006c159dae7810e0912159dc86c8967c77332a85/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | html5-qrcode react plugin 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scanapp-org/html5-qrcode-react/006c159dae7810e0912159dc86c8967c77332a85/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scanapp-org/html5-qrcode-react/006c159dae7810e0912159dc86c8967c77332a85/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-section { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | color: white; 24 | } 25 | 26 | .App-section-title { 27 | font-size: calc(10px + 2vmin); 28 | } 29 | 30 | .App-link { 31 | color: #61dafb; 32 | } 33 | 34 | @keyframes App-logo-spin { 35 | from { 36 | transform: rotate(0deg); 37 | } 38 | 39 | to { 40 | transform: rotate(360deg); 41 | } 42 | } 43 | 44 | #html5qr-code-full-region { 45 | background-color: #adb5bd; 46 | color: #282c34; 47 | font-size: 12pt; 48 | } 49 | 50 | 51 | @media(max-width: 640px) { 52 | #html5qr-code-full-region { 53 | width: 300px; 54 | } 55 | } 56 | 57 | @media(min-width: 640px) { 58 | #html5qr-code-full-region { 59 | width: 600px; 60 | } 61 | } 62 | 63 | .Result-container { 64 | min-width: 600px; 65 | margin: auto; 66 | display: block; 67 | margin-top: 10px; 68 | margin-bottom: 10px; 69 | text-align: center; 70 | } 71 | 72 | .Result-header { 73 | font-size: 16pt; 74 | margin-bottom: 20px; 75 | } 76 | 77 | table { 78 | width: 800px; 79 | border-collapse: collapse; 80 | overflow: hidden; 81 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); 82 | } 83 | 84 | th, 85 | td { 86 | padding: 15px; 87 | background-color: rgba(255, 255, 255, 0.2); 88 | color: #fff; 89 | } 90 | 91 | th { 92 | text-align: left; 93 | } 94 | 95 | thead th { 96 | background-color: #55608f; 97 | } 98 | 99 | tbody tr:hover { 100 | background-color: rgba(255, 255, 255, 0.3); 101 | } 102 | 103 | tbody td { 104 | position: relative; 105 | } 106 | 107 | tbody td:hover:before { 108 | content: ""; 109 | position: absolute; 110 | left: 0; 111 | right: 0; 112 | top: -9999px; 113 | bottom: -9999px; 114 | background-color: rgba(255, 255, 255, 0.2); 115 | z-index: -1; 116 | } 117 | 118 | a { 119 | color: white 120 | } 121 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import React, { useState } from 'react'; 4 | import './App.css'; 5 | import HowToUse from './HowToUse.jsx'; 6 | import Html5QrcodePlugin from './Html5QrcodePlugin.jsx'; 7 | import ResultContainerPlugin from './ResultContainerPlugin.jsx'; 8 | 9 | const App = (props) => { 10 | const [decodedResults, setDecodedResults] = useState([]); 11 | const onNewScanResult = (decodedText, decodedResult) => { 12 | console.log("App [result]", decodedResult); 13 | setDecodedResults(prev => [...prev, decodedResult]); 14 | }; 15 | 16 | return ( 17 |
18 |
19 |
Html5-qrcode React demo
20 |
21 |
22 |
23 | 29 | 30 | 31 |
32 |
33 | ); 34 | }; 35 | 36 | export default App; 37 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/HowToUse.jsx: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const HowToUse = (props) => { 3 | return ( 4 |
5 |

How to use?

6 |
7 | Check out  8 | 9 | scanapp-org/html5-qrcode-react 10 |   11 | on Github for reference. 12 |
13 |
14 | Built using  15 | 16 | mebjas/html5-qrcode 17 | . 18 |
19 |
20 | Free QR code scanner and barcode scanner at scanapp.org. 21 |
22 |
23 | ); 24 | }; 25 | 26 | export default HowToUse; 27 | -------------------------------------------------------------------------------- /src/Html5QrcodePlugin.jsx: -------------------------------------------------------------------------------- 1 | import { Html5QrcodeScanner } from 'html5-qrcode'; 2 | import { useEffect } from 'react'; 3 | 4 | const qrcodeRegionId = "html5qr-code-full-region"; 5 | 6 | // Creates the configuration object for Html5QrcodeScanner. 7 | const createConfig = (props) => { 8 | let config = {}; 9 | if (props.fps) { 10 | config.fps = props.fps; 11 | } 12 | if (props.qrbox) { 13 | config.qrbox = props.qrbox; 14 | } 15 | if (props.aspectRatio) { 16 | config.aspectRatio = props.aspectRatio; 17 | } 18 | if (props.disableFlip !== undefined) { 19 | config.disableFlip = props.disableFlip; 20 | } 21 | return config; 22 | }; 23 | 24 | const Html5QrcodePlugin = (props) => { 25 | 26 | useEffect(() => { 27 | // when component mounts 28 | const config = createConfig(props); 29 | const verbose = props.verbose === true; 30 | // Suceess callback is required. 31 | if (!(props.qrCodeSuccessCallback)) { 32 | throw "qrCodeSuccessCallback is required callback."; 33 | } 34 | const html5QrcodeScanner = new Html5QrcodeScanner(qrcodeRegionId, config, verbose); 35 | html5QrcodeScanner.render(props.qrCodeSuccessCallback, props.qrCodeErrorCallback); 36 | 37 | // cleanup function when component will unmount 38 | return () => { 39 | html5QrcodeScanner.clear().catch(error => { 40 | console.error("Failed to clear html5QrcodeScanner. ", error); 41 | }); 42 | }; 43 | }, []); 44 | 45 | return ( 46 |
47 | ); 48 | }; 49 | 50 | export default Html5QrcodePlugin; 51 | -------------------------------------------------------------------------------- /src/ResultContainerPlugin.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function filterResults (results) { 4 | let filteredResults = []; 5 | for (var i = 0; i < results.length; ++i) { 6 | if (i === 0) { 7 | filteredResults.push(results[i]); 8 | continue; 9 | } 10 | 11 | if (results[i].decodedText !== results[i - 1].decodedText) { 12 | filteredResults.push(results[i]); 13 | } 14 | } 15 | return filteredResults; 16 | } 17 | 18 | const ResultContainerTable = ({ data }) => { 19 | const results = filterResults(data); 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | { 31 | results.map((result, i) => { 32 | console.log(result); 33 | return ( 34 | 35 | 36 | 37 | ); 38 | }) 39 | } 40 | 41 |
#Decoded TextFormat
{i}{result.decodedText}{result.result.format.formatName}
42 | ); 43 | }; 44 | 45 | const ResultContainerPlugin = (props) => { 46 | const results = filterResults(props.results); 47 | return ( 48 |
49 |
Scanned results ({results.length})
50 |
51 | 52 |
53 |
54 | ); 55 | }; 56 | 57 | export default ResultContainerPlugin; 58 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | --------------------------------------------------------------------------------