<?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: Kittisak Ma</title>
    <description>The latest articles on DEV Community by Kittisak Ma (@9haroon).</description>
    <link>https://dev.to/9haroon</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%2F1121960%2F1c75ba41-ef71-4ad3-8051-79c2a55058e0.jpeg</url>
      <title>DEV Community: Kittisak Ma</title>
      <link>https://dev.to/9haroon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/9haroon"/>
    <language>en</language>
    <item>
      <title>PDFium vs PDF.js: Choosing a React PDF Engine (2026)</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Tue, 18 Aug 2026 07:14:10 +0000</pubDate>
      <link>https://dev.to/9haroon/pdfium-vs-pdfjs-choosing-a-react-pdf-engine-2026-220p</link>
      <guid>https://dev.to/9haroon/pdfium-vs-pdfjs-choosing-a-react-pdf-engine-2026-220p</guid>
      <description>&lt;p&gt;A few months ago, I opened an architecture layout in a PDF.js-based viewer. It loaded fine. Then I zoomed in, and the re-render lagged. Zoom out, lag again. The page held thousands of vector strokes, and every zoom step seemed to redraw all of them. That's the kind of thing that starts a PDFium vs PDF.js argument in your own head.&lt;/p&gt;

&lt;p&gt;Chrome's built-in viewer renders with PDFium, so I opened the same file there to check whether the engine was the difference. It re-rendered smoothly. Later, while researching what PDFium can actually do in a browser, I came across EmbedPDF, which runs PDFium through WebAssembly, and tried the file in that too. Also smooth. That's when I started taking the engine question seriously. And as I dug in, I found the lag traced to viewer zoom strategy more than to the engine itself (more on that below).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quick disclosure: I work on React PDF Kit, which runs on PDF.js. So I have a side, and that's exactly why I tested PDFium fairly before writing this. If anything, I've overcorrected in its favor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most content on this topic is either marketing from companies selling a PDFium-based SDK, or a &lt;a href="https://groups.google.com/g/emscripten-discuss/c/nO0MvRpeJLg/m/wakkMPqNX_oJ" rel="noopener noreferrer"&gt;2014 benchmark&lt;/a&gt; of an early PDFium-to-JS port that beat PDF.js with the text layer switched off. There's no current take from a React developer's point of view. The belief that PDFium just renders better leans on &lt;a href="https://dropbox.tech/infrastructure/improving-document-preview-performance" rel="noopener noreferrer"&gt;Dropbox's 2017 migration writeup&lt;/a&gt; and vendor blog posts, with no neutral in-browser benchmark behind it.&lt;/p&gt;

&lt;p&gt;So this isn't a horse race, it's an architecture decision. PDFium's strongest arguments come first here, so don't read the running order as a verdict. The one edge people reliably hand PDFium is color. It turns out PDF.js can match it, through color management that ships but sits off by default behind a setting almost nobody flips on. So PDFium's real advantage is narrower than its reputation: it gets color right with nothing to configure. PDF.js's clean wins are the text layer and bundle size. I'll show you what's verifiable and flag what you should prototype yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What PDFium and PDF.js actually are
&lt;/h2&gt;

&lt;p&gt;You probably know PDF.js already. It's Mozilla's JavaScript PDF renderer, and it parses and rasterizes PDFs in pure JavaScript onto an HTML5 canvas. Nearly every React PDF library sits on top of it: wojtekmaj/react-pdf, @anaralabs/lector, @mikecousins/react-pdf, the archived react-pdf-viewer (phuocng), and the one I work on, React PDF Kit. As of late July 2026, pdfjs-dist is at 6.1.200 (published June 27, 2026), with around 15M weekly npm downloads and 53K GitHub stars. It's the default substrate for React PDF work.&lt;/p&gt;

