npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

docx-handlebars

v0.3.3

Published

A Rust library for processing DOCX files with Handlebars templates, supporting WASM, Node.js, Deno, and browsers

Readme

docx-handlebars

Crates.io Documentation License

English | 中文文档 |Demo

A Rust library for processing DOCX files with Handlebars templates, supporting multiple platforms:

  • 🦀 Rust native
  • 🌐 WebAssembly (WASM)
  • 📦 npm package
  • 🟢 Node.js
  • 🦕 Deno
  • 🌍 Browser
  • 📋 JSR (JavaScript Registry)

Features

  • Smart Merging: Automatically handles Handlebars syntax split by XML tags
  • DOCX Validation: Built-in file format validation to ensure valid input files
  • Handlebars Support: Full template engine with variables, conditionals, loops, and helper functions
  • Cross-platform: Rust native + WASM support for multiple runtimes
  • TypeScript: Complete type definitions and intelligent code completion
  • Zero Dependencies: WASM binary with no external dependencies

Installation

Rust

cargo add docx-handlebars

npm

npm install docx-handlebars

Deno

import init, { render_template } from "jsr:@sail/docx-handlebars";

Usage Examples

Rust

use docx_handlebars::render_template;
use serde_json::json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Read DOCX template file
    let template_bytes = std::fs::read("template.docx")?;
    
    // Prepare data
    let data = json!({
        "name": "John Doe",
        "company": "ABC Technology Ltd.",
        "position": "Software Engineer",
        "projects": [
            {"name": "Project A", "status": "Completed"},
            {"name": "Project B", "status": "In Progress"}
        ],
        "has_bonus": true,
        "bonus_amount": 5000
    });
    
    // Render template
    let result = render_template(template_bytes, &data)?;
    
    // Save result
    std::fs::write("output.docx", result)?;
    
    Ok(())
}

JavaScript/TypeScript (Node.js)

import init, { render_template } from 'docx-handlebars';
import fs from 'fs';

async function processTemplate() {
    // Initialize WASM module
    await init();
    
    // Read template file
    const templateBytes = fs.readFileSync('template.docx');
    
    // Prepare data
    const data = {
        name: "Jane Smith",
        company: "XYZ Tech Ltd.",
        position: "Senior Developer",
        projects: [
            { name: "E-commerce Platform", status: "Completed" },
            { name: "Mobile App", status: "In Development" }
        ],
        has_bonus: true,
        bonus_amount: 8000
    };
    
    // Render template
    const result = render_template(templateBytes, JSON.stringify(data));
    
    // Save result
    fs.writeFileSync('output.docx', new Uint8Array(result));
}

processTemplate().catch(console.error);

Deno

import init, { render_template } from "https://deno.land/x/docx_handlebars/mod.ts";

async function processTemplate() {
    // Initialize WASM module
    await init();
    
    // Read template file
    const templateBytes = await Deno.readFile("template.docx");
    
    // Prepare data
    const data = {
        name: "Alice Johnson",
        department: "R&D Department",
        projects: [
            { name: "AI Customer Service", status: "Live" },
            { name: "Data Visualization Platform", status: "In Development" }
        ]
    };
    
    // Render template
    const result = render_template(templateBytes, JSON.stringify(data));
    
    // Save result
    await Deno.writeFile("output.docx", new Uint8Array(result));
}

if (import.meta.main) {
    await processTemplate();
}

Browser

<!DOCTYPE html>
<html>
<head>
    <title>DOCX Handlebars Example</title>
</head>
<body>
    <input type="file" id="fileInput" accept=".docx">
    <button onclick="processFile()">Process Template</button>
    
    <script type="module">
        import init, { render_template } from './pkg/docx_handlebars.js';
        
        // Initialize WASM
        await init();
        
        window.processFile = async function() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            
            if (!file) return;
            
            const arrayBuffer = await file.arrayBuffer();
            const templateBytes = new Uint8Array(arrayBuffer);
            
            const data = {
                name: "John Doe",
                company: "Example Company"
            };
            
            try {
                const result = render_template(templateBytes, JSON.stringify(data));
                
                // Download result
                const blob = new Blob([new Uint8Array(result)], {
                    type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
                });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = 'processed.docx';
                a.click();
            } catch (error) {
                console.error('Processing failed:', error);
            }
        };
    </script>
</body>
</html>

