{"id":94290,"date":"2022-05-23T16:46:04","date_gmt":"2022-05-23T16:46:04","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=94290"},"modified":"2026-09-23T09:36:47","modified_gmt":"2026-09-23T09:36:47","slug":"how-to-add-inline-pdf-viewer-angular-app","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/angular\/how-to-add-inline-pdf-viewer-angular-app\/","title":{"rendered":"How to add an inline PDF viewer to an Angular app (complete guide)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong><code>ng2-pdfjs-viewer<\/code> is an Angular component that renders PDFs inline or in a separate window, built on Mozilla&#8217;s PDF.js and viewer.js. This article covers installing and configuring it, hooking into its loading and printing events, and rendering a PDF returned as a byte array from a server API \u2014 with a C#\/ASP.NET Core example.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Angular developers like myself have suffered greatly due to a lack of a quality library that can be used to show a PDF without losing the user experience.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now, enter <a href=\"https:\/\/www.npmjs.com\/package\/ng2-pdfjs-viewer\"><em><strong>ng2-pdfjs-viewer<\/strong><\/em><\/a>, and the Angular developer&#8217;s fight with PDF is finally over!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <em>ng2-pdfjs-viewer<\/em> component is built on top of Mozilla&#8217;s <em>viewerjs<\/em> and <em>pdfjs<\/em>, so its core is solid. It does have easy-to-use attributes along with the ability to customize anything which <em>pdfjs<\/em> supports. You can find the code demonstrated in this article <a href=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/05\/app.component.zip\">here<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Usage can be as simple as:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;ng2-pdfjs-viewer pdfSrc=\"sample.pdf\"&gt;&lt;\/ng2-pdfjs-viewer&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And viola, the PDF sits right inside the Angular application like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1429\" height=\"476\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/05\/diagram-description-automatically-generated-1.png\" alt=\"An image showing the ng2-pdfjs-viewer with a sample PDF file\" class=\"wp-image-94291\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now the question is &#8211; what else can I get out of it?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here are some examples. Suppose you wanted to show two versions of the same document for comparison side by side in your web application. Now you can. Do you want to open the PDF in a separate browser window for traditional viewing? No problem. Do you like to show a print preview dialog automatically after opening the PDF in a new browser window? <a href=\"https:\/\/ng2-pdfjs-viewer.azurewebsites.net\/externalwindow\">Piece of cake<\/a>. Automatically download the pdf? <a href=\"https:\/\/ng2-pdfjs-viewer.azurewebsites.net\/autodownload\">Got it<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This pdf viewer supports tons of other features, and you have fine control of what to do with it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-how-to-set-up-ng2-pdfjs-viewer-in-an-angular-application\">How to set up ng2-pdfjs-viewer in an Angular application<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Follow these steps to set up the component:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">1. Installation &#8211; Like any other package &#8211; get it from the npm registry<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">$ npm install ng2-pdfjs-viewer --save<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is a standard npm package installation command. Make sure it gets installed in <em>dependencies<\/em> using the <code>--save<\/code> parameter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">2. Configuration &#8211; Let your app know you would like to use it<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Set it up in your angular <code>AppModule<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">import { BrowserModule } from '@angular\/platform-browser';\nimport { NgModule } from '@angular\/core';\nimport { AppComponent } from '.\/app.component';\nimport { PdfJsViewerModule } from 'ng2-pdfjs-viewer'; \/\/ &lt;-- Import PdfJsViewerModule module\n@NgModule({\n  declarations: [\n\tAppComponent,\n  ],\n  imports: [\n\tBrowserModule,\n\tPdfJsViewerModule \/\/ &lt;-- Add to declarations\n  ],\n  providers: [],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As shown here, the module to be imported is <code>PdfJsViewerModule<\/code>; this is required as this is the module that makes sure <em>ng2-pdfjs-viewer<\/em> is ready to be used.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is equally important to add the module <code>PdfJsViewerModule<\/code> into the <code>imports<\/code> section of <code>@NgModule<\/code>. With this step, you are almost ready to use the inline PDF viewer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">3. Build &#8211; Add a build step, so that your angular app has a copy of pdfjs<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>ng2-pdfjs-viewer<\/code> component is built on top of <em>pdfjs<\/em>, which also means that it\u2019s needed for this angular component to work properly. There are several ways this can be achieved, from the manual process of copying relevant files to automated build scripts. Here are the two most popular mechanisms angular developers use when they need extra files to be copied as part of the build step.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Either add an <em>angular<\/em> build step into <em>angular.json<\/em>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\"assets\": [\n  { \"glob\": \"**\/*\", \"input\": \"node_modules\/ng2-pdfjs-viewer\/pdfjs\", \"output\": \"\/assets\/pdfjs\" },\n]<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Or use <em>webpack<\/em> or similar bundlers (Hmm, are you still using webpack?)<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">var TransferWebpackPlugin = require('transfer-webpack-plugin');\n...\nplugins: [\n  new TransferWebpackPlugin([\n\t{ from: 'node_modules\\ng2-pdfjs-viewer\\pdfjs', to: path.join(__dirname, 'assets') }\n  ])\n]<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-pdf-loading-events\">PDF loading events<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Often, you might like to tap into the PDF pipeline of printing or loading to execute a task. This could be to show a message to the user that the PDF is loaded for large PDFs or that the PDF is successfully printed, etc. The code found <a href=\"https:\/\/github.com\/intbot\/ng2-pdfjs-viewer\">here<\/a> provides several events hooks for that.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-html\">HTML<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;!-- your.component.html --&gt;\n&lt;ng2-pdfjs-viewer pdfSrc=\"gre_research_validity_data.pdf\"\n              \tviewerId=\"MyUniqueID\"\n              \t(onBeforePrint)=\"testBeforePrint()\"\n              \t(onAfterPrint)=\"testAfterPrint()\"\n              \t(onPagesLoaded)=\"testPagesLoaded($event)\"&gt;\n&lt;\/ng2-pdfjs-viewer&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For events to work properly, you should set <code>viewerId<\/code>. This helps event routing understand which component the event is to be sent to, even if there is more than one <em>ng2-pdfjs-viewer<\/em> component on the page. <code>viewerId<\/code> should be a unique id (like a <code>guid<\/code>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-angular-component\">Angular component<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Once capturing these events, developers can execute custom tasks\/code to take action based on these events. Given below is an event which emits and displays a number of pages in PDF.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;!-- your.component.ts --&gt;\npublic testBeforePrint() {\n\tconsole.log(\"testBeforePrint() successfully called\");\n}\npublic testAfterPrint() {\n\tconsole.log(\"testAfterPrint() successfully called\");\n}\npublic testPagesLoaded(count: number) {\n\tconsole.log(\"testPagesLoaded() successfully called. Total pages # : \" + count);\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can see the events that are emitted on the developer console as shown below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1033\" height=\"296\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2022\/05\/graphical-user-interface-text-application-email.png\" alt=\"Image showing screenshot of DevTools. Important part highlighted: testPagesLoaded Total Page #4. TestBeforeProject, TestSfterPrint\" class=\"wp-image-94292\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-what-about-pdf-files-returned-from-an-api\">What about PDF files returned from an API?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The <em>ng2-pdfjs-viewer<\/em> component can also work with other server-side APIs to render returned PDFs. To get this going, you would convert the PDF into a <code>byte[] <\/code>array or <code>blob<\/code> and give it to the viewer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-a-bit-of-html-in-an-angular-component\">A bit of HTML in an Angular component<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;!-- your.component.html --&gt;\n&lt;div style=\"height: 600px\"&gt;\n\t&lt;ng2-pdfjs-viewer #pdfViewer&gt;&lt;\/ng2-pdfjs-viewer&gt;\n&lt;\/div&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Notice the <code>#pdfViewer<\/code>. This is your reference.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"h-some-download-code\">Some download code<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>downloadFile()<\/code> function calls the API; you may use any <em>HTTP<\/em> querying mechanism here. The requirement is that the API endpoint returns the pdf as a <code>byte<\/code> array.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;!-- your.component.ts --&gt;\t\n @ViewChild('pdfViewer') public pdfViewer;\nconstructor(private http: HttpClient) {\n\tlet url = \"api\/document\/getmypdf\";\n\tthis.downloadFile(url).subscribe(\n    \t(res) =&gt; {\n        \tthis.pdfViewer.pdfSrc = res; \/\/ pdfSrc can be Blob or Uint8Array\n        \tthis.pdfViewer.refresh(); \/\/ Ask pdf viewer to load\/refresh pdf\n    \t}\n\t);\n}\nprivate downloadFile(url: string): any {\n\treturn this.http.get(url, { responseType: 'blob' })\n    \t.pipe(\n        \tmap((result: any) =&gt; {\n            \treturn result;\n        \t})\n    \t);\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-a-sample-api-using-c-and-asp-net-core\">A sample API using C# and ASP.NET Core<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\">[HttpGet]\n[Route(\"GetMyPdf\")]\npublic IActionResult GetMyPdf()\n{\n\tvar pdfPath = Path.Combine(Directory.GetCurrentDirectory(),\"sample.pdf\");\n\tbyte[] bytes = System.IO.File.ReadAllBytes(pdfPath);\n\treturn File(bytes, \"application\/pdf\");\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I used <em>ASP.NET Core<\/em> and some C# here. Don&#8217;t worry; you can do this in <em>Python<\/em> or <em>Ruby<\/em> or whatever server-side technology you prefer, or any APIs supplying pdf through HTTP endpoints.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"h-conclusion\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <a href=\"https:\/\/www.npmjs.com\/package\/ng2-pdfjs-viewer\"><em>ng2-pdfjs-viewer<\/em><\/a> component is a powerful tool to display PDF files without losing the user experience. If you are a student programmer who is building a pdf book library angular app or an enterprise programmer who wants to display a PDF stored away somewhere in SharePoint, this library is useful. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The ability to show more than one PDF on the same page also helps with the comparison of different versions of a document.<\/p>\n\n\n\n<section id=\"my-first-block-block_e59f7af456d5d38690cac8343b08a342\" class=\"my-first-block alignwide\">\n    <div class=\"bg-brand-600 text-base-white py-5xl px-4xl rounded-sm bg-gradient-to-r from-brand-600 to-brand-500 red\">\n        <div class=\"gap-4xl items-start md:items-center flex flex-col md:flex-row justify-between\">\n            <div class=\"flex-1 col-span-10 lg:col-span-7\">\n                <h3 class=\"mt-0 font-display mb-2 text-display-sm\">Save 35% on Redgate&#8217;s .NET Developer Bundle<\/h3>\n                <div class=\"child:last-of-type:mb-0\">\n                                            Fantastic value on our .NET development tools for performance optimization and debugging.                                    <\/div>\n            <\/div>\n                                            <a href=\"https:\/\/www.red-gate.com\/products\/dotnet-developer-bundle\/\" class=\"btn btn--secondary btn--lg\" aria-label=\"Learn more: Save 35% on Redgate&#039;s .NET Developer Bundle\">Learn more<\/a>\n                    <\/div>\n    <\/div>\n<\/section>\n\n\n<section id=\"faq\" class=\"faq-block my-5xl\">\n    <h2>FAQs<\/h2>\n\n            <h3 class=\"mt-4xl\">1. How do you detect when a PDF has finished loading in ng2-pdfjs-viewer?<\/h3>\n        <div class=\"faq-answer\">\n            <p>Bind to the <code>onPagesLoaded<\/code> event on the <code>&lt;ng2-pdfjs-viewer&gt;<\/code> element, which emits the total page count once loading completes. Set a unique <code>viewerId<\/code> first if the page has more than one viewer instance, so events route to the right component.<\/p>\n        <\/div>\n            <h3 class=\"mt-4xl\">2. Can ng2-pdfjs-viewer display a PDF returned from a server API instead of a static file?<\/h3>\n        <div class=\"faq-answer\">\n            <p>Yes \u2014 have the API return the PDF as a byte array or blob, assign it to the viewer&#8217;s <code>pdfSrc<\/code> property via a <code>@ViewChild<\/code> reference, then call <code>.refresh()<\/code> to render it.<\/p>\n        <\/div>\n            <h3 class=\"mt-4xl\">3. Does ng2-pdfjs-viewer require a specific backend technology?<\/h3>\n        <div class=\"faq-answer\">\n            <p>No \u2014 the article&#8217;s example API uses C# and ASP.NET Core, but any backend that can return a PDF as a byte array over HTTP works the same way from the Angular side.<\/p>\n        <\/div>\n    <\/section>\n\n\n\n<p class=\"wp-block-paragraph\">&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to add an inline PDF viewer to an Angular app with ng2-pdfjs-viewer \u2014 installation, print\/load events, and rendering PDFs from a server API.&hellip;<\/p>\n","protected":false},"author":339718,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":true,"footnotes":""},"categories":[143538,146992,143526,53],"tags":[4143,146994,4371,4359],"coauthors":[146982],"class_list":["post-94290","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","category-angular","category-development","category-featured","tag-net","tag-angular","tag-c","tag-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/94290","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/339718"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=94290"}],"version-history":[{"count":9,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/94290\/revisions"}],"predecessor-version":[{"id":113292,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/94290\/revisions\/113292"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=94290"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=94290"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=94290"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=94290"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}