<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hanna Miller</title>
    <description>The latest articles on DEV Community by Hanna Miller (@hanna_miller_apryse).</description>
    <link>https://dev.to/hanna_miller_apryse</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4092662%2F6810e289-db84-4280-bec4-cc4cafca6354.PNG</url>
      <title>DEV Community: Hanna Miller</title>
      <link>https://dev.to/hanna_miller_apryse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hanna_miller_apryse"/>
    <language>en</language>
    <item>
      <title>JavaScript Spreadsheet SDK: How to Embed Excel-Like XLSX Editing into Your React App</title>
      <dc:creator>Hanna Miller</dc:creator>
      <pubDate>Wed, 23 Sep 2026 17:59:16 +0000</pubDate>
      <link>https://dev.to/apryse/javascript-spreadsheet-sdk-how-to-embed-excel-like-xlsx-editing-into-your-react-app-4oef</link>
      <guid>https://dev.to/apryse/javascript-spreadsheet-sdk-how-to-embed-excel-like-xlsx-editing-into-your-react-app-4oef</guid>
      <description>&lt;p&gt;&lt;em&gt;The best JavaScript SDK for editing Excel files in the browser depends on the complexity of your spreadsheet workflows and the other document formats your applications need to support. If users need to open, edit, and export native Excel files with formulas, multiple sheets, and formatting intact, a dedicated spreadsheet SDK is generally a better fit than a data grid designed primarily for displaying tabular data. For teams already handling PDF or Word documents in the same application, Apryse Spreadsheet Editor SDK is a strong fit, since it runs all three formats through one client-side engine using WebAssembly, keeping files in the browser instead of round-tripping to a server. When spreadsheet editing is the only document type in scope, some dedicated spreadsheet SDKs specialize in extensive formula libraries or high-speed data streaming, which can be worth the tradeoff. Lighter-weight libraries fit budget-constrained, low-complexity projects.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Data sovereignty and user experience are everything. Forcing your users to leave your app to edit a spreadsheet is a risky move. Whether they are downloading a sensitive financial report to open it in Excel or jumping over to a shared Google Sheet, you’ve lost control of the workflow, and potentially the security of that data.&lt;/p&gt;

&lt;p&gt;Embedding a professional spreadsheet editor, like the Apryse Spreadsheet Editor, directly into your web application keeps the data in-house and the user focused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Embed an Editor?
&lt;/h2&gt;

&lt;p&gt;Google Sheets and Excel Online are powerful, but they work only until they don't. A dedicated JavaScript SDK gives you the control they can't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Custom Branding:&lt;/strong&gt; You aren't stuck with third-party logos or UI breaking your app's immersion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline Support:&lt;/strong&gt; A dedicated SDK, like the &lt;a href="https://apryse.com/products/webviewer" rel="noopener noreferrer"&gt;Apryse Web SDK&lt;/a&gt;, allows for in-memory editing that doesn't die the moment the Wi-Fi drops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Sovereignty:&lt;/strong&gt; Crucial for banks or healthcare; files are processed in the browser, not on an external server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility:&lt;/strong&gt; Professional SDKs often come with WCAG 2.2-AA compliance out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spreadsheet SDK vs. Data Grid: Knowing What You Need
&lt;/h2&gt;

&lt;p&gt;You might think that a data grid, like AG Grid, can handle all your spreadsheet editing needs like a spreadsheet editor does, but in reality, they just can’t compete. They solve different problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Grids:&lt;/strong&gt; Designed to render JSON data. They are great for sorting and filtering database records, but they don't handle .xlsx files very well, especially complex workbooks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spreadsheet SDKs:&lt;/strong&gt; These are built for .xlsx files. They support cell merging, complex formula engines (SUMIF, VLOOKUP), and multiple tabs. If your users need to upload an Excel file, change a formula, and export it back out; you need an SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing the Top JavaScript Spreadsheet SDKs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26optm2jolww74j6lo0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F26optm2jolww74j6lo0e.png" alt="Figure 1: Comparing SpreadJS, Syncfusion, Handsontable, Jspreadsheet, Ignite UI, and Apryse Spreadsheet SDKs" width="679" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Benefit of a Unified Document SDK
&lt;/h2&gt;

&lt;p&gt;Most enterprise apps manage a mixture of document types such as contracts (PDF), letters (Word), and reports (Excel). Using separate SDKs for each means multiple dependencies and inconsistent UI.&lt;/p&gt;

&lt;p&gt;The Apryse Spreadsheet Editor (a WebViewer add-on) handles all three in one component. Since Summer 2025, it has supported full client-side editing, performing all operations in memory via WebAssembly. This ensures sensitive data, like financial proposals or compliance checklists, never leaves the client’s machine. It even supports chart rendering and editing, so users can refine data points and see visual updates in real-time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Use Cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Financial Proposals:&lt;/strong&gt; Banks can present Excel-based proposals safely within their portals without sensitive data leaving the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compliance Checklists:&lt;/strong&gt; Users can view and edit templates without file downloads or format breakage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaborating on Reports:&lt;/strong&gt; Generate spreadsheets for client reviews without users having to switch tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Embed Apryse Spreadsheet Editor into a React App
&lt;/h2&gt;

&lt;p&gt;Embedding a spreadsheet editor into your React app is a straightforward process. The engine is entirely client-side, meaning you can load files from local storage or remote URLs.&lt;/p&gt;

&lt;p&gt;For a quick reference of the whole set up, check out the &lt;a href="https://youtu.be/zsCTIjQCtJ0?si=L3MhOpjqiPMfs5ay" rel="noopener noreferrer"&gt;React with Vite video&lt;/a&gt; for Spreadsheet Editor 11.10+. It shows the integration of the Apryse WebViewer with Spreadsheet Editor enabled, all within a React project.&lt;/p&gt;

