How to Generate DOCX and Save as a PDF

In this step-by-step tutorial, we’ll walk you through how to generate an Office Word or DOCX document programmatically and then either download it or display it in PDFTron’s WebViewer.

Everything is done in client-side JavaScript without any MS Office or server-side dependencies. You can do this in a vanilla JS app, React, or any other framework of your choice. The same functionality is also available if you are building a Node.js app.

For this walkthrough, we’ll use a React app. If you want to skip the steps and just look at the code, you can check out the repo on our GitHub.

Now let’s get started. First, generate a React app by running:

npx create-react-app

Generate DOCX Document Programmatically

We’ll use the popular DOCX library by Dolan. This library allows us to generate DOCX documents programmatically, directly in the browser or in a Node.js environment.

npm i docx

Inside of App.js, let’s create a new function to generate our document.

import './App.css';
import { Document, Packer, Paragraph, TextRun } from 'docx';

function App() {
 // generate DOCX document
 const generateDocx = async () => {
   const doc = new Document({
     sections: [
       {
         properties: {},
         children: [
           new Paragraph({
             children: [
               new TextRun(`DOCX lib and PDFTron's WebViewer is cutting-edge`),
             ],
           }),
         ],
       },
     ],
   });

   const blob = await Packer.toBlob(doc);

   return blob;
 };

 return <div className='App'></div>;
}

export default App;

So far, we have a Word document with a text paragraph that says `DOCX lib and PDFTron's WebViewer is cutting-edge’. At this stage, we can go ahead and download the DOCX document; however, let’s first add some tables, populate those with data, and then display the document back to the user.

Display Generated DOCX to the User

First, we’ll need to add a viewing component to display our DOCX documents. PDFTron’s WebViewer component can also be used to convert from DOCX to PDF, client-side, without any Office dependencies.

Create a new component in src/component/Viewer.js:

import React, { useRef, useEffect, useContext } from 'react';
import WebViewer from '@pdftron/webviewer';
import WebViewerContext from '../context/webviewer.js';

const Viewer = () => {
 const viewer = useRef(null);
 const { setInstance } = useContext(WebViewerContext);

 // if using a class, equivalent of componentDidMount
 useEffect(() => {
   WebViewer(
     {
       path: '/webviewer/lib',
     },
     viewer.current
   ).then((instance) => {
     setInstance(instance);
   });
 }, []);

 return <div className='webviewer' ref={viewer} style={{height: "100vh"}}></div>;
};

export default Viewer;

We’ll use context to recycle the WebViewer instance. Create a new file in src/context/webviewer.js:

import React from 'react';

const WebViewerContext = React.createContext({});

export default WebViewerContext;

Then, update our App.js:

import './App.css';
import { useEffect, useState } from 'react';
import { Document, Packer, Paragraph, TextRun } from 'docx';
import Viewer from './components/Viewer';
import WebViewerContext from './context/webviewer.js';

function App() {
 const [instance, setInstance] = useState();

 // generate DOCX document
 const generateDocx = async () => {
   const doc = new Document({
     sections: [
       {
         properties: {},
         children: [
           new Paragraph({
             children: [
               new TextRun(`DOCX lib and PDFTron's WebViewer is awesome!`),
             ],
           }),
         ],
       },
     ],
   });

   const blob = await Packer.toBlob(doc);

   return blob;
 };

 useEffect(() => {
   const generateAndLoadDocument = async () => {
     const docBlob = await generateDocx();
     await instance.Core.documentViewer.loadDocument(docBlob, {
       extension: 'docx',
     });
   };
   if (instance) {
     generateAndLoadDocument();
   }
 }, [instance]);

 return (
   <WebViewerContext.Provider value={{ instance, setInstance }}>
     <div className='App'>
       <Viewer />
     </div>
   </WebViewerContext.Provider>
 );
}

export default App;

At this stage, we’ve generated and loaded our Word document in WebViewer. From here, users can annotate, sign, highlight, and comment on the document.

Convert DOCX to PDF in the Browser

If we wanted to simply convert and download a PDF instead, and the UI is not needed, here’s how WebViewer can be leveraged to create a blob. Inside of a useEffect, make the following modifications:

useEffect(() => {
   const generateAndLoadDocument = async () => {
     const docBlob = await generateDocx();
     const buffer = await instance.Core.officeToPDFBuffer(docBlob);
     const blob = new Blob([buffer], { type: 'application/pdf' });
   };
   if (instance) {
     generateAndLoadDocument();
   }
 }, [instance]);

You can also load the library without initializing WebViewer instance. Here is a guide that demonstrates how to get Core.

If you'd like to do it server-side, you can explore the Node.js guide.

Wrap Up

We hope you found this article helpful! If you have any questions or feedback, feel free to email me directly.