A TypeScript library for reading, writing, and manipulating Office Open XML documents (.docx, .xlsx, .pptx) in Node.js and browser environments. Inspired by the .NET Open-Xml-Sdk, this library brings the same familiar programming model to the TypeScript ecosystem.
Full API reference and guides are published at ericwhitedev.github.io/OpenXmlSdkTs.
To build the documentation locally:
npm run docs
Output is written to docs/api/. Open docs/api/index.html in a browser.
RelationshipType.styles, ContentType.mainDocument) instead of long, error-prone URIs.W, S, P, A, etc.) provide pre-initialized XName and XNamespace objects for every element and attribute in the Open XML specification. Because XNamespace and XName objects are atomized (two objects with the same namespace and local name are the same object), this gives excellent performance when querying and modifying markup.ltxmlts, a faithful TypeScript port of .NET's LINQ to XML. Query and transform XML with elements(), descendants(), attributes(), and the rest of the LINQ to XML API you already know.mainDocumentPart(), styleDefinitionsPart(), worksheetParts(), slideParts(), and many more.jszip and ltxmlts.npm install openxmlsdkts
Both openxmlsdkts and its companion library ltxmlts are available on npmjs.
OpenXmlPackage Base class for all Office document packages
├── WmlPackage Word (.docx) packages
├── SmlPackage Excel (.xlsx) packages
└── PmlPackage PowerPoint (.pptx) packages
OpenXmlPart Base class for all parts within a package
├── WmlPart Word parts (document, styles, headers, footers, etc.)
├── SmlPart Excel parts (workbook, worksheets, charts, etc.)
└── PmlPart PowerPoint parts (presentation, slides, masters, etc.)
OpenXmlRelationship Represents a relationship between a package/part and a target
ContentType Static lookup of content type labels → URIs
RelationshipType Static lookup of relationship type labels → URIs
Namespace Classes (static) Pre-initialized XNamespace / XName objects
├── W WordprocessingML
├── S SpreadsheetML
├── P PresentationML
├── A DrawingML
├── R, M, MC, W14, ... 30+ additional namespace classes
└── NoNamespace Attributes with no namespace
import * as fs from "fs";
import { WmlPackage, W, WmlPart } from "openxmlsdkts";
async function capitalizeFirstWord() {
// 1. Load the document from a file
const buffer = fs.readFileSync("my-document.docx");
const blob = new Blob([buffer]);
const doc = await WmlPackage.open(blob);
// 2. Get the main document part and its XML
const mainPart: WmlPart | undefined = await doc.mainDocumentPart();
if (!mainPart) throw new Error("No main document part found");
const xDoc = await mainPart.getXDocument();
// 3. Find the first word of the first run of text and capitalize it
const body = xDoc.root!.element(W.body);
if (body) {
const firstParagraph = body.element(W.p);
if (firstParagraph) {
const firstRun = firstParagraph.element(W.r);
if (firstRun) {
const textElement = firstRun.element(W.t);
if (textElement && textElement.value) {
const words = textElement.value.split(" ");
words[0] = words[0].toUpperCase();
textElement.value = words.join(" ");
}
}
}
}
// 4. Save the modified XML back to the part
mainPart.putXDocument(xDoc);
// 5. Save the document back to a file
const savedBlob = await doc.saveToBlobAsync();
const arrayBuffer = await savedBlob.arrayBuffer();
fs.writeFileSync("my-document-modified.docx", Buffer.from(arrayBuffer));
}
capitalizeFirstWord();
OpenXmlSdkTs supports three document formats. The open() method auto-detects the format from the input.
The standard .docx / .xlsx / .pptx ZIP-based format.
// Open
const buffer = fs.readFileSync("report.docx");
const doc = await WmlPackage.open(new Blob([buffer]));
// Save
const blob = await doc.saveToBlobAsync();
A single-file XML representation of the entire package. This is the format you must work with when building Office JavaScript/TypeScript add-in applications.
// Open
const flatOpcXml = fs.readFileSync("report.xml", "utf-8");
const doc = await WmlPackage.open(flatOpcXml);
// Save
const flatOpc: string = await doc.saveToFlatOpcAsync();
Convenient for APIs, data URIs, and serialization scenarios.
// Open
const base64 = fs.readFileSync("report.txt", "utf-8"); // base64-encoded docx
const doc = await WmlPackage.open(base64);
// Save
const savedBase64: string = await doc.saveToBase64Async();
Each package type provides typed methods to access its constituent parts.
const doc = await WmlPackage.open(blob);
// Package-level
const mainPart = await doc.mainDocumentPart();
const contentParts = await doc.contentParts(); // main + headers + footers + footnotes + endnotes
// Part-level navigation
const styles = await mainPart!.styleDefinitionsPart();
const theme = await mainPart!.themePart();
const numbering = await mainPart!.numberingDefinitionsPart();
const headers = await mainPart!.headerParts();
const footers = await mainPart!.footerParts();
const comments = await mainPart!.wordprocessingCommentsPart();
const fonts = await mainPart!.fontTablePart();
const settings = await mainPart!.documentSettingsPart();
const doc = await SmlPackage.open(blob);
const workbook = await doc.workbookPart();
const worksheets = await workbook!.worksheetParts();
const sharedStrings = await workbook!.sharedStringTablePart();
const wbStyles = await workbook!.workbookStylesPart();
const charts = await workbook!.chartsheetParts();
const pivotTables = await workbook!.pivotTableParts();
const doc = await PmlPackage.open(blob);
const presentation = await doc.presentationPart();
const slides = await presentation!.slideParts();
const masters = await presentation!.slideMasterParts();
const layout = await presentation!.slideLayoutPart();
const notesMaster = await presentation!.notesMasterPart();
All XML manipulation uses the LINQ to XML API from ltxmlts. The pre-initialized namespace classes make queries concise and performant.
import { WmlPackage, W, NoNamespace, XElement } from "openxmlsdkts";
const doc = await WmlPackage.open(blob);
const mainPart = await doc.mainDocumentPart();
const xDoc = await mainPart!.getXDocument();
// Query all paragraphs
const paragraphs = xDoc.root!.element(W.body)!.elements(W.p);
// Find paragraphs with a specific style
for (const para of paragraphs) {
const pPr = para.element(W.pPr);
const pStyle = pPr?.element(W.pStyle);
const styleVal = pStyle?.attribute(NoNamespace.val)?.value;
if (styleVal === "Heading1") {
console.log("Found Heading1 paragraph");
}
}
// Add a new paragraph
const newPara = new XElement(W.p,
new XElement(W.r,
new XElement(W.t, "Hello from OpenXmlSdkTs!")
)
);
xDoc.root!.element(W.body)!.add(newPara);
// Write the modified XML back to the part
mainPart!.putXDocument(xDoc);
Instead of using long URIs, use the ContentType and RelationshipType static lookups.
import { ContentType, RelationshipType } from "openxmlsdkts";
// Navigate parts by relationship type
const stylePart = await mainPart!.getPartByRelationshipType(RelationshipType.styles);
const themePart = await mainPart!.getPartByRelationshipType(RelationshipType.theme);
const imageParts = await mainPart!.getPartsByRelationshipType(RelationshipType.image);
// Query relationships
const rels = await mainPart!.getRelationships();
for (const rel of rels) {
console.log(rel.getType()); // e.g. the styles relationship URI
console.log(rel.getTarget()); // e.g. "styles.xml"
}
Over 40 static classes cover every namespace in the Open XML specification:
| Class | Namespace | Description |
|---|---|---|
W |
wordprocessingml/2006/main |
WordprocessingML elements and attributes |
S |
spreadsheetml/2006/main |
SpreadsheetML elements and attributes |
P |
presentationml/2006/main |
PresentationML elements and attributes |
A |
drawingml/2006/main |
DrawingML elements and attributes |
R |
officeDocument/2006/relationships |
Relationship attributes |
M |
officeDocument/2006/math |
Office Math |
W14 |
microsoft.com/office/word/2010/wordml |
Word 2010 extensions |
MC |
markup-compatibility/2006 |
Markup Compatibility |
NoNamespace |
(none) | Attributes with no namespace (e.g. val, id) |
| ... | ... | 30+ additional namespace classes |
Each class exposes a namespace property and XName properties for every element and attribute:
W.namespace // XNamespace for WordprocessingML
W.body // XName for <w:body>
W.p // XName for <w:p>
W.r // XName for <w:r>
W.t // XName for <w:t>
Because XName and XNamespace objects are atomized, equality checks are identity checks (===), giving excellent query performance.
Flat OPC is the document format you must use when building Word, Excel, and PowerPoint JavaScript/TypeScript add-ins:
// In an Office Add-in — get the document as Flat OPC via the Office.js API
// then manipulate with OpenXmlSdkTs
const doc = await WmlPackage.open(flatOpcString);
const mainPart = await doc.mainDocumentPart();
// ... modify the document ...
const modifiedFlatOpc = await doc.saveToFlatOpcAsync();
// Set the modified Flat OPC back via the Office.js API
| Class | Description | Key Methods |
|---|---|---|
WmlPackage |
Word documents | open(), mainDocumentPart(), contentParts(), saveToBase64Async(), saveToBlobAsync(), saveToFlatOpcAsync() |
SmlPackage |
Excel spreadsheets | open(), workbookPart(), saveToBase64Async(), saveToBlobAsync(), saveToFlatOpcAsync() |
PmlPackage |
PowerPoint presentations | open(), presentationPart(), saveToBase64Async(), saveToBlobAsync(), saveToFlatOpcAsync() |
| Class | Notable Navigation Methods |
|---|---|
WmlPart |
headerParts(), footerParts(), styleDefinitionsPart(), themePart(), numberingDefinitionsPart(), fontTablePart(), documentSettingsPart(), endnotesPart(), footnotesPart(), wordprocessingCommentsPart(), glossaryDocumentPart() |
SmlPart |
worksheetParts(), chartsheetParts(), sharedStringTablePart(), workbookStylesPart(), calculationChainPart(), pivotTableParts(), tableDefinitionParts() |
PmlPart |
slideParts(), slideMasterParts(), slideLayoutPart(), notesMasterPart(), notesSlidePart(), handoutMasterPart(), commentAuthorsPart(), presentationPropertiesPart(), viewPropertiesPart() |
| Method | Description |
|---|---|
getXDocument() |
Get the part's XML as an XDocument |
putXDocument(xDoc) |
Write modified XML back to the part |
getRelationships() |
Get all relationships from this part |
getPartByRelationshipType(type) |
Get a single related part by relationship type |
getPartsByRelationshipType(type) |
Get all related parts by relationship type |
getUri() |
Get the part's URI within the package |
getContentType() |
Get the part's content type |
| Package | Purpose |
|---|---|
ltxmlts |
LINQ to XML for TypeScript — XML querying and manipulation |
jszip |
ZIP compression for binary Open XML packages |
Blob supportMIT License. Copyright (c) 2026 Eric White.
Eric White