&lt;p&gt;Note: If you are using Vite 8, see our &lt;a href="https://apryse.com/blog/webviewer-vite-8-fix" rel="noopener noreferrer"&gt;blog&lt;/a&gt; on how to handle an error when trying to access WebViewer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start setting up Spreadsheet Editor, we need to do some groundwork for our project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="https://nodejs.org/en/download" rel="noopener noreferrer"&gt;Node and npm&lt;/a&gt; to use as your run-time environment/package manager.&lt;/li&gt;
&lt;li&gt;Create a folder for your project.&lt;/li&gt;
&lt;li&gt;Open a text editor like Visual Studio Code.&lt;/li&gt;
&lt;li&gt;Get your &lt;a href="https://docs.apryse.com/web/guides/get-started/trial-key" rel="noopener noreferrer"&gt;Apryse trial key&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we’ve done that, we can start our project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffold a React Project with Vite
&lt;/h2&gt;

&lt;p&gt;First, we need to create the project.&lt;/p&gt;

&lt;p&gt;On the command line, switch to the folder where you want your new project to sit.&lt;/p&gt;

&lt;p&gt;Run the following on the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Shell
npx create-vite@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, complete the following tasks from the command line during project creation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Change the default Project name to a project name of your choice.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;React&lt;/strong&gt; for your framework.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;JavaScript&lt;/strong&gt; for your variant.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Navigate to the New React Project
&lt;/h2&gt;

&lt;p&gt;Enter the following on the command line to navigate into your new project, replacing &lt;strong&gt;&lt;/strong&gt; with the name you chose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Shell
&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;project-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integrate WebViewer
&lt;/h2&gt;

&lt;p&gt;Run the following command on the command line, from your React project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Shell
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the project with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Shell
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can start editing the code in the files using Visual Studio Code as our editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy Static Assets
&lt;/h2&gt;

&lt;p&gt;Let’s install vite-plugin-static-copy so that when we install WebViewer, all the static assets that are required will be copied over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Shell
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; vite-plugin-static-copy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s also edit the &lt;strong&gt;vite.config&lt;/strong&gt; file to specify where to copy the files from and to. After adding the necessary code, the &lt;strong&gt;vite.config&lt;/strong&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;JSX&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;viteStaticCopy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite-plugin-static-copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 

&lt;span class="c1"&gt;// https://vite.dev/config/ &lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;viteStaticCopy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
    &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; 
      &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules/@pdftron/webviewer/public/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib/webviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
      &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;]&lt;/span&gt; 
  &lt;span class="p"&gt;})&lt;/span&gt; 
&lt;span class="p"&gt;],&lt;/span&gt; 
&lt;span class="p"&gt;})&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install WebViewer
&lt;/h2&gt;

&lt;p&gt;To install WebViewer, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Shell
npm i @pdftron/webviewer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ll set up our CSS files. First, delete all the contents from the &lt;strong&gt;app.css&lt;/strong&gt; and &lt;strong&gt;index.css&lt;/strong&gt; files.&lt;/p&gt;

&lt;p&gt;Note: Don't delete the files completely in case you want to add your own CSS later.&lt;/p&gt;

&lt;p&gt;Next, let’s modify the &lt;strong&gt;app.css&lt;/strong&gt; file to specify the height of the webviewer class. Your code for this file should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;CSS&lt;/span&gt;
&lt;span class="nc"&gt;.webviewer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up the App
&lt;/h2&gt;

&lt;p&gt;In the &lt;strong&gt;app.jsx&lt;/strong&gt; file, delete most of the code so that your file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;JSX&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ll add some code back into the &lt;strong&gt;app.jsx&lt;/strong&gt; file to import WebViewer and enable Spreadsheet Editor. The file should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;JSX&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;WebViewer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@pdftron/webviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; 
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isInstantiated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isInstantiated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
      &lt;span class="nx"&gt;isInstantiated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
      &lt;span class="nc"&gt;WebViewer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
         &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib/webviewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="na"&gt;licenseKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;YOUR_LICENSE_KEY &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="na"&gt;enableFilePicker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
         &lt;span class="na"&gt;initialMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;WebViewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Modes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SPREADSHEET_EDITOR&lt;/span&gt; 
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; 
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt; 
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt; 
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"webviewer"&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;viewer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt; 
  &lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we have a React app with Spreadsheet Editor running and able to view and edit spreadsheets.&lt;/p&gt;

&lt;p&gt;The most important line of all the code above is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;JSX&lt;/span&gt;
&lt;span class="nx"&gt;initialMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;WebViewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Modes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SPREADSHEET_EDITOR&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line of code transforms your app from a PDF editor into a spreadsheet editor, opening up a world of new functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Can We Do Now?
&lt;/h2&gt;

&lt;p&gt;There’s a wide range of modifications you can now make to a spreadsheet including changes to sheets, rows and columns, individual or groups of cells, formulas, and more. Check out our &lt;a href="https://docs.apryse.com/web/guides/spreadsheet-editor/edit-mode" rel="noopener noreferrer"&gt;Spreadsheet Editor - Edit Mode&lt;/a&gt; page for examples with code.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What file types are supported?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The editor is purpose-built for high-fidelity .xlsx viewing and editing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this work on mobile?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Currently, the Spreadsheet Editor add-on is not supported on mobile browsers due to the complexity of the grid interactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does client-side editing improve security?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because it uses WebAssembly (WASM), the file is never uploaded to a third-party server for processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is search and replace available?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Developers can perform search and replace programmatically across the entire workbook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As we’ve seen in this blog, adding spreadsheet editing capabilities to an app is easily done using the &lt;a href="https://docs.apryse.com/web/guides/get-started" rel="noopener noreferrer"&gt;Apryse Web SDK&lt;/a&gt; and Spreadsheet Editor add-on.&lt;/p&gt;

&lt;p&gt;Choosing a Spreadsheet Editor SDK is about maintaining the integrity of your users' data. By bringing these capabilities in-house, you reduce workflow interruptions and ensure your sensitive data stays exactly where it belongs: inside your application.&lt;/p&gt;