&lt;p&gt;PDFium needs more of an intro, because most React devs have used it without knowing. It's Google's C++ PDF engine, originally Foxit's commercial SDK before Google open-sourced it, and it's the engine inside Chrome, Edge, and Android's PDF viewer. Every time you open a PDF in Chrome, that's PDFium doing the work. It's versioned by Chromium milestone, around 150.x right now, not by npm.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;PDF.js&lt;/th&gt;
&lt;th&gt;PDFium&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maker&lt;/td&gt;
&lt;td&gt;Mozilla&lt;/td&gt;
&lt;td&gt;Google (originally Foxit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;C++&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;BSD-3-Clause&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reaches the browser as&lt;/td&gt;
&lt;td&gt;JavaScript plus a worker&lt;/td&gt;
&lt;td&gt;a WebAssembly binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Powers&lt;/td&gt;
&lt;td&gt;Firefox's viewer&lt;/td&gt;
&lt;td&gt;Chrome, Edge, Android viewers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versioned by&lt;/td&gt;
&lt;td&gt;pdfjs-dist on npm (6.1.200)&lt;/td&gt;
&lt;td&gt;Chromium milestone (~150.x)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The catch is that PDFium is C++. To run in a browser it has to be compiled to WebAssembly. That one fact is the whole story: one engine is JavaScript that ships as JavaScript, the other is C++ that ships as a WASM binary. Most of the tradeoffs in this piece fall straight out of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The library landscape: which engine is under your viewer
&lt;/h2&gt;

&lt;p&gt;Most of the time you don't pick an engine directly. You pick a React library, and it picks the engine. Here's the map.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;wojtekmaj/react-pdf&lt;/td&gt;
&lt;td&gt;PDF.js&lt;/td&gt;
&lt;td&gt;Rendering primitives, no built-in viewer UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@anaralabs/lector&lt;/td&gt;
&lt;td&gt;PDF.js&lt;/td&gt;
&lt;td&gt;Headless viewer toolkit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@mikecousins/react-pdf&lt;/td&gt;
&lt;td&gt;PDF.js&lt;/td&gt;
&lt;td&gt;Lightweight hook-based viewer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;react-pdf-viewer (phuocng)&lt;/td&gt;
&lt;td&gt;PDF.js&lt;/td&gt;
&lt;td&gt;Archived March 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React PDF Kit&lt;/td&gt;
&lt;td&gt;PDF.js&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@react-pdf-kit/viewer&lt;/code&gt; 2.7.1, on pdfjs-dist 5.4.530&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EmbedPDF&lt;/td&gt;
&lt;td&gt;PDFium (WASM)&lt;/td&gt;
&lt;td&gt;by CloudPDF, 2.14.4, MIT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The PDF.js column is crowded because PDF.js is the default substrate for React PDF work. If you want a PDF.js alternative in React at the engine level, EmbedPDF is effectively the production-ready option today, with low-level wrappers like &lt;code&gt;@embedpdf/pdfium&lt;/code&gt; and &lt;code&gt;@hyzyla/pdfium&lt;/code&gt; underneath it.&lt;/p&gt;

&lt;p&gt;Two caveats before you use that table. EmbedPDF is one PDFium-in-React viewer, not PDFium itself, so don't treat "embedpdf react" and "PDFium" as synonyms. And the commercial SDKs sit in their own tier. Nutrient (formerly PSPDFKit) actually runs a PDFium-derived fork, while Apryse (formerly PDFTron) runs its own engine, so "commercial means custom engine" is only half true. I couldn't confirm Syncfusion's engine lineage, so I left it out rather than guess.&lt;/p&gt;

&lt;p&gt;For the full feature-by-feature comparison of the React libraries themselves, see &lt;a href="https://www.react-pdf-kit.dev/blog/how-to-build-a-pdf-viewer-in-react-with-pdf-js-ai-wojtekmaj-react-pdf-anaralabs-lector-and-react-pdf-kit-2026?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=pdfium-vs-pdfjs-choosing-a-react-pdf-engine-2026-220p"&gt;our React PDF library comparison&lt;/a&gt; on the blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering fidelity: fonts, color, and complex graphics
&lt;/h2&gt;

&lt;p&gt;This is the number one reason a team looks at PDFium. It's the engine Chrome ships, so its output matches what users see when they open the file natively, and it tends to match Acrobat on the weird stuff. PDF.js does its parsing and font work in JavaScript, and that's where the gaps show up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Color and CMYK
&lt;/h3&gt;

&lt;p&gt;Start here, because it's the claim people are surest about, and the one that turns out to be backwards. A quick note on terms, since not everyone works in print. CMYK is the four-ink color model print uses, screens use RGB, and an ICC profile carries a document's instructions for how its colors should map between the two. The reputation says PDF.js can't manage color: it renders in RGB, ignores embedded ICC profiles, and converts CMYK through a rough built-in fallback. That stopped being true in 5.0, when PDF.js bundled QCMS, Mozilla's own color engine, as a WebAssembly module, together with a CGATS001 DeviceCMYK reference profile. That is real ICC color management. It's just off by default.&lt;/p&gt;

&lt;p&gt;Two &lt;code&gt;getDocument&lt;/code&gt; options switch it on. &lt;code&gt;wasmUrl&lt;/code&gt; turns on the ICC path for embedded profiles, so ICCBased streams run through QCMS instead of the old alternate-colorspace fallback, the embedded-profile gap that &lt;a href="https://github.com/mozilla/pdf.js/issues/2856" rel="noopener noreferrer"&gt;issue #2856&lt;/a&gt; tracked for years. &lt;code&gt;iccUrl&lt;/code&gt; goes further and routes DeviceCMYK through an ICC transform against the CGATS001 profile rather than a polynomial approximation, which is the imperfect default conversion a Mozilla maintainer described in &lt;a href="https://github.com/mozilla/pdf.js/issues/8500" rel="noopener noreferrer"&gt;issue #8500&lt;/a&gt;. Both of those long-standing complaints describe the default path, and the ICC path is what closes them.&lt;/p&gt;

&lt;p&gt;The gap is easy to measure and easy to see. Open a CMYK-heavy file in a stock PDF.js viewer next to Chrome's native one, and the colors are visibly off. On a DeviceCMYK swatch file, default PDF.js on 5.4.530 (the build tested here) is off from Chrome's PDFium by as much as 60 out of 255 on a single channel. Turn the ICC options on and the same build comes within 3 out of 255, down at the level of screenshot sampling noise. So the color difference people blame on the engine is really a config default, not an engine limit. For marketing collateral, print proofs, packaging, or spot-color design files, that drift is the kind of error that surfaces only when the printed piece comes back wrong. And the fix is a few lines of config.&lt;/p&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%2Fqgbfl28p8c86x2056fam.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%2Fqgbfl28p8c86x2056fam.png" alt="Eight DeviceCMYK swatches rendered three ways, PDF.js with default settings, PDF.js with ICC color management enabled, and Chrome's native PDFium, showing default PDF.js drifting up to 60/255 while the ICC-enabled render matches PDFium within 3/255" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PDFium's color story is narrower than its reputation too, and it's worth stating fairly. It matches Chrome because it is Chrome's engine, through an internal C++ CMYK-to-RGB path rather than app-layer color management. The browser viewers built on it pass that output straight through, so there's no separate ICC pipeline doing the work. What PDFium gives you is correct color with nothing to set up. That's a real convenience, but it's parity by default, not something PDF.js can't do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turning it on, and the trap
&lt;/h3&gt;

&lt;p&gt;Setting &lt;code&gt;wasmUrl&lt;/code&gt; and &lt;code&gt;iccUrl&lt;/code&gt; does nothing on its own. The ICC path only activates when PDF.js is cleared to fetch worker assets, and that clearance turns on only if &lt;code&gt;cMapUrl&lt;/code&gt;, &lt;code&gt;standardFontDataUrl&lt;/code&gt;, and &lt;code&gt;wasmUrl&lt;/code&gt; are all set and all resolve to valid fetch URLs against your document base. Leave out &lt;code&gt;standardFontDataUrl&lt;/code&gt; and the color options are ignored in silence, no error, and no warning unless you've raised the verbosity level. A working call passes all of them:&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;pdfjsLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cMapUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pdfjs/cmaps/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standardFontDataUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pdfjs/standard_fonts/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;wasmUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pdfjs/wasm/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;iccUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pdfjs/iccs/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last catch: those paths resolve inside the worker (the background thread PDF.js does its parsing in), not on the page. A relative URL there is taken relative to the worker script, not your page, so it quietly points at the wrong place. Pass absolute URLs instead. None of this is documented, so you only find it by reading the source, and it's easy to get half-right. That is the real reason so few PDF.js viewers turn color management on, not any missing capability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fonts and CJK
&lt;/h3&gt;

&lt;p&gt;PDF.js converts and sanitizes embedded fonts in JavaScript, which is where its edge cases live: missing glyphs in Source Han Sans (&lt;a href="https://github.com/mozilla/pdf.js/issues/5377" rel="noopener noreferrer"&gt;#5377&lt;/a&gt;), type3 font bugs (&lt;a href="https://github.com/mozilla/pdf.js/issues/19634" rel="noopener noreferrer"&gt;#19634&lt;/a&gt;), a CJK glyph regression (&lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1146106" rel="noopener noreferrer"&gt;Bugzilla 1146106&lt;/a&gt;). If your documents are CJK-heavy, PDFium lands closer to Acrobat more often.&lt;/p&gt;

&lt;p&gt;But most everyday PDFs render fine in PDF.js. &lt;a href="https://hacks.mozilla.org/2014/05/how-fast-is-pdf-js/" rel="noopener noreferrer"&gt;Mozilla's own benchmark&lt;/a&gt; treats the badly-broken cases as unusual files. And not every difference is the engine's fault. &lt;a href="https://github.com/mozilla/pdf.js/issues/19221" rel="noopener noreferrer"&gt;Issue #19221&lt;/a&gt; is a font error that also breaks in Adobe Reader, because the PDF itself is malformed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The gap is a moving target
&lt;/h3&gt;

&lt;p&gt;One more thing on fidelity, and it cuts against my own anchor. PDF.js 6.0 shipped on May 30, 2026, and it landed work in exactly the areas where PDFium historically led: CID and CJK font recovery, Adobe-parity hinting, and knockout and soft-mask transparency fixes. Those are the CJK and obscure-feature gaps, narrowing in a single release.&lt;/p&gt;

&lt;p&gt;So the gap is real today, but it's a trajectory, not a constant. PDFium moves too, with every Chromium milestone, so "date your comparison" cuts both ways. The full v6 rendering story deserves its own write-up, and that one's coming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering performance on heavy and complex PDFs
&lt;/h2&gt;

&lt;p&gt;The first question is usually which engine is faster. It depends on your documents, and I'll get to why the public evidence can't settle it. What you feel right away is how each one copes with a heavy document, and it doesn't take an exotic file. An ordinary layout-heavy PDF with many elements and text runs is enough to make any renderer sweat. Past that sit the hard cases: CAD and architectural exports with huge path-operator counts (&lt;a href="https://github.com/mozilla/pdf.js/issues/14652" rel="noopener noreferrer"&gt;PDF.js #14652&lt;/a&gt;), large-format pages with dense vector linework, files with many optional-content layers that cost parse time even when hidden, and multi-hundred-page scans.&lt;/p&gt;

&lt;p&gt;Here's the correction to my own first take. I'd assumed PDF.js re-renders the canvas on every zoom step and therefore lags. That's an oversimplification. Zoom smoothness depends on the &lt;strong&gt;viewer's zoom strategy&lt;/strong&gt; more than on the engine.&lt;/p&gt;

&lt;p&gt;Some PDF.js viewers apply a cheap visual scale during the gesture and defer the sharp re-render until you let go, which feels smooth even on dense pages at the cost of a moment of blur. Others re-raster at every step, staying sharp throughout but lagging on complex documents. PDFium viewers pair native or WASM rasterization with virtual scrolling and tiling, which reads as responsive, but they pay the same cost re-rasterizing dense vector content at high zoom.&lt;/p&gt;

&lt;p&gt;That's what I was actually seeing in my architecture layout. It painted fine, then lagged on every scale change in one PDF.js viewer. The same file zoomed smoothly in Chrome's native PDFium, in EmbedPDF, and in the hosted PDF.js demo. The demo isn't getting that for free from the engine. It CSS-scales the existing canvas during the gesture and defers the sharp re-render until about 400 ms after you stop zooming, while the viewer I started in re-rasterizes on every step. Same engine, opposite feel. What I hit was a viewer-configuration gap, not the PDF.js engine failing to keep up.&lt;/p&gt;

&lt;p&gt;The strain underneath is still real. Canvas trouble on complex pages is a known PDF.js limit (&lt;a href="https://github.com/mozilla/pdf.js/issues/7265" rel="noopener noreferrer"&gt;issue #7265&lt;/a&gt;). Teams used to reach for the SVG backend as an escape hatch, but it never really worked and has since been removed. &lt;a href="https://apryse.com/blog/pdf-js/pdf-js-reliability-benchmark" rel="noopener noreferrer"&gt;Apryse's 2019 benchmark&lt;/a&gt;, from a vendor selling a competing engine, found CAD exports from GrabCAD failing to open or crashing the browser as often as 1 in 10. That's a limit on what's practical in a browser without careful viewer engineering, whichever engine you pick.&lt;/p&gt;

&lt;p&gt;And this is where the evidence gets thinnest. Dropbox's famous result, halving 75th-percentile Time to Interactive, was server-side PDFium-to-PNG in 2017, not in-browser WASM. The 2014 microbenchmarks disabled the text layer. So I won't tell you PDFium is faster. On heavy PDFs the lag comes from how the viewer is built, not the engine underneath.&lt;/p&gt;

&lt;p&gt;Test what matters to you instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gather your heaviest files: CAD and architectural exports, large scans, layer-heavy documents.&lt;/li&gt;
&lt;li&gt;Open each one in every viewer you're considering, and zoom in and out repeatedly on the densest areas.&lt;/li&gt;
&lt;li&gt;Time how long it takes to get from the gesture to a sharp render, and watch memory on long scrolls.&lt;/li&gt;
&lt;li&gt;Check it on real target hardware, not just your dev machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Forms and form fidelity
&lt;/h2&gt;

&lt;p&gt;The assumption going in is usually "PDFium does forms better." It's mostly a wash, with one persistent myth worth clearing up.&lt;/p&gt;

&lt;p&gt;AcroForms work on both. PDF.js added AcroForm rendering in Firefox 84 and form JavaScript in 88. PDFium renders and fills them with Chrome and Acrobat fidelity, plus signatures. For the forms you'll actually run into, there's no real gap.&lt;/p&gt;

&lt;p&gt;XFA is the part people get wrong. The naive line, "PDFium can't do XFA," is false. PDF.js renders XFA out of the box at the viewer layer (Firefox 93). On the PDFium side it's a build-flag and implementation choice. Chrome's shipped PDFium disables XFA, but the engine has the module and EmbedPDF's own WASM compiles it in. I checked: &lt;code&gt;FPDF_LoadXFA&lt;/code&gt; and the XFA packet functions are exported in EmbedPDF's &lt;code&gt;pdfium.wasm&lt;/code&gt;. Its plugin just doesn't surface XFA in the UI. So PDF.js gives you XFA rendering for free, and on PDFium the engine has it but you'd wire it up yourself. Not "PDFium can't."&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One note on React PDF Kit. The library ships AcroForm only and skips XFA, even though PDF.js the engine could render it. Don't read "PDF.js renders XFA" as "React PDF Kit renders XFA."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But XFA barely comes up anymore. It's deprecated, removed from the PDF 2.0 spec and excluded from PDF/A, surviving only in legacy government, banking, and legal forms. If you don't have XFA, none of this matters. If you do, PDF.js is the lower-effort path. Neither engine is a form-authoring SDK. That's Nutrient, Apryse, or Syncfusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where PDF.js pulls ahead: bundle size, text, and accessibility
&lt;/h2&gt;

&lt;p&gt;The last few sections leaned PDFium's way or came out even. This one doesn't. PDF.js leads on the pieces a viewer is actually made of.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bundle size and load model
&lt;/h3&gt;

&lt;p&gt;PDF.js is the lighter default, and more of its weight is deferrable. You ship the display layer plus a worker. Reported sizes put the minified display layer around 299 KB (&lt;a href="https://github.com/mozilla/pdf.js/issues/9087" rel="noopener noreferrer"&gt;issue #9087&lt;/a&gt;), and a source-map breakdown puts it near 840 KB display plus 699 KB worker uncompressed, so roughly 1 to 1.5 MB before gzip, and less over the wire. The CJK cmaps and standard fonts load on demand through &lt;code&gt;cMapUrl&lt;/code&gt; and &lt;code&gt;standardFontDataUrl&lt;/code&gt;. You don't pay for them unless a document needs them.&lt;/p&gt;

&lt;p&gt;PDFium is a fixed cost. It's one WASM binary, fetched and instantiated up front, the same size no matter how simple the document. I measured EmbedPDF's &lt;code&gt;pdfium.wasm&lt;/code&gt; at 4.63 MB uncompressed, around 2.1 MB gzipped over the wire, fetched from a CDN by default, plus a one-time instantiation cost on cold start. There's no on-demand splitting of the engine itself. It caches after first load and stays constant, which is the upside, but the up-front number is real. If first paint matters in your app, PDF.js starts lighter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Text, search, and accessibility
&lt;/h3&gt;

&lt;p&gt;This is the quiet, decisive one. PDF.js builds a transparent text layer of positioned spans over the canvas. That gives you real selectable text, in-document search that maps to actual glyphs, and tagged-PDF accessibility for screen readers, which landed in Firefox 89. Those are the features you build a viewer around.&lt;/p&gt;

&lt;p&gt;PDFium hands you none of that in the browser. Dropbox noted that text extraction and positioning were trickier with PDFium than with PDF.js. &lt;a href="https://groups.google.com/g/pdfium-bugs/c/yt0WJ5h-GcU" rel="noopener noreferrer"&gt;pdfium-bugs #1523&lt;/a&gt; is a screen-reader user flagging missing selection support. EmbedPDF rebuilds its own text and selection layer on top of PDFium to fill the hole, which works, but it's work the viewer has to redo that PDF.js gives you for free. If you want to understand how this layer actually works, we covered &lt;a href="https://www.react-pdf-kit.dev/blog/understanding-pdfjs-layers-and-how-to-use-them-in-reactjs" rel="noopener noreferrer"&gt;PDF.js layers&lt;/a&gt; in a separate article on the React PDF Kit blog.&lt;/p&gt;

&lt;h3&gt;
  
  
  A smaller security surface
&lt;/h3&gt;

&lt;p&gt;PDF.js runs as ordinary JavaScript using only normal web APIs, so its attack surface is small and browser-standard. PDFium is C++, with the usual memory-safety bug class and historically a larger surface, mitigated by Google's continuous updates, sandboxing, and the browser's WASM sandbox. Both engines restrict the Acrobat JavaScript APIs, and both run client-side now, so files don't have to leave the device.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means in a React project
&lt;/h2&gt;

&lt;p&gt;Engine choice eventually becomes a setup choice. This is what each one actually looks like in React and Next.js.&lt;/p&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%2Ft464gi7q72fu9lvb5njj.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%2Ft464gi7q72fu9lvb5njj.png" alt="PDF.js JavaScript-and-worker architecture versus PDFium WebAssembly architecture" width="799" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the PDF.js side, you need pdfjs-dist and a configured worker. React PDF Kit handles the worker for you. It sets &lt;code&gt;GlobalWorkerOptions.workerSrc&lt;/code&gt; to a bundled worker file behind a window guard, and lets you override it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pdfjs&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;pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultWorkerUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdfjs-dist/build/pdf.worker.min.mjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;
  &lt;span class="nx"&gt;pdfjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GlobalWorkerOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workerSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;workerUrl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;defaultWorkerUrl&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The viewer's entry components are marked &lt;code&gt;'use client'&lt;/code&gt;, so in Next.js App Router you load it as a client component. When something breaks, you debug it with normal JavaScript stack traces in devtools. That's underrated.&lt;/p&gt;

&lt;p&gt;On the PDFium side, EmbedPDF boots the engine through a hook. &lt;code&gt;usePdfiumEngine()&lt;/code&gt; fetches and initializes the PDFium WASM (from a CDN by default, with a &lt;code&gt;worker: true&lt;/code&gt; option), and you hand the engine to a provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;EmbedPDF&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;@embedpdf/core/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;usePdfiumEngine&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;@embedpdf/engines/react&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePdfiumEngine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;// register plugins: viewport, scroll, render, tiling, search, selection&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;&lt;/span&gt;&lt;span class="nc"&gt;EmbedPDF&lt;/span&gt; &lt;span class="na"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Viewport with render, search, and selection layers */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;EmbedPDF&lt;/span&gt;&lt;span class="p"&gt;&amp;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;p&gt;The manual C-style memory model PDFium is known for (&lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt;, &lt;code&gt;FPDF_LoadMemDocument&lt;/code&gt;, &lt;code&gt;FPDF_GetLastError&lt;/code&gt;) is real, but it lives inside the library. App developers use &lt;code&gt;usePdfiumEngine&lt;/code&gt;, not raw memory.&lt;/p&gt;

&lt;p&gt;One misconception to clear up: both of these are client-only in Next.js, but the mechanism differs. React PDF Kit uses &lt;code&gt;'use client'&lt;/code&gt;. EmbedPDF uses &lt;code&gt;typeof window&lt;/code&gt; guards plus dynamic imports. SSR-skipping isn't a PDF.js-only tax. The real difference is a worker file to resolve versus a WASM binary to fetch, which is a different problem, not a harder one in absolute terms.&lt;/p&gt;

&lt;p&gt;If you're on PDF.js and don't want to reimplement worker config and the client-only wrapper for the hundredth time, that's the gap React PDF Kit fills. It's tailored for React, and it's built to save you the weeks that the worker setup and the surrounding viewer features usually cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  PDFium vs PDF.js: how to choose
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pick PDFium when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need Chrome and Acrobat color parity with nothing to configure. PDF.js can match it, but only once you wire up its ICC options, so if you don't control the viewer's config, PDFium gets you there by default.&lt;/li&gt;
&lt;li&gt;Your documents are CJK-heavy, or lean toward construction, large-format, or technical drawings where Acrobat parity is the explicit bar.&lt;/li&gt;
&lt;li&gt;You're shipping a document-review tool where a wrong color or a dropped glyph is a real problem. PDFium earns its WASM payload here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pick PDF.js when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want the broader ecosystem and JavaScript-native debugging, the path nearly every React PDF library already takes.&lt;/li&gt;
&lt;li&gt;The mature text, search, and accessibility layer matters more to you than fidelity on the cases above.&lt;/li&gt;
&lt;li&gt;Color-critical work doesn't force you off PDF.js by itself. The ICC path ships in 5.x, so you can turn on color-managed CMYK when you need it, as long as you clear the config trap above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Two honest footnotes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;v6 isn't a free upgrade. PDF.js 6 raises the minimum supported browser versions even in its legacy build, so staying on the 5.x line is a real fidelity-versus-browser-support tradeoff.&lt;/li&gt;
&lt;li&gt;The comparison dates fast in both directions. Even Dropbox, whose 2017 migration anchors a lot of the received wisdom here, has since &lt;a href="https://gubello.me/blog/pdf-viewers-xss-and-pdf-files/" rel="noopener noreferrer"&gt;moved its previews off PDFium to a commercial SDK&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One honest caveat before you commit: Forms are a nuance, not a clean win for either engine, and even color, the one edge this piece let PDFium keep, turns out to be a PDF.js setting that ships off by default rather than a missing capability. The "PDFium just renders better and faster" reputation rests on dated and vendor evidence with no current neutral benchmark behind it. So don't take my word for it or theirs: prototype both engines on your trickiest documents and see what actually differs.&lt;/p&gt;

&lt;p&gt;If you're staying on PDF.js and want the viewer layer handled instead of rebuilt, React PDF Kit's docs and live demos are at &lt;a href="https://www.react-pdf-kit.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=pdfium-vs-pdfjs-choosing-a-react-pdf-engine-2026-220p"&gt;react-pdf-kit.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And put a date on whatever you decide. PDF.js ships roughly once a month and PDFium tracks Chromium, so there's no permanent answer here, only the right one for your documents on today's builds.&lt;/p&gt;

</description>
      <category>react</category>
      <category>pdf</category>
      <category>javascript</category>
      <category>pdfium</category>
    </item>
    <item>
      <title>How to Add PDF Annotations in Vue.js: PDF.js, vue-pdf-embed, @tato30/vue-pdf, and Vue PDF Viewer (2026)</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Tue, 04 Aug 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/9haroon/how-to-add-pdf-annotations-in-vuejs-pdfjs-vue-pdf-embed-tato30vue-pdf-and-vue-pdf-viewer-5b40</link>
      <guid>https://dev.to/9haroon/how-to-add-pdf-annotations-in-vuejs-pdfjs-vue-pdf-embed-tato30vue-pdf-and-vue-pdf-viewer-5b40</guid>
      <description>&lt;p&gt;Vue PDF Viewer didn't start as an annotation tool. It started as a viewer. Render a PDF, give it a toolbar, let people zoom and search and print. That's what people asked for first, and for its first year that was the whole product.&lt;/p&gt;

&lt;p&gt;Then, our users wanted to mark up the documents they were reading. So we added the first annotation tools in September 2025: highlight, free text, and image. The requests didn't stop there, because a highlighter alone can't say everything people need to say on a document. A highlight means "pay attention here," an underline marks a reference, a strikethrough means "cut this." Force all three through one yellow highlight and you lose the distinction, so underline and strikethrough followed in February 2026. I worked on that plugin. That's the disclosure, and it's also why I can tell you where each option's seams are, ours included.&lt;/p&gt;

&lt;p&gt;Here's the part worth knowing before you write any code. Vue PDF Viewer renders with PDF.js, like most of the Vue ecosystem, so PDF.js's own annotation editor was the obvious place to start on the creation side. It creates free text, ink, and stamps, and it has a highlight tool. It has no underline tool and no strikethrough tool. That gap has held for years. And the highlight it does give you is free-form rather than snapping cleanly to a text selection. So the text-selection toolkit ended up being something we built ourselves.&lt;/p&gt;

&lt;p&gt;"Annotations" sounds like one feature. It's really three separate jobs. This walks through how to add PDF annotations in Vue.js across four approaches (PDF.js directly, vue-pdf-embed, @tato30/vue-pdf, and Vue PDF Viewer), plus a couple of escape hatches for when none of them fit.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Versions tested: Vue 3.5.x, pdfjs-dist 5.4.530, vue-pdf-embed 2.1.4, @tato30/vue-pdf 2.1.0, @vue-pdf-viewer/viewer 4.3.0 with @vue-pdf-viewer/annotation 1.6.0, on Chrome and macOS.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering, creating, and persisting are three different jobs
&lt;/h2&gt;

&lt;p&gt;There are three problems hiding inside the word "annotation," and conflating them is the most expensive mistake you can make. Someone drops a marked-up PDF into each demo, sees every highlight render perfectly, picks the smallest library, and finds out two sprints later that it can't create a highlight at all.&lt;/p&gt;

&lt;p&gt;The first is &lt;strong&gt;rendering&lt;/strong&gt;: a PDF may already contain annotations (highlights, notes, stamps someone added in Acrobat), and your viewer has to display them. This is the easy problem. Every library here handles it, because PDF.js handles it.&lt;/p&gt;

&lt;p&gt;The second is &lt;strong&gt;creating&lt;/strong&gt;: your user selects a sentence and highlights it, draws on the page, leaves a comment. Different problem. It needs editing tools, a toolbar, state, and a way to turn a drag of the mouse into an annotation the PDF format understands. This is where the libraries split apart, and where most of them stop.&lt;/p&gt;

&lt;p&gt;The third is &lt;strong&gt;persisting&lt;/strong&gt;: the highlight your user just made has to survive a reload, a round-trip to your backend, and ideally open correctly in someone else's PDF reader. Teams tend to find this one last, usually after shipping, when it's too late to change the first two choices.&lt;/p&gt;

&lt;p&gt;A library can render annotations perfectly and still create none of its own, or let users create them and then save them where no other reader can read them. Decide which of the three you need before you evaluate anything:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Render existing&lt;/th&gt;
&lt;th&gt;Create new&lt;/th&gt;
&lt;th&gt;Persist &amp;amp; export&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PDF.js (direct)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partial, no underline/strikethrough&lt;/td&gt;
&lt;td&gt;DIY, and fragile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vue-pdf-embed&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;@tato30/vue-pdf&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (beta, single-page)&lt;/td&gt;
&lt;td&gt;DIY&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vue PDF Viewer&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (5 types)&lt;/td&gt;
&lt;td&gt;Save / print / download, XFDF in and out&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We'll take them one problem at a time, starting with the easy one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering existing PDF annotations costs you one prop
&lt;/h2&gt;

&lt;p&gt;Someone marks up a contract in Acrobat. Three highlights, a strikethrough through a clause they want gone, an ink scribble in the margin, one sticky note. They email you the file and your Vue app has to show all of it.&lt;/p&gt;

&lt;p&gt;That's one prop.&lt;/p&gt;

&lt;p&gt;PDF.js ships an annotation layer that walks the annotation objects already stored in the file and draws them over the rendered page. Both lightweight Vue libraries expose it the same way, as a boolean.&lt;/p&gt;

&lt;p&gt;vue-pdf-embed (hrynko's package on npm, not &lt;code&gt;vue-pdf&lt;/code&gt; by FranckFreiburger, which is Vue 2 only and abandoned since 2021):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VuePdfEmbed&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;vue-pdf-embed&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;vue-pdf-embed/dist/styles/annotationLayer.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;VuePdfEmbed&lt;/span&gt; &lt;span class="na"&gt;source=&lt;/span&gt;&lt;span class="s"&gt;"/marked-up.pdf"&lt;/span&gt; &lt;span class="na"&gt;annotation-layer&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;@tato30/vue-pdf does the same job with a per-page API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;VuePDF&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;usePDF&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;@tato30/vue-pdf&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;@tato30/vue-pdf/style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePDF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/marked-up.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;VuePDF&lt;/span&gt; &lt;span class="na"&gt;:pdf=&lt;/span&gt;&lt;span class="s"&gt;"pdf"&lt;/span&gt; &lt;span class="na"&gt;annotation-layer&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you forget the stylesheet, the layer mounts invisibly, which is the one catch here and costs an afternoon the first time. Otherwise you get the whole subtype set: link, text, highlight, underline, strikeout, squiggly, ink, stamp, free text. Underline and strikeout included, which matters shortly.&lt;/p&gt;

&lt;p&gt;Our own changelog puts a date on just how far rendering ran ahead of creating. Vue PDF Viewer v1.5.0 shipped on 29 October 2024 and rendered highlight, strikeout, underline, squiggly, ink and free text. The first annotation a user of ours could &lt;em&gt;create&lt;/em&gt; shipped in September 2025, eleven months later, and it was highlight, free text and image only. Underline and strikethrough creation didn't release until February 2026, sixteen months after we could already draw them on a page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating PDF annotations is where the libraries come apart
&lt;/h2&gt;

&lt;p&gt;Four ways to let a user make a mark, ordered by how much ships versus how much you build.&lt;/p&gt;

&lt;h3&gt;
  
  
  The PDF.js annotation editor
&lt;/h3&gt;

&lt;p&gt;Everything below except pdf-lib is standing on this, so start here. PDF.js has an editor layer, the same one behind the markup tools in Firefox's built-in viewer. You turn it on and set a mode:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AnnotationEditorType&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;pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// on a PDFViewer instance you've already set up yourself&lt;/span&gt;
&lt;span class="nx"&gt;pdfViewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;annotationEditorMode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AnnotationEditorType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HIGHLIGHT&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// FREETEXT | HIGHLIGHT | INK | STAMP, plus a signature editor in recent builds.&lt;/span&gt;
&lt;span class="c1"&gt;// There is no AnnotationEditorType.UNDERLINE.&lt;/span&gt;
&lt;span class="c1"&gt;// There is no AnnotationEditorType.STRIKEOUT.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That enum is the entire story. Free text, highlight, ink, stamp, and a signature editor built on top of stamp in recent builds. No underline tool. No strikethrough tool. Not on any version, including Mozilla's unreleased development code. This isn't a "not yet," it's the shape the project has had for years, and the annotations it refuses to author are the same ones it renders without complaint if they're already in the file.&lt;/p&gt;

&lt;p&gt;On versions: if you're building straight on PDF.js, 6.1.200 is the current release, and none of the above changes. We test against the 5.x line ourselves, because Vue PDF Viewer's peer range (&lt;code&gt;^5.4.530&lt;/code&gt;) excludes 6.x by construction.&lt;/p&gt;

&lt;p&gt;The highlight tool you do get is free-form. There's a text-selection mode in there, and on a clean single-column page it behaves. Give it a two-column layout with a table in the middle, though, and the snapping gets too unreliable to put in front of users. That's our experience building on it rather than a documented limitation, so weigh it as such. What you can promise is "highlight a region," not "highlight that sentence."&lt;/p&gt;

&lt;p&gt;Two more things fall on you. The marks the editor makes are HTML overlays on top of the page, not part of the PDF itself. Draw a highlight in &lt;a href="https://mozilla.github.io/pdf.js/web/viewer.html" rel="noopener noreferrer"&gt;Mozilla's demo viewer&lt;/a&gt;, open DevTools, and here is what you actually made:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"annotationEditorLayer highlightEditing"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pdfjs_internal_editor_0"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"highlightEditor selectedEditor"&lt;/span&gt;
       &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"mark"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Highlight editor"&lt;/span&gt;
       &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"left: 51.69%; top: 49.9%; width: 39.36%; height: 3.99%;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"internal"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"clip-path: url(#clip_path_0);"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"editToolbar"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"toolbar"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;…&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A percentage-positioned &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with a clip-path, sitting in a sibling layer above &lt;code&gt;canvasWrapper&lt;/code&gt; and &lt;code&gt;textLayer&lt;/code&gt;. Note the id: &lt;code&gt;pdfjs_internal_editor_0&lt;/code&gt;. Internal, as the name says. It's a DOM node in your app, not a byte in the PDF. Saving it into the file where another reader can see it is a separate job (that's Section 4's problem). And the toolbar is yours to build. All of it: the tool picker, the colour swatches, the delete button, and the state that tracks which tool is active.&lt;/p&gt;

&lt;h3&gt;
  
  
  @tato30/vue-pdf's editor-layer is that same editor in Vue clothing
&lt;/h3&gt;

&lt;p&gt;This is not a second, separate approach. It's the PDF.js editor from above, wrapped in a Vue component so you're setting a prop instead of managing a viewer instance. That wrapping earns @tato30 a distinction worth naming: it's the only lightweight Vue PDF library that creates annotations at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;VuePDF&lt;/span&gt; &lt;span class="na"&gt;:pdf=&lt;/span&gt;&lt;span class="s"&gt;"pdf"&lt;/span&gt; &lt;span class="na"&gt;:page=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="na"&gt;editor-layer&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get the primitives in Vue components: free text, highlight, ink, stamp, plus comments. If drawing is what you need, ink is the primitive here, and it works.&lt;/p&gt;

&lt;p&gt;Two limits worth knowing before you commit. It's beta-stage, and interactions are limited to single pages, which matters the moment your review UI scrolls. A contract clause that starts at the bottom of page 4 and finishes at the top of page 5 becomes two separate annotations on two &lt;code&gt;&amp;lt;VuePDF&amp;gt;&lt;/code&gt; instances, and stitching them back into one highlight is your code. TaTo30's release notes are unusually straight about the ceiling: other annotation types won't be supported as long as pdf.js doesn't support them. That's the honest answer, and it's also exactly the problem. The library inherits PDF.js's gaps along with its primitives, so underline and strikethrough aren't here either.&lt;/p&gt;

&lt;p&gt;You still build the UX. What you save is the wiring, not the toolbar.&lt;/p&gt;

&lt;h3&gt;
  
  
  pdf-lib, for when you just need it burned in
&lt;/h3&gt;

&lt;p&gt;Different shape entirely. pdf-lib has no viewer and no UI. It's a framework-agnostic library that opens a PDF, draws into it, and hands you back bytes. Useful server-side, or as a pre-render step before the document ever reaches your Vue component.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rgb&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;pdf-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfDoc&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;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingBytes&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;page&lt;/span&gt; &lt;span class="o"&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;getPages&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;APPROVED&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="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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;stamped&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;pdfDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The catch is what those marks are. They're content drawn into the page, not standard PDF annotation objects, so they won't appear in Acrobat's or Preview's annotation panel and nobody can select, edit or delete them afterward. That's the point if you're stamping an approval watermark. It's the wrong tool if a human needs to take the mark back off.&lt;/p&gt;

&lt;p&gt;Maintenance, honestly: v1.17.1 is the latest, released November 2021. Nothing since. It still works, and plenty of production pipelines run on it, but you're adopting a frozen dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Vue PDF Viewer annotation plugin
&lt;/h3&gt;

&lt;p&gt;Our option, and the disclosure from the top of this article applies. Activation is one prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;VPdfViewer&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;@vue-pdf-viewer/viewer&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;VPdfAnnotationPlugin&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;@vue-pdf-viewer/annotation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;VPdfViewer&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/marked-up.pdf"&lt;/span&gt; &lt;span class="na"&gt;:plugins=&lt;/span&gt;&lt;span class="s"&gt;"[VPdfAnnotationPlugin()]"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gets you a create UI for highlight, underline, strikethrough, free text and image (the docs call the image one Image Overlay). Underline and strikethrough are in there because we wrote the text-selection toolkit ourselves rather than inheriting PDF.js's editor, which is the whole reason this article exists. Highlight, free text and image came first in September 2025; underline and strikethrough followed by request in February 2026.&lt;/p&gt;

&lt;p&gt;Now the edges, because you'll find them anyway. Comments are view-only today: the panel displays comment threads already in the file, and there's no create button. Drawing, shapes and a stamp palette aren't in the plugin today (the image tool covers arbitrary user-supplied images, not preset approval stamps), and e-signatures and form-field authoring aren't either; I won't even tell you those two are planned, because they aren't. If ink drawing is your core requirement today, the honest answer is @tato30's editor-layer or raw PDF.js, not this plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persisting PDF annotations is the part that bites you later
&lt;/h2&gt;

&lt;p&gt;Your user highlights a clause and closes the tab. Now what? That question decides more than rendering and creating combined. There are three sub-questions inside it. Where does the annotation data live, how is it serialized (as XFDF, the XML standard other readers can import, or as JSON only your own app understands), and can anything besides your app read it back?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PDF.js editor.&lt;/strong&gt; You save the annotations yourself and store them somewhere. Then there's the problem Section 3 handed forward: those editor overlays are DOM nodes, and getting them into the file is a job PDF.js won't do for you. Your highlights work in your app. Open the same file in Preview and they were never there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@tato30/vue-pdf.&lt;/strong&gt; Same editor, same problem, plus the single-page caveat shaping how you batch what you save.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pdf-lib.&lt;/strong&gt; The marks are drawn straight into the page, which is the point: there's no separate save step, they're just part of the file pdf-lib hands back. No round-trip either. They're page content now, not annotations, so there's nothing to export, edit or remove.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vue PDF Viewer.&lt;/strong&gt; Save, print and download with annotations baked in, plus XFDF export and import through the XFDF Controller in the Instance API. That shipped on 3 July 2026 in annotation v1.6.0 and viewer v4.3.0.&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="c1"&gt;// `viewer` is a template ref on the &amp;lt;VPdfViewer&amp;gt; component&lt;/span&gt;
&lt;span class="c1"&gt;// export the current annotations as an XFDF string, store it wherever you like&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xfdf&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;viewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;annotationXfdfControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exportToXfdf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// later, on a fresh mount of the same document (importFromXfdf is synchronous)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;skipped&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;annotationXfdfControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;importFromXfdf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xfdf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What comes back is a document, not a private blob. A single highlight serializes to something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;xfdf&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://ns.adobe.com/xfdf/"&lt;/span&gt; &lt;span class="na"&gt;xml:space=&lt;/span&gt;&lt;span class="s"&gt;"preserve"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;f&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"contract.pdf"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;annots&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;highlight&lt;/span&gt; &lt;span class="na"&gt;page=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;rect=&lt;/span&gt;&lt;span class="s"&gt;"72.0000,690.2000,331.5000,704.8000"&lt;/span&gt;
               &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"#FFE066"&lt;/span&gt; &lt;span class="na"&gt;opacity=&lt;/span&gt;&lt;span class="s"&gt;"0.4"&lt;/span&gt;
               &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"6f1c…"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Vue PDF Viewer"&lt;/span&gt;
               &lt;span class="na"&gt;date=&lt;/span&gt;&lt;span class="s"&gt;"D:20260703T101500"&lt;/span&gt; &lt;span class="na"&gt;creationdate=&lt;/span&gt;&lt;span class="s"&gt;"D:20260703T101500"&lt;/span&gt;
               &lt;span class="na"&gt;flags=&lt;/span&gt;&lt;span class="s"&gt;"print"&lt;/span&gt;
               &lt;span class="na"&gt;coords=&lt;/span&gt;&lt;span class="s"&gt;"72.0000,704.8000,331.5000,704.8000,72.0000,690.2000,331.5000,690.2000"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/annots&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/xfdf&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A zero-based page index, the colour, and the quad points of the highlighted text, in a schema Acrobat and Preview already parse. The &lt;code&gt;&amp;lt;f href&amp;gt;&lt;/code&gt; names the document the marks belong to; &lt;code&gt;coords&lt;/code&gt; is an attribute, not a child element, because that's the one spelling Acrobat actually reads. That's why the string is worth storing in your own database: it's readable by things that aren't your app.&lt;/p&gt;

&lt;p&gt;One precision, because I'd rather you hear it here than in a support ticket: this is programmatic, not a button. There's no end-user Export XFDF item in the toolbar. You call it from the Instance API and you decide when. If what you want is a file on the user's disk rather than a string, &lt;code&gt;exportToXfdf({ download: true })&lt;/code&gt; does that.&lt;/p&gt;

&lt;p&gt;And it's not only the marks your users made. Export walks the whole document and merges what's in the editor with the annotations already in the file when it loaded, so a PDF that arrives with ink from someone's tablet and squares from a reviewer's Acrobat round-trips those too. Thirteen annotation types survive the trip, not just the five the toolbar can create.&lt;/p&gt;

&lt;p&gt;The reason XFDF matters isn't that it's ours. It's that a standards-based round-trip is the exact thing PDF.js's overlays can't do and the exact thing pdf-lib gives up by drawing straight into the page. A highlight your reviewer makes in your Vue app opens in their lawyer's Acrobat. That's the whole argument, and it's a format argument rather than a product one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signatures deserve a mention&lt;/strong&gt;, since for many people they're the whole reason for using annotations. Free-text and image annotations can serve as lightweight signatures, and persistence is what makes one stick: an image that vanishes on reload isn't a signature. But none of these lightweight Vue options is a real e-signature &lt;em&gt;workflow&lt;/em&gt; product.&lt;/p&gt;

&lt;h2&gt;
  
  
  When you actually need the enterprise tier
&lt;/h2&gt;

&lt;p&gt;Two options sit above everything discussed so far, and it would be dishonest to leave them out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syncfusion&lt;/strong&gt;'s EJ2 Vue PDF Viewer ships text markup, shapes, stamps, ink, free text, sticky notes, measurement tools, form filling, e-signatures and redaction. &lt;strong&gt;Nutrient&lt;/strong&gt; (formerly PSPDFKit) covers 17+ annotation types with create, edit and remove available through the UI or programmatically, XFDF and JSON round-trips, and real-time collaboration sync.&lt;/p&gt;

&lt;p&gt;Both are commercial, both are priced accordingly, and both are genuinely more capable than anything else on this page. Reach for them when annotation breadth, compliance or multi-user collaboration is the product. Not when markup is a supporting feature rather than the core of the product or system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking a path
&lt;/h2&gt;

&lt;p&gt;Match the tool to what you're actually building, not to the shortest README.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Displaying markup that already exists.&lt;/strong&gt; vue-pdf-embed, or @tato30/vue-pdf if you want per-page control. One prop, MIT, done this afternoon. Reaching past this tier for a read-only viewer is how projects acquire dependencies they regret.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lightweight DIY markup, and you're willing to build the UI.&lt;/strong&gt; The PDF.js editor layer directly, or @tato30's &lt;code&gt;editor-layer&lt;/code&gt; for the Vue wrapping. You get free text, highlight, ink and stamp. You write the toolbar, the state, and the persistence. Budget real time for the last one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-side or pre-render stamping, no UI at all.&lt;/strong&gt; pdf-lib. Frozen since 2021 and fine for the job, as long as the job is burning marks in permanently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shipping a review or markup feature without building the toolbar.&lt;/strong&gt; Vue PDF Viewer, and here the disclosure at the top applies: it's the only option that ships underline and strikethrough creation, because PDF.js has no tool to create them and everything downstream inherits that gap. Add the XFDF round-trip and the toolbar you'd otherwise be writing, and the trade is a commercial licence against the weeks in Section 3. If your project can't take a paid licence, that trade doesn't exist and @tato30's editor-layer is your answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compliance, collaboration, or 17+ annotation types.&lt;/strong&gt; Syncfusion or Nutrient.&lt;/p&gt;

&lt;p&gt;Here's the full picture, pdf-lib included:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;PDF.js (direct)&lt;/th&gt;
&lt;th&gt;vue-pdf-embed&lt;/th&gt;
&lt;th&gt;@tato30/vue-pdf&lt;/th&gt;
&lt;th&gt;pdf-lib&lt;/th&gt;
&lt;th&gt;Vue PDF Viewer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;License&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Commercial (perpetual)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latest version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.1.200 (5.4.530 used here)&lt;/td&gt;
&lt;td&gt;2.1.5 (Jun 2026)&lt;/td&gt;
&lt;td&gt;2.1.0 (May 2026)&lt;/td&gt;
&lt;td&gt;1.17.1 (Nov 2021)&lt;/td&gt;
&lt;td&gt;viewer 4.3.0 + annotation 1.6.0 (3 Jul 2026)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Render existing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;annotation-layer&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;annotation-layer&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No (not a viewer)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create (types)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free text, highlight, ink, stamp&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Free text, highlight, ink, stamp, comments&lt;/td&gt;
&lt;td&gt;Text, image, shapes (as page content)&lt;/td&gt;
&lt;td&gt;Highlight, underline, strikethrough, free text, image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Create UI shipped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No, primitives only&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Partial, beta&lt;/td&gt;
&lt;td&gt;No UI at all&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Persist / export&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DIY, overlays may not reach the PDF binary&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;DIY, same overlay caveat&lt;/td&gt;
&lt;td&gt;Burned in, no round-trip&lt;/td&gt;
&lt;td&gt;Save / print / download + XFDF import/export (programmatic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Known limits&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No underline or strikethrough tool, free-form highlight&lt;/td&gt;
&lt;td&gt;Render only&lt;/td&gt;
&lt;td&gt;Beta, single-page interactions&lt;/td&gt;
&lt;td&gt;Not standard annotation objects, unmaintained&lt;/td&gt;
&lt;td&gt;Comments view-only, no stamps or e-signatures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vue 3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual setup&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Framework-agnostic&lt;/td&gt;
&lt;td&gt;Yes (purpose-built)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Versions verified 16 July 2026. Releases move. Re-check before you commit to any of this.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What to decide before you install anything
&lt;/h2&gt;

&lt;p&gt;Rendering is one prop. Creating is where the libraries come apart, and the gap runs deeper than any of them advertise: PDF.js has no underline or strikethrough tool, so nothing built on PDF.js has one either unless someone sat down and wrote it. Persisting is the one that surfaces after launch, when someone opens your reviewed contract in Acrobat and finds a clean page.&lt;/p&gt;

&lt;p&gt;So the useful question isn't which library is best. When you ask how to add PDF annotations in Vue, you're asking about three jobs, and the one that matters is whichever you're actually signing up for. Answer that first and most of this page collapses into one row of the table.&lt;/p&gt;

&lt;p&gt;If it turns out you need the create UI and the round-trip, the &lt;a href="https://www.vue-pdf-viewer.dev/docs/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=how-to-add-pdf-annotations-in-vuejs-pdfjs-vue-pdf-embed-tato30vue-pdf-and-vue-pdf-viewer"&gt;annotation plugin docs&lt;/a&gt; and live demo are the fastest way to check whether it fits, including the parts it doesn't do.&lt;/p&gt;

</description>
      <category>pdf</category>
      <category>javascript</category>
      <category>vuepdf</category>
      <category>vue</category>
    </item>
    <item>
      <title>How to Build a React PDF Viewer for Next.js in Minutes</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Wed, 11 Jun 2025 12:01:00 +0000</pubDate>
      <link>https://dev.to/9haroon/how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes-3pj</link>
      <guid>https://dev.to/9haroon/how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes-3pj</guid>
      <description>&lt;p&gt;Since my last post on &lt;a href="https://blog.react-pdf.dev/best-4-methods-to-build-a-pdf-viewer-in-reactjs-pdfjs-react-pdf-and-more-2024-guide" rel="noopener noreferrer"&gt;Best 4 Methods to Build a PDF Viewer in React.js: PDF.js, react-pdf, and More&lt;/a&gt;, my team and I launched React PDF in March 2025, a library that help React developers easily add a React PDF viewer to their Next.js apps.&lt;/p&gt;

&lt;p&gt;In this article, I'll show you how to display the default PDF Viewer in Next.js in just a few steps. I'll also share a few use cases to help you make the most out of React PDF in your own projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExNTY1cm1ndDAzcGkyNm5zN3ZiNmR1cmpkb3NjY2Q3a3AwOWgzbTQ1NyZlcD12MV9naWZzX3NlYXJjaCZjdD1n/vOJDTW3FApxfgRXBtD/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExNTY1cm1ndDAzcGkyNm5zN3ZiNmR1cmpkb3NjY2Q3a3AwOWgzbTQ1NyZlcD12MV9naWZzX3NlYXJjaCZjdD1n/vOJDTW3FApxfgRXBtD/giphy.gif" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why React PDF?
&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.amazonaws.com%2Fuploads%2Farticles%2Fv7ulxlvyjvt74iedoyeg.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.amazonaws.com%2Fuploads%2Farticles%2Fv7ulxlvyjvt74iedoyeg.png" alt="React PDF" width="800" height="790"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re building a PDF viewer in a Next.js app, you need a component that just works. &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes"&gt;React PDF&lt;/a&gt; stands out for its balance of powerful features and simple setup. You can drop it into your Next.js project with little work and handles most of what React devs expect. Its API feels natural if you know React, so no relearning is required.&lt;/p&gt;

&lt;p&gt;React PDF offers everything most apps need right out of the box. Here’s what you get:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;React-First&lt;/strong&gt;: Designed specifically for React.js developers, React PDF uses familiar component-based patterns, and hooks fit right in with your Next.js app’s structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customizable&lt;/strong&gt;: You can style or customize easily. From changing icons and colors to match your branding or customize your toolbar layout with React Context APIs to fit your app's needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Responsive Design&lt;/strong&gt;: Fits devices big or small, so mobile users can scroll, zoom, and interact with ease.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;High Performance&lt;/strong&gt;: Optimized for handling large PDFs without sacrificing speed or performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No surprises&lt;/strong&gt;: The documentation is clear, and support is strong. Bugs get addressed fast. Most developers will find everything they need quickly.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Set up a Next.js Project
&lt;/h2&gt;

&lt;p&gt;In this tutorial, I'll use StackBlitz to demonstrate how to build a React PDF Viewer in a Next.js project. Here's how to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Visit StackBlitz &amp;amp; Create a New Project
&lt;/h3&gt;

&lt;p&gt;Go to &lt;a href="https://stackblitz.com" rel="noopener noreferrer"&gt;https://stackblitz.com&lt;/a&gt;. Assuming you already have an account and are logged in, click “New Project” in the top-right corner. You’ll see a list of available frameworks—select Next.js.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fcmue0qxrycen01s5d4j8.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.amazonaws.com%2Fuploads%2Farticles%2Fcmue0qxrycen01s5d4j8.png" alt="StackBlitz - New Project frameworks" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Wait for StackBlitz to Set Things Up
&lt;/h3&gt;

&lt;p&gt;StackBlitz will automatically install the necessary dependencies and launch your Next.js project in the browser.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fi53d547znrlrzks9dlfr.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.amazonaws.com%2Fuploads%2Farticles%2Fi53d547znrlrzks9dlfr.png" alt="StackBlitz - Next.js project" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it! You're now ready to integrate React PDF Viewer into your StackBlitz-based Next.js project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Integrate React PDF into a Next.js Project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Install React PDF
&lt;/h3&gt;

&lt;p&gt;First, install the &lt;code&gt;@pdf-viewer/react&lt;/code&gt; package in your project.&lt;br&gt;
In the StackBlitz terminal, press &lt;code&gt;Ctrl + C&lt;/code&gt; to stop the current process, then 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;npm &lt;span class="nb"&gt;install&lt;/span&gt; @pdf-viewer/react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for the installation to complete. This should only take a few seconds.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fibo3r86ym0plfcqk7ah2.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.amazonaws.com%2Fuploads%2Farticles%2Fibo3r86ym0plfcqk7ah2.png" alt="StackBlitz's terminal - install the react pdf viewer" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're installing this library in your own Next.js project, check out the &lt;a href="https://docs.react-pdf.dev/introduction/getting-started#installation?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes"&gt;installation guide&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Set Up the Viewer
&lt;/h3&gt;

&lt;p&gt;After installing, follow these steps to set up the PDF viewer:&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Pro Tip&lt;/strong&gt;: Always use client-side rendering for these components using dynamic imports to prevent hydration mismatches and SSR-related issues with PDF rendering.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add RPConfig at the Root Level
&lt;/h4&gt;

&lt;p&gt;The RPConfig component provides the global configuration for React PDF and should wrap your entire app.&lt;/p&gt;

&lt;p&gt;Place the &lt;code&gt;RPConfig&lt;/code&gt; component at the root of your application (e.g., in _app.tsx or a root layout component). Make sure it only runs on the client side.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;components/AppProviders.tsx&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;RPConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RPConfigProps&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;@pdf-viewer/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&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="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AppProviders&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RPConfigProps&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RPConfig&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RPConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;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;AppProviders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;components/LazyAppProviders.tsx&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dynamic&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;next/dynamic&lt;/span&gt;&lt;span class="dl"&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;LazyAppProviders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./AppProviders&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="na"&gt;ssr&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="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;LazyAppProviders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;app/layout.tsx&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&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;LazyAppProviders&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;./components/LazyAppProviders&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;PropsWithChildren&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;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LazyAppProviders&lt;/span&gt; &lt;span class="na"&gt;licenseKey&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"your-license-key"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;LazyAppProviders&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;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;
  
  
  Step 3: Create a Reusable PDF Viewer Component
&lt;/h2&gt;

&lt;p&gt;To keep your code clean and reusable, it’s a good practice to wrap the viewer in its own component. This also makes it easier to customize or extend the viewer later as your app grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the &lt;code&gt;AppPdfViewer&lt;/code&gt; component
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;components/AppPdfViewer.tsx&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&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;RPProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RPDefaultLayout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RPPages&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;@pdf-viewer/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;OwnProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;AppPdfViewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OwnProps&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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;&lt;/span&gt;&lt;span class="nc"&gt;RPProvider&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RPDefaultLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RPPages&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RPDefaultLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RPProvider&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;AppPdfViewer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a Lazy-Loaded Wrapper Component
&lt;/h3&gt;

&lt;p&gt;Since React PDF is client-side only, we’ll also create a lazy wrapper to prevent SSR issues and hydration errors in Next.js.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;components/LazyAppPdfViewer.tsx&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&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;dynamic&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;next/dynamic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LazyAppPdfViewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./AppPdfViewer&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="na"&gt;ssr&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;LazyAppPdfViewer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use the PDF Viewer in Your App
&lt;/h3&gt;

&lt;p&gt;You can now use &lt;code&gt;LazyAppPdfViewer&lt;/code&gt; anywhere in your Next.js application.&lt;/p&gt;

&lt;p&gt;Here's an example of how to use it on the index page:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app/index.tsx&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;LazyAppPdfViewer&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;../components/LazyAppPdfViewer&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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;pdfUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cdn.codewithmosh.com/image/upload/v1721763853/guides/web-roadmap.pdf&lt;/span&gt;&lt;span class="dl"&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;...&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdf-viewer mb-3 w-[500px] lg:w-[992px]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LazyAppPdfViewer&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pdfUrl&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   ...
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! You should now see your PDF in action.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Faxrl5x9biqp2hwma50sl.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.amazonaws.com%2Fuploads%2Farticles%2Faxrl5x9biqp2hwma50sl.png" alt="React PDF Viewer on Stackblitz" width="800" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: At the moment, &lt;code&gt;Hot Module Reload (HMR)&lt;/code&gt; does not apply css style automatically to a component that dynamically import with &lt;code&gt;ssr: false&lt;/code&gt;. It is recommended to refresh the page after changing the component.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking for a Faster Setup?
&lt;/h2&gt;

&lt;p&gt;If you want to skip boilerplate and get straight to building, check out the &lt;a href="https://docs.react-pdf.dev/usage-guide/starter-toolkit?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes"&gt;React PDF Starter Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It includes ready-to-use templates for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;React (Vite / Webpack)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Next.js (JavaScript / TypeScript)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each template comes with a clean folder structure and sample code so you can build a PDF viewer quickly without spending hours configuring dependencies. Just clone, customize, and go. 🚀&lt;/p&gt;




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

&lt;h3&gt;
  
  
  Customize the PDF Viewer to Match Your Branding
&lt;/h3&gt;

&lt;p&gt;Every project has its own look and feel. &lt;code&gt;@pdf-viewer/react&lt;/code&gt; makes it easy to tailor the PDF viewer’s appearance so it fits right into your app — whether it’s light mode, dark mode, or a custom color palette.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;customVariables&lt;/code&gt; prop in the &lt;code&gt;RPTheme&lt;/code&gt; component, you can quickly apply brand colors to the viewer without digging into complex CSS overrides.&lt;/p&gt;

&lt;p&gt;Here’s a simple example of applying a custom background color:&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="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppPdfViewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OwnProps&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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;&lt;/span&gt;&lt;span class="nc"&gt;RPProvider&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RPTheme&lt;/span&gt;
          &lt;span class="na"&gt;customVariables&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--rp-toolbar-background&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F7E3D5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;customDarkVariables&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--rp-toolbar-background&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#874707&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RPDefaultLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RPPages&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RPDefaultLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RPTheme&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;RPProvider&lt;/span&gt;&lt;span class="p"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;AppPdfViewer&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Light Mode&lt;/em&gt;&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fok6uvu4cqm464hal1g5u.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.amazonaws.com%2Fuploads%2Farticles%2Fok6uvu4cqm464hal1g5u.png" alt="React PDF Viewer with light mode customization" width="800" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Dark Mode&lt;/em&gt;&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F9a06wz2hvwf71fwmdaem.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.amazonaws.com%2Fuploads%2Farticles%2F9a06wz2hvwf71fwmdaem.png" alt="React PDF Viewer with dark mode customization" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why this is useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides a consistent branded experience&lt;/li&gt;
&lt;li&gt;Works out of the box with theme switching (light/dark mode)&lt;/li&gt;
&lt;li&gt;Saves time styling PDF viewers for modern UIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Curious about more CSS variables you can override? Check out &lt;a href="https://docs.react-pdf.dev/usage-guide/overriding-style?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes"&gt;Overriding Style&lt;/a&gt; for details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customize Any Toolbar Icon
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;@pdf-viewer/react&lt;/code&gt; component offers flexibility in customizing the user interface. You can easily replace any default icons with your own, allowing you to maintain brand consistency or match with the rest of your application’s UI.&lt;/p&gt;

&lt;p&gt;Here’s an example of how to replace the default toolbar icons in &lt;code&gt;@pdf-viewer/react&lt;/code&gt; with &lt;a href="https://lucide.dev/" rel="noopener noreferrer"&gt;Lucide Icons&lt;/a&gt;, allowing for a more customized and consistent user interface.&lt;/p&gt;

&lt;p&gt;To get started, install the Lucide Icons package:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Once installed, you can import and use Lucide icons to replace the default icons in &lt;code&gt;@pdf-viewer/react&lt;/code&gt;.&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="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppPdfViewer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OwnProps&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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;defaultIconDimension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&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;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RPDefaultLayout&lt;/span&gt;
      &lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;zoomInIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ZoomInIcon&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultIconDimension&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
        &lt;span class="na"&gt;zoomOutIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ZoomOutIcon&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultIconDimension&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
        &lt;span class="na"&gt;downloadIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DownloadIcon&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultIconDimension&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
        &lt;span class="na"&gt;searchIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchIcon&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultIconDimension&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&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="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RPPages&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;RPDefaultLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   ...
  )
}
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, all of the viewer's toolbar icons have been successfully replaced.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fkxh0xqqaqxgpc56rmlj1.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.amazonaws.com%2Fuploads%2Farticles%2Fkxh0xqqaqxgpc56rmlj1.png" alt="React PDF Viewer - toolbar icon customization" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to see the example code in action, check out my &lt;a href="https://stackblitz.com/edit/stackblitz-starters-wure37ms?file=components%2FAppPdfViewer.tsx" rel="noopener noreferrer"&gt;StackBlitz&lt;/a&gt; for this article.&lt;/p&gt;




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

&lt;p&gt;Adding a PDF viewer to your Next.js app is simple with &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes"&gt;React PDF&lt;/a&gt; viewer. The setup takes just a few steps, and you instantly get a flexible, fast and robust tool. The viewer works responsively across different devices and screen sizes. You can easily style it to match your app’s look or let users zoom in and jump to pages without any hassle. &lt;/p&gt;

&lt;p&gt;React PDF is a great choice for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple PDF reading experiences&lt;/li&gt;
&lt;li&gt;Document workflows and approval systems&lt;/li&gt;
&lt;li&gt;AI-powered tools that need to display PDFs&lt;/li&gt;
&lt;li&gt;Business and productivity apps with embedded documents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It takes care of PDF rendering behind the scenes — so you can focus on building great features your users want.&lt;/p&gt;

&lt;p&gt;Try it out and see how easy it is to upgrade your project’s PDF viewing experience! 🚀&lt;/p&gt;

&lt;p&gt;👉 Want to explore more? Check out the full &lt;a href="https://docs.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=how-to-build-a-react-pdf-viewer-for-nextjs-in-minutes"&gt;React PDF documentation&lt;/a&gt; to see what else you can build.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you found this helpful, let me know! And if you build something cool with React PDF, I’d love to hear about it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>pdf</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>🔥 Understanding PDF.js Layers and How to Use them in React.js</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Mon, 26 May 2025 11:58:00 +0000</pubDate>
      <link>https://dev.to/9haroon/understanding-pdfjs-layers-and-how-to-use-them-in-reactjs-586m</link>
      <guid>https://dev.to/9haroon/understanding-pdfjs-layers-and-how-to-use-them-in-reactjs-586m</guid>
      <description>&lt;p&gt;PDF files are everywhere but working with PDFs in a React.js project can be frustrating. You might run into clunky plugins, awkward iframes, or glitches that break the user experience. PDF.js gives you a powerful, open-source JavaScript library to display and interact with PDF files right in the browser.&lt;/p&gt;

&lt;p&gt;Now, figuring out how to use PDF.js in your React project can be daunting at first. To leverage PDF.js's capabilities, it's important to understand how its layered structure works. &lt;/p&gt;

&lt;p&gt;If you’ve ever wondered, “How do I highlight text selection?” or “How do I interact with links inside the PDF?” or even “How do I build advanced features like annotations?”, the answer usually lies in understanding these layers.&lt;/p&gt;

&lt;p&gt;In this guide, you'll see how PDF.js splits content into layers, what those layers do, and how you can use them in your next React project. Let’s get going!&lt;/p&gt;




&lt;h2&gt;
  
  
  React PDF: Flexible and Powerful React PDF Viewer
&lt;/h2&gt;

&lt;p&gt;Before diving into the technical side of things, let me introduce you to React PDF—a library I’ve been working on: &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=layers-of-pdfjs-and-how-you-can-use-them-in-reactjs"&gt;React PDF&lt;/a&gt; is a handy PDF Viewer that renders PDFs right within your React.js or Next.js app. It packs over 20 features, including out-of-the-box default toolbar, customization and responsive design, so your users never have to leave your site to interact with your documents.&lt;/p&gt;

&lt;p&gt;React PDF can integrated into various applications whether it's for a Document Management System, Workflow Management System, AI tool or simply as a PDF reader.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fyrnscvdyq12mtzmdked3.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.amazonaws.com%2Fuploads%2Farticles%2Fyrnscvdyq12mtzmdked3.png" alt="React PDF"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If that sounds interesting, I’d love for you to give &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=layers-of-pdfjs-and-how-you-can-use-them-in-reactjs"&gt;React PDF&lt;/a&gt; a try. Your support helps me keep creating awesome tools and tutorials like this one. ❤️&lt;/p&gt;




&lt;h2&gt;
  
  
  Overview of PDF.js Layers
&lt;/h2&gt;

&lt;p&gt;Before we dive in, Here’s a quick overview of the four main layers in PDF.js. Each layer handles a specific aspect of rendering or user interaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Canvas Layer&lt;/strong&gt;: This layer draws the core visuals of your PDF—shapes, images, and text as graphics. It forms the base of your PDF Viewer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text Layer&lt;/strong&gt;: Placed above the canvas, this layer lets users select and search text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Annotation Layer&lt;/strong&gt;: This layer manages interactive items like links, form fields, and highlights, so users can click, enter information, or move around the document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structural Layer&lt;/strong&gt;: This layer keeps everything in order, handling layout, alignment, and scaling for all other layers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By separating these roles into different layers, PDF.js stays organized and easy to update when you need to adjust or add features.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fhd1oby5zy790sntw3bzo.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.amazonaws.com%2Fuploads%2Farticles%2Fhd1oby5zy790sntw3bzo.png" alt="PDF.js Layers"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Let’s start by creating a fresh React project to integrate PDF.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Set up React.js Project
&lt;/h2&gt;

&lt;p&gt;In this article, I use Codepen as a code editor. If you want to follow along, feel free to follow along the steps below.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new Pen on CodePen.&lt;/li&gt;
&lt;li&gt;Click the Settings button (top-right corner of the editor).&lt;/li&gt;
&lt;li&gt;In the Pen Settings dialog:

&lt;ul&gt;
&lt;li&gt;Go to the JavaScript tab.&lt;/li&gt;
&lt;li&gt;Set the JavaScript Preprocessor to 'Babel'.&lt;/li&gt;
&lt;li&gt;Under 'Add External Scripts / Pens', search for and add: &lt;code&gt;react&lt;/code&gt; &amp;amp; &lt;code&gt;react-dom&lt;/code&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.amazonaws.com%2Fuploads%2Farticles%2Fp91tdd2i3k3byfd4scjb.png" alt="React Codepen - start configuration"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Save &amp;amp; Close&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Now you can write React code using JSX in the JavaScript editor like below.&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2Fg596k3w2tycx190nct2z.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.amazonaws.com%2Fuploads%2Farticles%2Fg596k3w2tycx190nct2z.png" alt="React Codepen - setting HTML"&gt;&lt;/a&gt;&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fgzb307h2phqt0ipm32v5.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.amazonaws.com%2Fuploads%2Farticles%2Fgzb307h2phqt0ipm32v5.png" alt="React Codepen - setting JS"&gt;&lt;/a&gt;&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fi526nrp56pvi7royt31g.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.amazonaws.com%2Fuploads%2Farticles%2Fi526nrp56pvi7royt31g.png" alt="React Codepen - result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Integrate PDF.js into a React project
&lt;/h2&gt;

&lt;p&gt;First, let’s configure your React project to use the &lt;code&gt;pdfjs-dist&lt;/code&gt; library.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open Pen Settings&lt;/strong&gt;: Click on the Settings button at the top of your Pen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Go to JavaScript Tab&lt;/strong&gt;: In the popup, switch to the JS tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Packages&lt;/strong&gt; In the Add Packages box, search for and add &lt;code&gt;pdfjs-dist&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save and Close&lt;/strong&gt;: Click the button to apply changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update Import Statement&lt;/strong&gt; CodePen might auto-generate an import that doesn't work correctly. Replace it like so
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// ❌ Wrong (auto-generated)&lt;/span&gt;
  &lt;span class="c1"&gt;// import pdfjsDist from "https://esm.sh/pdfjs-dist";&lt;/span&gt;

  &lt;span class="c1"&gt;// ✅ Correct&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add PDF.js Worker and Configure It&lt;/strong&gt; You'll need to explicitly import and configure the worker. Here's the complete setup
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="s2"&gt;https://esm.sh/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PDFWorker&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/pdfjs-dist/build/pdf.worker.min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GlobalWorkerOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workerSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PDFWorker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pdfjsWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PDFWorker&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;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;br&gt;
Before loading the PDF, we need to configure the PDF worker. The worker is responsible for processing the PDF in a separate thread, improving performance.&lt;/p&gt;

&lt;p&gt;That’s it! You’re now set up to start experimenting with PDF.js layers within a React environment. So let's get to the layers.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Canvas Layer
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Canvas Layer&lt;/strong&gt; is the foundation—it’s where PDF.js draws the visual elements such as images, shapes, and text (as graphics). Essentially, what you see on-screen is coming from this layer.&lt;/p&gt;
&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PDF.js uses the HTML &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element to display the PDF's visual content&lt;/li&gt;
&lt;li&gt;It utilizes the browser's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D" rel="noopener noreferrer"&gt;2D canvas API&lt;/a&gt; for high-performance rendering.&lt;/li&gt;
&lt;li&gt;This means your PDF content will look consistent and accurate across various screen sizes and devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ideal for &lt;strong&gt;non-interactive&lt;/strong&gt; or &lt;strong&gt;static&lt;/strong&gt; PDF content.&lt;/li&gt;
&lt;li&gt;It renders the document as an image, ensuring PDF’s fonts, colors, and layouts are consistent across all devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Example of Canvas Layer Code with React
&lt;/h3&gt;

&lt;p&gt;Here’s a quick CodePen demo showcasing a minimal Canvas Layer setup in React.js.  &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/9haroon/embed/jEOppVz?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 How the Code Works
&lt;/h3&gt;

&lt;p&gt;Below, I’ll walk through the major steps for rendering a PDF page onto the canvas.&lt;/p&gt;

&lt;h4&gt;
  
  
  (1) Fetching and Loading the PDF
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PDF_SRC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://pdfobject.com/pdf/pdf_open_parameters_acro8.pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;React&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="nf"&gt;processLoadingTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PDF_SRC&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processLoadingTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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;loadingTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;loadingTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&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;docProxy&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="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;page&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="nx"&gt;canvasLayer&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="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;canvasLayer&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="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;height&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvasLayer&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;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&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;renderContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;canvasContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderContext&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;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;processLoadingTask&lt;/code&gt; method starts the PDF loading process using &lt;code&gt;PDFJS.getDocument(source)&lt;/code&gt; from &lt;code&gt;pdfjs-dist&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the PDF is loaded from &lt;code&gt;PDF_SRC&lt;/code&gt;, we obtain a &lt;code&gt;docProxy&lt;/code&gt;—a PDF document instance that contains all pages of the PDF.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In this example, only the &lt;strong&gt;first page&lt;/strong&gt; is used for rendering.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The total number of pages is retrieved from &lt;code&gt;docProxy.numPages&lt;/code&gt; and stored in the &lt;code&gt;totalPages&lt;/code&gt; state.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (2) Rendering the Canvas Layer
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt; &lt;span class="p"&gt;...&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvasLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pdfDimension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPdfDimension&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;...&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;
        &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdf__canvas-layer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
         &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pdfDimension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pdfDimension&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1px solid #dfdfdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 auto&lt;/span&gt;&lt;span class="dl"&gt;'&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&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;canvasLayer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     ... 
 )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After the PDF is loaded and we have the first page object then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scales the canvas to match the PDF page dimensions by updating &lt;code&gt;pdfDimension&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element (canvasLayer) is used to render the PDF page as an image.&lt;/li&gt;
&lt;li&gt;The rendering process is handled by &lt;code&gt;page.render(renderContext)&lt;/code&gt;, where &lt;code&gt;canvasContext&lt;/code&gt; paints the page content onto the canvas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures the final output accurately matches the original PDF’s appearance.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Text Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Text Layer&lt;/strong&gt; ensures that the text in your PDF is &lt;strong&gt;selectable, searchable, and accessible&lt;/strong&gt;. While the Canvas Layer does the heavy lifting of rendering visuals, the Text Layer is what lets you highlight and copy actual text.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PDF.js extracts the &lt;strong&gt;text content&lt;/strong&gt; separately from the PDF.&lt;/li&gt;
&lt;li&gt;It positions each piece of text exactly over the underlying canvas, using absolutely positioned &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements.&lt;/li&gt;
&lt;li&gt;Even though you might not see these text &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; visually (they’re often transparent), they’re there to support &lt;strong&gt;selection&lt;/strong&gt; and &lt;strong&gt;searching&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enable &lt;strong&gt;text selection&lt;/strong&gt; and &lt;strong&gt;copying&lt;/strong&gt; from the PDF.&lt;/li&gt;
&lt;li&gt;Text layer is crucial for &lt;strong&gt;accessibility&lt;/strong&gt;, as it enables screen readers to interpret the content.&lt;/li&gt;
&lt;li&gt;Provide text searching and highlighting within the PDF viewer.&lt;/li&gt;
&lt;li&gt;Keep the text aligned with the rendered PDF page&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Text layer Code Example
&lt;/h3&gt;

&lt;p&gt;Here’s a React.js CodePen example that demonstrates a Text Layer in action:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/9haroon/embed/NPPyMQm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With this text layer, you can use your mouse to select text directly on the PDF.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 How the Code Works
&lt;/h3&gt;

&lt;h4&gt;
  
  
  (1) Fetching and Loading the PDF
&lt;/h4&gt;

&lt;p&gt;Identical to the steps in the &lt;strong&gt;Canvas Layer&lt;/strong&gt;—we load the PDF document first.&lt;/p&gt;

&lt;h4&gt;
  
  
  (2) Structuring the Layers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdf-layers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&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="o"&gt;&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;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pdf-layer__canvas"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&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;canvasLayer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&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="p"&gt;&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;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;textLayer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pdf-layer__text"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;pdf-layers&lt;/code&gt; is a container that holds both the &lt;strong&gt;Canvas Layer&lt;/strong&gt; and the &lt;strong&gt;Text Layer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Canvas Layer&lt;/strong&gt; (canvasLayer) displays the PDF page visually.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Text Layer&lt;/strong&gt; (textLayer) is styled to sit on top of the canvas, enabling text selection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (3) Rendering the Text Layer
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderTextLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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;viewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nx"&gt;textLayer&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="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--total-scale-factor&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTextContent&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;content&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;textLayer&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="na"&gt;textContentSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;dontFlip&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="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;renderTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Render the text layer with &lt;code&gt;renderTextLayer(page)&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page.getTextContent()&lt;/code&gt; retrieves the text data from the PDF page.&lt;/li&gt;
&lt;li&gt;This data is passed into a &lt;code&gt;new PDFJS.TextLayer&lt;/code&gt;, specifying:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;container&lt;/code&gt;: The DOM element where the text will be rendered.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;textContentSource&lt;/code&gt;: Extracted text data from the PDF page.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;viewport&lt;/code&gt;: Define text positioning and scaling.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;renderTask.render()&lt;/code&gt; overlays invisible but selectable text &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; over the canvas.&lt;/li&gt;

&lt;li&gt;Check the CSS panel in CodePen to understand how the canvas and text layers are stacked together.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Annotation Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Annotation Layer&lt;/strong&gt; handles &lt;strong&gt;interactive elements&lt;/strong&gt; like hyperlinks, highlights, form fields, and comments. If you need your PDF viewer to support internal linking (jumping to different pages), or if your PDFs have fillable forms, this layer is for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PDF.js extracts annotation data (links, form fields, etc.) from the PDF.&lt;/li&gt;
&lt;li&gt;It overlays these data as &lt;strong&gt;interactive HTML elements&lt;/strong&gt; (e.g. &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;), which sit above the &lt;strong&gt;Canvas Layer&lt;/strong&gt; and &lt;strong&gt;Text Layer&lt;/strong&gt; positioned using CSS.&lt;/li&gt;
&lt;li&gt;This layer ensures that users can click, type, and interact without altering the PDF’s static visuals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clicking on links within PDF to navigate between pages.&lt;/li&gt;
&lt;li&gt;Rendering highlights, underlines, and notes from PDF annotations.&lt;/li&gt;
&lt;li&gt;Displaying interactive form fields within a PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Annotation layer Code Example
&lt;/h3&gt;

&lt;p&gt;Check out this CodePen example on the &lt;strong&gt;Annotation Layer&lt;/strong&gt; for React.js:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can click the links inside the Codepen to change the PDF's page.&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/9haroon/embed/oggEyma?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 How the Code Works
&lt;/h3&gt;

&lt;h4&gt;
  
  
  (1) Fetching and Loading the PDF
&lt;/h4&gt;

&lt;p&gt;Again, the same initial PDF loading steps as before.&lt;/p&gt;

&lt;h4&gt;
  
  
  (2) Structuring the Layers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdf-layers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&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="o"&gt;&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;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pdf-layer__canvas"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&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;canvasLayer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&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="p"&gt;&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;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;textLayer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pdf-layer__text"&lt;/span&gt; &lt;span class="p"&gt;/&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;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;annotationLayer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"pdf-layer__annotation"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We add another &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; for the &lt;strong&gt;Annotation Layer&lt;/strong&gt; within &lt;code&gt;pdf-layers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Annotation Layer&lt;/strong&gt; is positioned on top of the &lt;strong&gt;Canvas Layer&lt;/strong&gt; and &lt;strong&gt;Text Layer&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (3) Rendering the Annotation Layer
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt; &lt;span class="p"&gt;....&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;annotationLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;...&lt;/span&gt;
 &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAnnotationsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAnnotations&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;display&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderAnnotationLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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;viewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;annotationLayer&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;replaceChildren&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;getAnnotationsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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;annotations&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="nx"&gt;annotations&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="nx"&gt;annotations&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;pdfAnnotationLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AnnotationLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;annotationLayer&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="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="nx"&gt;pdfAnnotationLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="nx"&gt;annotations&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getAnnotationsAsync()&lt;/code&gt; retrieves all annotation data from the PDF page—such as links, form fields, and highlights.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;viewport&lt;/code&gt; is cloned using &lt;code&gt;viewport.clone({ dontFlip: true })&lt;/code&gt; to ensure correct positioning and scaling for the annotation layer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;renderAnnotationLayer(page)&lt;/code&gt; handles rendering by:

&lt;ul&gt;
&lt;li&gt;Clearing or replacing any existing annotation elements inside the &lt;code&gt;annotationLayer.current&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Creating a new &lt;code&gt;PDFJS.AnnotationLayer&lt;/code&gt; instance to manage rendering.&lt;/li&gt;
&lt;li&gt;Calling &lt;code&gt;pdfAnnotationLayer.render({ ... })&lt;/code&gt; with: The annotations retrieved from the page, the cloned viewport, the container where annotations should appear, and so on.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This ensures that annotations are accurately rendered on top of the PDF page.&lt;/p&gt;

&lt;h4&gt;
  
  
  (4) Handling Internal Link Clicks
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;annotationEventAbortCtrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;bindAnnotationLayerEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&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;annotationEventAbortCtrl&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="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Already binded&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;}&lt;/span&gt;
    &lt;span class="nx"&gt;annotationEventAbortCtrl&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;annotationEventAbortCtrl&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="nx"&gt;annotationLayer&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;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;annotationTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;annotationTarget&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;}&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;annotationTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;annotationId&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;id&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;}&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;annotationLinkId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getAnnotationsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ele&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ele&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;annotationLinkId&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;}&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageIndex&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;pdfDocProxy&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;getPageIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;annotationLinkId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setCurrentPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;signal&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function listens for clicks on internal links (annotations) in the PDF.&lt;/li&gt;
&lt;li&gt;When a user clicks a link, it checks if the clicked target has an associated &lt;code&gt;annotationId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If found, it looks up the annotation to get its destination using &lt;code&gt;getAnnotationsAsync(page)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then it uses &lt;code&gt;getPageIndex()&lt;/code&gt; to find which page the link points to.&lt;/li&gt;
&lt;li&gt;Finally, it updates the current page with &lt;code&gt;setCurrentPage()&lt;/code&gt; to navigate to the target page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (5) Handling Page Navigation
&lt;/h4&gt;

&lt;p&gt;A simple navigation bar lets users switch between pages.&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="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCurrentPage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTotalPages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;React&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="nf"&gt;processRenderPdfLayers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PDF_SRC&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="nx"&gt;currentPage&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;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"page-navigation"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
       &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
       &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCurrentPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="ni"&gt;&amp;amp;larr;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; / &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;totalPages&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;totalPages&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCurrentPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="ni"&gt;&amp;amp;rarr;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&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="p"&gt;&amp;gt;&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;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;currentPage&lt;/code&gt; value updates when navigation buttons are clicked.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;useEffect&lt;/code&gt; with &lt;code&gt;currentPage&lt;/code&gt; dependency ensures that all layers are re-rendered when the page changes.&lt;/li&gt;
&lt;li&gt;This ensures all layers—Canvas, Text, and Annotation—stay in sync.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Structural Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;Think of the &lt;strong&gt;Structural Layer&lt;/strong&gt; as the master layout manager. It keeps the Canvas, Text, and Annotation layers correctly aligned, scaled, and positioned when users zoom in or out, or when the window is resized.&lt;/p&gt;

&lt;p&gt;This layer is a must-have when creating a &lt;strong&gt;PDF viewer&lt;/strong&gt; as it serves as the &lt;strong&gt;foundation&lt;/strong&gt; that glues all other layers together. &lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Structural Layer&lt;/strong&gt; is typically implemented by a container element (&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;) that wraps all other layers.&lt;/li&gt;
&lt;li&gt;Maintain consistent positioning among layers so they don’t drift apart during zooms or scrolls.&lt;/li&gt;
&lt;li&gt;Act as the foundation that ties everything together, so your PDF viewer remains cohesive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure a &lt;strong&gt;consistent user experience&lt;/strong&gt; across different screen sizes and resolutions.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;smooth zooming&lt;/strong&gt; by scaling all layers proportionally.&lt;/li&gt;
&lt;li&gt;Centralize navigation and layout logic (e.g. controlling how the viewer scrolls from one page to another.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our code examples, the &lt;code&gt;&amp;lt;div ref="pdfLayersWrapper" class="pdf__layers"&amp;gt;&lt;/code&gt; element serves as the Structural Layer, wrapping for other layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"pdfLayersWrapper"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__layers"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__canvas-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"canvasLayer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"textLayer"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__text-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"annotationLayer"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__annotation-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why is this important?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Structural Layer&lt;/strong&gt; wrapper ensures the Canvas, Text, and Annotation Layers all stay in sync, even when you zoom or flip through pages.&lt;/li&gt;
&lt;li&gt;You can customize the wrapper to handle special behaviors like lazy-loading pages, scroll snapping and other performance optimizations.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Layers Work Together
&lt;/h2&gt;

&lt;p&gt;Here’s how all four layers in PDF.js work together in harmony:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rendering Process&lt;/strong&gt;:&lt;br&gt;
Each page is first drawn onto the &lt;strong&gt;Canvas Layer&lt;/strong&gt; (visual content). Next, the &lt;strong&gt;Text Layer&lt;/strong&gt; is layered on top to enable selection and searching. Finally, the &lt;strong&gt;Annotation Layer&lt;/strong&gt; is placed at the very top, providing interactive elements like links and form fields.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interactivity&lt;/strong&gt;:&lt;br&gt;
The &lt;strong&gt;Annotation Layer&lt;/strong&gt; handles user events (like clicking on links or typing into form fields), while the Text Layer ensures text can be highlighted, copied, or searched.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PDF Viewer&lt;/strong&gt;:&lt;br&gt;
The &lt;strong&gt;Structural Layer&lt;/strong&gt; wraps everything up, ensuring that zooming, resizing, and navigation across pages remain consistent. It keeps all layers aligned and responsive, so users get a polished viewing experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Beyond the Fundamentals
&lt;/h2&gt;

&lt;p&gt;PDF.js gives you the basics for working with PDFs in JavaScript, but plugging it into a React project means you'll want more control. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customize UI:&lt;/strong&gt; Use React hooks and state to create a smooth, custom viewer that fits right into your app. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handling Large PDFs:&lt;/strong&gt; For large PDFs, skip loading the whole file up front. Choose lazy loading or virtualization to keep your app feeling quick, even with hundreds of pages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adding Toolbar Control:&lt;/strong&gt; Build your own toolbar with features like zoom, page jumps, and download options, so users get a great experience. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the right setup, PDF.js and React work together to handle big files and let you shape the viewer to fit your project. &lt;/p&gt;




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

&lt;p&gt;I’ve worked with PDF.js across many React.js projects, from straightforward file previews to detailed, interactive document tools. Its layered structure is always the foundation.&lt;/p&gt;

&lt;p&gt;This setup matters because you get &lt;strong&gt;full control&lt;/strong&gt; over each part of the PDF viewer. You can tune specific layers to &lt;strong&gt;boost performance&lt;/strong&gt; or improve the user interface. It’s easy to &lt;strong&gt;refine how your viewer&lt;/strong&gt; responds to things like search, form input, or user actions.&lt;/p&gt;

&lt;p&gt;By understanding these layers, you will be able to optimize PDF.js implementations, customize features, and deliver a better user experience. Whether you’re building a simple React PDF Viewer or a complex Document Processing System, leveraging these layers effectively will set your project up for success.&lt;/p&gt;




&lt;h2&gt;
  
  
  React PDF: The PDF Viewer Built for React.js Developers 🚀
&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.amazonaws.com%2Fuploads%2Farticles%2Fyrnscvdyq12mtzmdked3.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.amazonaws.com%2Fuploads%2Farticles%2Fyrnscvdyq12mtzmdked3.png" alt="React PDF"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, I’d love for you to check out &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=layers-of-pdfjs-and-how-you-can-use-them-in-reactjs"&gt;React PDF&lt;/a&gt;. It’s a PDF Viewer built from the ground up for React and Next.js applications. Whether you’re just starting out or already running a large-scale app, React PDF got you covered.&lt;/p&gt;

&lt;p&gt;Designed with developers in mind, React PDF offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Out-of-the-box features - Check out the &lt;a href="https://docs.react-pdf.dev/introduction/getting-started?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=layers-of-pdfjs-and-how-you-can-use-them-in-reactjs"&gt;Getting Started&lt;/a&gt; guide&lt;/li&gt;
&lt;li&gt;Customizable look and feel - Learn how to &lt;a href="https://docs.react-pdf.dev/tutorial/customizing-toolbar?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=layers-of-pdfjs-and-how-you-can-use-them-in-reactjs"&gt;customize components&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Web Responsive layouts - See how the React PDF Viewer renders on &lt;a href="https://docs.react-pdf.dev/usage-guide/mobile-view?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=layers-of-pdfjs-and-how-you-can-use-them-in-reactjs"&gt;mobile view&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Developer-friendly APIs - Explore the full &lt;a href="https://docs.react-pdf.dev/hooks/overview?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=layers-of-pdfjs-and-how-you-can-use-them-in-reactjs"&gt;API documentation&lt;/a&gt; for more information&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your support encourages me to keep building tools and publish content for the React community. Thank you for checking out React PDF in advance! 🙏&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>pdf</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🚀 Launch a PDF Viewer in Nuxt Using @vue-pdf-viewer [Guide for 2025] 🔥</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Tue, 06 May 2025 12:00:00 +0000</pubDate>
      <link>https://dev.to/9haroon/launch-a-pdf-viewer-in-nuxt-using-vue-pdf-viewer-guide-for-2025-2o85</link>
      <guid>https://dev.to/9haroon/launch-a-pdf-viewer-in-nuxt-using-vue-pdf-viewer-guide-for-2025-2o85</guid>
      <description>&lt;p&gt;PDF viewing is now a must-have for modern web apps, from online contracts to digital classrooms. If you’re building with Nuxt, you want tools that fit right in and work fast. That’s where &lt;a href="https://www.npmjs.com/package/@vue-pdf-viewer/viewer" rel="noopener noreferrer"&gt;@vue-pdf-viewer&lt;/a&gt; shines 🌟&lt;/p&gt;

&lt;p&gt;@vue-pdf-viewer lets you open, zoom, and interact with PDFs right inside your Vue or Nuxt project. The setup is quick, with features like keyboard controls, responsive layouts, and even advanced document options ready to go. For developers and teams, it’s a straightforward, reliable way to add rich PDF support to any Nuxt-powered app.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I work at Vue PDF Viewer.&lt;/em&gt;&lt;/p&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%2Fmedia2.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExOTJ6b3hlanNpOGRlbDNsY2F6cDFnZDY5MTUxeHRldnUxOW1heTB4dSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FvKHKDIdvxvN7vTAEOM%2Fgiphy.gif" 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%2Fmedia2.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExOTJ6b3hlanNpOGRlbDNsY2F6cDFnZDY5MTUxeHRldnUxOW1heTB4dSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2FvKHKDIdvxvN7vTAEOM%2Fgiphy.gif" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Choose @vue-pdf-viewer for Nuxt Applications
&lt;/h2&gt;

&lt;p&gt;If you want smooth PDF handling in your Nuxt project, you need a component that just works. Vue PDF Viewer stands out for its balance of powerful features and simple setup. You can add the library into almost any Nuxt page or component with minimal setup. It's built for Nuxt and Vue, so you avoid heavy solutions or flaky workarounds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features that Set @vue-pdf-viewer Apart
&lt;/h3&gt;

&lt;p&gt;Vue PDF Viewer offers everything most apps need right out of the box. Here’s what you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smooth zoom and rotation:&lt;/strong&gt; Users can view their PDFs up close, rotate pages, and not miss a detail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard navigation:&lt;/strong&gt; Move through documents without leaving the keyboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive layouts:&lt;/strong&gt; Fits devices big or small, so mobile users can scroll, zoom, and interact with ease.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF layers support:&lt;/strong&gt; Support for text, canvas and annotation layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Search and text selection:&lt;/strong&gt; Users find what they need fast, selecting and copying text directly from the PDF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy customization:&lt;/strong&gt; Vue PDF Viewer's toolbar or theme can be configured easily to fit your business needs. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want the full list, check the official documentation’s &lt;a href="https://docs.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_content=launch-a-pdf-viewer-in-nuxt-using-@vue-pdf-viewer"&gt;Feature Overview&lt;/a&gt;. You’ll see plenty of advanced options, but the defaults work for most projects.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fis79td9zi36gxdjh1aq5.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.amazonaws.com%2Fuploads%2Farticles%2Fis79td9zi36gxdjh1aq5.png" alt="Documentation Feature Overview" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparing @vue-pdf-viewer to Other Popular Alternatives
&lt;/h3&gt;

&lt;p&gt;Several tools such as Apryse and Syncfusion compete in this space, but Vue PDF Viewer stands out for its simplicity and Vue-first design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vue-native&lt;/strong&gt;: Compared to other PDF.js wrappers, Vue PDF Viewer is written entirely in Vue, which means fewer compatibility headaches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight footprint&lt;/strong&gt;: It’s lighter than some heavyweight tools like Apryse or Syncfusion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature-rich yet accessible&lt;/strong&gt;: While many open-source options lack advanced controls or built-in Nuxt support, Vue PDF Viewer gives you both out-of-the-box.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where @vue-pdf-viewer Shines in Nuxt
&lt;/h3&gt;

&lt;p&gt;You’ll see it in action everywhere—from simple PDF previews to interactive AI-powered PDF tools and full-fledged document management systems. It's a great fit for projects where:&lt;/p&gt;

&lt;p&gt;✅ You need solid, bug-free PDF rendering.&lt;br&gt;
✅ Custom UI or branding matters.&lt;br&gt;
✅ Fast time-to-launch is key and the focus is on Vue or Nuxt development.&lt;/p&gt;

&lt;p&gt;The component’s flexibility lets you scale from basic views to complex document tools without starting over each time.&lt;/p&gt;


&lt;h2&gt;
  
  
  Setting Up Your Nuxt Project for PDF Viewing
&lt;/h2&gt;

&lt;p&gt;Getting your Nuxt project ready for PDF viewing starts with a solid foundation. You'll want installation to be smooth and straightforward asset handling to ensure reliable PDF rendering and easy file maintenance.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install Dependencies and Project Scaffolding
&lt;/h3&gt;

&lt;p&gt;Start by creating a new Nuxt application, or open your existing one.&lt;/p&gt;
&lt;h4&gt;
  
  
  Nuxt 3 (recommended):
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Initialize your project:

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;npx nuxi init my-nuxt-app&lt;/code&gt; to create a basic structure.&lt;/li&gt;
&lt;li&gt;Enter the project folder: &lt;code&gt;cd my-nuxt-app&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install dependencies: &lt;code&gt;npm install&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Add @vue-pdf-viewer:

&lt;ul&gt;
&lt;li&gt;Install the package with &lt;code&gt;npm install @vue-pdf-viewer&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a quick checklist before moving ahead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue 3&lt;/li&gt;
&lt;li&gt;Node 16+ for Nuxt 3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With your dependencies set, you’re ready to handle asset organization.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure Static Assets for PDF Files
&lt;/h3&gt;

&lt;p&gt;How you store and serve your PDF files matters, especially when it comes to access speed and basic security. Nuxt 3 typically uses the &lt;code&gt;public&lt;/code&gt; folder for this job, but there are a few gotchas to keep your files organized and safe.&lt;/p&gt;

&lt;p&gt;In Nuxt 3, any files placed in the &lt;code&gt;public&lt;/code&gt; folder are served from the root level. For example, a PDF saved as &lt;code&gt;public/sample.pdf&lt;/code&gt; will be available at &lt;code&gt;/sample.pdf&lt;/code&gt; in your browser.&lt;/p&gt;
&lt;h4&gt;
  
  
  Best practices for asset management:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Organize your PDFs in subfolders (e.g. &lt;code&gt;public/pdfs/&lt;/code&gt;) if you plan to store more than a few documents.&lt;/li&gt;
&lt;li&gt;Keep clear, consistent filenames and avoid spaces, use dashes or underscores.&lt;/li&gt;
&lt;li&gt;Regularly clean out unused files to keep your build lightweight.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  File access security:
&lt;/h4&gt;

&lt;p&gt;Keep in mind that assets in &lt;code&gt;public&lt;/code&gt; or &lt;code&gt;static&lt;/code&gt; are publicly accessible. If you're dealing with sensitive or private PDFs, don't  place them here. Instead: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use authentication and store the files outside &lt;code&gt;public&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Serve them through a secure API or custom middleware.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Useful Resources
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;For more on how Nuxt handles public and static files, review the &lt;a href="https://nuxt.com/docs/getting-started/assets" rel="noopener noreferrer"&gt;official assets documentation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you want a guide to help you decide where files belong or to understand asset handling differences, check out &lt;a href="https://masteringnuxt.com/blog/handling-assets-in-nuxt-3" rel="noopener noreferrer"&gt;this breakdown on handling assets in Nuxt 3&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For quick comparisons on when to use each folder, see the Stack Overflow thread on &lt;a href="https://stackoverflow.com/questions/48808182/nuxt-assets-and-static-folder-when-to-use-which" rel="noopener noreferrer"&gt;Nuxt assets and static folders&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By planning where your PDFs live from the start, you avoid confusion later on—especially as your app grows. Keep things neat and protect anything sensitive, and your PDF workflow in Nuxt will stay smooth and manageable.&lt;/p&gt;
&lt;h2&gt;
  
  
  Integrating @vue-pdf-viewer in a Nuxt Component
&lt;/h2&gt;

&lt;p&gt;With your Nuxt app set up and PDFs organized, the next step is to actually bring your PDF viewer to life. You have a few key choices here: create a self-contained component for on-demand use, wrap the viewer in client-only rendering to avoid SSR issues, or register it globally for reuse in bigger projects. &lt;/p&gt;

&lt;p&gt;Here’s a step-by-step guide to each approach so you choose the one that fits your needs best.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📘 &lt;strong&gt;Related:&lt;/strong&gt; You can also follow the official &lt;a href="https://docs.vue-pdf-viewer.dev/introduction/basic-usage.html#nuxt?utm_source=dev.to&amp;amp;utm_content=launch-a-pdf-viewer-in-nuxt-using-@vue-pdf-viewer&amp;amp;utm_medium=referral"&gt;Nuxt setup guide&lt;/a&gt; in the documentation for a simpler quick-start example.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Creating a Reusable PDF Viewer Component
&lt;/h3&gt;

&lt;p&gt;To start, it’s best to build a dedicated Vue component that handles all your PDF viewing logic. This keeps your code tidy and lets you drop the viewer anywhere in your app. &lt;/p&gt;

&lt;p&gt;Here’s how you can do it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new file in your components folder. e.g. &lt;code&gt;PdfViewer.vue&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Import &lt;code&gt;@vue-pdf-viewer/viewer&lt;/code&gt; and define your props.&lt;/li&gt;
&lt;li&gt;Pass in the &lt;code&gt;src&lt;/code&gt; prop, which is the path to your PDF file.&lt;/li&gt;
&lt;li&gt;Optionally, add viewer options to customize controls or appearance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example code: &lt;code&gt;components/PdfViewer.vue&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;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;ref&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;vue&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;VPdfViewer&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;@vue-pdf-viewer/viewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pdfs/sample.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// or use a prop for dynamic paths&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;VPdfViewer&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"pdfSrc"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup lets you easily update the source PDF or tweak viewer settings as needed. For more details about best practices with Vue components in Nuxt, check out the &lt;a href="https://nuxt.com/docs/guide/directory-structure/components" rel="noopener noreferrer"&gt;Nuxt components directory guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side Only Rendering with 
&lt;/h3&gt;

&lt;p&gt;Rendering PDF viewers on the server (SSR) can cause headaches since browser-specific APIs don’t exist during server-side rendering. Nuxt provides a simple fix with &lt;code&gt;&amp;lt;ClientOnly&amp;gt;&lt;/code&gt; component in Nuxt 3.&lt;/p&gt;

&lt;h4&gt;
  
  
  How to use it:
&lt;/h4&gt;

&lt;p&gt;In &lt;strong&gt;Nuxt 3&lt;/strong&gt;, Wrap your PDF viewer like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;PdfViewer&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;./components/PdfViewer.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;ClientOnly&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;PdfViewer&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;/ClientOnly&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What does this do?
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;ClientOnly&amp;gt;&lt;/code&gt; tells Nuxt to delay rendering of your PDF viewer component until it's running in the browser. This prevents SSR-related errors from happening when browser-only code is accessed too early. &lt;/p&gt;

&lt;p&gt;If you need a deeper explanation or run into issues, check out &lt;a href="https://stackoverflow.com/questions/72367724/client-only-nuxt-3-vue-plugin" rel="noopener noreferrer"&gt;this discussion on Nuxt client-only plugins&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Nuxt Plugins for Global Registration
&lt;/h3&gt;

&lt;p&gt;If your app needs to display PDFs in many places, it helps to register the viewer as a global component. Nuxt’s plugin system makes this smooth and keeps your code DRY (don’t repeat yourself).&lt;/p&gt;

&lt;p&gt;Here’s the approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new plugin file in &lt;code&gt;plugins/vue-pdf-viewer.js&lt;/code&gt; (or &lt;code&gt;.ts&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Import and register &lt;code&gt;@vue-pdf-viewer&lt;/code&gt; globally.&lt;/li&gt;
&lt;li&gt;In your Nuxt config, make sure the plugin is loaded only on the client.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example for Nuxt 3:&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="c1"&gt;// plugins/vue-pdf-viewer.client.js&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;defineNuxtPlugin&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;#app&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;VPdfViewer&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;@vue-pdf-viewer/viewer&lt;/span&gt;&lt;span class="dl"&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="nf"&gt;defineNuxtPlugin&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;nuxtApp&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="nx"&gt;nuxtApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vueApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;VPdfViewer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;VPdfViewer&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;p&gt;Nuxt automatically reads the files in the &lt;code&gt;plugins/&lt;/code&gt; directory and loads them at the creation of the Vue application. For full details, visit the &lt;a href="https://nuxt.com/docs/guide/directory-structure/plugins" rel="noopener noreferrer"&gt;Nuxt plugins directory documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the viewer globally registered, you can use &lt;code&gt;&amp;lt;VPdfViewer /&amp;gt;&lt;/code&gt; anywhere in your project—no manual imports needed. This approach saves you from repetitive imports and keeps your codebase neat as your app scales.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus Section: Styling Your PDF Viewer
&lt;/h2&gt;

&lt;p&gt;Nobody wants a PDF viewer that looks out of place or breaks your app’s design flow. With a little effort, you can easily style @vue-pdf-viewer to match your branding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tips for styling:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use CSS for visual tweaks:&lt;/strong&gt; Adjust background colors, spacing, and toolbar icons by targeting the viewer’s CSS classes. For detailed steps and best practices, check out this resource on &lt;a href="https://docs.vue-pdf-viewer.dev/tutorial/changing-viewer-appearance.html#changing-viewer-appearance?utm_source=dev.to&amp;amp;utm_content=launch-a-pdf-viewer-in-nuxt-using-@vue-pdf-viewer"&gt;customizing CSS in PDF viewers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Slots for custom controls:&lt;/strong&gt; Vue’s slot system makes it easy to insert your own buttons or menus on the toolbar or sidebar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hide or reorder UI parts:&lt;/strong&gt; Vue PDF Viewer toolbar can be toggled or reorganized through props or CSS. Keep only what you need on the screen.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&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.amazonaws.com%2Fuploads%2Farticles%2Fdlrpafnax7k26xnylsra.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.amazonaws.com%2Fuploads%2Farticles%2Fdlrpafnax7k26xnylsra.png" alt="Vue PDF Viewer" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Launching a PDF viewer in Nuxt with &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_content=launch-a-pdf-viewer-in-nuxt-using-@vue-pdf-viewer"&gt;Vue PDF Viewer&lt;/a&gt; is fast and hassle-free. You get powerful display options, modern controls, and responsive layouts—ready to go with just a few steps. The component’s flexibility means you can start simple or customize for a tailored user experience.&lt;/p&gt;

&lt;p&gt;🙏 Thanks for reading—if you’ve tried this in your own Nuxt app, please feel to share what you built or what you want to see next in Vue PDF Viewer. &lt;/p&gt;

&lt;p&gt;I'm looking forward to seeing what you build with &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_content=launch-a-pdf-viewer-in-nuxt-using-@vue-pdf-viewer"&gt;Vue PDF Viewer&lt;/a&gt; 🚀&lt;/p&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%2Fmedia4.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExbzZ4MXB3cnNsam9uNTFtZHFvN24wMjYweTBxMmw0bHB2Z2FkazkyYyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fa3IWyhkEC0p32%2Fgiphy.gif" 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%2Fmedia4.giphy.com%2Fmedia%2Fv1.Y2lkPTc5MGI3NjExbzZ4MXB3cnNsam9uNTFtZHFvN24wMjYweTBxMmw0bHB2Z2FkazkyYyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw%2Fa3IWyhkEC0p32%2Fgiphy.gif" width="500" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>nuxt</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What are PDF.js Layers and How You Can Use them in Vue.js</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Tue, 04 Mar 2025 08:00:00 +0000</pubDate>
      <link>https://dev.to/9haroon/what-are-pdfjs-layers-and-how-you-can-use-them-in-vuejs-1454</link>
      <guid>https://dev.to/9haroon/what-are-pdfjs-layers-and-how-you-can-use-them-in-vuejs-1454</guid>
      <description>&lt;p&gt;PDF is everywhere, no matter what kind of web app you’re building, you’re bound to need a convenient PDF viewer at some point. But if you’ve ever tried to handle PDFs on the web, you know it can feel like wrestling with outdated plugins, iframe workaround or settling for awkward pop-ups.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExZXJscm9zemUwdXlqb3N4bzdxc3Z5MDBzemZqZjdob2pzdDYybWI2YiZlcD12MV9naWZzX3NlYXJjaCZjdD1n/p8CIVScK4TqUI2aBdA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExZXJscm9zemUwdXlqb3N4bzdxc3Z5MDBzemZqZjdob2pzdDYybWI2YiZlcD12MV9naWZzX3NlYXJjaCZjdD1n/p8CIVScK4TqUI2aBdA/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s where PDF.js comes in—it's an open-source JavaScript library for rendering PDFs right in your browser. Now, if you’re like me and build apps in Vue.js, figuring out how to use PDF.js in your project can be tricky at first.&lt;/p&gt;

&lt;p&gt;To leverage PDF.js's capabilities, it's important to understand its layered architecture. If you’ve ever wondered, “How do I enable text selection?” or “How do I handle links inside the PDF?” or even “How do I build advanced features like annotations?”, the answer usually lies in understanding these layers.&lt;/p&gt;

&lt;p&gt;In this article, I’ll show you how PDF.js creates layers, what each layer is responsible for, and how you can apply them to your next Vue project. Let’s get going!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExZG15dXkxZXZqNmVsYWFjcGlyaGJteG03dnVtbm45anAzZTg2Zm84eSZlcD12MV9naWZzX3NlYXJjaCZjdD1n/TRFcNpyLTdo2kUmkwm/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExZG15dXkxZXZqNmVsYWFjcGlyaGJteG03dnVtbm45anAzZTg2Zm84eSZlcD12MV9naWZzX3NlYXJjaCZjdD1n/TRFcNpyLTdo2kUmkwm/giphy.gif" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Vue PDF Viewer: Flexible and Powerful Vue.js PDF Component
&lt;/h2&gt;

&lt;p&gt;Quick heads up on something I’ve been working on: &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_content=how-pdfjs-creates-layers-and-what-each-layer-is-for"&gt;Vue PDF Viewer&lt;/a&gt;, a handy PDF Viewer that renders PDFs right within your Vue or Nuxt app. It packs over 20 features, including out-of-the-box default toolbar, customization and responsive design, so your users never have to leave your site to interact with your documents.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F4mej2yw6utliflphjc4j.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.amazonaws.com%2Fuploads%2Farticles%2F4mej2yw6utliflphjc4j.png" alt="Vue PDF Viewer" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If that sounds interesting, I’d love for you to give &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_content=how-pdfjs-creates-layers-and-what-each-layer-is-for"&gt;Vue PDF Viewer&lt;/a&gt; a try. Your support helps me keep creating awesome tools and tutorials like this one. ❤️&lt;/p&gt;




&lt;h2&gt;
  
  
  Overview of PDF.js Layers
&lt;/h2&gt;

&lt;p&gt;Before we dive in, here’s a quick snapshot of the four major layers in PDF.js. Each layer handles a specific aspect of rendering or user interaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Canvas Layer&lt;/strong&gt;: Render the static visual content of your PDF (shapes, images, text-as-graphics). It is the foundation of the PDF Viewer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text Layer&lt;/strong&gt;: Sit on top of the canvas to ensure that text is selectable and searchable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Annotation Layer&lt;/strong&gt;: Handle interactive elements (links, forms, highlights) so users can click, type, or navigate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structural Layer&lt;/strong&gt;: Manage the overall layout, including alignment and scaling for all the other layers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By splitting different functionalities into distinct layers, PDF.js remains modular, efficient, and surprisingly easy to tweak. &lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F4b3vrvck9hqc7udt1u4x.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.amazonaws.com%2Fuploads%2Farticles%2F4b3vrvck9hqc7udt1u4x.png" alt="PDF.js Layers" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Let’s start by creating a fresh Vue.js project to integrate PDF.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up Vue.js Project
&lt;/h2&gt;

&lt;p&gt;In this article, I use Codepen as a code editor. If you want to follow along, you can create a new &lt;code&gt;Pen&lt;/code&gt; on Codepen and follow the steps below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Pick a Vue Pen Template
&lt;/h3&gt;

&lt;p&gt;The easiest way to get started is by using CodePen’s built-in Vue template. From the left menu, click on the &lt;code&gt;Pen&lt;/code&gt; menu to open the options, then pick &lt;code&gt;Vue Pen&lt;/code&gt;.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Farjjfxblv9ji5o9g5lo7.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.amazonaws.com%2Fuploads%2Farticles%2Farjjfxblv9ji5o9g5lo7.png" alt="Codepen - Vue Pen" width="642" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This gives you a pre-configured environment with a default Vue setup. In this interface, you can experiment with Vue codes and instantly see your results in the preview area.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fzg9vfgf0ofr9ey5bxhv7.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.amazonaws.com%2Fuploads%2Farticles%2Fzg9vfgf0ofr9ey5bxhv7.png" alt="Codepen - first impression of Vue code editor" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Configure the project and install PDF.js
&lt;/h3&gt;

&lt;p&gt;Next, let’s configure your Vue project to use the &lt;code&gt;pdfjs-dist&lt;/code&gt; library.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open Settings&lt;/strong&gt;: Click on the Settings button at the top of your Pen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select JS Options&lt;/strong&gt;: In the popup, switch to the JS tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick Vue Version&lt;/strong&gt;: Under Vue version, select Vue 3 (or whichever version fits your needs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Packages&lt;/strong&gt;: In the Add Packages box, search for and add &lt;code&gt;pdfjs-dist&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save and Close&lt;/strong&gt;: Click Save &amp;amp; Close to apply your changes.&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2Fydastf7pmpyaf5z0z5g8.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.amazonaws.com%2Fuploads%2Farticles%2Fydastf7pmpyaf5z0z5g8.png" alt="Codepen - how to set Vue version and install dependencies" width="800" height="850"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CodePen will automatically inject an import statement for &lt;code&gt;pdfjs-dist&lt;/code&gt; into your code. You’ll typically want to move that statement below your &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; section to keep things clean and organized.&lt;/p&gt;

&lt;p&gt;That’s it! You’re now set up to start experimenting with PDF.js layers within a Vue environment. So let us get to the layers.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Canvas Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Canvas Layer&lt;/strong&gt; is the foundation—it’s where PDF.js draws the visual elements such as images, shapes, and text (as graphics). Essentially, what you see on-screen is coming from this layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PDF.js uses the HTML &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element to display the PDF's visual content&lt;/li&gt;
&lt;li&gt;It utilizes the browser's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D" rel="noopener noreferrer"&gt;2D canvas API&lt;/a&gt; for high-performance rendering.&lt;/li&gt;
&lt;li&gt;This means your PDF content will look consistent and accurate across various screen sizes and devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ideal for &lt;strong&gt;non-interactive&lt;/strong&gt; or &lt;strong&gt;static&lt;/strong&gt; PDF content.&lt;/li&gt;
&lt;li&gt;It renders the document as an image, ensuring PDF’s fonts, colors, and layouts are consistent across all devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example of Canvas layer Code with Vue
&lt;/h3&gt;

&lt;p&gt;Here’s a quick CodePen demo showcasing a minimal Canvas Layer setup in Vue.js.  &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/9haroon/embed/vEBVBqR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 How the Code Works
&lt;/h3&gt;

&lt;p&gt;Below, I’ll walk through the major steps for rendering a PDF page onto the canvas.&lt;/p&gt;

&lt;h4&gt;
  
  
  (1) Setting Up the PDF Worker
&lt;/h4&gt;

&lt;p&gt;Before loading the PDF, we need to configure the &lt;strong&gt;PDF worker&lt;/strong&gt;. The worker is responsible for processing the PDF in a separate thread, improving performance.&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="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PDFWorker&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://esm.sh/pdfjs-dist/build/pdf.worker.min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GlobalWorkerOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workerSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PDFWorker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pdfjsWorker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PDFWorker&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here, we import &lt;code&gt;pdf.worker.min.js&lt;/code&gt; and set it as the worker source.&lt;/li&gt;
&lt;li&gt;The worker processes the PDF in a separate thread so our UI is not blocked.&lt;/li&gt;
&lt;li&gt;If an error occurs, the worker is assigned to &lt;code&gt;window.pdfjsWorker&lt;/code&gt; as a fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (2) Fetching and Loading the PDF
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PDF_SRC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://pdfobject.com/pdf/pdf_open_parameters_acro8.pdf&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;processLoadingTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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;loadingTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;loadingTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&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;docProxy&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="na"&gt;canvasContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;
         &lt;span class="p"&gt;};&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderContext&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="c1"&gt;// Start the process when the component is mounted&lt;/span&gt;
   &lt;span class="nf"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processLoadingTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PDF_SRC&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;processLoadingTask&lt;/code&gt; method initializes PDF loading via &lt;code&gt;PDFJS.getDocument(source)&lt;/code&gt; from &lt;code&gt;pdfjs-dist&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Once the PDF is fetched (from &lt;code&gt;PDF_SRC&lt;/code&gt;), we store the document proxy (&lt;code&gt;docProxy&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The total number of pages is determined and stored in &lt;code&gt;totalPages&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (3) Rendering the Canvas Layer
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;br&gt;
After the PDF is loaded and we have a page object, the first page is extracted using &lt;code&gt;getPage(1)&lt;/code&gt;. PDF.js then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scales the canvas to match the PDF page dimensions.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element (&lt;code&gt;canvasLayer&lt;/code&gt;) is used to render the PDF page as an image.&lt;/li&gt;
&lt;li&gt;The rendering process is handled by &lt;code&gt;page.render(renderContext)&lt;/code&gt;, where &lt;code&gt;canvasContext&lt;/code&gt; paints the page content onto the canvas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures the final output accurately matches the original PDF’s appearance.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Text Layer
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Text Layer&lt;/strong&gt; ensures that the text in your PDF is &lt;strong&gt;selectable, searchable, and accessible&lt;/strong&gt;. While the Canvas Layer does the heavy lifting of rendering visuals, the Text Layer is what lets you highlight and copy actual text.&lt;/p&gt;
&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PDF.js extracts the &lt;strong&gt;text content&lt;/strong&gt; separately from the PDF.&lt;/li&gt;
&lt;li&gt;It positions each piece of text exactly over the underlying canvas, using absolutely positioned &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elements.&lt;/li&gt;
&lt;li&gt;Even though you might not see these text &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; visually (they’re often transparent), they’re there to support &lt;strong&gt;selection&lt;/strong&gt; and &lt;strong&gt;searching&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Enable &lt;strong&gt;text selection&lt;/strong&gt; and &lt;strong&gt;copying&lt;/strong&gt; from the PDF.&lt;/li&gt;
&lt;li&gt;Text layer is crucial for &lt;strong&gt;accessibility&lt;/strong&gt;, as it enables screen readers to interpret the content.&lt;/li&gt;
&lt;li&gt;Provide text searching and highlighting within the PDF viewer.&lt;/li&gt;
&lt;li&gt;Keep the text aligned with the rendered PDF page&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Text layer Code Example
&lt;/h3&gt;

&lt;p&gt;Here’s a Vue.js CodePen example that demonstrates a Text Layer in action:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/9haroon/embed/GgKbgmm?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 How the Code Works
&lt;/h3&gt;

&lt;h4&gt;
  
  
  (1) Structuring the Layers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"pdfLayersWrapper"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__layers"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__canvas-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"canvasLayer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"textLayer"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__text-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;pdfLayersWrapper&lt;/code&gt; is a container that holds both the &lt;strong&gt;Canvas Layer&lt;/strong&gt; and the &lt;strong&gt;Text Layer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Canvas Layer&lt;/strong&gt; (&lt;code&gt;canvasLayer&lt;/code&gt;) displays the PDF page visually.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Text Layer&lt;/strong&gt; (&lt;code&gt;textLayer&lt;/code&gt;) sits on top of the canvas, allowing text selection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (2) Fetching and Loading the PDF
&lt;/h4&gt;

&lt;p&gt;(Identical to the steps in the &lt;strong&gt;Canvas Layer&lt;/strong&gt;—we load the PDF document first.)&lt;/p&gt;

&lt;h4&gt;
  
  
  (3) Rendering the Text Layer
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;renderText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfPageProxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;textLayerContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewport&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="nx"&gt;pdfPageProxy&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTextContent&lt;/span&gt;&lt;span class="p"&gt;()&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;content&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;textLayerContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;textContentSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;dontFlip&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="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;renderTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&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="nf"&gt;processLoadingTask &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;renderText&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Render the text layer (&lt;code&gt;this.renderText()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pdfPageProxy.getTextContent()&lt;/code&gt; retrieves the text data from the PDF page.&lt;/li&gt;
&lt;li&gt;This data is passed into a new &lt;code&gt;TextLayer&lt;/code&gt;, specifying:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;container&lt;/code&gt;: Container where text is rendered.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;textContentSource&lt;/code&gt;: Extract text data from the PDF.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;viewport&lt;/code&gt;: Define text positioning and scaling.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;renderTask.render()&lt;/code&gt; places invisible but selectable text &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; over the canvas.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Annotation Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Annotation Layer&lt;/strong&gt; handles &lt;strong&gt;interactive elements&lt;/strong&gt; like hyperlinks, highlights, form fields, and comments. If you need your PDF viewer to support internal linking (jumping to different pages), or if your PDFs have fillable forms, this layer is for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PDF.js extracts annotation data (links, form fields, etc.) from the PDF.&lt;/li&gt;
&lt;li&gt;It overlays these data as &lt;strong&gt;interactive HTML elements&lt;/strong&gt; (e.g. &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;), which sit above the &lt;strong&gt;Canvas Layer&lt;/strong&gt; and &lt;strong&gt;Text Layer&lt;/strong&gt; positioned using CSS.&lt;/li&gt;
&lt;li&gt;This layer ensures that users can click, type, and interact without altering the PDF’s static visuals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clicking on links within PDF to navigate between pages.&lt;/li&gt;
&lt;li&gt;Rendering highlights, underlines, and notes from PDF annotations.&lt;/li&gt;
&lt;li&gt;Displaying interactive form fields within a PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Annotation layer Code Example
&lt;/h3&gt;

&lt;p&gt;Check out this CodePen example on the &lt;strong&gt;Annotation Layer&lt;/strong&gt; for Vue.js:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can click the links inside the Codepen to change the PDF's page.&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/9haroon/embed/qEWzOzz?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  📜 How the Code Works
&lt;/h3&gt;

&lt;h4&gt;
  
  
  (1) Structuring the Layers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"pdfLayersWrapper"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__layers"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__canvas-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"canvasLayer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"textLayer"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__text-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"annotationLayer"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__annotation-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We add another &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; for the &lt;strong&gt;Annotation Layer&lt;/strong&gt; within &lt;code&gt;pdfLayersWrapper&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Annotation Layer&lt;/strong&gt; is positioned on top of the &lt;strong&gt;Canvas Layer&lt;/strong&gt; and &lt;strong&gt;Text Layer&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (2) Fetching and Loading the PDF
&lt;/h4&gt;

&lt;p&gt;(Again, the same initial PDF loading steps as before.)&lt;/p&gt;

&lt;h4&gt;
  
  
  (3) Rendering the Annotation Layer
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;getAnnotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageProxy&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;annotations&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;pageProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAnnotations&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;display&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;renderAnnotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfPageProxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;annotationLayerContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;viewport&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;annotations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAnnotations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfPageProxy&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;annotationLayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AnnotationLayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;annotationLayerContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;clonedViewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pdfPageProxy&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;annotationLayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...,&lt;/span&gt;
    &lt;span class="nx"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;linkService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleLinkService&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getAnnotations&lt;/code&gt; retrieves all annotation info from the PDF page (links, form fields, etc.).&lt;/li&gt;
&lt;li&gt;We clone the viewport to match the page’s dimensions for correct positioning.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;renderAnnotations&lt;/code&gt; is then called to replace existing elements in &lt;code&gt;annotationLayerContainer&lt;/code&gt; before rendering new ones. It does so by:

&lt;ul&gt;
&lt;li&gt;Creating a new &lt;code&gt;AnnotationLayer&lt;/code&gt; instance to manage and render annotation elements.&lt;/li&gt;
&lt;li&gt;Displaying the annotation layer by &lt;code&gt;annotationLayer.render({...})&lt;/code&gt;, passing in the annotations and  &lt;code&gt;SimpleLinkService()&lt;/code&gt; to handle internal PDF links.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  (4) Handling Internal Link Clicks
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;annotationLayerContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;annotationLinkId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ele&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ele&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;id&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;pageIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pdfDocProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPageIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;annotationLinkId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a user &lt;strong&gt;clicks on a link&lt;/strong&gt; inside the PDF, the event checks if it's an annotation.&lt;/li&gt;
&lt;li&gt;If it is, we look up the destination page by &lt;code&gt;getPageIndex()&lt;/code&gt; and see if it points to a different page in the PDF.&lt;/li&gt;
&lt;li&gt;We then navigate to that page by updating &lt;code&gt;this.currentPage&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  (5) Handling Page Navigation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A simple navigation bar lets users switch between pages.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page-navigation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;:disabled=&lt;/span&gt;&lt;span class="s"&gt;"currentPage &amp;lt;= 1"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"--currentPage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="ni"&gt;&amp;amp;larr;&lt;/span&gt; 
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{ currentPage }}/{{ totalPages }}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;:disabled=&lt;/span&gt;&lt;span class="s"&gt;"currentPage &amp;gt;= totalPages"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"++currentPage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="ni"&gt;&amp;amp;rarr;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;currentPage&lt;/code&gt; value updates when navigation buttons are clicked.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;watch: { currentPage(newValue) { ... } }&lt;/code&gt; ensures that all layers are re-rendered when the page changes.&lt;/li&gt;
&lt;li&gt;This ensures all layers—&lt;strong&gt;Canvas&lt;/strong&gt;, &lt;strong&gt;Text&lt;/strong&gt;, and &lt;strong&gt;Annotation&lt;/strong&gt;—stay in sync.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Structural Layer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Purpose
&lt;/h3&gt;

&lt;p&gt;Think of the &lt;strong&gt;Structural Layer&lt;/strong&gt; as the master layout manager. It keeps the Canvas, Text, and Annotation layers correctly aligned, scaled, and positioned when users zoom in or out, or when the window is resized.&lt;/p&gt;

&lt;p&gt;This layer is a must-have when creating a &lt;strong&gt;PDF viewer&lt;/strong&gt; as it serves as the &lt;strong&gt;foundation&lt;/strong&gt; that glues all other layers together. &lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Structural Layer&lt;/strong&gt; is typically implemented by a container element (&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;) that wraps all other layers.&lt;/li&gt;
&lt;li&gt;Maintain consistent positioning among layers so they don’t drift apart during zooms or scrolls.&lt;/li&gt;
&lt;li&gt;Act as the foundation that ties everything together, so your PDF viewer remains cohesive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Ensure a &lt;strong&gt;consistent user experience&lt;/strong&gt; across different screen sizes and resolutions.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;smooth zooming&lt;/strong&gt; by scaling all layers proportionally.&lt;/li&gt;
&lt;li&gt;Centralize navigation and layout logic (e.g. controlling how the viewer scrolls from one page to another.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our code examples, the &lt;code&gt;&amp;lt;div ref="pdfLayersWrapper" class="pdf__layers"&amp;gt;&lt;/code&gt; element serves as the Structural Layer, wrapping for other layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"pdfLayersWrapper"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__layers"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__canvas-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"canvasLayer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"textLayer"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__text-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"annotationLayer"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pdf__annotation-layer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why is this important?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Structural Layer&lt;/strong&gt; wrapper ensures the Canvas, Text, and Annotation Layers all stay in sync, even when you zoom or flip through pages.&lt;/li&gt;
&lt;li&gt;You can customize the wrapper to handle special behaviors like lazy-loading pages, scroll snapping and other performance optimizations.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Layers Work Together
&lt;/h2&gt;

&lt;p&gt;Here’s how all four layers in PDF.js work together in harmony:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rendering Process&lt;/strong&gt;:&lt;br&gt;
Each page is first drawn onto the &lt;strong&gt;Canvas Layer&lt;/strong&gt; (visual content). Next, the &lt;strong&gt;Text Layer&lt;/strong&gt; is layered on top to enable selection and searching. Finally, the &lt;strong&gt;Annotation Layer&lt;/strong&gt; is placed at the very top, providing interactive elements like links and form fields.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interactivity&lt;/strong&gt;:&lt;br&gt;
The &lt;strong&gt;Annotation Layer&lt;/strong&gt; handles user events (like clicking on links or typing into form fields), while the Text Layer ensures text can be highlighted, copied, or searched.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PDF Viewer&lt;/strong&gt;:&lt;br&gt;
The &lt;strong&gt;Structural Layer&lt;/strong&gt; wraps everything up, ensuring that zooming, resizing, and navigation across pages remain consistent. It keeps all layers aligned and responsive, so users get a polished viewing experience.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




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

&lt;p&gt;I’ve used PDF.js in a variety of Vue.js projects, from simple PDF previews to fully interactive document systems, the layered architecture is always at the core.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does this matter for you?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can optimize each layer for performance or user experience.&lt;/li&gt;
&lt;li&gt;You can customize how your viewer handles interactions, text searches, or forms.&lt;/li&gt;
&lt;li&gt;You’ll find it much easier to maintain or extend your PDF viewer code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding these layers, you will be able to optimize PDF.js implementations, customize features, and deliver a better user experience. Whether you’re building a simple Vue.js PDF viewer or a complex document management system, leveraging these layers effectively will set your project up for success.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vue PDF Viewer: The PDF Viewer Built for Vue.js Developers 🚀
&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.amazonaws.com%2Fuploads%2Farticles%2F4mej2yw6utliflphjc4j.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.amazonaws.com%2Fuploads%2Farticles%2F4mej2yw6utliflphjc4j.png" alt="Vue PDF Viewer" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you enjoyed this article, I’d love for you to check out &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_content=how-pdfjs-creates-layers-and-what-each-layer-is-for"&gt;Vue PDF Viewer&lt;/a&gt;. It’s a PDF Viewer built from the ground up for Vue and Nuxt applications. Whether you’re just starting out or already running a large-scale app, Vue PDF Viewer got you covered.&lt;/p&gt;

&lt;p&gt;Designed with developers in mind, Vue PDF Viewer offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple Vue integration to get you up and running fast.&lt;/li&gt;
&lt;li&gt;Advanced customization so you can nail that perfect look and feel.&lt;/li&gt;
&lt;li&gt;Responsive layouts that adapt to any device.&lt;/li&gt;
&lt;li&gt;Developer-friendly APIs for more flexibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your support motivates me to keep creating tools and content for the Vue community. Thank you for checking out &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_content=how-pdfjs-creates-layers-and-what-each-layer-is-for"&gt;Vue PDF Viewer&lt;/a&gt;. Happy coding! 🙏&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExM2Y2OHdzN2syZXd2ZmMxc24zdGJiM2NvZWd6enV6cHZ4bWNtejFyaCZlcD12MV9naWZzX3NlYXJjaCZjdD1n/ule4vhcY1xEKQ/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExM2Y2OHdzN2syZXd2ZmMxc24zdGJiM2NvZWd6enV6cHZ4bWNtejFyaCZlcD12MV9naWZzX3NlYXJjaCZjdD1n/ule4vhcY1xEKQ/giphy.gif" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Best 4 Methods to Build a PDF Viewer in React.js: PDF.js, react-pdf, and More (2024 Guide) 🚀</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Wed, 23 Oct 2024 07:33:41 +0000</pubDate>
      <link>https://dev.to/9haroon/best-4-methods-to-build-a-pdf-viewer-in-reactjs-pdfjs-react-pdf-and-more-2024-guide-1613</link>
      <guid>https://dev.to/9haroon/best-4-methods-to-build-a-pdf-viewer-in-reactjs-pdfjs-react-pdf-and-more-2024-guide-1613</guid>
      <description>&lt;p&gt;When working on React projects, one thing I’ve learned is that having the right tools can make or break the user experience. And when it comes to displaying documents, PDFs are still one of the best ways to share content, whether it’s reports, manuals, or e-books. The challenge, though, is how to integrate a reliable PDF viewer into your app without jumping through hoops.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through four different ways to build a PDF viewer in React.js. From quick and simple methods like iFrame and opening PDFs in a new tab, to more customizable options like PDF.js and react-pdf—you’ll be able to choose the approach that works best for your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdDY3aXp0MGV5aXdxNXRieW11aG90aW1yaTk4ZDh2emZiZjhrOXhzeiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/yaR8Dux1s0fAI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdDY3aXp0MGV5aXdxNXRieW11aG90aW1yaTk4ZDh2emZiZjhrOXhzeiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/yaR8Dux1s0fAI/giphy.gif" width="500" height="303"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why PDFs Matter in Web Development&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Rendering PDFs on websites comes with a wide range of use cases. Here are some scenarios where PDFs can really come in handy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Financial Reports&lt;/strong&gt;&lt;br&gt;
Imagine you’re building a finance dashboard, and your users need to access quarterly reports or balance sheets. By embedding these documents as PDFs, users can easily download, print, or view them in high resolution on any device. Plus, you keep the formatting consistent and professional, making sure nothing looks out of place.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sales Invoices&lt;/strong&gt;&lt;br&gt;
For apps that generate or display invoices, PDFs are the perfect format to ensure clarity and accuracy. You can provide users with downloadable, printable invoices that maintain the exact layout and branding you’ve designed. No more worrying about documents being altered or misinterpreted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Research Papers&lt;/strong&gt;&lt;br&gt;
If you’re working on a platform that handles academic content or research, PDFs are a great way to present papers, journal articles, and white papers. Researchers can zoom in for detail, highlight key points, or leave annotations—all while preserving the document’s original format.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Legal Contracts&lt;/strong&gt;&lt;br&gt;
For businesses dealing with contracts, confidentiality agreements, or any legal documents, PDFs are essential for maintaining integrity and security. PDFs ensure that legal contracts remain tamper-proof, preserving the original formatting and allowing for secure digital signatures. This is crucial when handling sensitive agreements, ensuring they can be easily shared, viewed, and stored by all parties involved without the risk of edits.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  React PDF: A Flexible and Powerful PDF Viewer for React.js
&lt;/h2&gt;

&lt;p&gt;Before diving into the technical side of things, let me introduce you to &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=best-4-methods-to-build-a-pdf-viewer-in-react-js-2024"&gt;React PDF&lt;/a&gt;—a library I’ve been working on that brings PDF viewing directly into your React or Next.js applications. With over 20 features including theme customization, external APIs, and responsive layouts, it’s perfect for ensuring your users can interact with PDFs without leaving your site.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fyrnscvdyq12mtzmdked3.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.amazonaws.com%2Fuploads%2Farticles%2Fyrnscvdyq12mtzmdked3.png" alt="React PDF" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in trying it out, I’d be super grateful if you could check out &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=best-4-methods-to-build-a-pdf-viewer-in-react-js-2024"&gt;React PDF&lt;/a&gt;. Your support will motivate me to keep creating more content! ❤️‍🔥&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 1: Open PDF in a New Tab
&lt;/h2&gt;

&lt;p&gt;If you're working in React and need a simple way to open PDFs, Method 1 is your go-to solution: opening the PDF in a new browser tab. Let's break it down:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Add a Link or Button
&lt;/h3&gt;

&lt;p&gt;In your React component, create a link or button to open the PDF.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/your-pdf.pdf"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pdfLink"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open PDF&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- or button --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"pdf-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open PDF&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Trigger the PDF
&lt;/h3&gt;

&lt;p&gt;Add an &lt;code&gt;onclick&lt;/code&gt; event to open the PDF in a new tab.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/your-pdf-url-goes-here.pdf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;onclick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdfUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_blank&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pros and Cons
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Pros:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easy to Implement&lt;/strong&gt;: This method is quick and simple.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Access&lt;/strong&gt;: Users can view the PDF without anything in between.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Cons:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited Control&lt;/strong&gt;: You can't fully control how the PDF is displayed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser Differences&lt;/strong&gt;: The way it behaves can vary between browsers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Use It
&lt;/h3&gt;

&lt;p&gt;Use this method when you want to keep the PDF viewer separate from your main website. For instance, if embedding it in an iframe isn't ideal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 2: Using an iFrame, Embed, or Object
&lt;/h2&gt;

&lt;p&gt;A simple way to display PDFs in React is by using an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; element. These methods leverage the browser’s built-in PDF capabilities, allowing you to embed the PDF directly into your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Implement:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create your React project.&lt;/li&gt;
&lt;li&gt;Add one of these elements (&lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt;) in your JSX.&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;src&lt;/code&gt; attribute to the PDF file’s URL.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; Example:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;iframe&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/your-pdf-url.pdf"&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"600px"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; Example:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;embed&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/your-pdf-url.pdf"&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"600px"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"application/pdf"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; Example:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;object&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/your-pdf-url.pdf"&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"600px"&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"application/pdf"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Your browser doesn’t support PDFs. Please download the PDF to view it: &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/your-pdf-url.pdf"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Download PDF&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see a React code in my &lt;a href="https://github.com/9haroon/pdf-embed-react-examples/tree/main/src/components" rel="noopener noreferrer"&gt;Github&lt;/a&gt; repository&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Super Easy&lt;/strong&gt;: It’s quick and simple to implement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uses Browser’s PDF Viewer&lt;/strong&gt;: No extra libraries required—just use the browser’s native PDF viewer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Setup Needed&lt;/strong&gt;: Works right away without additional configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited Control&lt;/strong&gt;: You can’t control the PDF viewer’s interface or customize it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser Compatibility&lt;/strong&gt;: Not all browsers handle these methods the same way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customization Limits&lt;/strong&gt;: These options don't offer much flexibility for customizing the viewing experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Use It:
&lt;/h3&gt;

&lt;p&gt;Use these methods when you want a fast, straightforward solution and don’t need to customize the PDF viewer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 3: Using PDF.js
&lt;/h2&gt;

&lt;p&gt;If you need more control over how PDFs are displayed and interacted with, PDF.js is a great option. Created by Mozilla, PDF.js lets you fully customize the PDF viewing experience within your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Implement:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install the PDF.js library in React project.
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a React component to render the PDF using PDF.js.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PdfProps&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="s2"&gt;../types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdfjs-dist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;PDFDocumentProxy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;RenderParameters&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="s2"&gt;pdfjs-dist/types/src/display/api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;useCallback&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;useState&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PdfJs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PdfProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GlobalWorkerOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workerSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://unpkg.com/pdfjs-dist@4.7.76/build/pdf.worker.min.mjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&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;canvasRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLCanvasElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="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="nx"&gt;setPdfDoc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PDFDocumentProxy&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCurrentPage&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;renderTask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RenderTask&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;renderPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageNum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pdfDoc&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvasRef&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pdf&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;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="c1"&gt;// canvas.hidden = true;&lt;/span&gt;
      &lt;span class="nx"&gt;pdf&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageNum&lt;/span&gt;&lt;span class="p"&gt;)&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;page&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
          &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;renderContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RenderParameters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;canvasContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;
          &lt;span class="k"&gt;try&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;renderTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;renderTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;renderTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;renderTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;pdfDoc&lt;/span&gt;&lt;span class="p"&gt;]&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="nf"&gt;renderPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&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="p"&gt;},&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="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;renderPage&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadingTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;loadingTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadedDoc&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="nf"&gt;setPdfDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loadedDoc&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="nx"&gt;error&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;span class="nx"&gt;src&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;nextPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;pdfDoc&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;pdfDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numPages&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;setCurrentPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;prevPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;setCurrentPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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;&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;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prevPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Previous
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
        &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;nextPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&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="nx"&gt;numPages&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Next
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;canvas&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;canvasRef&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;canvas&lt;/span&gt;&lt;span class="p"&gt;&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="p"&gt;&amp;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;p&gt;You can find a complete example code on my &lt;a href="https://github.com/9haroon/pdf-embed-react-examples/blob/main/src/components/PdfJs.tsx" rel="noopener noreferrer"&gt;Github&lt;/a&gt;: &lt;code&gt;components/PdfJs.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the result 👇&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fkpbzymc0wni33mt219fq.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fkpbzymc0wni33mt219fq.gif" alt="React with PDF.js" width="640" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Features&lt;/strong&gt;: PDF.js provides powerful PDF rendering with options like zoom, page navigation, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly Customizable&lt;/strong&gt;: You have full control over how the PDF is displayed and interacted with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Browser Support&lt;/strong&gt;: Works across most modern browsers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-Maintained&lt;/strong&gt;: Backed by a big community with regular updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learning Curve&lt;/strong&gt;: It takes time to learn and implement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More Complex&lt;/strong&gt;: Setting it up is harder compared to using iFrame or opening PDFs in a new tab.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation Challenges&lt;/strong&gt;: The docs can be tough to follow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Issues&lt;/strong&gt;: Customizing it for frameworks like React, Angular, or Vue can be tricky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher Complexity&lt;/strong&gt;: Handling advanced interactions with PDFs increases the complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Use It:
&lt;/h3&gt;

&lt;p&gt;Use PDF.js when you need full control over the PDF viewer and want to add advanced features directly into your app’s interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Method 4: Using react-pdf (MIT)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;react-pdf&lt;/strong&gt; is an open-source package built on top of PDF.js that makes it easier to display PDFs in React apps while simplifying the development process.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Implement:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;react-pdf&lt;/code&gt; using npm or yarn.
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Import the &lt;code&gt;react-pdf&lt;/code&gt; component into your app.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pdfjs&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="s2"&gt;react-pdf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;pdfjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GlobalWorkerOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workerSrc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`//unpkg.com/pdfjs-dist@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pdfjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/build/pdf.worker.min.mjs`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Follow the instructions on &lt;a href="https://github.com/wojtekmaj/react-pdf" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; for detailed setup.&lt;/li&gt;
&lt;li&gt;Create a React component to render PDF
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PdfReactPdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;PdfProps&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setNumPages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPageNumber&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onDocumentLoadSuccess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;numPages&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setNumPages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;nextPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setPageNumber&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;prevPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setPageNumber&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;v&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;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prevPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Previous
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;nextPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numPages&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Next
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Document&lt;/span&gt;
        &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onLoadSuccess&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onDocumentLoadSuccess&lt;/span&gt;&lt;span class="si"&gt;}&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;"my-react-pdf"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="na"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Page &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; of &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&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="p"&gt;&amp;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;p&gt;You can check out my example on my &lt;a href="https://github.com/9haroon/pdf-embed-react-examples/blob/main/src/components/PdfReactPdf.tsx" rel="noopener noreferrer"&gt;Github&lt;/a&gt;: &lt;code&gt;components/PdfReactPdf.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easy Integration&lt;/strong&gt;: Works smoothly with React. Better developer documentation compared to PDF.js.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More Control&lt;/strong&gt;: Offers more customization than iFrame or New Tab methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive and Customizable&lt;/strong&gt;: Adjusts well to different screen sizes and offers some customization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;More Setup&lt;/strong&gt;: Takes a bit more effort to set up compared to simpler methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Advanced Features&lt;/strong&gt;: Doesn’t offer as much flexibility as using the full PDF.js library.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Use It:
&lt;/h3&gt;

&lt;p&gt;Use react-pdf if you need an easy-to-implement, customizable PDF viewer for your React project.&lt;/p&gt;




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

&lt;p&gt;There are several ways to implement a PDF viewer in React.js. Using iFrame or opening the PDF in a new tab offers simplicity but limits customization. &lt;strong&gt;PDF.js&lt;/strong&gt; provides advanced features and control, but it comes with added complexity. &lt;strong&gt;react-pdf&lt;/strong&gt; (open-source) bridges the gap by offering ease of use with customization options.&lt;/p&gt;

&lt;p&gt;Choose the method that best fits your project’s needs, whether it’s speed, simplicity, or control over the user experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExajJmcHpkb29qc3c4ZGZ4ODd6dXBmMTI5eXR6ZXUxYnp4YmxyYXV1NCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/26FeVbl67iSv0LR4c/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExajJmcHpkb29qc3c4ZGZ4ODd6dXBmMTI5eXR6ZXUxYnp4YmxyYXV1NCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/26FeVbl67iSv0LR4c/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Is there a Better Solution?
&lt;/h2&gt;

&lt;p&gt;While methods like iFrame or opening PDFs in a new tab can be quick fixes, they don’t offer much control or customization. PDF.js and react-pdf (an open-source library for React) provide more advanced options, but they come with their own challenges—especially if you’re looking for something easier to implement, maintain, and customize for your specific React project.&lt;/p&gt;

