Back to Blog
6 min read

Microsoft 365 Development: Building Productivity Apps

Microsoft 365 development encompasses building apps for Word, Excel, PowerPoint, Outlook, and SharePoint. These integrations bring your functionality directly into the applications users work with daily.

Office Add-ins Overview

Office Add-ins use web technologies and run across platforms:

# Install Yeoman generator
npm install -g yo generator-office

# Create Excel add-in
yo office --projectType taskpane --name "MyExcelAddin" --host excel --ts true

# Create Word add-in
yo office --projectType taskpane --name "MyWordAddin" --host word --ts true

Excel Add-in

Create an Excel add-in for data analysis:

// src/taskpane/taskpane.ts
Office.onReady((info) => {
  if (info.host === Office.HostType.Excel) {
    document.getElementById("analyze-btn")!.onclick = analyzeData;
    document.getElementById("chart-btn")!.onclick = createChart;
  }
});

async function analyzeData(): Promise<void> {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getActiveWorksheet();
    const range = sheet.getUsedRange();
    range.load("values");

    await context.sync();

    const values = range.values;

    // Calculate statistics
    const numericValues = values
      .flat()
      .filter((v) => typeof v === "number") as number[];

    const stats = {
      count: numericValues.length,
      sum: numericValues.reduce((a, b) => a + b, 0),
      average: 0,
      min: Math.min(...numericValues),
      max: Math.max(...numericValues),
    };
    stats.average = stats.sum / stats.count;

    // Write results to new sheet
    const resultsSheet = context.workbook.worksheets.add("Analysis Results");

    const resultsRange = resultsSheet.getRange("A1:B5");
    resultsRange.values = [
      ["Metric", "Value"],
      ["Count", stats.count],
      ["Sum", stats.sum],
      ["Average", stats.average],
      ["Min", stats.min],
    ];

    resultsRange.format.autofitColumns();

    const headerRange = resultsSheet.getRange("A1:B1");
    headerRange.format.font.bold = true;
    headerRange.format.fill.color = "#4472C4";
    headerRange.format.font.color = "white";

    await context.sync();
  });
}

async function createChart(): Promise<void> {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getActiveWorksheet();
    const dataRange = sheet.getUsedRange();

    const chart = sheet.charts.add(
      Excel.ChartType.columnClustered,
      dataRange,
      Excel.ChartSeriesBy.auto
    );

    chart.title.text = "Data Analysis Chart";
    chart.legend.position = Excel.ChartLegendPosition.right;
    chart.setPosition("E2", "L15");

    await context.sync();
  });
}

async function formatTable(): Promise<void> {
  await Excel.run(async (context) => {
    const sheet = context.workbook.worksheets.getActiveWorksheet();
    const range = sheet.getUsedRange();

    // Create table
    const table = sheet.tables.add(range, true);
    table.name = "DataTable";
    table.style = "TableStyleMedium2";

    // Add conditional formatting
    const conditionalFormat = range.conditionalFormats.add(
      Excel.ConditionalFormatType.colorScale
    );

    conditionalFormat.colorScale.criteria = {
      minimum: {
        type: Excel.ConditionalFormatColorCriterionType.lowestValue,
        color: "#FF6B6B",
      },
      midpoint: {
        type: Excel.ConditionalFormatColorCriterionType.percentile,
        value: 50,
        color: "#FFE66D",
      },
      maximum: {
        type: Excel.ConditionalFormatColorCriterionType.highestValue,
        color: "#4ECDC4",
      },
    };

    await context.sync();
  });
}

Word Add-in

Create a Word add-in for document processing:

// src/taskpane/word-taskpane.ts
Office.onReady((info) => {
  if (info.host === Office.HostType.Word) {
    document.getElementById("insert-btn")!.onclick = insertContent;
    document.getElementById("analyze-btn")!.onclick = analyzeDocument;
  }
});

async function insertContent(): Promise<void> {
  await Word.run(async (context) => {
    const selection = context.document.getSelection();

    // Insert formatted content
    selection.insertHtml(
      `
      <h1>Document Title</h1>
      <p>This is a paragraph with <strong>bold</strong> and <em>italic</em> text.</p>
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
      </ul>
    `,
      Word.InsertLocation.replace
    );

    await context.sync();
  });
}

async function analyzeDocument(): Promise<void> {
  await Word.run(async (context) => {
    const body = context.document.body;
    body.load("text");

    const paragraphs = body.paragraphs;
    paragraphs.load("items");

    await context.sync();

    const text = body.text;
    const wordCount = text.split(/\s+/).filter((w) => w.length > 0).length;
    const charCount = text.length;
    const paragraphCount = paragraphs.items.length;

    // Calculate reading time (200 words per minute)
    const readingTime = Math.ceil(wordCount / 200);

    // Display results
    const results = document.getElementById("results")!;
    results.innerHTML = `
      <h3>Document Statistics</h3>
      <p>Words: ${wordCount}</p>
      <p>Characters: ${charCount}</p>
      <p>Paragraphs: ${paragraphCount}</p>
      <p>Reading Time: ~${readingTime} minutes</p>
    `;
  });
}

async function searchAndHighlight(searchText: string): Promise<void> {
  await Word.run(async (context) => {
    const body = context.document.body;
    const searchResults = body.search(searchText, {
      matchCase: false,
      matchWholeWord: false,
    });

    searchResults.load("items");
    await context.sync();

    for (const result of searchResults.items) {
      result.font.highlightColor = "yellow";
      result.font.bold = true;
    }

    await context.sync();
  });
}