&lt;p&gt;Check out our &lt;a href="https://showcase.apryse.com/spreadsheet-editor" rel="noopener noreferrer"&gt;Spreadsheet Editor demo&lt;/a&gt; or get started with the &lt;a href="https://docs.apryse.com/web/guides/get-started" rel="noopener noreferrer"&gt;Apryse Web SDK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For support or sales questions, you can &lt;a href="https://apryse.com/form/contact-sales" rel="noopener noreferrer"&gt;contact our sales team&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Apryse Suggested Reads
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blog: &lt;a href="https://apryse.com/blog/webviewer-file-type-detection-edit-pdf-docx-spreadsheet" rel="noopener noreferrer"&gt;One Editor to Rule Them All: Working with PDF, DOCX &amp;amp; Spreadsheet files using the Apryse Web SDK&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog: &lt;a href="https://apryse.com/blog/view-edit-spreadsheets-react-apryse-sdk" rel="noopener noreferrer"&gt;How to Create a Browser-based Spreadsheet Editor Using TypeScript, React and Vite&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog: &lt;a href="https://apryse.com/blog/spreadsheet-editor-api-customization" rel="noopener noreferrer"&gt;Using the API to get more from Spreadsheet Editor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Blog: &lt;a href="https://dev.toSearch%20&amp;amp;%20Replace%20and%20Usability%20Enhancements%20in%20Spreadsheet%20Editor"&gt;Search &amp;amp; Replace and Usability Enhancements in Spreadsheet Editor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>How to Create a Browser-based Spreadsheet Editor Using TypeScript, React and Vite</title>
      <dc:creator>Hanna Miller</dc:creator>
      <pubDate>Thu, 17 Sep 2026 18:23:42 +0000</pubDate>
      <link>https://dev.to/apryse/how-to-create-a-browser-based-spreadsheet-editor-using-typescript-react-and-vite-npn</link>
      <guid>https://dev.to/apryse/how-to-create-a-browser-based-spreadsheet-editor-using-typescript-react-and-vite-npn</guid>
      <description>&lt;p&gt;To add spreadsheet editing to a React application, use a document SDK that renders Excel files natively in the browser. Apryse WebViewer provides this through its SpreadsheetEditor mode, installed via npm and mounted directly in a React component. Spreadsheet support requires WebViewer instantiated as an Iframe, not a WebComponent.&lt;/p&gt;

&lt;p&gt;Setup takes four steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install the package:&lt;/strong&gt; Add WebViewer to your project via npm install.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure assets:&lt;/strong&gt; Copy WebViewer's static assets with a Vite plugin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mount the component:&lt;/strong&gt; Attach WebViewer to a div element using a useRef hook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set the mode:&lt;/strong&gt; Pass initialMode: WebViewer.Modes.SPREADSHEET_EDITOR in the constructor options.
You'll need a valid license key, available with your free trial.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While Google Sheets or an embedded Office editor fill the gap, those options move your data into a third-party environment. Keeping spreadsheets in the browser can reduce exposure of downloaded copies or hidden columns. A working implementation of Apryse Spreadsheet Editor SDK runs under 40 lines.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Summary:&lt;/strong&gt; Learn how to securely view and edit spreadsheets directly in the browser using the Apryse Web SDK. This guide covers integration with a React + Vite app, offering a lightweight, secure alternative to traditional spreadsheet tools—no Office license or external services needed.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Spreadsheets are more than just a tool for accountants—they're a flexible, lightweight data platform used across virtually every industry. From financial modeling in banking to inventory tracking in retail, from experiment logging in scientific research to project planning in construction, spreadsheets remain a critical part of day-to-day operations.&lt;/p&gt;

&lt;p&gt;In the past, if you needed to view or edit a spreadsheet, you would download it, edit it in a desktop or browser-based app then save it again. While that works, it risks copies of the spreadsheet getting left on the hard drive, with associated risks of confidential data being compromised in a &lt;a href="https://www.cyfirma.com/research/tracking-ransomware-june-2025/" rel="noopener noreferrer"&gt;ransomware attack or data breach&lt;/a&gt;. Working with spreadsheets in a general editor also risks copies being &lt;a href="https://www.theguardian.com/uk-news/2025/jul/16/the-spreadsheet-the-superinjunction-and-the-relocation-scheme-afghan-data-leak-timeline" rel="noopener noreferrer"&gt;inadvertently shared with confidential data in hidden columns&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You could, of course, use the Google offering, but there are &lt;a href="https://www.sisainfosec.com/?post_type=threat-a-licious&amp;amp;p=36914" rel="noopener noreferrer"&gt;significant security risks with Google Sheets&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;So, what’s an alternative?&lt;/p&gt;

&lt;p&gt;Enter Apryse&lt;/p&gt;

&lt;p&gt;The Apryse Web SDK has been around for several years – first offering the ability to view and edit PDFs using &lt;a href="https://docs.apryse.com/web/guides" rel="noopener noreferrer"&gt;WebViewer&lt;/a&gt;, then since 2024, the ability to &lt;a href="https://docs.apryse.com/web/guides/docx-editor" rel="noopener noreferrer"&gt;edit DOCX files&lt;/a&gt; entirely within the browser (without needing an Office license), and now, supporting, &lt;a href="https://apryse.com/capabilities/spreadsheet-editor" rel="noopener noreferrer"&gt;viewing and editing Spreadsheets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Better still, it’s easy to get started. If you are already an Apryse Web SDK user, you only need to change one line of code to get the benefit of this functionality.&lt;/p&gt;

&lt;p&gt;In this article we will step through what is needed to &lt;a href="https://docs.apryse.com/web/guides/spreadsheet-editor/edit-mode" rel="noopener noreferrer"&gt;view and edit Spreadsheets in a React based application&lt;/a&gt; that runs within your browser, and we’ll do that using &lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Learn how to integrate Apryse WebViewer in JavaScript, React, Vue, Angular, and more in this &lt;a href="https://youtu.be/UIIWdERmBtU" rel="noopener noreferrer"&gt;video&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few words on configuration
&lt;/h2&gt;

&lt;p&gt;I’m using Windows 11, but everything that you see should also work with macOS and Linux.&lt;/p&gt;

&lt;p&gt;You will need npm and Node installed. I used Node v 22 for this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1. Scaffold the app using create-vite
&lt;/h2&gt;