&lt;p&gt;That’s why I’m building &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=best-4-methods-to-build-a-pdf-viewer-in-react-js-2024"&gt;React PDF&lt;/a&gt; (not to be confused with react-pdf). While react-pdf is a great open-source library, React PDF is designed to offer more flexibility, powerful customization options, and a simpler setup process tailored to developers working on both small and complex React applications.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fh8dq0x1y4fdu0j2r2gac.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.amazonaws.com%2Fuploads%2Farticles%2Fh8dq0x1y4fdu0j2r2gac.png" alt="React PDF" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Made for React.js&lt;/strong&gt;: Unlike more general libraries, React PDF is built from the ground up to work seamlessly with React. Whether you need quick rendering or advanced features, it integrates easily into your project with just a few lines of code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customization without Complexity&lt;/strong&gt;: With React PDF, you have full control over the appearance and behavior of your PDF viewer. You can match the viewer’s design to your app’s theme, add interactive features like search and bookmarking, and customize the layout—all without the hassle of dealing with complex configurations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extensive Feature Set&lt;/strong&gt;: Whether you need basic functionality like viewing PDFs or more advanced features like rotation, different scrolling modes, and responsive design, React PDF has you covered. Plus, it’s built with performance in mind, so you don’t have to worry about lag when handling large files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;User-Friendly Documentation&lt;/strong&gt;: I’ve made sure the technical docs are clear and easy to follow. From starter kits to detailed API references, everything is in place to help you get up and running fast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reliable Support and Updates&lt;/strong&gt;: As developers, I know how important it is to have reliable support. That’s why React PDF comes with responsive customer support and regular updates to keep the library optimized and bug-free.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If this sounds like what you need, I’d be grateful if you could check out &lt;a href="https://www.react-pdf.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_content=best-4-methods-to-build-a-pdf-viewer-in-react-js-2024"&gt;React PDF&lt;/a&gt;. Your support encourages me to keep improving and building tools for the community. Thank you! 🙏&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExc29lZzQ1MnJ2dm00MmV1cnFlb3BvdWVodWxjcHVpdmpmMzcxaHN4eSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/NkjMuxGZkziUMZnBHW/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExc29lZzQ1MnJ2dm00MmV1cnFlb3BvdWVodWxjcHVpdmpmMzcxaHN4eSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/NkjMuxGZkziUMZnBHW/giphy.gif" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>MUI Date Picker Showdown: Community vs. Pro version</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Thu, 11 Apr 2024 12:30:00 +0000</pubDate>
      <link>https://dev.to/9haroon/mui-date-picker-showdown-community-vs-pro-version-4ki0</link>
      <guid>https://dev.to/9haroon/mui-date-picker-showdown-community-vs-pro-version-4ki0</guid>
      <description>&lt;p&gt;As a developer, I find Material-UI (MUI) to be an absolute gem in the React ecosystem. When I embark on a new web application project or need to create sleek and functional interfaces, it’s my go-to toolkit. MUI is like a trusty companion that walks alongside me. &lt;br&gt;