async function insertTable(): Promise<void> {
  await Word.run(async (context) => {
    const selection = context.document.getSelection();

    const tableData = [
      ["Name", "Department", "Email"],
      ["John Doe", "Engineering", "john@example.com"],
      ["Jane Smith", "Marketing", "jane@example.com"],
      ["Bob Johnson", "Sales", "bob@example.com"],
    ];

    const table = selection.insertTable(
      tableData.length,
      tableData[0].length,
      Word.InsertLocation.after,
      tableData
    );

    table.styleBuiltIn = Word.Style.gridTable5Dark_Accent1;

    await context.sync();
  });
}

Outlook Add-in

Create an Outlook add-in for email processing:

// src/taskpane/outlook-taskpane.ts
Office.onReady((info) => {
  if (info.host === Office.HostType.Outlook) {
    loadEmailData();
    document.getElementById("save-btn")!.onclick = saveToSystem;
  }
});

async function loadEmailData(): Promise<void> {
  const item = Office.context.mailbox.item;

  if (!item) return;

  // Load basic properties
  const subject = item.subject;
  const from = item.from?.emailAddress;

  // Get recipients
  item.to.getAsync((result) => {
    if (result.status === Office.AsyncResultStatus.Succeeded) {
      const recipients = result.value.map((r) => r.emailAddress).join(", ");
      document.getElementById("to-field")!.textContent = recipients;
    }
  });

  // Get body
  item.body.getAsync(Office.CoercionType.Text, (result) => {
    if (result.status === Office.AsyncResultStatus.Succeeded) {
      document.getElementById("body-preview")!.textContent =
        result.value.substring(0, 500) + "...";
    }
  });

  // Get attachments
  if (item.attachments && item.attachments.length > 0) {
    const attachmentList = item.attachments
      .map((a) => `<li>${a.name} (${formatBytes(a.size)})</li>`)
      .join("");
    document.getElementById(
      "attachments"
    )!.innerHTML = `<ul>${attachmentList}</ul>`;
  }

  document.getElementById("subject")!.textContent = subject;
  document.getElementById("from")!.textContent = from || "Unknown";
}

async function saveToSystem(): Promise<void> {
  const item = Office.context.mailbox.item;

  if (!item) return;

  item.body.getAsync(Office.CoercionType.Html, async (result) => {
    if (result.status === Office.AsyncResultStatus.Succeeded) {
      const emailData = {
        subject: item.subject,
        from: item.from?.emailAddress,
        body: result.value,
        receivedDate: item.dateTimeCreated,
      };

      // Send to your API
      try {
        const response = await fetch("https://api.example.com/emails", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(emailData),
        });

        if (response.ok) {
          showNotification("Success", "Email saved to system");
        }
      } catch (error) {
        showNotification("Error", "Failed to save email");
      }
    }
  });
}

function showNotification(title: string, message: string): void {
  Office.context.mailbox.item?.notificationMessages.addAsync("notification", {
    type: Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage,
    message: message,
    icon: "icon-16",
    persistent: false,
  });
}

function formatBytes(bytes: number): string {
  if (bytes === 0) return "0 Bytes";
  const k = 1024;
  const sizes = ["Bytes", "KB", "MB", "GB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}

SharePoint Framework (SPFx)

Build SharePoint web parts:

// src/webparts/dataViewer/DataViewerWebPart.ts
import { Version } from "@microsoft/sp-core-library";
import {
  IPropertyPaneConfiguration,
  PropertyPaneTextField,
} from "@microsoft/sp-property-pane";
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
import styles from "./DataViewerWebPart.module.scss";

export interface IDataViewerWebPartProps {
  listName: string;
}

export default class DataViewerWebPart extends BaseClientSideWebPart<IDataViewerWebPartProps> {
  public render(): void {
    this.domElement.innerHTML = `
      <div class="${styles.dataViewer}">
        <h2>List Data Viewer</h2>
        <div id="listData">Loading...</div>
      </div>
    `;

    this.loadListData();
  }

  private async loadListData(): Promise<void> {
    const listName = this.properties.listName || "Documents";

    try {
      const response: SPHttpClientResponse = await this.context.spHttpClient.get(
        `${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${listName}')/items?$top=10`,
        SPHttpClient.configurations.v1
      );

      const data = await response.json();
      this.renderList(data.value);
    } catch (error) {
      this.domElement.querySelector("#listData")!.innerHTML =
        "Error loading data";
    }
  }

  private renderList(items: any[]): void {
    const html = items
      .map(
        (item) => `
      <div class="${styles.listItem}">
        <span class="${styles.title}">${item.Title || item.FileLeafRef}</span>
        <span class="${styles.date}">${new Date(item.Modified).toLocaleDateString()}</span>
      </div>
    `
      )
      .join("");

    this.domElement.querySelector("#listData")!.innerHTML =
      html || "No items found";
  }

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: { description: "Configuration" },
          groups: [
            {
              groupName: "Settings",
              groupFields: [
                PropertyPaneTextField("listName", {
                  label: "List Name",
                  description: "Enter the name of the SharePoint list",
                }),
              ],
            },
          ],
        },
      ],
    };
  }

  protected get dataVersion(): Version {
    return Version.parse("1.0");
  }
}

Summary

Microsoft 365 development offers:

  • Cross-platform Office Add-ins
  • SharePoint Framework for portals
  • Rich APIs for document manipulation
  • Integration with Microsoft Graph
  • Marketplace distribution

Build productivity-enhancing solutions that integrate seamlessly with Microsoft 365.


References:

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.