&lt;p&gt;In a terminal window create a new app using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shell
npx create-vite@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will take you through a series of steps where it will ask you to specify a project name (I called it “spreadsheet-editor”), a Framework (choose React) and a variant (choose TypeScript).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0tc7d8bo0dsxa35g9r5o.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0tc7d8bo0dsxa35g9r5o.webp" alt="Figure 1 - Working through the steps of creating an app with create-vite." width="404" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a moment or two the project will be scaffolded, and you will be advised how to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfhpu2dyykog0d9tdghk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdfhpu2dyykog0d9tdghk.webp" alt="Figure 2 - The end result after successful project creation." width="630" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the directory and install the dependencies using npm install.&lt;/p&gt;

&lt;p&gt;If you want, then you can run the project at this point - it currently is just the default Vite+React app, but it’s useful to know that everything is working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft2ysn89oe2rmmlypn35d.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft2ysn89oe2rmmlypn35d.webp" alt="Figure 3 - The default app, running in a browser." width="660" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2. Remove the default code&lt;br&gt;
Copied to clipboard&lt;br&gt;
Vite scaffolds a fully working app, but much of what was created isn’t needed, so let’s delete that.&lt;/p&gt;

&lt;p&gt;You could do this from the command line or another IDE, but I’ll open the project in &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, remove everything from the App.css file. We will want to add things back in as we move from a prototype to production, but for now we can just delete all of the contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoalp3vm8e7i1iyzpjc6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faoalp3vm8e7i1iyzpjc6.webp" alt="Figure 4 - The default App.css file contains lots of things that we don’t need at the moment. Delete all of the contents, so that we can concentrate on WebViewer." width="660" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will also remove the contents of the &lt;strong&gt;index.css&lt;/strong&gt; file at this point.&lt;/p&gt;

&lt;p&gt;Next remove virtually everything from the &lt;strong&gt;App.tsx&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw62g55og1u4mm07khcxc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw62g55og1u4mm07khcxc.webp" alt="Figure 5 - In App.tsx, virtually everything can be deleted." width="290" height="318"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3. Get ready to install WebViewer
&lt;/h2&gt;

&lt;p&gt;Great, we have now got to the point where we can start rebuilding. We need to add Apryse WebViewer which we can do using &lt;strong&gt;npm install&lt;/strong&gt;. Apryse was previously known as &lt;a href="https://apryse.com/blog/news/pdftron-rebrands-as-apryse" rel="noopener noreferrer"&gt;PDFTron and rebranded in 2023&lt;/a&gt;. As such, the WebViewer package still uses the name “pdftron” in its title.&lt;/p&gt;

&lt;p&gt;WebViewer is written using JavaScript and is intended to be used within the browser. As such, we need to make sure that our code can access the files at runtime. For security reasons React can only access files in the project’s &lt;strong&gt;public&lt;/strong&gt; folder, and not from the &lt;strong&gt;node_modules&lt;/strong&gt; folder.&lt;/p&gt;

&lt;p&gt;There are various ways that we can &lt;a href="https://docs.apryse.com/web/guides/get-started/copy-assets" rel="noopener noreferrer"&gt;make those static files available at runtime&lt;/a&gt; – we can copy them manually, run a script, or use a plugin.&lt;/p&gt;

&lt;p&gt;For this article we will install the plugin &lt;a href="https://www.npmjs.com/package/vite-plugin-static-copy" rel="noopener noreferrer"&gt;vite-plugin-static-copy&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shell
npm i -D vite-plugin-static-copy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to installing the plugin, you will also need to update the &lt;strong&gt;vite.config.ts&lt;/strong&gt; file to specify where the files should be copied from and to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeScript
import { defineConfig } from 'vite' 
import react from '@vitejs/plugin-react' 
import { viteStaticCopy } from 'vite-plugin-static-copy' 

// https://vite.dev/config/ 

export default defineConfig({ 
 plugins: [react(), 
  viteStaticCopy({ 
  targets: [ 
   { 
    src: 'node_modules/@pdftron/webviewer/public/*', 
    dest: 'lib/webviewer' 
   } 
  ] 
 }) 
 ], 
}) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhktcms975o94nz5wfvcv.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhktcms975o94nz5wfvcv.webp" alt="Figure 6 - The updated vite.config.ts file." width="576" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now when we install Webviewer, the plugin will copy the files in '&lt;strong&gt;node_modules/@pdftron/webviewer/public/&lt;/strong&gt;’ into '&lt;strong&gt;lib/webviewer&lt;/strong&gt;'&lt;/p&gt;

&lt;p&gt;You can specify an alternative destination if you wish – but if you do then you will need to modify the WebViewer initialization code – we will talk about that in a moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - Install WebViewer
&lt;/h2&gt;

&lt;p&gt;Next, we need to install WebViewer itself, which we can do using npm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shell
npm install @pdftron/webviewer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will copy the package into &lt;strong&gt;node_modules&lt;/strong&gt; and add it to the &lt;strong&gt;package.json&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpgg0xj7a2700lg56jbe4.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpgg0xj7a2700lg56jbe4.webp" alt="Figure 7 - Folder structure showing where to copy the files." width="324" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you have chosen not to use the plugin, then you will need to manually copy the files from the public folder into a location where they can be accessed by the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5. Create an HTML element where WebViewer will be mounted
&lt;/h2&gt;

&lt;p&gt;Now within the &lt;strong&gt;App.tsx&lt;/strong&gt; file, add some html to create a div where WebViewer will be mounted. We will specify the element with a &lt;strong&gt;useRef&lt;/strong&gt; and also include a className to allow us to style it with CSS.&lt;/p&gt;

&lt;p&gt;Your entire App.tsx file should now look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JavaScript
import {useRef} from 'react' 
import './App.css' 

function App() { 
 const viewer = useRef(null) 
 return ( 
  &amp;lt;&amp;gt; 
   &amp;lt;div className='webviewer' ref={viewer}&amp;gt;&amp;lt;/div&amp;gt; 
  &amp;lt;/&amp;gt; 
 ) 
} 
export default App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6. Instantiate WebViewer
&lt;/h2&gt;