&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExcGM5bjFkZGhldTNnMzNxaTlkMW5jb2M1Njh5eG5jYmIzbThrM2Y2MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/LnXN7UHFoqcoYMV4nU/giphy-downsized-large.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExcGM5bjFkZGhldTNnMzNxaTlkMW5jb2M1Njh5eG5jYmIzbThrM2Y2MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/LnXN7UHFoqcoYMV4nU/giphy-downsized-large.gif" width="436" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inspired by Google’s Material Design, it brings a sense of consistency and modernity to my projects, no matter the platform. The pre-built components in MUI are like building blocks. They save me precious time — time that I’d rather spend sipping coffee and brainstorming creative solutions. Whether I am crafting a complex enterprise app or a single-page site, MUI has my back.&lt;/p&gt;

&lt;p&gt;Among its many components, the Date Picker stands out as an essential tool for applications that requires date selections. It is offered in both the Community and Pro versions of MUI X, with the Pro version offering unique features and capabilities over the Community version. But which one is the best fit for your project? Let's dive into the MUI Date Picker showdown: Community vs. Pro.&lt;/p&gt;




&lt;h2&gt;
  
  
  Community Edition: The Open-Source Hero
&lt;/h2&gt;

&lt;p&gt;The Community version of the MUI Date Picker is a go-to choice for developers looking for a free and reliable solution. It offers basic date picking functionalities like selecting dates from a calendar or inputting them through a text field.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Basic Date Selection&lt;/strong&gt;: Allows users to select and input a single date, single time or single date &amp;amp; time, facilitating basic scheduling and planning functionalities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Integration&lt;/strong&gt;: Can be easily integrated into any React application, offering a straightforward setup process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customization and Theming&lt;/strong&gt;: Provides standard customization options that adhere to Material Design principles, ensuring a consistent look and feel across your application.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To further elaborate, the Community version comes with the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Date Components&lt;/strong&gt;: These comprise a date picker, a date field for simple date selection and a date calendar to display.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Flym36p38d691tk5u6zwq.gif" width="1314" height="1170"&gt;Date Picker, Mobile Date Picker and Date Field



