How to Build a Document Scanner in Next.js
Next.js is the recommended framework to create React applications with extra features like server-side rendering and an intuitive router.
In this article, we are going to create a document scanning app with Next.js and Dynamic Web TWAIN.
Getting Started With Dynamic Web TWAIN
Build a Document Scanning Web App with Next.js
Let’s do this in steps.
New Project
Create a new Next.js project named document-scanner
:
npx create-next-app@latest document-scanner
Then, we can run the following to test it:
cd document-scanner
npm run dev
Install Dependencies
Install Dynamic Web TWAIN from npm:
npm install dwt
Create a Component for Dynamic Web TWAIN
Dynamic Web TWAIN provides a document viewer control and a bunch of APIs to scan and manage documents. We are going to wrap the viewer as a React component and expose the object of Dynamic Web TWAIN to call different APIs.
Here is the basic content of the component:
interface props {
onWebTWAINReady?: (dwt:WebTwain) => void;
}
const DWT: React.FC<props> = (props: props) => {
const containerID = "dwtcontrolContainer";
const container = useRef<HTMLDivElement>(null);
useEffect(()=>{
Dynamsoft.DWT.RegisterEvent('OnWebTwainReady', () => {
const DWObject = Dynamsoft.DWT.GetWebTwain(containerID);
if (props.onWebTWAINReady) {
props.onWebTWAINReady(DWObject); // expose the object of Dynamic Web TWAIN
}
});
Dynamsoft.DWT.ResourcesPath = "https://unpkg.com/dwt@18.1.1/dist"; //use the resources from a CDN
Dynamsoft.DWT.Containers = [{
WebTwainId: 'dwtObject',
ContainerId: containerID
}];
Dynamsoft.DWT.Load();
},[]);
return (
<div ref={container} id={containerID}></div>
);
}
export default DWT;
There are some additional props we can add to the component:
-
A license to activate Dynamic Web TWAIN. We can apply for a trial license here.
interface props { license?:string; } useEffect(()=>{ if (props.license) { Dynamsoft.DWT.ProductKey = props.license; } },[]);
-
Width and height for the viewer.
interface props { width?: string; height?: string; } useEffect(()=>{ Dynamsoft.DWT.RegisterEvent('OnWebTwainReady', () => { const DWObject = Dynamsoft.DWT.GetWebTwain(containerID); DWObject.Viewer.width = "100%"; DWObject.Viewer.height = "100%"; if (props.width) { if (container.current) { container.current.style.width = props.width; } } if (props.height) { if (container.current) { container.current.style.height = props.height; } } }); },[]);
-
View mode for the viewer:
interface props { viewMode?: {cols:number,rows:number}; } useEffect(()=>{ Dynamsoft.DWT.RegisterEvent('OnWebTwainReady', () => { const DWObject = Dynamsoft.DWT.GetWebTwain(containerID); if (props.viewMode) { DWObject.Viewer.setViewMode(props.viewMode.cols,props.viewMode.rows); } }); },[]);
Use the Component in the App
Next, we are going to use the component in the app.
-
Because Dynamic Web TWAIN uses
navigator
which is only available in the browser, we need to disable server-side rendering for the component. We can import the component with the following code to do this:const DWT = dynamic(() => import("../components/DWT"), { ssr: false, loading: () => <p>Initializing Document Scanner</p>, });
-
Update
index.tsx
to use the component.import { WebTwain } from 'dwt/dist/types/WebTwain'; import dynamic from 'next/dynamic'; import Head from 'next/head' import styles from '../styles/Home.module.css' import React from 'react'; const DWT = dynamic(() => import("../components/DWT"), { ssr: false, loading: () => <p>Initializing Document Scanner</p>, }); export default function Home() { const DWObject = React.useRef<WebTwain>(); const onWebTWAINReady = (dwtObject:WebTwain) => { DWObject.current = dwtObject; } return ( <> <Head> <title>Next.js Document Scanner</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> <h2>Document Scanner</h2> <div className={styles.container}> <DWT width='100%' height='100%' viewMode={{cols:2,rows:2}} onWebTWAINReady={onWebTWAINReady} ></DWT> </div> </div> </main> </> ) }
-
Add a scan button to scan documents.
const scan = () => { if (DWObject.current) { DWObject.current.SelectSource(function () { DWObject.current!.OpenSource(); DWObject.current!.AcquireImage(); }, function () { console.log("SelectSource failed!"); } ); } } return ( <> <main> <div> <h2>Document Scanner</h2> <button onClick={scan}>Scan</button> </div> </main> </> )
-
Add a save button to save the scanned documents into a PDF file.
const save = () => { if (DWObject.current) { DWObject.current.SaveAllAsPDF("Scanned"); } } return ( <> <main> <div> <h2>Document Scanner</h2> <button onClick={save}>Save</button> </div> </main> </> )
All right, we’ve now finished the document scanner. You can use the online demo to have a try.