&lt;p&gt;WebViewer can be instantiated as a &lt;strong&gt;WebComponent&lt;/strong&gt; (the default option), or as an &lt;strong&gt;Iframe&lt;/strong&gt;. When working with the SpreadsheetEditor, &lt;strong&gt;WebViewer must be instantiated as an Iframe&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The WebViewer constructor allows us to specify various options and requires us to include the element where it should be mounted (in our case &lt;strong&gt;viewerDiv.current&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;In this example we will specify three important options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;path:&lt;/strong&gt; which indicates the location where the plugin will be copying files to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;licenseKey:&lt;/strong&gt; which includes the trial or commercial license. If you haven’t got one, then it is free to &lt;a href="https://docs.apryse.com/web/guides/get-started/trial-key" rel="noopener noreferrer"&gt;get a trial license for WebViewer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;initialMode:&lt;/strong&gt; which specifies whether WebViewer should have the initial UI for working with PDFs, DOCX files or Spreadsheets. We will use &lt;strong&gt;WebViewer.Modes.SPREADSHEET_EDITOR.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many other options that you can use (check out the &lt;a href="https://sdk.apryse.com/api/web/global.html#WebViewerOptions" rel="noopener noreferrer"&gt;WebViewer documentation&lt;/a&gt;), but for now we will just use &lt;strong&gt;enableFilePicker&lt;/strong&gt;, so that we can choose files from the UI, and initialDoc (in this case “&lt;strong&gt;invoice_template.xlsx&lt;/strong&gt;“) to specify the file that should be loaded when WebViewer is created.&lt;/p&gt;

&lt;p&gt;The WebViewer constructor is async and returns a promise. When the promise resolves, you can use the &lt;strong&gt;instance&lt;/strong&gt; object to work with WebViewer. We won’t need that just now for our simple app, but we will create an empty code block so that it is available when you decide to further explore what WebViewer can do.&lt;/p&gt;

&lt;p&gt;Our constructor should now look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*TypeScript
WebViewer.Iframe({ 
   path: "lib/webviewer", 
   initialMode: WebViewer.Modes.SPREADSHEET_EDITOR, 
  licenseKey:"[Your license key]", 
   enableFilePicker: true, 
   initialDoc:"invoice_template.xlsx" 
  }, 

   viewerDiv.current as HTMLDivElement).then())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we are using React, we can put this code into a UseEffect hook with an empty dependency array so that it fires when the component mounts.&lt;/p&gt;

&lt;p&gt;In recent versions of React the useEffect will fire a second time for Debug builds if &lt;strong&gt;strict mode&lt;/strong&gt; is true.&lt;/p&gt;

&lt;p&gt;There are several workarounds that I have used at different times. My preferred method at the moment is to create another useRef variable (“&lt;strong&gt;isInstantiated&lt;/strong&gt;”) and only instantiate the WebViewer if that has not already occurred. This adds a little extra code that isn’t needed when you move to production but will be useful during development.&lt;/p&gt;

&lt;p&gt;Your entire App.tsx file should now look like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JavaScript
import { useRef, useEffect } from 'react' 
import './App.css' 
import WebViewer from '@pdftron/webviewer' 

function App() { 
 const viewerDiv = useRef&amp;lt;HTMLDivElement&amp;gt;(null) 
 const isInitialized = useRef&amp;lt;boolean&amp;gt;(false) 
 useEffect(() =&amp;gt; { 
  //Not needed in production 
  if (isInitialized.current) return 
  isInitialized.current = true 

  WebViewer.Iframe({ 
   path: "lib/webviewer", 
   initialMode: WebViewer.Modes.SPREADSHEET_EDITOR, 
   licenseKey:”[Your license key]”, 
   enableFilePicker: true, 
   initialDoc:"invoice_template.xlsx" 
  }, 

   viewerDiv.current as HTMLDivElement).then((instance =&amp;gt; { 

   }) 
   ); 
 }, [ 
 ]) 

 return ( 
  &amp;lt;&amp;gt; 
   &amp;lt;div className='webviewer' ref={viewerDiv}&amp;gt; 
   &amp;lt;/div&amp;gt; 
  &amp;lt;/&amp;gt; 
 ) 
} 

export default App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point the code is effectively complete, so you can start the project using &lt;strong&gt;npm run dev&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You may find that the Spreadsheet editor appears to load but no grid is available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs2q5pau5mbzbbiccxvqe.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs2q5pau5mbzbbiccxvqe.webp" alt="Figure 8 - If no grid area is available, then it is likely a CSS issue." width="660" height="440"&gt;&lt;/a&gt;&lt;br&gt;
If you look in the browser’s DevTools window, it shows messages to indicate that WebViewer has loaded, and we can even see the name of the file at the top of the browser too.&lt;/p&gt;

&lt;p&gt;This isn’t a problem with WebViewer – it’s a CSS issue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 7. Solving issues with CSS
&lt;/h2&gt;