&lt;p&gt;&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fcbtadzwtpqqydrjim72l.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fcbtadzwtpqqydrjim72l.gif" width="1314" height="1170"&gt;&lt;/a&gt;&lt;/p&gt;
Date Calendar



&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Time Components&lt;/strong&gt;: Included here are a time picker and a time field, facilitating time selection processes.&lt;br&gt;
&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Ffdnsnd8cw571k8ifp7ya.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Ffdnsnd8cw571k8ifp7ya.gif" width="800" height="712"&gt;&lt;/a&gt;&lt;/p&gt;
Time Picker, Mobile Time Picker and Time Field



&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Date-Time Components&lt;/strong&gt;: The version comes with a date-time picker and a date-time field, enabling selection and display of both date and time simultaneously.&lt;br&gt;
&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fguo5svvasxvhnmyrlsrq.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fguo5svvasxvhnmyrlsrq.gif" width="1422" height="1170"&gt;&lt;/a&gt;&lt;/p&gt;
Date Time Picker, Mobile Date Time Picker and Date time Field



&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Community version is perfectly suited for small to medium-scale projects that strictly follow the Material UI design system, requiring basic date picking functionalities within budget constraints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pro Version: Elevating Date Range Selection
&lt;/h2&gt;

&lt;p&gt;For developers seeking more advanced features and dedicated support, the Pro version of MUI X Date Picker offers additional features on top of all the features included in the Community version:&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Date and Time Range Selection&lt;/strong&gt;: In addition to basic date picking, the Pro version offers a date/time range selector, allowing users to make precise time selections within a specified range.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated Support&lt;/strong&gt;: Subscribers to the Pro version gain access to MUI's dedicated support team for troubleshooting and guidance.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparing MUI X Plans
&lt;/h2&gt;