Template Syntax

Basic Variable Substitution

Employee Name: {{name}}
Company: {{company}}
Position: {{position}}

Conditional Rendering

{{#if has_bonus}}
Bonus: ${{bonus_amount}}
{{else}}
No bonus
{{/if}}

{{#unless is_intern}}
Full-time employee
{{/unless}}

Loop Rendering

Project Experience:
{{#each projects}}
- {{name}}: {{description}} ({{status}})
{{/each}}

Skills:
{{#each skills}}
{{@index}}. {{this}}
{{/each}}

Helper Functions

Built-in helper functions:

{{upper name}}           <!-- Convert to uppercase -->
{{lower company}}        <!-- Convert to lowercase -->
{{len projects}}         <!-- Array length -->
{{#if (eq status "completed")}}Completed{{/if}}    <!-- Equality comparison -->
{{#if (gt score 90)}}Excellent{{/if}}              <!-- Greater than comparison -->
{{#if (lt age 30)}}Young{{/if}}                    <!-- Less than comparison -->

<!-- Image insertion -->
{{img base64_data}}                    <!-- Inline image -->
{{img base64_data 300 200}}           <!-- Inline image with specified dimensions -->
{{img base64_data 300 200 options}}   <!-- Floating image with positioning options -->

Complex Example

=== Employee Report ===

Basic Information:
Name: {{employee.name}}
Department: {{employee.department}}
Position: {{employee.position}}
Hire Date: {{employee.hire_date}}

{{#if employee.has_bonus}}
💰 Bonus: ${{employee.bonus_amount}}
{{/if}}

Project Experience ({{len projects}} total):
{{#each projects}}
{{@index}}. {{name}}
   Description: {{description}}
   Status: {{status}}
   Team Size: {{team_size}} people
   
{{/each}}

Skills Assessment:
{{#each skills}}
- {{name}}: {{level}}/10 ({{years}} years experience)
{{/each}}

To delete an entire table row, simply add the following to any cell in that row:
{{removeTableRow}}

{{#if (gt performance.score 90)}}
🎉 Performance Rating: Excellent
{{else if (gt performance.score 80)}}
👍 Performance Rating: Good
{{else}}
📈 Performance Rating: Needs Improvement
{{/if}}

Image:
{{img base64_image_data [width] [height] [options]}}
  only height: {{img base64 "" 200}}
  only width: {{img base64 300}}
  no width/height: {{img base64}}
  both width/height: {{img base64 300 200}}
  
  Floating image example:
  {{img base64 200 100 options}}
  
  Where options can be:
  {
    "anchor": true,          // Whether to use anchor positioning (floating image)
    "behind_doc": false,     // Whether image is behind text (false=overlay text, true=text overlays image)
    "allow_overlap": true,   // Whether to allow overlap with other objects
    "position_h": 40,        // Horizontal offset in pixels (negative values shift left)
    "position_v": -14        // Vertical offset in pixels (negative values shift up)
  }
  
  Image positioning guide:
  - anchor: false (default) - Inline image, flows with text
  - anchor: true - Floating image, can be freely positioned and overlay text
  - position_h/position_v: When not provided, defaults to half the image size for centering
  - Supports negative offsets for precise image positioning

Build and Development

Build WASM Packages

# Build all targets
npm run build

# Or build separately
npm run build:web    # Browser version
npm run build:npm    # Node.js version 
npm run build:jsr    # Deno version

Run Examples

# Rust example
cargo run --example rust_example

# Node.js example
node examples/node_example.js

# Deno example  
deno run --allow-read --allow-write examples/deno_example.ts

# Browser example
cd tests/npm_test
node serve.js
# Then open http://localhost:8080 in your browser
# Select examples/template.docx file to test

Technical Features

Smart Merging Algorithm

The core innovation of this library is the intelligent merging of Handlebars syntax that has been split by XML tags. In DOCX files, when users input template syntax, Word may split it into multiple XML tags.

Performance and Compatibility

  • Zero Copy: Efficient memory management between Rust and WASM
  • Streaming Processing: Suitable for handling large DOCX files
  • Cross-platform: Support for Windows, macOS, Linux, Web
  • Modern Browsers: Support for all modern browsers that support WASM

License

This project is licensed under the MIT License - see the LICENSE-MIT file for details.

Support


Support this project with a donation via Alipay:

Support this project with a donation