&lt;p&gt;When you have a real app, you will spend a lot of time carefully crafting the CSS for it, but straight out of the box this can be confusing. The fix is straightforward - add CSS to specify the height for the webviewer class (which we specified in the div where WebViewer is mounted).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CSS
.webviewer{
height: 100vh;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you reload the page, you can see a fully functional WebViewer. Awesome work!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2yomhs03ayd048d9jf9x.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2yomhs03ayd048d9jf9x.webp" alt="Figure 9 - WebViewer running within the browser, displaying a spreadsheet." width="660" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where next?
&lt;/h2&gt;

&lt;p&gt;With less than 40 lines of code, and as little as 10 minutes from creating the project, you now have a running project containing a Spreadsheet Editor within a web page.&lt;/p&gt;

&lt;p&gt;Getting The Spreadsheet Editor running is just the beginning though. We’ll look at how we can work with the API in the next blog in this series, as a way to &lt;a href="https://apryse.com/blog/client-side-spreadsheet-support-apryse-editor" rel="noopener noreferrer"&gt;work programmatically with the contents of the Workbook&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Web SDK offers more than just the ability to work with spreadsheets. With just a few changes to the code you can view and edit DOCX files, or PDFs. You can even set up a complete workflow for generating files from a template, converting them to a PDF, then digitally signing them.&lt;/p&gt;

&lt;p&gt;One tool – a wealth of uses!&lt;/p&gt;

&lt;p&gt;Don’t just take my word for it though – &lt;a href="https://docs.apryse.com/web/guides/get-started" rel="noopener noreferrer"&gt;try it our for yourself&lt;/a&gt;. Get a trial license and see what you can do. If you have any questions, then reach out to us on our &lt;a href="https://discord.com/invite/wggqUR9v7T" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; channel and we will be happy to help.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>typescript</category>
      <category>vite</category>
      <category>apryse</category>
    </item>
    <item>
      <title>Exporting OCR Data to JSON with Apryse</title>
      <dc:creator>Hanna Miller</dc:creator>
      <pubDate>Thu, 03 Sep 2026 15:28:10 +0000</pubDate>
      <link>https://dev.to/apryse/exporting-ocr-data-to-json-with-apryse-1l0i</link>
      <guid>https://dev.to/apryse/exporting-ocr-data-to-json-with-apryse-1l0i</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Summary:&lt;/strong&gt; OCR technology has revolutionized the handling of printed documents by converting images of text into machine-readable data. However, this data is often unstructured and hard to analyze. Converting OCR output into a structured format like JSON makes the text searchable, accessible, and easy to integrate with various applications. This enables efficient data analysis, automation, and archiving, unlocking the value of previously inaccessible information.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
Exporting OCR data to JSON with Apryse converts unstructured, machine-readable text into a structured format that preserves not just the extracted words but also metadata like font size, orientation, and position within the document. This makes the output searchable, easy to integrate with other applications, and simple to store, analyze, or feed back into automated workflows.&lt;/p&gt;

&lt;p&gt;The process uses the Apryse OCR Module within the Server SDK, which runs entirely on your own infrastructure so documents never leave your environment. After installing the module, a single function call, getOCRJsonFromImage, extracts the OCR results directly to JSON. The output is organized as nested arrays covering pages, paragraphs, lines, and words, each carrying its own metadata. From there, the structured data can be stored for later use, fed into another application or database, or applied back onto the original document to correct or update it.&lt;/p&gt;

&lt;p&gt;Optical Character Recognition (OCR) technology has transformed how we handle printed documents. Converting images of text into a machine-readable form often results in unstructured and difficult to analyze data. A powerful solution is to structure this OCR output into a format like JSON (JavaScript Object Notation). &lt;a href="https://docs.apryse.com/core/guides/ocr/workflow" rel="noopener noreferrer"&gt;OCR to JSON extraction&lt;/a&gt; &lt;strong&gt;makes text searchable, accessible,&lt;/strong&gt; and &lt;strong&gt;easy to integrate&lt;/strong&gt; with various applications, enabling efficient data analysis, automated entry, archiving, and more. Ultimately, OCR to JSON unlocks the value of previously inaccessible information.&lt;/p&gt;

&lt;p&gt;In this blog, we’ll explore JSON, exporting data to JSON using an OCR SDK, how the &lt;a href="https://apryse.com/capabilities/ocr" rel="noopener noreferrer"&gt;Apryse OCR SDK&lt;/a&gt; will be your top choice, and the process to export OCR to JSON.&lt;/p&gt;
&lt;h2&gt;
  
  
  JSON
&lt;/h2&gt;

&lt;p&gt;JSON, a lightweight data-interchange format, is easily usable by humans and machines. Built on a subset of JavaScript, and drawing familiar conventions from languages like C, C++, C#, Java, JavaScript, Python, and others, JSON offers an ideal and flexible way to exchange data. Its straightforward structure requires no specialized knowledge or tools for analysis and interpretation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Apryse OCR
&lt;/h2&gt;

&lt;p&gt;The Apryse OCR Module adds enterprise-grade text recognition to the Apryse Server SDK, ready to transform your document workflows.&lt;/p&gt;

&lt;p&gt;Key Features&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles various document types, including tables, multiple languages, and different text orientations.&lt;/li&gt;
&lt;li&gt;Includes preprocessing steps such as deskew and despeckle that you apply before recognition.&lt;/li&gt;
&lt;li&gt;Efficiently handles high-volume workflows.&lt;/li&gt;
&lt;li&gt;Easily integrated into existing systems and server environments.&lt;/li&gt;
&lt;li&gt;Can be fine-tuned for optimal performance by adjusting processing parameters.&lt;/li&gt;
&lt;li&gt;Runs entirely on your own infrastructure, so documents never leave your environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers can fine-tune the OCR engine for optimal performance by adjusting processing parameters to meet the needs and requirements of any project.&lt;/p&gt;

&lt;p&gt;From reduced costs and increased efficiency to improved accessibility and customized data output, the Apryse OCR offers a comprehensive suite of benefits for your business. If you’re looking for a practical example, see how to &lt;a href="https://apryse.com/blog/create-net-core-crossplatform-ocr-application" rel="noopener noreferrer"&gt;create a .NET Core cross-platform OCR application.&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apryse.com/blog/choosing-ocr-sdk-for-business" rel="noopener noreferrer"&gt;Read more about finding the right OCR module for your business. &lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  OCR Engines
&lt;/h2&gt;

&lt;p&gt;The Apryse Server SDK offers more than one OCR module, so you can match the recognition engine to your document set rather than accept a single fixed path. The default module is built on deep learning neural networks and covers a broad language set including CJK, Arabic, Cyrillic, and Latin-alphabet languages, the right starting point for most projects, and the one this blog uses. Alternative modules are available for workloads with existing accuracy baselines. If only one module is installed, the SDK uses it automatically; with more than one present, you select through the OCR options object. See the add-on modules &lt;a href="https://docs.apryse.com/core/guides/ocr" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for the current list and installation steps.&lt;/p&gt;
&lt;h2&gt;
  
  
  Export OCR to JSON
&lt;/h2&gt;

&lt;p&gt;JSON’s structured format makes it ideal for organized data storage and seamless exchange between applications and systems. When you export OCR to JSON, you not only preserve the extracted text but also &lt;a href="https://apryse.com/blog/search-keywords-in-PDF-metadata" rel="noopener noreferrer"&gt;include valuable metadata&lt;/a&gt; – information about the text’s appearance with the document, such as font-size, orientation, and more. This rich data set simplifies storage, access, and future use. Furthermore, the extraction process itself can be implemented with minimal code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; &lt;a href="//Download%20and%20install%20the%20OCR%20module."&gt;Download and install the OCR module. &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Now let’s use OCR to extract the data to a JSON file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;JavaScript&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// Setup empty destination doc&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PDFDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path/to/image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Extract OCR results as JSON&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OCRModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOCRJsonFromImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;PDFNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runWithCleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output Attributes&lt;/strong&gt;&lt;br&gt;
Now that we have our JSON file with the extracted OCR data, we can see the various attributes of the data. Output consists of nested arrays for pages, paragraphs, lines, and words. Pages have the following metadata:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdj1vj4w8j2jeb2oz6rpc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdj1vj4w8j2jeb2oz6rpc.webp" alt="Figure 1: Page metadata in JSON"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each word has the following metadata:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fix3gbr152wid1lij6xpc.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fix3gbr152wid1lij6xpc.webp" alt="Figure 2: Word metadata in JSON"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample JSON Output
&lt;/h2&gt;

&lt;p&gt;Here we can see what the JSON output from OCR extraction could look like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flseiuytfyvhe1vwwk4zr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flseiuytfyvhe1vwwk4zr.webp" alt="Figure 3: Sample JSON output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So, what do I do with the data?
&lt;/h2&gt;

&lt;p&gt;Great question! Now that our data has been extracted, organized, and stored in a JSON file, we can explore its potential uses, such as:&lt;/p&gt;

&lt;p&gt;Storing the dat&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a for later use.&lt;/li&gt;
&lt;li&gt;Using the data in another application, database, or system.&lt;/li&gt;
&lt;li&gt;Applying corrected or processed data back onto the original inputdocument.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, by utilizing the robust Apryse Server SDK's OCR Module to extract OCR data to JSON, businesses can unlock valuable information trapped within static formats. Streamlined by the OCR capabilities, this process makes data searchable and accessible and facilitates streamlined integration with various applications and workflows. When using Apryse’s OCR to JSON functionality, you can maximize efficiency, improve accessibility, and realize the full potential of your information.&lt;/p&gt;

&lt;p&gt;Check out a &lt;a href="https://showcase.apryse.com/" rel="noopener noreferrer"&gt;demo&lt;/a&gt; of &lt;a href="https://apryse.com/capabilities/ocr" rel="noopener noreferrer"&gt;Apryse OCR module&lt;/a&gt; in action. &lt;a href="https://apryse.com/form/contact-sales" rel="noopener noreferrer"&gt;Contact our sales team&lt;/a&gt; for any questions.&lt;/p&gt;

&lt;p&gt;Need help setting up? &lt;a href="https://discord.com/invite/wggqUR9v7T" rel="noopener noreferrer"&gt;Join our Discord community&lt;/a&gt; for support and discussions.&lt;/p&gt;

</description>
      <category>ocr</category>
      <category>json</category>
      <category>automation</category>
      <category>apryse</category>
    </item>
    <item>
      <title>React-pdf vs Apryse WebViewer: Which Is Easier to Customize?</title>
      <dc:creator>Hanna Miller</dc:creator>
      <pubDate>Thu, 27 Aug 2026 19:21:28 +0000</pubDate>
      <link>https://dev.to/apryse/react-pdf-vs-apryse-webviewer-which-is-easier-to-customize-46bj</link>
      <guid>https://dev.to/apryse/react-pdf-vs-apryse-webviewer-which-is-easier-to-customize-46bj</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2s0g0b1ni5w040p9q08r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2s0g0b1ni5w040p9q08r.png" alt="When it comes to react-pdf vs. Apryse WebViewer — which is easier to customize?" width="799" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Customizable" means different things depending on where you're starting from and for React developers choosing between react-pdf and Apryse WebViewer, that distinction shapes months of engineering time down the line. This post breaks down the real difference: react-pdf hands you a lightweight rendering foundation you build everything on top of, while WebViewer gives you a full document experience you customize instead. Including a real-world example of a team that outgrew the build-it-yourself approach, here's what to weigh before you commit.&lt;/p&gt;

&lt;p&gt;Developers often compare react-pdf and &lt;a href="https://apryse.com/products/webviewer" rel="noopener noreferrer"&gt;Apryse WebViewer&lt;/a&gt;, a &lt;a href="https://apryse.com/blog/react-pdf-alternatives-production-applications" rel="noopener noreferrer"&gt;react-pdf alternative&lt;/a&gt;, when building React applications.&lt;/p&gt;

&lt;p&gt;Both can be customized. The difference lies in what you are customizing.&lt;/p&gt;

&lt;h2&gt;
  
  
  React-pdf
&lt;/h2&gt;

&lt;p&gt;React-pdf focuses on rendering PDF documents inside React applications. It intentionally provides a lightweight foundation.&lt;br&gt;
Developers typically build their own toolbar, navigation, search, annotation experience, document workflows, and branding.&lt;br&gt;
This approach offers maximum implementation freedom but places more responsibility on the application team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apryse WebViewer
&lt;/h2&gt;

&lt;p&gt;Apryse WebViewer starts from a different philosophy. Rather than providing only rendering, it includes a complete document experience with a modular user interface.&lt;br&gt;
Developers can customize colors and themes, branding, toolbars, menus, side panels, workflows, permissions, annotation behavior, and integrations.&lt;/p&gt;

&lt;p&gt;Instead of building these capabilities from scratch, teams customize an existing foundation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Is More Customizable?
&lt;/h2&gt;

&lt;p&gt;The answer depends on what "customizable" means.&lt;/p&gt;

&lt;p&gt;If customization means changing source code for a minimal viewer, react-pdf offers that flexibility.&lt;/p&gt;

&lt;p&gt;If customization means creating a production-ready, branded document experience with minimal rebuilding, Apryse WebViewer provides substantially more functionality to customize from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Blue Voice initially built its application using an open-source React PDF viewer. As the product expanded across police departments, maintaining PDF functionality internally began consuming engineering time that could have been spent improving the company's core product. The team ultimately adopted Apryse WebViewer, allowing developers to focus on customer-facing innovation instead of maintaining document infrastructure. They later expanded their application with AI-powered document workflows built on top of the same platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Should You Choose?
&lt;/h2&gt;

&lt;p&gt;Choose react-pdf if your application only requires PDF rendering and you expect to build the surrounding experience yourself.&lt;/p&gt;

&lt;p&gt;Choose Apryse WebViewer if you want a customizable document platform that includes advanced capabilities while remaining flexible enough to match your application's design and workflows.&lt;/p&gt;

&lt;p&gt;For many teams, the decision of react-pdf vs Apryse WebViewer is less about open source versus commercial software and more about whether they want to build document infrastructure themselves or customize an existing platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is react-pdf harder to customize than Apryse WebViewer?&lt;/strong&gt;&lt;br&gt;
Not harder but different. React-pdf gives full source-level control over a minimal viewer; Apryse WebViewer gives a full-featured, modular UI that's customized rather than built from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I migrate from react-pdf to Apryse WebViewer?&lt;/strong&gt;&lt;br&gt;
Yes. Teams commonly migrate once the surrounding experience they've built on react-pdf, such as toolbar, search, annotations, workflows, becomes more costly to maintain than adopting a platform that includes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Apryse WebViewer work well in React applications?&lt;/strong&gt;&lt;br&gt;
Yes. WebViewer integrates into React applications and provides the modular UI, annotation, and workflow capabilities react-pdf leaves to the development team to build.&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactjsdevelopment</category>
      <category>developer</category>
    </item>
    <item>
      <title>Best React PDF Viewer Alternatives for Production Applications</title>
      <dc:creator>Hanna Miller</dc:creator>
      <pubDate>Tue, 25 Aug 2026 14:53:50 +0000</pubDate>
      <link>https://dev.to/apryse/best-react-pdf-viewer-alternatives-for-production-applications-5hfi</link>
      <guid>https://dev.to/apryse/best-react-pdf-viewer-alternatives-for-production-applications-5hfi</guid>
      <description>&lt;p&gt;React-pdf will get you PDF rendering fast, but for teams building production applications, the real question is what happens when your document requirements outgrow that lightweight foundation. This post lays out how react-pdf and Apryse WebViewer compare on the features that tend to surface as products mature: annotations, forms, redaction, signatures, and UI customization. With a quick-reference comparison table and a real migration story, it’s a practical read for anyone choosing a React PDF viewer with an eye on where their app is headed, not just where it is today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short Answer
&lt;/h2&gt;

&lt;p&gt;If you need a basic PDF viewer in a React application, react-pdf (built on Mozilla PDF.js) is an excellent starting point.&lt;/p&gt;

&lt;p&gt;If you need a viewer that can grow with your application — supporting advanced document workflows, extensive UI customization, &lt;a href="https://apryse.com/capabilities/annotation" rel="noopener noreferrer"&gt;annotations&lt;/a&gt;, redaction, signatures, and additional document formats — &lt;a href="https://apryse.com/products/webviewer" rel="noopener noreferrer"&gt;Apryse WebViewer&lt;/a&gt; is the stronger long-term choice.&lt;/p&gt;

&lt;p&gt;The right decision depends less on whether a library is open source and more on how much functionality your application will eventually require.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9tt9a6w6wiuntsunyp7v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9tt9a6w6wiuntsunyp7v.png" alt="Comparison table of react-pdf/PDF.js and Apryse WebViewer" width="677" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Which React PDF Viewer Is Most Adaptable?
&lt;/h2&gt;

&lt;p&gt;If adaptability means the ability to customize your application’s document experience over time, Apryse WebViewer provides significantly more flexibility than many developers expect.&lt;/p&gt;

&lt;p&gt;While react-pdf is open source, it intentionally provides a lightweight viewer. Development teams typically build their own toolbar, search experience, annotation interface, document workflows, and branding on top of it.&lt;/p&gt;

&lt;p&gt;Apryse WebViewer approaches the problem differently. It includes a fully featured viewer while exposing a modular, themeable UI that developers can customize, extend, or replace without rebuilding core document functionality.&lt;/p&gt;

&lt;p&gt;For many production teams, that means spending more time building product-specific features instead of document infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Cost of Starting Simple
&lt;/h2&gt;

&lt;p&gt;Open-source libraries reduce licensing costs, but they do not eliminate engineering costs.&lt;/p&gt;

&lt;p&gt;As applications mature, teams often need features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;Annotation&lt;/li&gt;
&lt;li&gt;Document comparison&lt;/li&gt;
&lt;li&gt;Redaction&lt;/li&gt;
&lt;li&gt;Digital signatures&lt;/li&gt;
&lt;li&gt;Accessibility improvements&lt;/li&gt;
&lt;li&gt;Workflow customization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each additional requirement increases the amount of custom code that must be maintained.&lt;/p&gt;

&lt;p&gt;Many organizations eventually decide that maintaining document infrastructure is not where they want to invest engineering effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Blue Voice initially built its application using an open-source React PDF viewer. As the product expanded across police departments, maintaining PDF functionality internally began consuming engineering time that could have been spent improving the company’s core product. The team ultimately adopted Apryse WebViewer, allowing developers to focus on customer-facing innovation instead of maintaining document infrastructure. They later expanded their application with AI-powered document workflows built on top of the same platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apryse.com/blog/customers/blue-voice-ai-policy-access-apryse" rel="noopener noreferrer"&gt;Learn how Blue Voice uses Apryse WebViewer to deliver AI-powered policy access across 200+ police departments.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is react-pdf good enough for production?&lt;/strong&gt;&lt;br&gt;
Yes. For applications that only need reliable PDF rendering and basic viewing. Applications that require advanced workflows often find themselves implementing many features manually over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Apryse WebViewer customizable?&lt;/strong&gt;&lt;br&gt;
Yes. The UI is modular and themeable, allowing organizations to customize branding, layouts, workflows, and user experience without rebuilding a document viewer from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which PDF viewer is better for enterprise applications?&lt;/strong&gt;&lt;br&gt;
For teams that expect document requirements to grow over time, Apryse WebViewer generally requires less custom infrastructure while providing a broader set of document capabilities.&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>react</category>
      <category>reactjsdevelopment</category>
    </item>
  </channel>
</rss>