&lt;p&gt;When considering the Pro plan for MUI X, it’s essential to weigh your specific requirements against the plan’s offerings. Sure, it nails the date-time range features, but it’s also got a secret stash of UI magic.&lt;/p&gt;

&lt;p&gt;The Pro plan not only caters to date-time functionalities but also provides a comprehensive suite of advanced UI components. These include powerful libraries like the advanced data grid and other enhancements. It’s tailor-made for developers and projects seeking a robust set of features and robust support.&lt;/p&gt;

&lt;p&gt;However, if your project’s focus primarily revolves around sophisticated date-time management, you might want to explore alternative solutions or carefully evaluate whether the Pro plan’s broader offerings align with your needs and budget.&lt;/p&gt;




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

&lt;p&gt;The choice between MUI Date Picker Community and Pro versions ultimately hinges on your project's specific needs and budget constraints. For developers working on smaller projects or those with limited requirements for date selection, the Community version offers a robust, cost-effective solution. On the other hand, the Pro version is best suited for enterprise-level applications or large projects demanding a wider range of functionalities, customization options, and global support.&lt;/p&gt;

&lt;p&gt;Regardless of your choice, both versions uphold the high standards and aesthetic appeal synonymous with Material-UI components. By comparing the two, developers can make an informed decision that best suits their project, ensuring a sleek and efficient user experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExOTF5YmtvMHFwY2FkZzZ1bWwxc3Jxbnk5c2poNDdubXpuNzlyOHFudyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/26FeVbl67iSv0LR4c/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExOTF5YmtvMHFwY2FkZzZ1bWwxc3Jxbnk5c2poNDdubXpuNzlyOHFudyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/26FeVbl67iSv0LR4c/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Is There a Better Solution?
&lt;/h2&gt;

&lt;p&gt;With MUI X Pro’s high cost, developers seeking budget-friendly date/time pickers often face a dilemma. While building custom solutions or using other libraries is an option, seamless integration with MUI can be tricky. To bridge this gap, I’m developing &lt;a href="https://www.mui-table-date.com/?utm_source=dev.to&amp;amp;utm_medium=mui-date-picker-showdown-community-vs-pro-version"&gt;MUI Table Date&lt;/a&gt;, a cost-effective alternative that combines functionality with ease of use.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fhy5v3ybvl26rjqacd1eu.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.amazonaws.com%2Fuploads%2Farticles%2Fhy5v3ybvl26rjqacd1eu.png" alt="MUI Table Date" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When comparing &lt;a href="https://www.mui-table-date.com/?utm_source=dev.to&amp;amp;utm_medium=mui-date-picker-showdown-community-vs-pro-version"&gt;MUI Table Date&lt;/a&gt; to MUI X, several factors make it a compelling alternative:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cost-Effective&lt;/strong&gt;: MUI Table Date is 81% cheaper than MUI X Pro. If you're looking for a wallet-friendly option without compromising functionality, it's a definite win.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Feature-Rich&lt;/strong&gt;: Don't let the price fool you. MUI Table Date packs a punch with advanced features such as Date Time Range Picker and advanced data grids.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple Licensing&lt;/strong&gt;: No convoluted terms here. MUI Table Date keeps it straightforward. No legal acrobatics – just clear licensing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Seamless Integration&lt;/strong&gt;: Worried about compatibility? Fear not! MUI Data Table plays well with MUI. It's like they were made for each other.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you find this library helpful, please consider exploring &lt;a href="https://www.mui-table-date.com/?utm_source=dev.to&amp;amp;utm_medium=mui-date-picker-showdown-community-vs-pro-version"&gt;MUI Table Date&lt;/a&gt;. Your support motivates me to create even more content. Thank you in advance! 🙏&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeHYyMjh5Zzg4OWVlN2lka2ZiemlkeDJ4cGZuMXY3eTkydWVtNXowcSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/4ml290TZ35zOM/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExeHYyMjh5Zzg4OWVlN2lka2ZiemlkeDJ4cGZuMXY3eTkydWVtNXowcSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/4ml290TZ35zOM/giphy.gif" width="500" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>📝 Using JS Object Schemas to Validate Data with Yup, Zod, Joi and more 🤩</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Tue, 26 Mar 2024 13:30:00 +0000</pubDate>
      <link>https://dev.to/9haroon/using-js-object-schemas-to-validate-data-with-yup-zod-joi-and-more-12fh</link>
      <guid>https://dev.to/9haroon/using-js-object-schemas-to-validate-data-with-yup-zod-joi-and-more-12fh</guid>
      <description>&lt;p&gt;I’m sure many of you who have developed interactive web applications have experience handling data validation. In fact, it’s probably a common topic. You could create custom validations or you may choose to validate via JavaScript Object Notation (JSON) Schemas . When I need to use JSON Schema library, I tend to use Yup. Having validate data with JSON Schema libraries and without one across different project scales, they are effective in their own ways.  &lt;/p&gt;

&lt;p&gt;Today, I want to dive deeper into the world of JSON, exploring popular libraries as well as other notable libraries in the ecosystem. Additionally, I will mention how you can do custom validations without using a JSON Schema library. And finally, I will compare both methods. &lt;/p&gt;

&lt;p&gt;Please note that I’m using code examples using JavaScript without basing on a JS framework such as React or Vue since you can apply both methods to any JS framework. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExMGdzbWNyMzNsMDV5YzVzOTY2ejBqc2c1bmUyaHJveDBqenk4aGE3byZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/xThtalkAxYnZd94uB2/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExMGdzbWNyMzNsMDV5YzVzOTY2ejBqc2c1bmUyaHJveDBqenk4aGE3byZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/xThtalkAxYnZd94uB2/giphy.gif" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Validex: The Most Complete Validation Collection for React&amp;nbsp;Forms
&lt;/h2&gt;

&lt;p&gt;Just a quick background about what I'm working on. &lt;a href="https://www.validex.dev/?utm_source=dev.to&amp;amp;utm_content=using-json-for-custom-validations"&gt;Validex&lt;/a&gt; can help you validate React forms in minutes. It has over 36 validators ranging from string, password, credit card, email, file, image and more. Each validator has TypeScript and JavaScript codes, RegEx and Non-RegEx options and unit tests.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fzpc9mt2h1jzvzgetnmbt.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.amazonaws.com%2Fuploads%2Farticles%2Fzpc9mt2h1jzvzgetnmbt.png" alt="Validex - Complete Collection of Form Validations for React" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would be grateful 🥹 if you could take a moment to explore &lt;a href="https://www.validex.dev/?utm_source=dev.to&amp;amp;utm_content=using-json-for-custom-validations"&gt;Validex&lt;/a&gt; out. It would motivate me to create even more contents.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is JavaScript Object Notation Schema?
&lt;/h2&gt;

&lt;p&gt;JSON Schemas define the structure and validation rules for JavaScript objects. They provide a way to specify the expected shape of an object and enforce validation rules on its properties. This helps ensure that data is in the correct format and meets certain criteria.&lt;/p&gt;

&lt;p&gt;Object schemas can be particularly useful in scenarios where user input needs to be validated before it is processed. For example, validating user registration forms, API request payloads, or form fields in a web application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introducing Yup and Zod
&lt;/h2&gt;

&lt;p&gt;Yup and Zod are two popular libraries in the JavaScript ecosystem that provide powerful and flexible schema validation capabilities. Both libraries offer rich features and strong type support, making them ideal choices when working with TypeScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Yup
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/jquense/yup?tab=readme-ov-file#yup" rel="noopener noreferrer"&gt;Yup&lt;/a&gt; is a schema validation library that focuses on simplicity and ease of use. It provides a fluent API for defining object schemas and supports a wide range of validation rules out of the box. With Yup, you can easily define schema validation rules such as required fields, minimum and maximum values, regular expressions, and more.&lt;/p&gt;

&lt;p&gt;Yup also supports asynchronous validation, allowing you to validate data against external data sources or perform complex validation logic. It also integrates well with popular frontend frameworks like React, making it a versatile choice for both server-side and client-side validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zod
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/colinhacks/zod?tab=readme-ov-file#zod" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; is another powerful schema validation library for JavaScript and TypeScript. It takes a pragmatic approach to validation and aims to provide a seamless development experience. Zod's API is inspired by Yup but comes with some unique features and optimizations.&lt;/p&gt;

&lt;p&gt;With Zod, you can define complex object schemas with ease. It supports various validation rules and provides a robust type inference system that ensures type safety throughout your codebase. Zod also includes features like partial and pick helpers, allowing you to create more flexible validation schemas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Libraries in the Ecosystem
&lt;/h3&gt;

&lt;p&gt;While Yup and Zod are popular choices, there are several other libraries available in the JavaScript ecosystem for schema validation. Some of these include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/hapijs/joi?tab=readme-ov-file#joi" rel="noopener noreferrer"&gt;Joi&lt;/a&gt;: Joi is a powerful schema validation library that focuses on server-side validation. It provides a comprehensive rule set and supports complex validation scenarios.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ajv-validator/ajv?tab=readme-ov-file#ajv-json-schema-validator" rel="noopener noreferrer"&gt;Ajv&lt;/a&gt;: Ajv is a fast and powerful JSON Schema validator that supports a wide range of validation keywords and features. It is highly extensible and widely used in the JavaScript community.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ianstormtaylor/superstruct?tab=readme-ov-file#why" rel="noopener noreferrer"&gt;Superstruct&lt;/a&gt;: Superstruct is a lightweight and minimalist validation library that leverages TypeScript's powerful typesystem to provide compile-time validation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These libraries offer different features and tradeoffs, so exploring the options and choosing the one that best fits your needs is recommended.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Utilize Each Library in a Customized Manner
&lt;/h2&gt;

&lt;p&gt;In this section, I will explore how to implement custom email validation using libraries such as Yup, Zod, and other popular options like Joi and Ajv. Email validation is a common requirement in many applications, and different libraries offer different approaches and features to handle this task. Let's dive into the examples and explore how each library can be used to validate email addresses in a customized manner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email Validation Rules
&lt;/h3&gt;

&lt;p&gt;Here are some commonly used email validation rules that I will be incorporating in the examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Email is required&lt;/strong&gt;: The email field must not be empty or null.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Email must include @ symbol&lt;/strong&gt;: The email address should contain the "@" symbol.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Email must include the domain&lt;/strong&gt;: The email address should include the domain name (e.g., example.com).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Email must include the top-level domain&lt;/strong&gt;: The email address should have a valid top-level domain (e.g., .com, .org, .net).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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.amazonaws.com%2Fuploads%2Farticles%2Fgs4f6gg3z0myz3mcftqa.gif" 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.amazonaws.com%2Fuploads%2Farticles%2Fgs4f6gg3z0myz3mcftqa.gif" alt="Email Validation with JS Object Schemas" width="548" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Yup
&lt;/h4&gt;

&lt;p&gt;For an email validation, Yup will use &lt;code&gt;string&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; methods to create custom validation. Here is an example.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&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="s2"&gt;yup&lt;/span&gt;&lt;span class="dl"&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;formSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide an email address&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-symbol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please include an `@` symbol&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="nx"&gt;value&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;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide an email's domain&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="nx"&gt;value&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;return&lt;/span&gt; &lt;span class="sr"&gt;/@&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-top-level-domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please include top level domain such as .co .org&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="nx"&gt;value&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;return&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;$/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email-format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email format&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="nx"&gt;value&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Z0-9._%+-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+@&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Z0-9.-&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;$/i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;emailRegex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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;p&gt;You may find the codes in this &lt;a href="https://codepen.io/9haroon/pen/xxBvqPb" rel="noopener noreferrer"&gt;CodePen link&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Zod
&lt;/h4&gt;

&lt;p&gt;Zod will use &lt;code&gt;string&lt;/code&gt; and &lt;code&gt;refine&lt;/code&gt; methods to create custom validation&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&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="s2"&gt;zod&lt;/span&gt;&lt;span class="dl"&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;formSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide an email address&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="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please include an `@` symbol&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="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/@&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide an email's domain&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="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;$/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please include top level domain such as .co .org&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://codepen.io/9haroon/pen/JjzgQEZ" rel="noopener noreferrer"&gt;CodePen link&lt;/a&gt; of Zod validation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Joi
&lt;/h4&gt;

&lt;p&gt;Joi will use &lt;code&gt;any&lt;/code&gt; and &lt;code&gt;custom&lt;/code&gt; methods to create non-primitive data validation.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;joi&lt;/span&gt;&lt;span class="dl"&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;validateEmailFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CustomValidator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;helpers&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;validateEmail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&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="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide an email address&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please include an `@` symbol&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/@&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please provide an email's domain&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;$/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&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="nx"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please include top level domain such as .co .org&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;types&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;formSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validateEmailFn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://codepen.io/9haroon/pen/RwObbjy" rel="noopener noreferrer"&gt;CodePen link&lt;/a&gt; of custom email validation with Joi.&lt;/p&gt;

&lt;h4&gt;
  
  
  Ajv
&lt;/h4&gt;

&lt;p&gt;For Ajv, you have to use &lt;code&gt;addKeyword&lt;/code&gt; method with &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;compile&lt;/code&gt; or &lt;code&gt;validate&lt;/code&gt; properties to custom how to validate your JSON schema. Referring to its &lt;a href="https://ajv.js.org/keywords.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, I have created an example with &lt;code&gt;validate&lt;/code&gt; property.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Ajv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ErrorObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSONSchemaType&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="s2"&gt;https://esm.sh/ajv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Create an instance of Ajv&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ajv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Ajv&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;allErrors&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="nx"&gt;ajv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addKeyword&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emailFormat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;errors&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;validate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;customValidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please provide an email address&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@&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="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please include an `@` symbol&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/@&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please provide an email&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s domain&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;$/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please include top level domain such as .co .org&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;customValidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="na"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emailFormat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}];&lt;/span&gt;
        &lt;span class="c1"&gt;// Use a regular expression to validate email format&lt;/span&gt;
        &lt;span class="c1"&gt;// const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i;&lt;/span&gt;
        &lt;span class="c1"&gt;// return emailRegex.test(value);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSONSchemaType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;emailFormat&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="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// Compile the schema&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ajv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows Ajv custom validation without &lt;code&gt;ajv-formats&lt;/code&gt;. &lt;a href="https://codepen.io/9haroon/pen/qBwWdda" rel="noopener noreferrer"&gt;CodePen link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExYnA1ZjNhOWU2dzBocTAzZDV5bndseno1emc4enV2aWx6MDg4NXQwbSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/5RFGTA8W0m3KxJfKTj/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExYnA1ZjNhOWU2dzBocTAzZDV5bndseno1emc4enV2aWx6MDg4NXQwbSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/5RFGTA8W0m3KxJfKTj/giphy.gif" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Custom Validation Without JSON Schema Libraries
&lt;/h2&gt;

&lt;p&gt;When it comes to data validation, an alternative approach to using JSON schema libraries is to implement custom validation logic. This involves writing code specifically tailored to validate the data based on the requirements and constraints of the application. Here is the example of the Email validation without any JSON schema library.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;debounce&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;https://codepen.io/9haroon/pen/GRLgddJ.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;email&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please provide an email address&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please include an `@` symbol&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/@&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please provide an email&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s domain&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.[&lt;/span&gt;&lt;span class="sr"&gt;a-zA-Z&lt;/span&gt;&lt;span class="se"&gt;]{2,}&lt;/span&gt;&lt;span class="sr"&gt;$/gi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please include top level domain such as .co .org&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&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="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#form&lt;/span&gt;&lt;span class="dl"&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;errorElem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#form &amp;gt; p&lt;/span&gt;&lt;span class="dl"&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;emailElem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name="email"]&lt;/span&gt;&lt;span class="dl"&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;submitCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Start Validation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;errorElem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validateEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailElem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;errorElem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&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="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;submitCallback&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="nx"&gt;emailElem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;submitCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The codes has an event listeners for the form submission event and the email input event. When the form is submitted, the &lt;code&gt;submitCallback&lt;/code&gt; function is executed. Similarly, when the user inputs text in the email field, the &lt;code&gt;submitCallback&lt;/code&gt; function is executed after a debounce of 500 milliseconds (to prevent rapid validation). In &lt;code&gt;submitCallback&lt;/code&gt; function. It clears any previous error messages, calls the validateEmail function to validate the email value entered by the user, and displays the error message (if any) in the &lt;code&gt;errorElem&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://codepen.io/9haroon/pen/JjVEPpb" rel="noopener noreferrer"&gt;CodePen link&lt;/a&gt;. Feel free to explore it further.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparing Using V. Without JSON Schema Libraries
&lt;/h2&gt;

&lt;p&gt;Based on various technical factors, I have made a side by side comparison summary below:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Aspect&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Using JSON Schema Libraries&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Without Using JSON Schema Libraries&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standardized Schema Definition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Provide a standardized way to define schemas for data validation&lt;/td&gt;
&lt;td&gt;Require manual implementation of validation logic without a standard schema format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validation Features&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Offer a wide range of validation features&lt;/td&gt;
&lt;td&gt;Need to be implemented for each validation requirement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema Reusability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can be reused across the application or in other projects&lt;/td&gt;
&lt;td&gt;May not be easily reusable and may result in code duplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Integrity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enforce validation rules to ensure only valid data is accepted&lt;/td&gt;
&lt;td&gt;Rely on discipline to maintain data integrity through custom validation logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Curve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Require time and effort to learn the syntax and best practices of the library&lt;/td&gt;
&lt;td&gt;Require understanding of JSON schemas and validation rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add a new dependency that needs to be managed and updated&lt;/td&gt;
&lt;td&gt;No additional dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance Overhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;May introduce performance overhead based on validation complexity and data size&lt;/td&gt;
&lt;td&gt;Can be optimized for specific requirements to potentially reduce performance overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Provide a structured way to define and enforce validation rules&lt;/td&gt;
&lt;td&gt;Offer flexibility without the constraints of a library&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simplified Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Require an integration of the library and learning its features&lt;/td&gt;
&lt;td&gt;Quick and easy setup for simple validation requirements or small-scale projects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Considering the pros and cons outlined above, the decision to use or not use JSON schema libraries for data validation depends on factors such as the project requirements, complexity of validation rules, familiarity with JSON schemas, and the trade-off between standardization and customization in data validation processes.&lt;/p&gt;




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

&lt;p&gt;In this article, I explore data validation various JavaScript Object Notation (JSON) Schemas libraries and show how you can use them to implement validation rules for schema validation in TypeScript. JSON Schema offers a formidable mechanism for defining and enforcing validation rules on objects, thereby ensuring data integrity and consistency.&lt;/p&gt;

&lt;p&gt;While JSON Schema libraries provide powerful tools, sometimes custom validation tailored to specific project requirements becomes necessary. When opting for custom validation, consider the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Define Custom Validation Functions&lt;/strong&gt;: Create custom validation functions that directly inspect the object properties and enforce your desired rules. These functions can be as simple or complex as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate Custom Validation Logic&lt;/strong&gt;: Incorporate your custom validation logic into your application code. You have the flexibility to check individual properties, cross-validate fields, or apply domain-specific rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling and Feedback&lt;/strong&gt;: When validation fails, provide meaningful error messages to guide users or developers. Clear feedback ensures better understanding and smoother debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unit Testing&lt;/strong&gt;: Rigorously test your custom validation functions to ensure they behave as expected. Unit tests help catch edge cases and maintain reliability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By embracing custom validation, you can tailor your validation rules precisely to your project’s needs. &lt;/p&gt;

&lt;p&gt;As summarized above, both methods have their own merits. In fact, depending on your requirements, you can even apply custom validation method to validate data via JSON Schema. What are your thoughts on this matter? Feel free to leave your comments in the comment section. Much appreciated!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN2gxYWtndGpiMTR6MWx6emQ1dHdoY3ZsMWkzMXo1cms1bW5yMGlsbyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/zRKJFP8sOSL6Zu7I7j/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExN2gxYWtndGpiMTR6MWx6emQ1dHdoY3ZsMWkzMXo1cms1bW5yMGlsbyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/zRKJFP8sOSL6Zu7I7j/giphy.gif" width="480" height="254"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Validex: The Most Complete Validation Collection for React&amp;nbsp;Forms
&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.amazonaws.com%2Fuploads%2Farticles%2Fzpc9mt2h1jzvzgetnmbt.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.amazonaws.com%2Fuploads%2Farticles%2Fzpc9mt2h1jzvzgetnmbt.png" alt="Validex - Complete Collection of Form Validations for React" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you feel like this article helped you, please check out &lt;a href="https://www.validex.dev/?utm_source=dev.to&amp;amp;utm_content=using-json-for-custom-validations"&gt;Validex&lt;/a&gt;. It has over 36 validators (such as string, password, credit card, email, file, image and more) so that you can validate React forms in minutes.&lt;/p&gt;

