How to extract text from a PDF in JavaScript: PDF.js and Nutrient (2026)

Table of contents

    Extract text from PDFs in JavaScript with PDF.js or Nutrient Web SDK, with complete browser examples and guidance for scans and complex layouts.
    How to extract text from a PDF in JavaScript: PDF.js and Nutrient (2026)
    See a full-featured PDF viewer in your browser

    Nutrient Web SDK renders, annotates, and fills forms client side. Try the live demo — no signup required.

    TL;DR

    To extract PDF text in JavaScript, load a document with PDF.js and call getTextContent() for each page, or use Nutrient Web SDK’s textLinesForPageIndex() to retrieve text grouped into lines. Both examples below process all pages and preserve page separators. They extract an existing text layer; image-only pages need optical character recognition (OCR) first.

    PDF text extraction starts with parsing a document, rather than reading the file as a plain string. A PDF stores text in positioned pieces, and the order of those pieces may differ from the order in which a person reads the page.

    This tutorial covers two browser examples: PDF.js(opens in a new tab) for open source text extraction, and Nutrient Web SDK for extraction through a document viewer. You’ll get a string containing text from every page, with guidance for missing spaces, scanned documents, and complex layouts.

    Requirements for extracting text from a PDF in JavaScript

    You’ll need a modern browser, Node.js and npm for the setup commands, and a PDF with selectable text. Create a working directory and save your PDF there as document.pdf. Our demo document is a starting point.

    Serve the directory over HTTP instead of opening an HTML file directly. From that directory, run:

    Terminal window
    npx serve -l 8080 .

    The examples use local library files so each worker matches its main library version. Run the setup commands in a second terminal while the server is running.

    Extracting text from a PDF file using PDF.js

    Install PDF.js in your working directory:

    Terminal window
    npm install pdfjs-dist@6.3.289

    Save the following as pdfjs.html. Open http://localhost:8080/pdfjs.html after installation:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8" />
    <title>Extract PDF text with PDF.js</title>
    </head>
    <body>
    <pre id="output">Extracting text…</pre>
    <script type="module">
    import * as pdfjsLib from './node_modules/pdfjs-dist/build/pdf.mjs';
    pdfjsLib.GlobalWorkerOptions.workerSrc =
    './node_modules/pdfjs-dist/build/pdf.worker.mjs';
    async function extractText(pdfUrl) {
    const loadingTask = pdfjsLib.getDocument({
    url: pdfUrl,
    cMapUrl: './node_modules/pdfjs-dist/cmaps/',
    cMapPacked: true,
    standardFontDataUrl: './node_modules/pdfjs-dist/standard_fonts/',
    wasmUrl: './node_modules/pdfjs-dist/wasm/',
    });
    try {
    const pdf = await loadingTask.promise;
    const pages = [];
    for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber++) {
    const page = await pdf.getPage(pageNumber);
    const content = await page.getTextContent();
    const text = content.items
    .filter((item) => 'str' in item)
    .map((item) => item.str + (item.hasEOL ? '\n' : ' '))
    .join('')
    .trim();
    pages.push(text);
    page.cleanup();
    }
    return pages.join('\n\n');
    } finally {
    await loadingTask.destroy();
    }
    }
    const output = document.getElementById('output');
    try {
    const text = await extractText('./document.pdf');
    output.textContent = text.trim()
    ? text
    : 'No text layer found. This document may need OCR.';
    console.log(text);
    } catch (error) {
    output.textContent = `Extraction failed: ${error.message}`;
    console.error(error);
    }
    </script>
    </body>
    </html>

    The function loads pages sequentially rather than starting work on every page at once. It releases page resources after extraction and destroys the loading task in a finally block, including when an error occurs.

    Understanding the PDF.js text output

    getTextContent()(opens in a new tab) returns an object containing an items array. Text items have a str property, position information, and a hasEOL flag. The filter skips any non-text items.

    PDF.js getTextContent return object with text items shown in browser developer tools

    The example inserts a line break when hasEOL is true, and a space otherwise. This spacing rule separates adjacent words, but it can also insert spaces inside a word split across several items. It doesn’t reconstruct tables or guarantee reading order in multicolumn documents.

    For layout-sensitive extraction, inspect each item’s coordinates, font information, and surrounding items instead of relying on a single join() rule. The two newlines between pages keep the last word on one page from merging with the first word on the next.

    Extracted PDF text logged to the browser console using PDF.js

    PDF.js vs. Nutrient for text extraction

    Choose based on the output and document features your application needs. Neither example performs OCR or reconstructs tables from the extracted text.

    RequirementPDF.jsNutrient Web SDK
    Text APIgetTextContent() returns positioned text itemstextLinesForPageIndex() returns text line objects
    Page numberingStarts at oneStarts at zero
    Layout informationText item transforms and dimensionsLine contents and bounding boxes
    Image-only pagesNeed a separate OCR stepNeed a separate OCR workflow; this standalone example doesn’t run OCR
    IntegrationBuild extraction into your own applicationExtract through an SDK instance with a document viewer
    LicensingApache 2.0Commercial, with a trial

    PDF.js fits an application that needs an open source parser and control over text processing. Nutrient is useful when extraction is part of a viewer with features such as annotations, redaction, or form filling, depending on your license. Test both with your own files before choosing.

    Adding Nutrient to your project

    In the same working directory, install the SDK and copy its JavaScript file into an assets directory. Download the matching runtime archive there too:

    Terminal window
    npm install @nutrient-sdk/viewer@1.21.0
    mkdir -p assets
    cp node_modules/@nutrient-sdk/viewer/dist/nutrient-viewer.js assets/
    curl --fail --location \
    https://cdn.cloud.nutrient.io/assets/pspdfkit-web-assets@1.21.0.zip \
    --output nutrient-assets.zip
    unzip nutrient-assets.zip -d assets

    The extracted directory must be assets/nutrient-viewer-lib/. You can copy and extract the files manually if those shell commands aren’t available on your system. Keep the JavaScript package and runtime archive on the same version when following the self-hosting instructions.

    Extracting text from a PDF using Nutrient

    Save this example as nutrient.html and open http://localhost:8080/nutrient.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8" />
    <title>Extract PDF text with Nutrient</title>
    </head>
    <body>
    <pre id="output">Extracting text…</pre>
    <div id="nutrient" style="height: 80vh"></div>
    <script src="./assets/nutrient-viewer.js"></script>
    <script type="module">
    const output = document.getElementById('output');
    const controller = new AbortController();
    window.addEventListener('pagehide', (event) => {
    if (!event.persisted) {
    controller.abort();
    NutrientViewer.unload('#nutrient');
    }
    });
    try {
    const response = await fetch('./document.pdf', {
    signal: controller.signal,
    });
    if (!response.ok) {
    throw new Error(`PDF download failed: ${response.status}`);
    }
    const pdfData = await response.arrayBuffer();
    const instance = await NutrientViewer.load({
    container: '#nutrient',
    document: pdfData,
    baseUrl: new URL('./assets/', window.location.href).href,
    signal: controller.signal,
    // Add your production license key here when deploying.
    });
    const pages = [];
    for (let pageIndex = 0; pageIndex < instance.totalPageCount; pageIndex++) {
    const lines = await instance.textLinesForPageIndex(pageIndex);
    pages.push(lines.map((line) => line.contents).join('\n'));
    }
    const text = pages.join('\n\n');
    output.textContent = text.trim()
    ? text
    : 'No text layer found. This document may need OCR.';
    console.log(text);
    } catch (error) {
    if (!controller.signal.aborted) {
    output.textContent = `Extraction failed: ${error.message}`;
    console.error(error);
    }
    }
    </script>
    </body>
    </html>

    textLinesForPageIndex() returns an immutable list of text lines. Each line provides contents and a boundingBox, which is useful when connecting extracted text to a location in the viewer. The page index starts at zero, unlike PDF.js’s page number.

    The example checks the HTTP response before passing the PDF bytes to the SDK. The viewer stays open after extraction so you can compare the text with the page. For deployment, supply your licenseKey and follow the Web SDK integration guide.

    Nutrient SDK rendering a PDF in the browser

    Troubleshooting PDF text extraction

    An empty result can mean the page contains only images. If it already has an OCR text layer, these examples can read that layer; otherwise, add OCR first. Nutrient’s OCR workflow with Document Engine requires the appropriate licensed components and a server-backed setup.

    If you get a worker version mismatch, use the main library and worker from the same PDF.js release. If a local file won’t load, check its URL and serve the example over HTTP. A PDF hosted on another origin also needs suitable cross-origin resource sharing (CORS) headers.

    Scrambled reading order, missing characters, or unexpected spaces can come from the PDF’s content order or font mappings. Keep a test set with columns, tables, non-Latin text, and scanned pages. Then compare extracted output with what a reader sees. A non-empty string isn’t proof that every word was recovered correctly.

    Conclusion

    Use PDF.js’s text items when you want to control extraction and layout processing yourself. Use Nutrient’s text lines when extraction belongs in a document viewer with additional PDF capabilities. Both examples return text from all pages, but structured fields, tables, and image-only content need further processing.

    To add extraction to your viewer, start with the Nutrient Web SDK integration guide. For a server workflow, see Document Engine’s page text endpoint. To compare extracted document versions, see PDF text comparison.

    FAQ

    How do I extract text from every PDF page in JavaScript?

    With PDF.js, loop from one through pdf.numPages, load each page, and call getTextContent(). With Nutrient, loop from zero to one less than instance.totalPageCount and call textLinesForPageIndex(). Join the results with explicit page separators.

    Can PDF.js extract text from a scanned PDF?

    It can read an existing text layer, including one added by OCR. It doesn’t recognize text from page images itself. An image-only scan needs a separate OCR step before text extraction.

    Why are spaces or reading order wrong in the extracted text?

    PDF text is positioned on the page, and its storage order may differ from its reading order. Joining text items can lose spaces, add unwanted spaces, or mix columns. Use position information and test the output with your document layouts.

    How do I extract text from a PDF in Node.js?

    The pdfjs-dist package also supports Node.js. Follow its Node.js examples(opens in a new tab) for the appropriate imports and runtime requirements; the browser HTML and worker setup above isn’t a Node.js entry point. You can also call Document Engine’s page text endpoint from a backend application.

    Can I extract text from a PDF without a library?

    You’d need to implement PDF parsing, stream decoding, and font handling yourself. Reading PDF bytes as plain text won’t reliably recover the document’s text. A PDF parser is the practical starting point.

    Yasoob Khalid

    Yasoob Khalid

    Hulya Masharipov

    Hulya Masharipov

    Technical Writer

    Hulya is a frontend web developer and technical writer who enjoys creating responsive, scalable, and maintainable web experiences. She’s passionate about open source, web accessibility, cybersecurity privacy, and blockchain.

    Explore related topics

    Try for free Add a complete PDF viewer to your app