There's a critical security update for PHP. Ensure you have updated PHP otherwise your BookStack instance is at risk!   More information
Hacks & Customizations
These hacks are unsupported customizations meant as unofficial workarounds.
They can cause instability, introduce issues and may conflict with future updates. Apply at your own risk!

WYSIWYG Docx Import

  • Author: @danb
  • Created: 7th Jun 2023
  • Updated: 29th Sep 2026
  • Last Tested On: v26.09.1

This hack adds the ability to import “.docx” files into the WYSIWYG editor, by dragging and dropping a “docx” file into the editor area. The file contents are converted to HTML then inserted into the editor at the current cursor position.

Conversion is performed browser-side using the mammoth.js library. Warning messages from the conversion will be logged to the browser console, and a popup warning notification will advise of this.

Considerations

  • This hack uses an externally hosted library, hosted on jsdelivr.net. You could host this locally instead if desired.
  • The conversion is relatively simplistic, to result in clean HTML for use in BookStack. Not all formatting is supported by the conversion and you’ll likely lose layout or formatting. Don’t expect a replica result in BookStack, this is more for easy importing of existing content.
  • Images will be within content, base64 included, until save when BookStack will attempt to extract them out. This may cause editor performance to be particularly slow until first save after import.
  • If using the older (currently default) WYSIWYG editor in BookStack, you will see a “Dropped file type is not supported” warning message pop-up in the editor on use.

Updates

  • 2026-09-29: This hack has been updated to also support the new WYSIWYG editor, so this now works for both. The mammoth.js version was also updated, and an integrity attribute added to protect against unexpected external script manipulation.

Code

head.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<script src="https://cdn.jsdelivr.net/npm/mammoth@1.13.0/mammoth.browser.min.js"
        integrity="sha256-kpFKNwjP3x5iy1U9GFan/tqyHag9jugoRd4viXs2jKs="
        crossorigin="anonymous"
        defer></script>

<script type="module">
    // Convert the given "file" instance to HTML, providing it back via the given callback
    function convertAndInsertDocx(file, callback) {
        // Use a FileReader to handle conversion via an ArrayBuffer
        const reader = new FileReader();
        reader.onload = async function(loadEvent) {
            // Get and convert the ArrayBuffer version of the file
            const arrayBuffer = loadEvent.target.result;
            const {value: html, messages} = await window.mammoth.convertToHtml({arrayBuffer});
            // If warnings exist from conversion, log them to the browser console then
            // show a warning alert via BookStack's event system.
            if (messages.length > 0) {
                console.error(messages);
                window.$events.emit('warning', `${messages.length} warnings logged to browser console during conversion`);
            }
            // Once we have our converted HTML, provide it to the callback function
            callback(html);
        }
        reader.readAsArrayBuffer(file);
    }

    // For each of the files in the drop event, pass them, alongside the editor instance
    // to our "convertAndInsertDocx" function above before providing the results to the given callback.
    function runCallbackForEachDocFileInEvent(event, callback) {
        const files = event?.dataTransfer?.files || [];
        let handledDocxFiles = false;
        for (const file of files) {
            if (file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' && window.mammoth) {
                convertAndInsertDocx(file, html => callback(html));
                handledDocxFiles = true;
            }
        }
        return handledDocxFiles;
    }

    // Listen to BookStack emitted WYSIWYG (TinyMCE) editor setup event
    window.addEventListener('editor-tinymce::setup', event => {
        // Get a reference to the editor and listen to file "drop" events
        const editor = event.detail.editor;
        editor.on('drop', event => {
            // For any docx files, insert the resulting HTML content into the editor
            runCallbackForEachDocFileInEvent(event, html => editor.insertContent(html));
        });
    });

    // Listen to BookStack emitted new-WYSIWYG editor setup event
    window.addEventListener('editor-wysiwyg::post-init', event => {
        const {usage, api} = event.detail;
        // Check that it's the page editor which is being loaded
        if (usage !== 'page-editor') {
            return;
        }

        // Add a custom drop listener for the editor area
        const editorArea = document.querySelector('.editor-content-area');
        if (!editorArea) {
            return;
        }

        editorArea.addEventListener('drop', event => {
            const htmlWasInserted = runCallbackForEachDocFileInEvent(event, html => api.content.insertHtml(html));
            if (htmlWasInserted) {
                event.preventDefault();
                event.stopPropagation();
            }
        });
    });
</script>

Install as Module (Beta)

If using BookStack v26.03 or later, you can install this hack as a theme module on your BookStack instance by running the below artisan command from your BookStack installation folder:

php artisan bookstack:install-module https://www.bookstackapp.com/hack-modules/wysiwyg-docx-import.zip


Request an Update

Hack not working on the latest version of BookStack?
You can request this hack to be updated & tested for a small one-time fee.
This helps keeps these hacks updated & maintained in a sustainable manner.


Latest Hacks