&lt;p&gt;It would encourage me to continue creating even more contents. Thank you in advance! 🙏&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>howto</category>
      <category>json</category>
    </item>
    <item>
      <title>🚀 Supercharge Your Repo for Hacktoberfest 2023</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Tue, 26 Sep 2023 12:05:00 +0000</pubDate>
      <link>https://dev.to/9haroon/supercharge-your-repo-for-hacktoberfest-2023-essential-guide-for-maintainers-2dl1</link>
      <guid>https://dev.to/9haroon/supercharge-your-repo-for-hacktoberfest-2023-essential-guide-for-maintainers-2dl1</guid>
      <description>&lt;p&gt;Hi everyone! This year, I'm tasked with setting up my company's Github repositories for &lt;a href="https://hacktoberfest.com/" rel="noopener noreferrer"&gt;Hacktoberfest&lt;/a&gt; 2023. While researching and preparing, I thought I would share what I found to be helpful. Hope the information is useful to any maintainers out there. &lt;/p&gt;

&lt;p&gt;Hacktoberfest, started by &lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt;, is celebrating its 10th anniversary this year 🥳 It is an annual event that celebrates open-source software development by having developers actively contribute to open-source projects. As of last year alone, there were over 147,000 participants.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/KzgyrhURK6J15ULd6a/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/KzgyrhURK6J15ULd6a/giphy.gif" width="480" height="270"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;For maintainers who want to get involved, the article will explain what Hacktoberfest is, why you should give it a try and what you need to do participate.&lt;/p&gt;

&lt;p&gt;For maintainers who have previously participated or heard of Hacktoberfest, you can treat this as a checklist or a refresher on what you need to prepare as maintainers. It has been a year after all 😂&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Hacktoberfest?
&lt;/h2&gt;

&lt;p&gt;Hacktoberfest is an event to encourages developers to contribute to open-source projects. Contributors are encouraged to make &lt;strong&gt;four&lt;/strong&gt; valid pull/merge requests to public and participating repositories to be eligible for digital rewards. It provides an opportunity for developers, both new and experienced, to engage with open-source projects and make meaningful contributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  When and Where does it take place?
&lt;/h2&gt;

&lt;p&gt;Hacktoberfest 2023 will take place between October 1 and October 31 (in any time zone, UTC-12 thru UTC+14).&lt;/p&gt;

&lt;p&gt;Pull/merge requests is only valid if made on either via the &lt;a href="https://github.com/topics/hacktoberfest" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://gitlab.com/explore/projects/topics/hacktoberfest" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should you participate as maintainers?
&lt;/h2&gt;

&lt;p&gt;Since contributors can only earn counts from qualified Pull/merge requests from issues created by maintainers, maintainers play a crucial role in the success of Hacktoberfest. By participating as a maintainer, you can benefit in various ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Increased community engagement:&lt;/strong&gt; Brings attention to your project and attracts new contributors, kickstarting or fostering an active community around your repository going forward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality contributions:&lt;/strong&gt; Ensure that the contributions made to your project are meaningful and adhere to your project's guidelines and standards. More on ways to handle spam in the next section.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill development and collaboration:&lt;/strong&gt; Provides a great opportunity to collaborate with developers from diverse backgrounds, learn from others, and improve your coding skills.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Although maintainers who review Pull/merge requests from others do not get digital rewards, you are still eligible for rewards if you submit Pull/merge requests to other participating repositories or even your own repositories.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wouldn't there be a lot of spam?
&lt;/h2&gt;

&lt;p&gt;There is a strict rule of disqualification by Hacktoberfest &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pull/merge requests that have a label containing the word spam won’t be counted, and participants with two or more requests identified as spam will be disqualified&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, due to the nature of the event, spams are definitely possible. The benefits definitely outweigh the trouble for maintainers who are looking to get more attention. For other maintainers, participating is one of the ways to give back to the open-source community. So what better timing to foster a stronger open-source community to attract new contributors (beginner or experienced developers).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/Eta7LALxu0uAzqFpXu/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/Eta7LALxu0uAzqFpXu/giphy.gif" width="480" height="480"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;To further minimize spam, here are some more suggestions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contribution guidelines:&lt;/strong&gt; Set up clear contribution guidelines to discourage spammy contributions. Include requirements such as meaningful code changes, proper documentation updates, or thorough bug reports. Here is an amazing &lt;a href="https://github.com/nayafia/contributing-template/blob/HEAD/CONTRIBUTING-template.md" rel="noopener noreferrer"&gt;contributing template&lt;/a&gt; by &lt;a href="https://twitter.com/nayafia" rel="noopener noreferrer"&gt;@nadia&lt;/a&gt; to get started. For a good real-life example, you may want to check out &lt;a href="https://github.com/amplication/amplication/blob/master/CONTRIBUTING.md" rel="noopener noreferrer"&gt;Amplication&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication and feedback:&lt;/strong&gt; Engage with contributors by providing constructive feedback if you consider a contribution a spam or invalid. By being patient and offering guidance and suggestions to improve their contributions, you can create a more welcoming environment for everyone to thrive in.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to participate as maintainers?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1A: Add a &lt;code&gt;hacktoberfest&lt;/code&gt; topic to your repository
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2F5zd2x6n936jae0pvole7.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.amazonaws.com%2Fuploads%2Farticles%2F5zd2x6n936jae0pvole7.png" alt="Add Hacktoberfest Topic" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1B: Add &lt;code&gt;hacktoberfest-accepted&lt;/code&gt; label to your issue
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2Fqcr3m357eppu66l9aw25.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.amazonaws.com%2Fuploads%2Farticles%2Fqcr3m357eppu66l9aw25.png" alt="Add Hacktoberfest-Accepted Label" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You can also choose to do both steps.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Create meaningful issues
&lt;/h3&gt;

&lt;p&gt;Like how we don't like contributors to spam us, we should not create "spam" issues. Bad issues like adding a name or profile to a list or arbitrarily curating content should be discouraged. Anyone can report a repository for Hacktoberfest to investigate.&lt;/p&gt;

&lt;p&gt;Although you may not need to prepare much for existing contributors of your repositories, you may want to create issues with &lt;code&gt;good first issue&lt;/code&gt; for new contributors who still unfamiliar with your repository to get involved. In my opinion, &lt;a href="https://github.com/ToolJet/ToolJet/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22" rel="noopener noreferrer"&gt;ToolJet&lt;/a&gt; and &lt;a href="https://github.com/PostHog/posthog/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22" rel="noopener noreferrer"&gt;Posthog&lt;/a&gt; have great examples of &lt;code&gt;good first issue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Besides issues related to coding, you can also create issues that are low-code or non-code in nature such as writing technical documentation, tutorials , blog posts, translating to different languages and many more. For more inspiration, please check out more examples at &lt;a href="https://opensource.com/life/16/1/8-ways-contribute-open-source-without-writing-code" rel="noopener noreferrer"&gt;8 non-code ways to contribute to open source&lt;/a&gt;.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fmchak4g5ai4pgnmfnc3j.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.amazonaws.com%2Fuploads%2Farticles%2Fmchak4g5ai4pgnmfnc3j.png" alt="Low Code and Non Code Contribution" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: For more information, please see &lt;a href="https://hacktoberfest.com/participation/#low-or-non-code" rel="noopener noreferrer"&gt;low-or-non-code&lt;/a&gt; section of Hacktoberfest.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Review Pull/Merge Request
&lt;/h3&gt;

&lt;p&gt;It's important to be kind and patient. If Pull/merge requests are rejected due to spam or invalid contributions, please explain to them. Some contributors may just be making honest mistakes. Everyone is a beginner at first 🤭&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/kFgzrTt798d2w/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/kFgzrTt798d2w/giphy.gif" width="312" height="222"&gt;&lt;/a&gt; &lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Preparing your Github repository for Hacktoberfest is essential to attract contributors, foster collaboration, and maintain the overall quality of your project. By actively engaging with the Hacktoberfest community and providing support to contributors, you can create a welcoming environment for open-source contributions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Could you support me by starring &lt;a href="https://github.com/logicspark/berryjam" rel="noopener noreferrer"&gt;Berryjam repo&lt;/a&gt;?
&lt;/h2&gt;

&lt;p&gt;Berryjam is a UI components analyzer for Vue 3 &amp;amp; Nuxt to detect component usage, relationships and more. If you feel this article is useful, could you consider starring ⭐⭐⭐ Berryjam’s Github &lt;a href="https://github.com/logicspark/berryjam" rel="noopener noreferrer"&gt;repo&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;It would mean the world and encourage me to create more content.&lt;/p&gt;

&lt;p&gt;Thank you in advance! 🙏&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/ZkVj8Dm9XboVBvtvfM/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ZkVj8Dm9XboVBvtvfM/giphy.gif" width="480" height="428"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;P.S Feel free to add your or other repos that you wish people would contribute to in the comment section.&lt;/p&gt;

</description>
      <category>github</category>
      <category>opensource</category>
      <category>hacktoberfest23</category>
      <category>hacktoberfest</category>
    </item>
    <item>
      <title>🎉Introducing Atomic Design in Vue.js [Updated]🔥</title>
      <dc:creator>Kittisak Ma</dc:creator>
      <pubDate>Wed, 23 Aug 2023 12:10:56 +0000</pubDate>
      <link>https://dev.to/vue-pdf-viewer/introducing-atomic-design-in-vuejs-1l2h</link>
      <guid>https://dev.to/vue-pdf-viewer/introducing-atomic-design-in-vuejs-1l2h</guid>
      <description>&lt;p&gt;In my experience of building and delivering systems for various clients, depending on their requirements or preferences, it’s inevitable that I may use UI frameworks and libraries like &lt;a href="https://antdv.com/" rel="noopener noreferrer"&gt;Ant Design&lt;/a&gt; and &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt;. Often, I have to build on top of each components. If you are working alone, it’s not such a big problem. But when working as a team, things get complicated quickly. So, I started looking at different methodologies which can provide development flexibility and system maintainability for the projects I’m leading. I choose Atomic Design methodology and want to share why it works for me. &lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fk7aq1o8l0u4bjkl7kd5b.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.amazonaws.com%2Fuploads%2Farticles%2Fk7aq1o8l0u4bjkl7kd5b.png" alt="Atomic Design Concept" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bradfrost.com/blog/post/atomic-web-design/" rel="noopener noreferrer"&gt;Atomic Design&lt;/a&gt; is a methodology for creating design systems that breaks down user interfaces into small, reusable components, namely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Atoms&lt;/li&gt;
&lt;li&gt;Molecules&lt;/li&gt;
&lt;li&gt;Organisms&lt;/li&gt;
&lt;li&gt;Templates&lt;/li&gt;
&lt;li&gt;Pages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following a modular approach to design, atomic design helps teams to create consistent, scalable, and maintainable UIs. &lt;/p&gt;

&lt;p&gt;In this post, for simplicity, we'll explore how to implement Atomic Design in &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt; with only HTML. I'll start with the basics of Atomic Design and then demonstrate how to apply its principles in Vue.js. &lt;/p&gt;

&lt;p&gt;At the end of the article, you will get a page that consists of a header, a form and a footer. You can use the example here to apply to any UI framework.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F72snuh5xpg9g54cfqcvd.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2F72snuh5xpg9g54cfqcvd.jpg" alt="Image of atomic design with vue components" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You may notice that each component has borders around it. This is intentional so you can identify whether it's an atom, a molecule, or an organism.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/jYLMZUnqZqLb2p113v/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/jYLMZUnqZqLb2p113v/giphy.gif" width="480" height="266"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;h2&gt;
  
  
  Vue-PDF-Viewer: A Versatile and Powerful PDF Solution for Vue.js
&lt;/h2&gt;

&lt;p&gt;Here’s a little background on what I’ve been working on—&lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_campaign=introducing-atomic-design-in-vuejs"&gt;Vue PDF Viewer&lt;/a&gt;. This library makes it easy to render PDF documents right within your Vue or Nuxt applications, allowing users to interact with PDFs directly on your site. With features like theme customization, built-in localization, and responsive design, it’s crafted to offer a flexible and seamless user experience tailored to your project’s needs.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2F2rq93qvo31hksg1gg8gn.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.amazonaws.com%2Fuploads%2Farticles%2F2rq93qvo31hksg1gg8gn.png" alt="Vue PDF Viewer" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find this helpful, I’d love for you to check out &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_campaign=introducing-atomic-design-in-vuejs"&gt;Vue PDF Viewer&lt;/a&gt;. Your support inspires me to keep creating and improving tools for the developer community!&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of Atomic Design
&lt;/h2&gt;

&lt;p&gt;Atomic Design consists of five levels that represent the building blocks of UIs. For this example, I have created an inverted tree structure to visualizing how each anatomy is connected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Page
 - Full Layout Template
  - Header Organism
    - Logo Atom
    - Search Form Molecule
      - TextBox Atom
      - Button Atom
  - Content Organsim
    - Form Molecule
      - 2x TextBox Atoms
      - Button Atom
  - Footer Organism
    - Copyright Atom
    - Subscribe Form Molecule
      - TextBox Atom
      - Button Atom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;1. Atoms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Atoms are the smallest units of UI that cannot be broken down further without losing their meaning. Examples of atoms include icons, buttons, labels, inputs, and typography. &lt;/p&gt;

&lt;p&gt;In Vue.js, atoms can be created as reusable components that accept props to customize their appearance and behavior. For this instance, we have a few atoms to prepare for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Textbox
&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.amazonaws.com%2Fuploads%2Farticles%2Finjdmpjzmuofnttsqk1y.png" alt="Image textBoxAtom" width="586" height="142"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"textBoxAtom"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;: &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;:placeholder=&lt;/span&gt;&lt;span class="s"&gt;"placeHolder"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TextBoxAtom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;labelName&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; 
    &lt;span class="na"&gt;placeHolder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75em&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Button
&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.amazonaws.com%2Fuploads%2Farticles%2Fuaqa6614jnivxhfxpcmh.png" alt="Image buttonAtom" width="264" height="140"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"buttonAtom"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;:disabled=&lt;/span&gt;&lt;span class="s"&gt;"disabled"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&lt;/span&gt;Button&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; 
 &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="p"&gt;{&lt;/span&gt; 
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ButtonAtom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4fc08d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inherit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="m"&gt;2em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Logo
&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.amazonaws.com%2Fuploads%2Farticles%2F6xfcbwy6vlpiz6wx04pw.png" alt="Image logoAtom" width="142" height="176"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"logoAtom"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;:src=&lt;/span&gt;&lt;span class="s"&gt;"computedImageUrl"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"logo"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;computedImageUrl&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="s2"&gt;`https://picsum.photos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a closer look, you can check out the codes in the &lt;a href="https://codepen.io/collection/xKagKz" rel="noopener noreferrer"&gt;Atoms collection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Molecules&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Molecules are combinations of two or more atoms that work together to perform a specific function. In Vue.js, molecules can be created by composing atoms as child components within a parent component. Examples of molecules include forms, search bars, navigation menus, and cards. &lt;/p&gt;

&lt;p&gt;Referring to the example above, we will need to combine the atoms to create the following molecules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subscribe Form Molecule 
&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.amazonaws.com%2Fuploads%2Farticles%2Fa8smh36m47d37aczvi51.png" alt="Image subscribeFormMolecule" width="800" height="180"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"subscribeFormMolecules"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TextboxAtom&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ButtonAtom&amp;gt;&lt;/span&gt;Subscribe&lt;span class="nt"&gt;&amp;lt;/ButtonAtom&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;TextboxAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/LYXgdKg.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ButtonAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/BaGqrJg.js&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ButtonAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TextboxAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Search Form Molecule
&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.amazonaws.com%2Fuploads%2Farticles%2Fufa9v06h7i8rdg77kd2e.png" alt="Image searchFormMolecule" width="800" height="189"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"searchFormMolecules"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;InputAtom&lt;/span&gt; &lt;span class="na"&gt;label=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ButtonAtom&amp;gt;&lt;/span&gt;Search&lt;span class="nt"&gt;&amp;lt;/ButtonAtom&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;InputAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/LYXgdKg.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ButtonAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/BaGqrJg.js&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ButtonAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InputAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;inline-flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Form Molecule
&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.amazonaws.com%2Fuploads%2Farticles%2Fj9jzqcoljqokgxp7b7bz.png" alt="Image formMolecule" width="608" height="488"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"form-molecule component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"formMolecules"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;InputAtom&lt;/span&gt; &lt;span class="na"&gt;:label=&lt;/span&gt;&lt;span class="s"&gt;"nameLabel"&lt;/span&gt; &lt;span class="na"&gt;:placeholder=&lt;/span&gt;&lt;span class="s"&gt;"namePlaceholder"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;InputAtom&lt;/span&gt; &lt;span class="na"&gt;:label=&lt;/span&gt;&lt;span class="s"&gt;"emailLabel"&lt;/span&gt; &lt;span class="na"&gt;:placeholder=&lt;/span&gt;&lt;span class="s"&gt;"emailPlaceholder"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ButtonAtom&lt;/span&gt; &lt;span class="na"&gt;:disabled=&lt;/span&gt;&lt;span class="s"&gt;"isSubmitDisabled"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;submitLabel&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/ButtonAtom&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;InputAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/LYXgdKg.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ButtonAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/BaGqrJg.js&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FormMolecule&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;InputAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ButtonAtom&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;nameLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;namePlaceholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;emailLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;emailPlaceholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;submitLabel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isSubmitDisabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Boolean&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find the codes in the &lt;a href="https://codepen.io/collection/qOkROP" rel="noopener noreferrer"&gt;Molecules collection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Organisms&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Organisms are combinations of molecules that form distinct sections of a UI, such as headers, footers, sidebars, and content blocks. In Vue.js, organisms can be created by composing molecules as child components within a layout component.&lt;/p&gt;

&lt;p&gt;For this exercise, three organisms are needed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Header Organism
&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.amazonaws.com%2Fuploads%2Farticles%2F9y2wrre2pgleeryj1hh9.png" alt="Image headerOrganism" width="800" height="92"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"headerOrganism"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;LogoAtom&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"60"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;SearchFormMoecules&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SearchFormMoecules&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/zYMmjqa.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;LogoAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/xxQMbeJ.js&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SearchFormMoecules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LogoAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Content Organism
&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.amazonaws.com%2Fuploads%2Farticles%2Flxtlh251u7ppw5wpbgtz.png" alt="Image contentOrganism" width="800" height="212"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page-organism"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content-wrapper-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Here might be a page title&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Here might be a page description&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--   This might includes some molecules or atoms   --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ContentOrganism&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nc"&gt;.page-organism&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;inset&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt; &lt;span class="m"&gt;15px&lt;/span&gt; &lt;span class="m"&gt;-3px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.content-wrapper-title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Footer Organism&lt;/li&gt;
&lt;/ul&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.amazonaws.com%2Fuploads%2Farticles%2F31uzkg2xmbv056t87qkq.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2F31uzkg2xmbv056t87qkq.jpg" alt="Image of atomic design with Vue.js - footer" width="800" height="69"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"component-wrapper"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"footerOrganism"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;CopyrightAtom&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;SubscribeFormMoecules&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SubscribeFormMoecules&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/ExOrarL.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;LogoAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/xxQMbeJ.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CopyrightAtom&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/gOQqOBj.js&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SubscribeFormMoecules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;LogoAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CopyrightAtom&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;justify-content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;space-between&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;align-items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per usual, here's a &lt;a href="https://codepen.io/collection/LPJxPO" rel="noopener noreferrer"&gt;collection of organisms' codes&lt;/a&gt; that I've created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Templates&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Templates are the structures that define the layout and composition of pages by specifying the placement and size of organisms within regions, such as headers, footers, and content areas. &lt;/p&gt;

&lt;p&gt;In Vue.js, templates can be created as parent components that accept named slots for child components. Applying the 3 organisms to the &lt;em&gt;Templates&lt;/em&gt; concept, here is how it 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%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frl41uqmlw0pnjgx8fx48.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Frl41uqmlw0pnjgx8fx48.jpg" alt="Image of atomic design with Vue.js - full layout template" width="800" height="505"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"full-layout-template"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;HeaderOrganism&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ContentOrganism&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;#title&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;default title&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="na"&gt;#description&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;default description&lt;span class="nt"&gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;slot&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ContentOrganism&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;FooterOrganism&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"page-footer"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;HeaderOrganism&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/WNYaJGR.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ContentOrganism&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/vYQbOeO.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;FooterOrganism&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/RwqvPRN.js&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FullLayoutTemplate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;HeaderOrganism&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;ContentOrganism&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;FooterOrganism&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nc"&gt;.full-layout-template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flex-direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;min-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.page-footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;flex-shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;here is the &lt;a href="https://codepen.io/9haroon/pen/GRwzpxx" rel="noopener noreferrer"&gt;CodePen's link&lt;/a&gt; of the template &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Pages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pages are the final presentation of UIs that combine templates with specific content to form complete views. In Atomic Design, pages are like instances of templates that represent unique experiences for users. &lt;/p&gt;

&lt;p&gt;In Vue.js, pages can be created by copying a template and replacing its slots with actual content. Although, in this example, I only change the content of &lt;em&gt;Content Organism&lt;/em&gt;, you could choose to change all or no content.&lt;/p&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.amazonaws.com%2Fuploads%2Farticles%2Fy63d3v1xuee3r5us7v26.jpg" 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.amazonaws.com%2Fuploads%2Farticles%2Fy63d3v1xuee3r5us7v26.jpg" alt="Image of atomic design with Vue.js - HomePage" width="800" height="513"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;FullLayoutTemplate&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;#title&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt; &lt;span class="na"&gt;#description&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fixed-width"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;FormMolecule&lt;/span&gt; &lt;span class="na"&gt;nameLabel=&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt; &lt;span class="na"&gt;emailLabel=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt; &lt;span class="na"&gt;submitLabel=&lt;/span&gt;&lt;span class="s"&gt;"Save"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/FullLayoutTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;FullLayoutTemplate&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/GRwzpxx.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;FormMolecule&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://codepen.io/9haroon/pen/PoxyRMo.js&lt;/span&gt;&lt;span class="dl"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HomePage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;FullLayoutTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;FormMolecule&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;data&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="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome to my example&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is an example of Atomic Design in Vue.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;copyright&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Copyright © 2023&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt; &lt;span class="na"&gt;scoped&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Avenir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.fixed-width&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;350px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;here is the &lt;a href="https://codepen.io/9haroon/pen/qBQgOGj" rel="noopener noreferrer"&gt;CodePen's link&lt;/a&gt; of the page &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbXVmOHF4ZHczZG15Y281djFzb3U3ZXRqeWliZHg5Y2N5MWloaGVnNiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/Y655D2pOIBCQU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExbXVmOHF4ZHczZG15Y281djFzb3U3ZXRqeWliZHg5Y2N5MWloaGVnNiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/Y655D2pOIBCQU/giphy.gif" width="524" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🆕 &lt;strong&gt;Update:&lt;/strong&gt; While this article uses Vue 2 in its code examples, I've also created a full Vue 3 + Composition API + TypeScript version on Stackblitz.&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://stackblitz.com/edit/atomic-design-with-vue3" rel="noopener noreferrer"&gt;Try it on Stackblitz&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Benefits of Atomic Design in Vue.js
&lt;/h2&gt;

&lt;p&gt;By using Atomic Design in Vue.js, you can achieve several benefits, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Consistency: By creating reusable components, you ensure that your UIs look and behave consistently across all pages. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scalability: By breaking down UIs into small pieces, you can easily add, remove, or update components without affecting other parts of the system. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maintainability: By organizing components into folders and files, you can easily find, edit, or debug them in isolation from other parts of the system. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reusability: By creating standalone components, you can reuse them in other projects or share them with the community, thus saving time and effort. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Atomic Design is a powerful methodology that can help you design better UIs in Vue.js. By following its principles, you can create reusable, modular, and scalable components that make your code more maintainable and your users more satisfied. So, go ahead, give it a try, and let me know how it worked for you!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3og0IPMeREHpEV0f60/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3og0IPMeREHpEV0f60/giphy.gif" width="480" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Vue PDF Viewer: A Complete PDF Solution for Vue.js
&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.amazonaws.com%2Fuploads%2Farticles%2F2rq93qvo31hksg1gg8gn.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.amazonaws.com%2Fuploads%2Farticles%2F2rq93qvo31hksg1gg8gn.png" alt="Vue PDF Viewer" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re looking for a powerful, flexible solution for rendering PDFs directly in your Vue or Nuxt applications, &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_campaign=introducing-atomic-design-in-vuejs"&gt;Vue PDF Viewer&lt;/a&gt; is the tool for you. Offering a wide array of features—like theme customization, localization support, and responsive layouts—it’s built to handle a range of project requirements while delivering a seamless, user-friendly experience.&lt;/p&gt;

&lt;p&gt;If this sounds useful to you, feel free to explore &lt;a href="https://www.vue-pdf-viewer.dev/?utm_source=dev.to&amp;amp;utm_medium=referral&amp;amp;utm_campaign=introducing-atomic-design-in-vuejs"&gt;Vue PDF Viewer&lt;/a&gt; and see how it can enhance your next project. It would mean the world and encourage me to create more content.&lt;/p&gt;

&lt;p&gt;Thank you in advance! 🙏&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
