How to build a Vue.js Word (DOC and DOCX) viewer using Nutrient

Table of contents

    How to build a Vue.js Word (DOC and DOCX) viewer using Nutrient

    In this blog post, you’ll learn how to build a Vue 3 Word viewer using Nutrient Web SDK. You’ll open DOC and DOCX files directly in the browser with Vite and Vue’s Composition API. The document is processed client-side, so this example doesn’t require a document conversion server.

    The image below shows what you’ll be building.

    You can check out the demo to see it in action.

    Opening and rendering Office documents in the browser

    Nutrient Web SDK brings Word, Excel, and PowerPoint support to your application without requiring Microsoft Office software, Microsoft Office licenses, or a third-party conversion service. It converts the Office document to PDF in the browser and renders the result in the JavaScript viewer. Opening Office files requires the Office Files component in your Nutrient license.

    Unlocking more capabilities with Office-to-PDF conversion

    By converting an Office document to PDF using client-side JavaScript, you can add functionality to the converted PDF representation, such as:

    • Text editing — Edit text in the converted PDF.
    • Page manipulation — Add, remove, or rearrange pages in the converted PDF.
    • Annotations — Boost collaboration by adding text highlights, comments, or stamps.
    • Adding signatures — Draw, type, or upload a signature to the converted PDF.
    Explore demo

    Requirements to get started

    To get started, you’ll need:

    Creating the Vue project

    Vue CLI is in maintenance mode. The recommended way to start a Vue 3 application is create-vue(opens in a new tab), which scaffolds a project powered by Vite.

    1. Create a new Vue project:
    Terminal window
    npm create vue@latest
    1. When prompted, use nutrient-vue-word-viewer as the project name and enable TypeScript. The other options are optional for this tutorial.
    2. Change to the project directory and install its dependencies:
    Terminal window
    cd nutrient-vue-word-viewer
    npm install

    Adding Nutrient

    Install the current Nutrient Web SDK package:

    Terminal window
    npm install @nutrient-sdk/viewer

    This tutorial uses useCDN: true, so the SDK loads its supporting assets from Nutrient’s content delivery network (CDN). If your deployment requires self-hosted assets, follow the current self-hosting guide instead of copying the deprecated pspdfkit-lib directory manually.

    Displaying a Word document

    1. Add a Word (DOC, DOCX) document you want to display to the public directory. You can use our demo document as an example.
    2. Add a component wrapper for Nutrient Web SDK and save it as src/components/WordViewer.vue:
    <script setup lang="ts">
    import { onMounted, onUnmounted, useTemplateRef, watch } from "vue";
    const props = defineProps<{
    document: string;
    }>();
    const emit = defineEmits<{
    loaded: [instance: unknown];
    }>();
    const containerRef = useTemplateRef("container");
    let NutrientViewer:
    | typeof import("@nutrient-sdk/viewer").default
    | undefined;
    let latestLoadId = 0;
    async function loadDocument() {
    const container = containerRef.value;
    if (!container) return;
    const loadId = ++latestLoadId;
    NutrientViewer ??= (await import("@nutrient-sdk/viewer")).default;
    const configuration = {
    document: props.document,
    useCDN: true,
    processorEngine: NutrientViewer.ProcessorEngine.fasterProcessing,
    };
    await NutrientViewer.preloadWorker(configuration);
    // A newer document was requested while this one was preparing; let it win.
    if (loadId !== latestLoadId) return;
    NutrientViewer.unload(container);
    const instance = await NutrientViewer.load({
    container,
    ...configuration,
    });
    if (loadId !== latestLoadId) return;
    emit("loaded", instance);
    }
    onMounted(loadDocument);
    watch(() => props.document, loadDocument);
    onUnmounted(() => {
    const container = containerRef.value;
    if (container && NutrientViewer) {
    NutrientViewer.unload(container);
    }
    });
    </script>
    <template>
    <div ref="container" class="doc-container" />
    </template>
    <style scoped>
    /* The parent layout is a column flex container, so the viewer fills whatever
    height remains below the toolbar — no hardcoded offsets. */
    .doc-container {
    flex: 1;
    min-height: 0;
    width: 100%;
    }
    </style>

    Here’s what’s happening in the component:

    • useTemplateRef() provides the document object model (DOM) container Nutrient needs to mount the viewer.
    • The SDK is imported dynamically after the component mounts, which keeps browser-only code out of server-side rendering paths.
    • preloadWorker() loads the WebAssembly worker and Office conversion engine before the viewer opens the document.
    • watch() reloads the viewer whenever the selected Word file changes.
    • latestLoadId makes rapid document switches safe. loadDocument() awaits the import and worker preload, so an older call can finish after a newer one; comparing its captured loadId against the latest lets the stale call bail out instead of overwriting the newer document or emitting loaded for the wrong file.
    • NutrientViewer.unload() cleans up the previous instance before a new document loads and when Vue unmounts the component.
    1. Now, replace the contents of src/App.vue with the following:
    <script setup lang="ts">
    import { onUnmounted, ref, useTemplateRef } from "vue";
    import WordViewer from "./components/WordViewer.vue";
    const fileInputRef = useTemplateRef("fileInput");
    const wordFile = ref("/document.docx");
    let objectUrl: string | undefined;
    function openDocument(event: Event) {
    const input = event.target as HTMLInputElement;
    const file = input.files?.[0];
    if (!file) return;
    if (objectUrl) {
    URL.revokeObjectURL(objectUrl);
    }
    objectUrl = URL.createObjectURL(file);
    wordFile.value = objectUrl;
    }
    function handleLoaded(instance: unknown) {
    console.log("Nutrient Web SDK loaded", instance);
    }
    onUnmounted(() => {
    if (objectUrl) {
    URL.revokeObjectURL(objectUrl);
    }
    });
    </script>
    <template>
    <main>
    <button
    type="button"
    class="custom-file-upload"
    @click="fileInputRef?.click()"
    >
    Open Word
    </button>
    <input
    ref="fileInput"
    type="file"
    accept=".doc,.docx"
    @change="openDocument"
    />
    <WordViewer :document="wordFile" @loaded="handleLoaded" />
    </main>
    </template>
    <style>
    body {
    margin: 0;
    }
    /* A column flex layout hands the viewer the height left over below the button,
    so nothing needs to know how tall the toolbar is. */
    main {
    display: flex;
    flex-direction: column;
    height: 100vh;
    }
    input[type="file"] {
    display: none;
    }
    .custom-file-upload {
    align-self: flex-start;
    margin: 8px;
    padding: 10px 14px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    background: #4a8fed;
    color: #fff;
    font: 600 16px/1 sans-serif;
    }
    </style>
    • In the template, a real button opens the hidden file input through a template ref, which keeps the picker focusable and operable from the keyboard — something a styled label doesn’t provide on its own. The input accepts DOC and DOCX files, and the active object URL is passed to WordViewer.
    • In the <script setup> block, Vue’s ref() stores the current document URL. The previous object URL is revoked when a different file is selected and when the component unmounts.
    • The @change and @loaded directives use Vue’s shorthand for v-on, while :document uses shorthand for v-bind.

    For example, these pairs are equivalent:

    v-on:change="openDocument"
    v-on:loaded="handleLoaded"
    @change="openDocument"
    @loaded="handleLoaded"

    The WordViewer binding can also be written in long form:

    <WordViewer v-bind:document="wordFile" v-on:loaded="handleLoaded" />
    1. Start the app:
    Terminal window
    npm run dev

    Vite prints the local development URL in your terminal — usually http://localhost:5173.

    If you can’t see your Word file rendered in the browser, make sure you actually uploaded a DOC or DOCX file inside the public directory.

    In the demo application, you can open different Word files by clicking the Open Word button. After conversion, you can add signatures, annotations, stamps, and more to the PDF.

    A note about fonts

    For client-side Office-to-PDF conversion, Nutrient substitutes fonts when the original typeface isn’t available. To improve fidelity, embed fonts in the source document, configure custom fonts, or use dynamic font loading so the SDK can fetch required fonts at runtime.

    Adding even more capabilities

    Once you’ve deployed your viewer, you can start customizing it to meet your specific requirements or easily add more capabilities. To help you get started, here are some of our most popular Vue.js guides:

    Conclusion

    In this blog post, you learned how to build a Word viewer with Vue 3, Vite, and Nutrient Web SDK. The current integration uses @nutrient-sdk/viewer, the Composition API, and client-side Office conversion. If you hit any snags, refer to the Vue integration guide or contact Nutrient Support.

    You can also integrate the viewer with Angular or React. Launch the Office viewer demo to test the experience, or start a free trial to build it into your application.

    Start your free trial

    FAQ

    What file formats can the Nutrient Word viewer handle?

    Nutrient Web SDK can open DOC and DOCX files. Available annotation, signing, and editing capabilities depend on the components enabled in your license.

    Do I need a server to use the Nutrient Word viewer?

    No. The example in this tutorial converts and renders Word files in the browser, so it doesn’t require a document conversion server.

    Is there a free trial available for Nutrient?

    Yes, you can start a free trial of Nutrient, although you’ll have a watermark on documents without a license key.

    Can I add annotations to my documents in the viewer?

    Yes. After conversion, the viewer allows you to add annotations, text highlights, and signatures to the PDF.

    What should I do if I encounter issues using the viewer?

    If you experience any issues, you can reach out to the Nutrient Support team for assistance.

    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 Ready to get started?