Download as pdf or txt
Download as pdf or txt
You are on page 1of 2399

Tell us about your PDF experience.

Office Add-ins documentation


Use the Office Add-ins platform to build solutions that extend Office applications and interact with
content in Office documents and in Outlook mail messages and calendar items. With Office Add-
ins, you can use familiar web technologies such as HTML, CSS, and JavaScript to build solutions
that can run in Office on the web, Windows, Mac, and mobile.

OVERVIEW QUICKSTART
Office Add-ins platform Set up an Excel add-in in 5
overview minutes

QUICKSTART QUICKSTART
Set up an Outlook add-in in 5 Set up a Word add-in in 5
minutes minutes

SAMPLE HOW-TO GUIDE


Samples for Office Add-ins Office Add-ins Beginner's guide

SAMPLE REFERENCE
Test and experiment with APIs Office JavaScript API reference
using Script Lab documentation

Your developer journey

Explore Build Test and debug


b Get started with Office g Develop a full Excel add-in c Clear the Office cache
Add-ins
g Develop a full Outlook e Check out the debugging
f Set up a PowerPoint add-in add-in overview
in 5 minutes
g Develop a full Word add-in p Troubleshoot user errors
Y Verify an add-in's manifest
d Introduction to Office i Read API reference
client customization with documentation
add-ins
d Extend Office clients with
Office Add-ins
h Microsoft 365 Developer
Blog

Deploy Publish Engage


` Explore deployment e Validate your Office Add-in h Join the Developer
options c Review the Microsoft 365 Program
` Test and deploy Microsoft App Store submission Y Request features
365 Apps by partners in guide i Ask questions about
the Integrated apps portal
Office.js on Microsoft Q&A
i Ask questions about
Office.js on Stack Overflow
i Report a bug or issue
Office Add-ins platform overview
Article • 03/21/2023

You can use the Office Add-ins platform to build solutions that extend Office
applications and interact with content in Office documents. With Office Add-ins, you can
use familiar web technologies such as HTML, CSS, and JavaScript to extend and interact
with Outlook, Excel, Word, PowerPoint, OneNote, and Project. Your solution can run in
Office across multiple platforms, including Windows, Mac, iPad, and in a browser.

Office Add-ins can do almost anything a webpage can do inside a browser. Use the
Office Add-ins platform to:

Add new functionality to Office clients - Bring external data into Office, automate
Office documents, expose functionality from Microsoft and others in Office clients,
and more. For example, use Microsoft Graph API to connect to data that drives
productivity.

Create new rich, interactive objects that can be embedded in Office documents -
Embed maps, charts, and interactive visualizations that users can add to their own
Excel spreadsheets and PowerPoint presentations.

How are Office Add-ins different from COM


and VSTO add-ins?
COM and VSTO add-ins are earlier Office integration solutions that run only in Office on
Windows. Unlike COM and VSTO add-ins, Office Add-ins are web add-ins and don't
involve code that runs on the user's device or in the Office client. For an Office Add-in,
the application (for example, Excel), reads the add-in manifest and hooks up the add-in's
custom ribbon buttons and menu commands in the UI. When needed, it loads the add-
in's JavaScript and HTML code, which executes in the context of a browser or webview
control in a sandbox.

Office Add-ins provide the following advantages over add-ins built using VBA, COM, or
VSTO.

Cross-platform support: Office Add-ins run in Office on the web, Windows, Mac,
and iPad.

Centralized deployment and distribution: Admins can deploy Office Add-ins


centrally across an organization.

Easy access via AppSource: You can make your solution available to a broad
audience by submitting it to AppSource.

Based on standard web technology: You can use any library you like to build Office
Add-ins.
) Important

COM and VSTO add-ins aren't supported in the new Outlook on Windows that's
currently in preview. These add-ins are still supported in the classic Outlook on
Windows desktop client. To learn more, see Develop Outlook add-ins for new
Outlook on Windows (preview).

Components of an Office Add-in


An Office Add-in includes two basic components: an XML manifest file, and your own
web application. The manifest defines various settings, including how your add-in
integrates with Office clients. Your web application needs to be hosted on a web server,
or web hosting service, such as Microsoft Azure.

Manifest
The manifest is an XML file that specifies settings and capabilities of the add-in, such as:

The add-in's display name, description, ID, version, and default locale.

How the add-in integrates with Office.

The permission level and data access requirements for the add-in.

Web app
The most basic Office Add-in consists of a static HTML page that is displayed inside an
Office application, but that doesn't interact with either the Office document or any other
Internet resource. However, to create an experience that interacts with Office documents
or allows the user to interact with online resources from an Office client application, you
can use any technologies, both client and server side, that your hosting provider
supports (such as ASP.NET, PHP, or Node.js). To interact with Office clients and
documents, you use the Office.js JavaScript APIs.

Extending and interacting with Office clients


Office Add-ins can do the following within an Office client application.

Extend functionality (any Office application)

Create new objects (Excel or PowerPoint)

Extend Office functionality


You can add new functionality to Office applications via the following:
Custom ribbon buttons and menu commands (collectively called "add-in
commands").

Insertable task panes.

Custom UI and task panes are specified in the add-in manifest.

Custom buttons and menu commands

You can add custom ribbon buttons and menu items to the ribbon in Office on the web
and on Windows. This makes it easy for users to access your add-in directly from their
Office application. Command buttons can launch different actions such as showing a
task pane with custom HTML or executing a JavaScript function.

Task panes
You can use task panes in addition to add-in commands to enable users to interact with
your solution. Clients that don't support add-in commands (Office on iPad) run your
add-in as a task pane. Users launch task pane add-ins via the My Add-ins button on the
Insert tab.
Extend Outlook functionality
Outlook add-ins can extend the Office app ribbon and also display contextually next to
an Outlook item when you're viewing or composing it. They can work with an email
message, meeting request, meeting response, meeting cancellation, or appointment
when a user is viewing a received item or replying or creating a new item.

Outlook add-ins can access contextual information from the item, such as an address or
tracking ID, and then use that data to access additional information on the server and
from web services to create compelling user experiences. In most cases, an Outlook
add-in runs without modification in the Outlook application to provide a seamless
experience on the desktop, web, and tablet and mobile devices.

For an overview of Outlook add-ins, see Outlook add-ins overview.

Create new objects in Office documents


You can embed web-based objects called content add-ins within Excel and PowerPoint
documents. With content add-ins, you can integrate rich, web-based data visualizations,
media (such as a YouTube video player or a picture gallery), and other external content.
Office JavaScript APIs
The Office JavaScript APIs contain objects and members for building add-ins and
interacting with Office content and web services. There's a common object model that's
shared by Excel, Outlook, Word, PowerPoint, OneNote, and Project. There are also more
extensive application-specific object models for Excel and Word. These APIs provide
access to well-known objects such as paragraphs and workbooks, which makes it easier
to create an add-in for a specific application.

Code samples
Learn how to build the simplest Office Add-in with only a manifest, HTML web page,
and a logo. The following samples will help you get started in the Office application
you're interested in.

Excel "Hello world" add-in


Outlook "Hello world" add-in
PowerPoint "Hello world" add-in
Word "Hello world" add-in
Next steps
For a more detailed introduction to developing Office Add-ins, see Develop Office Add-
ins.

See also
Core concepts for Office Add-ins
Develop Office Add-ins
Design Office Add-ins
Test and debug Office Add-ins
Publish Office Add-ins
Learn about the Microsoft 365 Developer Program
Beginner's guide
Article • 03/09/2023

Want to get started building your own cross-platform Office extensions? The following
steps show you what to read first, what tools to install, and recommended tutorials to
complete.

7 Note

If you're experienced in creating VSTO add-ins for Office, we recommend that you
immediately turn to VSTO add-in developer's guide, which is a superset of the
information in this article.

Step 0: Prerequisites
Office Add-ins are essentially web applications embedded in Office. So, you should
first have a basic understanding of web applications and how they are hosted on
the web. There is an enormous amount of information about this on the Internet,
in books, and in online courses. A good way to start if you have no prior
knowledge of web applications at all is to search for "What is a web app?" on Bing.
The primary programming language you will use in creating Office Add-ins is
JavaScript or TypeScript. You can think of TypeScript as a strongly-typed version of
JavaScript. If you are not familiar with either of these languages, but you have
experience with VBA, VB.Net, C#, you will probably find TypeScript easier to learn.
Again, there is a wealth of information about these languages on the Internet, in
books, and in online courses.

Step 1: Begin with fundamentals


We know you're eager to start coding, but there are some things about Office Add-ins
that you should read before you open your IDE or code editor.

Office Add-ins Platform Overview: Find out what Office Web Add-ins are and how
they differ from older ways of extending Office, such as VSTO add-ins.
Develop Office Add-ins: Get an overview of Office Add-in development and
lifecycle including tooling, creating an add-in UI, and using the JavaScript APIs to
interact with the Office document.
"Hello world" samples : Learn how to build the simplest Office Add-in with only a
manifest, HTML web page, and a logo. These samples will help you understand the
fundamental parts of an Office Add-in.

There are a lot of links in those articles, but if you're a beginner with Office Add-ins, we
recommend that you come back here when you've read them and continue with the
next section.

Step 2: Install tools and create your first add-in


You've got the big picture now, so dive in with one of our quick starts. For purposes of
learning the platform, we recommend the Excel quick start. There is a version that is
based on Visual Studio and a version that is based in Node.js and Visual Studio Code.

Visual Studio
Node.js and Visual Studio Code

Step 3: Code
You can't learn to drive by reading the owner's manual, so start coding with this Excel
tutorial. You'll be using the Office JavaScript library and some XML in the add-in's
manifest. There's no need to memorize anything, because you'll be getting more
background about both in a later steps.

Step 4: Understand the JavaScript library


First, get the big picture of the Office JavaScript library with this tutorial from Microsoft
Learn training: Understand the Office JavaScript APIs.

Then explore the Office JavaScript APIs with our Script Lab tool -- a sandbox for running
and exploring the APIs.

Step 5: Understand the manifest


Get an understanding of the purposes of the add-in manifest and an introduction to its
XML markup or JSON in Office Add-ins manifest.

Next Steps
Congratulations on finishing the beginner's learning path for Office Add-ins! Here are
some suggestions for further exploration of our documentation:
Tutorials or quick starts for other Office applications:
OneNote quick start
Outlook tutorial
PowerPoint tutorial
Project quick start
Word tutorial

Other important subjects:


Develop Office Add-ins
Best practices for developing Office Add-ins
Design Office Add-ins
Test and debug Office Add-ins
Deploy and publish Office Add-ins
Resources
Learn about the Microsoft 365 Developer Program
Build an Excel task pane add-in
Article • 03/28/2023

In this article, you'll walk through the process of building an Excel task pane add-in.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Create the add-in project
Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Excel

After you complete the wizard, the generator creates the project and installs
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a basic task pane add-in. If you'd like to explore the components of your
add-in project, open the project in your code editor and review the files listed
below. When you're ready to try out your add-in, proceed to the next section.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in. To learn more about the manifest.xml file, see
Office Add-ins XML manifest.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and the Office client
application.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your
add-in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're
developing. If you're prompted to install a certificate after you run one of
the following commands, accept the prompt to install the certificate that
the Yeoman generator provides. You may also have to run your command
prompt or terminal as an administrator for the changes to be made.

 Tip
If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server

To test your add-in in Excel, run the following command in the root
directory of your project. This starts the local web server and opens Excel
with your add-in loaded.

command line

npm start

To test your add-in in Excel on a browser, run the following command in


the root directory of your project. When you run this command, the local
web server starts. Replace "{url}" with the URL of an Excel document on
your OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single


quotation marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798Bpuhwl

uxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP
If your add-in doesn't sideload in the document, manually sideload it by
following the instructions in Manually sideload add-ins to Office on the
web.

3. In Excel, choose the Home tab, and then choose the Show Taskpane button
on the ribbon to open the add-in task pane.

4. Select any range of cells in the worksheet.

5. At the bottom of the task pane, choose the Run link to set the color of the
selected range to yellow.

Next steps
Congratulations, you've successfully created an Excel task pane add-in! Next, learn
more about the capabilities of an Excel add-in and build a more complex add-in by
following along with the Excel add-in tutorial.

Code samples
Excel "Hello world" add-in : Learn how to build a simple Office Add-in with only a
manifest, HTML web page, and a logo.

See also
Office Add-ins platform overview
Develop Office Add-ins
Excel JavaScript object model in Office Add-ins
Excel add-in code samples
Excel JavaScript API reference
Using Visual Studio Code to publish
Get started developing Excel custom
functions
Article • 03/28/2023

With custom functions, developers can add new functions to Excel by defining them in
JavaScript or TypeScript as part of an add-in. Excel users can access custom functions
just as they would any native function in Excel, such as SUM() .

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Office connected to a Microsoft 365 subscription (including Office on the web).

7 Note

If you don't already have Office, you can join the Microsoft 365 developer
program to get a free, 90-day renewable Microsoft 365 subscription to use
during development.
Build your first custom functions project
To start, you'll use the Yeoman generator to create the custom functions project. This
will set up your project with the correct folder structure, source files, and dependencies
to begin coding your custom functions.

1. Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Excel Custom Functions using a Shared Runtime


Choose a script type: JavaScript
What do you want to name your add-in? My custom functions add-in

The Yeoman generator will create the project files and install supporting Node
components.

2. The Yeoman generator will give you some instructions in your command line about
what to do with the project, but ignore them and continue to follow our
instructions. Navigate to the root folder of the project.
command line

cd "My custom functions add-in"

3. Build the project.

command line

npm run build

4. Start the local web server, which runs in Node.js. You can try out the custom
function add-in in Excel. You may be prompted to open the add-in's task pane,
although this is optional. You can still run your custom functions without opening
your add-in's task pane.

Excel on Windows or Mac

To test your add-in in Excel on Windows or Mac, run the following command. When
you run this command, the local web server will start and Excel will open with your
add-in loaded.

command line

npm run start:desktop

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

Try out a prebuilt custom function


The custom functions project that you created by using the Yeoman generator contains
some prebuilt custom functions, defined within the ./src/functions/functions.js file. The
./manifest.xml file in the root directory of the project specifies that all custom functions
belong to the CONTOSO namespace.
In your Excel workbook, try out the ADD custom function by completing the following
steps.

1. Select a cell and type =CONTOSO . Notice that the autocomplete menu shows the list
of all functions in the CONTOSO namespace.

2. Run the CONTOSO.ADD function, using numbers 10 and 200 as input parameters, by
typing the value =CONTOSO.ADD(10,200) in the cell and pressing enter.

The ADD custom function computes the sum of the two numbers that you specify as
input parameters. Typing =CONTOSO.ADD(10,200) should produce the result 210 in the cell
after you press enter.

If the CONTOSO namespace isn't available in the autocomplete menu, take the following
steps to register the add-in in Excel.

Excel on Windows or Mac

1. In Excel, choose the Insert tab and then choose the down-arrow located to the
right of My Add-ins.

2. In the list of available add-ins, find the Developer Add-ins section and select
My custom functions add-in to register it.
Next steps
Congratulations, you've successfully created a custom function in an Excel add-in! Next,
build a more complex add-in with streaming data capability. The following link takes
you through the next steps in the Excel add-in with custom functions tutorial.

Excel custom functions add-in tutorial

Troubleshooting
You may encounter issues if you run the quick start multiple times. If the Office cache
already has an instance of a function with the same name, your add-in gets an error
when it sideloads. You can prevent this by clearing the Office cache before running npm
run start .

See also
Custom functions overview
Custom functions metadata
Runtime for Excel custom functions
Using Visual Studio Code to publish
Build your first OneNote task pane add-
in
Article • 05/02/2023

In this article, you'll walk through the process of building a OneNote task pane add-in.

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Create the add-in project


Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note
When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? OneNote

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample code
for a very basic task pane add-in.

The ./manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in
the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code that
facilitates interaction between the task pane and the Office client application.

Update the code


In your code editor, open the file ./src/taskpane/taskpane.js and add the following code
within the run function. This code uses the OneNote JavaScript API to set the page title
and add an outline to the body of the page.

JavaScript

try {
await OneNote.run(async (context) => {

// Get the current page.


const page = context.application.getActivePage();

// Queue a command to set the page title.


page.title = "Hello World";

// Queue a command to add an outline to the page.


const html = "<p><ol><li>Item #1</li><li>Item #2</li></ol></p>";
page.addOutline(40, 90, html);

// Run the queued commands.


await context.sync();
});
} catch (error) {
console.log("Error: " + error);
}

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Start the local web server. Run the following command in the root directory of your
project.
command line

npm run dev-server

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

3. In OneNote on the web , open a notebook and create a new page.

4. Choose Insert > Office Add-ins to open the Office Add-ins dialog.

If you're signed in with your consumer account, select the MY ADD-INS tab,
and then choose Upload My Add-in.

If you're signed in with your work or education account, select the MY


ORGANIZATION tab, and then select Upload My Add-in.

The following image shows the MY ADD-INS tab for consumer notebooks.

5. In the Upload Add-in dialog, browse to manifest.xml in your project folder, and
then choose Upload.

6. From the Home tab, choose the Show Taskpane button on the ribbon. The add-in
task pane opens in an iFrame next to the OneNote page.

7. At the bottom of the task pane, choose the Run link to set the page title and add
an outline to the body of the page.
Next steps
Congratulations, you've successfully created a OneNote task pane add-in! Next, learn
more about the core concepts of building OneNote add-ins.

OneNote JavaScript API programming overview

See also
Office Add-ins platform overview
Develop Office Add-ins
OneNote JavaScript API programming overview
OneNote JavaScript API reference
Rubric Grader sample
Using Visual Studio Code to publish
Build your first Outlook add-in
Article • 03/28/2023

In this article, you'll walk through the process of building an Outlook task pane add-in
that displays at least one property of a selected message.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Visual Studio Code (VS Code) or your preferred code editor

Outlook 2016 or later on Windows (connected to a Microsoft 365 account) or


Outlook on the web

Create the add-in project


1. Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about
the data collection policies of Yeoman and the Office Add-in CLI tools.
Use the information that's provided to respond to the prompts as you
see fit.

When prompted, provide the following information to create your add-in


project.

Choose a project type - Office Add-in Task Pane project

Choose a script type - JavaScript

What do you want to name your add-in? - My Office Add-in

Which Office client application would you like to support? - Outlook


After you complete the wizard, the generator will create the project and install
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator
provides after the add-in project's been created. The step-by-step
instructions within this article provide all of the guidance you'll need to
complete this tutorial.

2. Navigate to the root folder of the web application project.

command line

cd "My Office Add-in"

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a very basic task pane add-in.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and Outlook.
Update the code
1. Open your project in VS Code or your preferred code editor.

 Tip

On Windows, you can navigate to the root directory of the project via the
command line and then enter code . to open that folder in VS Code. On
Mac, you'll need to add the code command to the path before you
can use that command to open the project folder in VS Code.

2. Open the file ./src/taskpane/taskpane.html and replace the entire <main>


element (within the <body> element) with the following markup. This new
markup adds a label where the script in ./src/taskpane/taskpane.js will write
data.

HTML

<main id="app-body" class="ms-welcome__main" style="display:


none;">
<h2 class="ms-font-xl"> Discover what Office Add-ins can do for
you today! </h2>
<p><label id="item-subject"></label></p>
<div role="button" id="run" class="ms-welcome__action ms-Button
ms-Button--hero ms-font-xl">
<span class="ms-Button-label">Run</span>
</div>
</main>

3. In your code editor, open the file ./src/taskpane/taskpane.js, then add the
following code to the run function. This code uses the Office JavaScript API to
get a reference to the current message and write its subject property value to
the task pane.

JavaScript

// Get a reference to the current message


const item = Office.context.mailbox.item;

// Write message property value to the task pane


document.getElementById("item-subject").innerHTML = "<b>Subject:
</b> <br/>" + item.subject;

Your taskpane.js file should now contain the following code.


JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/

/* global document, Office */

Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
document.getElementById("sideload-msg").style.display = "none";
document.getElementById("app-body").style.display = "flex";
document.getElementById("run").onclick = run;
}
});

export async function run() {


// Get a reference to the current message
const item = Office.context.mailbox.item;

// Write message property value to the task pane


document.getElementById("item-subject").innerHTML = "<b>Subject:
</b> <br/>" + item.subject;
}

Try it out

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

1. Run the following command in the root directory of your project. When you
run this command, the local web server starts and your add-in will be
sideloaded.

command line

npm start
2. In Outlook, view a message in the Reading Pane , or open the message in its
own window.

3. Choose the Home tab (or the Message tab if you opened the message in a
new window), and then choose the Show Taskpane button on the ribbon to
open the add-in task pane.

7 Note

If you receive the error "We can't open this add-in from localhost" in the
task pane, follow the steps outlined in the troubleshooting article.

4. When prompted with the WebView Stop On Load dialog box, select OK.

7 Note

If you select Cancel, the dialog won't be shown again while this instance
of the add-in is running. However, if you restart your add-in, you'll see
the dialog again.

5. Scroll to the bottom of the task pane and choose the Run link to write the
message subject to the task pane.
Next steps
Congratulations, you've successfully created your first Outlook task pane add-in!
Next, learn more about the capabilities of an Outlook add-in and build a more
complex add-in by following along with the Outlook add-in tutorial.

See also
Using Visual Studio Code to publish
Build your first PowerPoint task pane
add-in
Article • 01/10/2023

In this article, you'll walk through the process of building a PowerPoint task pane add-in.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Create the add-in project
Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? PowerPoint

After you complete the wizard, the generator creates the project and installs
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a basic task pane add-in. If you'd like to explore the components of your
add-in project, open the project in your code editor and review the files listed
below. When you're ready to try out your add-in, proceed to the next section.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in. To learn more about the manifest.xml file, see
Office Add-ins XML manifest.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and the Office client
application.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your
add-in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're
developing. If you're prompted to install a certificate after you run one of
the following commands, accept the prompt to install the certificate that
the Yeoman generator provides. You may also have to run your command
prompt or terminal as an administrator for the changes to be made.

 Tip
If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server

To test your add-in in PowerPoint, run the following command in the


root directory of your project. This starts the local web server (if it's not
already running) and opens PowerPoint with your add-in loaded.

command line

npm start

To test your add-in in PowerPoint on a browser, run the following


command in the root directory of your project. When you run this
command, the local web server starts. Replace "{url}" with the URL of a
PowerPoint document on your OneDrive or a SharePoint library to which
you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single


quotation marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798Bpuhwl
uxCMfF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document


https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp

npm run start:web -- --document https://contoso-my.sharepoint-


df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP
If your add-in doesn't sideload in the document, manually sideload it by
following the instructions in Manually sideload add-ins to Office on the
web.

3. In PowerPoint, insert a new blank slide, choose the Home tab, and then
choose the Show Taskpane button on the ribbon to open the add-in task
pane.

4. At the bottom of the task pane, choose the Run link to insert the text "Hello
World" into the current slide.

Next steps
Congratulations, you've successfully created a PowerPoint task pane add-in! Next,
learn more about the capabilities of a PowerPoint add-in and build a more complex
add-in by following along with the PowerPoint add-in tutorial.

See also
Office Add-ins platform overview
Develop Office Add-ins
Using Visual Studio Code to publish
Build your first Project task pane add-in
Article • 05/02/2023

In this article, you'll walk through the process of building a Project task pane add-in.

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Project 2016 or later on Windows

Create the add-in


Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note
When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Project

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample code
for a very basic task pane add-in.

The ./manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in
the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code that
facilitates interaction between the task pane and the Office client application. In
this quick start, the code sets the Name field and Notes field of the selected task of
a project.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Start the local web server.

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

Run the following command in the root directory of your project. When you run
this command, the local web server will start.

command line

npm run dev-server

3. In Project, create a simple project plan.

4. Load your add-in in Project by following the instructions in Sideload Office Add-ins
on Windows.

5. Select a single task within the project.


6. At the bottom of the task pane, choose the Run link to rename the selected task
and add notes to the selected task.

Next steps
Congratulations, you've successfully created a Project task pane add-in! Next, learn
more about the capabilities of a Project add-in and explore common scenarios.

Project add-ins

See also
Develop Office Add-ins
Core concepts for Office Add-ins
Using Visual Studio Code to publish
Build your first Word task pane add-in
Article • 04/17/2023

In this article, you'll walk through the process of building a Word task pane add-in.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Create the add-in project
Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: JavaScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Word

After you complete the wizard, the generator creates the project and installs
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a basic task pane add-in. If you'd like to explore the components of your
add-in project, open the project in your code editor and review the files listed
below. When you're ready to try out your add-in, proceed to the next section.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in. To learn more about the manifest.xml file, see
Office Add-ins XML manifest.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and the Office client
application.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your
add-in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're
developing. If you're prompted to install a certificate after you run one of
the following commands, accept the prompt to install the certificate that
the Yeoman generator provides. You may also have to run your command
prompt or terminal as an administrator for the changes to be made.

 Tip
If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server

To test your add-in in Word, run the following command in the root
directory of your project. This starts the local web server (if it's not
already running) and opens Word with your add-in loaded.

command line

npm start

To test your add-in in Word on a browser, run the following command in


the root directory of your project. When you run this command, the local
web server starts. Replace "{url}" with the URL of a Word document on
your OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single


quotation marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798Bpuhwl

uxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP
If your add-in doesn't sideload in the document, manually sideload it by
following the instructions in Manually sideload add-ins to Office on the
web.

3. In Word, if the "My Office Add-in" task pane isn't already open, open a new
document, choose the Home tab, and then choose the Show Taskpane button
on the ribbon to open the add-in task pane.

4. At the bottom of the task pane, choose the Run link to add the text "Hello
World" to the document in blue font.

Next steps
Congratulations, you've successfully created a Word task pane add-in! Next, learn
more about the capabilities of a Word add-in and build a more complex add-in by
following along with the Word add-in tutorial.

Word add-in tutorial

Code samples
Word "Hello world" add-in : Learn how to build a simple Office Add-in with only
a manifest, HTML web page, and a logo.

See also
Office Add-ins platform overview
Develop Office Add-ins
Word add-ins overview
Word add-in code samples
Word JavaScript API reference
Using Visual Studio Code to publish
Single sign-on (SSO) quick start
Article • 05/02/2023

In this article, you'll use the Yeoman generator for Office Add-ins to create an Office
Add-in for Excel, Outlook, Word, or PowerPoint that uses single sign-on (SSO).

7 Note

The SSO template provided by the Yeoman generator for Office Add-ins only runs
on localhost and cannot be deployed. If you're building a new Office Add-in with
SSO for production purposes, follow the instructions in Create a Node.js Office
Add-in that uses single sign-on.

Prerequisites
Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

If you're using a Mac and don't have the Azure CLI installed on your machine, you
must install Homebrew . The SSO configuration script that you'll run during this
quick start will use Homebrew to install the Azure CLI, and will then use the Azure
CLI to configure SSO within Azure.

Create the add-in project

 Tip
The Yeoman generator can create an SSO-enabled Office Add-in for Excel, Outlook,
Word, or PowerPoint with script type of JavaScript or TypeScript. The following
instructions specify JavaScript and Excel , but you should choose the script type
and Office client application that best suits your scenario.

Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project supporting single sign-
on (localhost)
Choose a script type: JavaScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Choose Excel ,
Outlook , Word , or Powerpoint .

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip
You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains code for an
SSO-enabled task pane add-in.

Configuration
The following files specify configuration settings for the add-in.

The ./manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in.

The ./.ENV file in the root directory of the project defines constants that are used
by the add-in project.

Task pane
The following files define the add-in's task pane UI and functionality.

The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.

The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in


the task pane.

In a JavaScript project, the ./src/taskpane/taskpane.js file contains code to


initialize the add-in. In a TypeScript project, the ./src/taskpane/taskpane.ts file
contains code to initialize the add-in and also code that uses the Office JavaScript
API library to add the data from Microsoft Graph to the Office document.

Authentication
The following files facilitate the SSO process and write data to the Office document.

In a JavaScript project, the ./src/helpers/documentHelper.js file contains code that


uses the Office JavaScript API library to add the data from Microsoft Graph to the
Office document. There is no such file in a TypeScript project; the code that uses
the Office JavaScript API library to add the data from Microsoft Graph to the Office
document exists in ./src/taskpane/taskpane.ts instead.

The ./src/helpers/fallbackauthdialog.html file is the UI-less page that loads the


JavaScript for the fallback authentication strategy.

The ./src/helpers/fallbackauthdialog.js file contains the JavaScript for the fallback


authentication strategy that signs in the user with msal.js.

The ./src/helpers/fallbackauthhelper.js file contains the task pane JavaScript that


invokes the fallback authentication strategy in scenarios when SSO authentication
is not supported.

The ./src/middle-tier/ssoauth-helper.js file contains the JavaScript call to the SSO


API, getAccessToken , receives the access token, initiates the swap of the access
token for a new access token with permissions to Microsoft Graph, and calls to
Microsoft Graph for the data.

Configure SSO
Now that your add-in project is created and contains the code that's necessary to
facilitate the SSO process, complete the following steps to configure SSO for your add-
in.

1. Go to the root folder of the project.

command line

cd "My Office Add-in"

2. Run the following command to configure SSO for the add-in.

command line

npm run configure-sso

2 Warning

This command will fail if your tenant is configured to require two-factor


authentication. In this scenario, you'll need to manually complete the Azure
app registration and SSO configuration steps by following all the steps in the
Create a Node.js Office Add-in that uses single sign-on tutorial.
3. A web browser window will open and prompt you to sign in to Azure. Sign in to
Azure using your Microsoft 365 administrator credentials. These credentials will be
used to register a new application in Azure and configure the settings required by
SSO.

7 Note

If you sign in to Azure using non-administrator credentials during this step,


the configure-sso script won't be able to provide administrator consent for
the add-in to users within your organization. SSO will therefore not be
available to users of the add-in and they'll be prompted to sign-in.

4. After you enter your credentials, close the browser window and return to the
command prompt. As the SSO configuration process continues, you'll see status
messages being written to the console. As described in the console messages, files
within the add-in project that the Yeoman generator created are automatically
updated with data that's required by the SSO process.

Test your add-in


If you've created an Excel, Word, or PowerPoint add-in, complete the steps in the
following section to try it. If you've created an Outlook add-in, complete the steps in the
Outlook section instead.

Excel, Word, and PowerPoint


Complete the following steps to test an Excel, Word, or PowerPoint add-in.

1. When the SSO configuration process completes, run the following command to
build the project, start the local web server, and sideload your add-in in the
previously selected Office client application.

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.
command line

npm start

2. When Excel, Word, or PowerPoint opens when you run the previous command,
make sure you're signed in with a user account that's a member of the same
Microsoft 365 organization as the Microsoft 365 administrator account that you
used to connect to Azure while configuring SSO in step 3 of the previous section.
Doing so establishes the appropriate conditions for SSO to succeed.

3. In the Office client application, choose the Home tab, and then choose Show
Taskpane to open the add-in task pane.

4. At the bottom of the task pane, choose the Get My User Profile Information
button to initiate the SSO process.

5. If a dialog window appears to request permissions on behalf of the add-in, this


means that SSO is not supported for your scenario and the add-in has instead
fallen back to an alternate method of user authentication. This may occur when the
tenant administrator hasn't granted consent for the add-in to access Microsoft
Graph, or when the user isn't signed in to Office with a valid Microsoft account or
Microsoft 365 Education or Work account. Choose Accept to continue.
7 Note

After a user accepts this permissions request, they won't be prompted again
in the future.

6. The add-in retrieves profile information for the signed-in user and writes it to the
document. The following image shows an example of profile information written to
an Excel worksheet.
Outlook
Complete the following steps to try out an Outlook add-in.

1. When the SSO configuration process completes, run the following command to
build the project and start the local web server.

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

command line

npm start

2. Follow the instructions in Sideload Outlook add-ins for testing to sideload the add-
in in Outlook. Make sure that you're signed in to Outlook with a user that's a
member of the same Microsoft 365 organization as the Microsoft 365
administrator account that you used to connect to Azure while configuring SSO in
step 3 of the previous section. Doing so establishes the appropriate conditions for
SSO to succeed.

3. In Outlook, compose a new message.

4. In the message compose window, choose the Show Taskpane button to open the
add-in task pane.

5. At the bottom of the task pane, choose the Get My User Profile Information
button to initiate the SSO process.
6. If a dialog window appears to request permissions on behalf of the add-in, this
means that SSO is not supported for your scenario and the add-in has instead
fallen back to an alternate method of user authentication. This may occur when the
tenant administrator hasn't granted consent for the add-in to access Microsoft
Graph, or when the user isn't signed in to Office with a valid Microsoft account or
Microsoft 365 Education or Work account. Choose Accept to continue.

7 Note

After a user accepts this permissions request, they won't be prompted again
in the future.

7. The add-in retrieves profile information for the signed-in user and writes it to the
body of the email message.
Next steps
Congratulations, you've successfully created a task pane add-in that uses SSO when
possible, and uses an alternate method of user authentication when SSO is not
supported. To learn about customizing your add-in to add new functionality that
requires different permissions, see Customize your Node.js SSO-enabled add-in.

See also
Enable single sign-on for Office Add-ins
Customize your Node.js SSO-enabled add-in
Create a Node.js Office Add-in that uses single sign-on
Troubleshoot error messages for single sign-on (SSO)
Using Visual Studio Code to publish
Explore Office JavaScript API using
Script Lab
Article • 05/20/2023

Script Lab is a free tool for anyone to learn how to develop Office Add-ins. Script Lab
lets you to code and run the Office JavaScript APIs alongside your document in Excel,
Outlook, PowerPoint, and Word. Use this convenient tool to prototype and verify the
functionality you want in your own add-in.

Get Script Lab for Excel, PowerPoint, and Word

Get Script Lab for Outlook

See Script Lab in action in this one-minute YouTube video.

What is Script Lab?


Script Lab is an add-in for prototyping add-ins. It uses the Office JavaScript API in Excel,
Outlook, Word, and PowerPoint and sits in a task pane inside your document,
spreadsheet, or email. It has an IntelliSense-enabled code editor, built on the same
framework used by Visual Studio Code . Through Script Lab, you can access a library of
samples to quickly try out features or use those samples as the starting point for your
own code. You can even try upcoming APIs in Script Lab that are still in developer
preview.
Key features
Script Lab offers a number of features to help you explore the Office JavaScript API and
prototype add-in functionality.

Explore samples
Get started quickly with a collection of built-in sample snippets that show how to
complete tasks with the API. You can run the samples to instantly see the result in the
task pane or document, examine the samples to learn how the API works, and even use
samples to prototype your own add-in.

Code and style


In addition to JavaScript or TypeScript code that calls the Office JS API, each snippet also
contains HTML markup that defines content of the task pane and CSS that defines the
appearance of the task pane. You can customize the HTML markup and CSS to
experiment with element placement and styling as you prototype task pane design for
your own add-in.
 Tip

To call preview APIs within a snippet, you'll need to update the snippet's libraries to
use the beta content delivery network (CDN)
( https://appsforoffice.microsoft.com/lib/beta/hosted/office.js ) and the preview
type definitions @types/office-js-preview . Additionally, some preview APIs are
only accessible if you've signed up for the Microsoft 365 Insider program and
are running an Insider build of Office.

Save and share snippets


By default, snippets that you open in Script Lab will be saved to your browser cache. To
save a snippet permanently, you can export it to a GitHub gist . Create a secret gist to
save a snippet exclusively for your own use, or create a public gist if you plan to share it
with others.

Import snippets
You can import a snippet into Script Lab either by specifying the URL to the public
GitHub gist where the snippet YAML is stored or by pasting in the complete YAML for
the snippet. This feature may be useful in scenarios where someone else has shared
their snippet with you by either publishing it to a GitHub gist or providing their snippet's
YAML.
Supported clients
Script Lab is supported for Excel, Word, and PowerPoint on the following clients.

Office on Windows*
Office on Mac
Office on the web

Script Lab for Outlook is available on the following clients.

Outlook on Windows*
Outlook on Mac
Outlook on the web when using Chrome, Microsoft Edge, or Safari browsers

For more details on Script Lab for Outlook, see the related blog post .

) Important

* Script Lab no longer works with combinations of platform and Office version that
use the Trident (Internet Explorer) webview to host add-ins. This includes perpetual
versions of Office through Office 2019. For more information, see Browsers and
webview controls used by Office Add-ins.

Next steps
To use Script Lab in Excel, Word, or PowerPoint, install the Script Lab add-in from
AppSource.

To use Script Lab for Outlook, install the Script Lab for Outlook add-in from
AppSource.

You're welcome to expand the sample library in Script Lab by contributing new snippets
to the office-js-snippets GitHub repository.

When you're ready to create your first Office Add-in, try out the quick start for Excel,
Outlook, Word, OneNote, PowerPoint, or Project.

See also
Get Script Lab for Excel, Word, or Powerpoint
Get Script Lab for Outlook
Script Lab on GitHub
Developing Office Add-ins
Set up your development environment
Article • 03/09/2023

This guide helps you set up tools so you can create Office Add-ins by following our
quick starts or tutorials. If you already have these installed, you're ready to begin a quick
start, such as this Excel React quick start.

Get Microsoft 365


You need a Microsoft 365 account. You can get a free, 90-day renewable Microsoft 365
subscription that includes all Office apps by joining the Microsoft 365 developer
program.

Install the environment


There are two kinds of development environments to choose from. The scaffolding of
Office Add-in projects that is created in the two environments is different, so if multiple
people will be working on an add-in project, they must all use the same environment.

Node.js environment: Recommended. In this environment, your tools are installed


and run at a command line. The server-side of the web application part of the add-
in is written in JavaScript or TypeScript and is hosted in a Node.js runtime. There
are many helpful add-in development tools in this environment, such as an Office
linter and a bundler/task-runner called WebPack. The project creation and
scaffolding tool, Yo Office, is updated frequently.
Visual Studio environment: Choose this environment only if your development
computer is Windows, and you want to develop the server-side of the add-in with
a .NET based language and framework, such as ASP.NET. The add-in project
templates in Visual Studio aren't updated as frequently as those in the Node.js
environment. Client-side code can't be debugged with the built-in Visual Studio
debugger, but you can debug client-side code with your browser's development
tools. More information later on the Visual Studio environment tab.

7 Note

Visual Studio for Mac doesn't include the project scaffolding templates for Office
Add-ins, so if your development computer is a Mac, you should work with the
Node.js environment.
Select the tab for the environment you choose.

Node.js environment

The main tools to be installed are:

Node.js
npm
A code editor of your choice
Yo Office
The Office JavaScript linter

This guide assumes that you know how to use a command-line tool.

Install Node.js and npm


Node.js is a JavaScript runtime you use to develop modern Office Add-ins.

Install Node.js by downloading the latest recommended version from their


website . Follow the installation instructions for your operating system.

npm is an open source software registry from which to download the packages
used in developing Office Add-ins. It's usually installed automatically when you
install Node.js. To check if you already have npm installed and see the installed
version, run the following in the command line.

command line

npm -v

If, for any reason, you want to install it manually, run the following in the command
line.

command line

npm install npm -g

 Tip

You may wish to use a Node version manager to allow you to switch between
multiple versions of Node.js and npm, but this isn't strictly necessary. For
details on how to do this, see npm's instructions .
Install a code editor
You can use any code editor or IDE that supports client-side development to build
your web part, such as:

Visual Studio Code (recommended)


Atom
Webstorm

Install the Yeoman generator — Yo Office


The project creation and scaffolding tool is Yeoman generator for Office Add-ins,
affectionately known as Yo Office. You need to install the latest version of
Yeoman and Yo Office. To install these tools globally, run the following command
via the command prompt.

command line

npm install -g yo generator-office

Install and use the Office JavaScript linter


Microsoft provides a JavaScript linter to help you catch common errors when using
the Office JavaScript library. To install the linter, run the following two commands
(after you've installed Node.js and npm).

command line

npm install office-addin-lint --save-dev


npm install eslint-plugin-office-addins --save-dev

If you create an Office Add-in project with the Yeoman generator for Office Add-ins
tool, then the rest of the setup is done for you. Run the linter with the following
command either in the terminal of an editor, such as Visual Studio Code, or in a
command prompt. Problems found by the linter appear in the terminal or prompt,
and also appear directly in the code when you're using an editor that supports
linter messages, such as Visual Studio Code. (For information about installing the
Yeoman generator, see Yeoman generator for Office Add-ins.)

command line

npm run lint


If your add-in project was created another way, take the following steps.

1. In the root of the project, create a text file named .eslintrc.json, if there isn't
one already there. Be sure it has properties named plugins and extends , both
of type array. The plugins array should include "office-addins" and the
extends array should include "plugin:office-addins/recommended" . The

following is a simple example. Your .eslintrc.json file may have additional


properties and additional members of the two arrays.

JSON

{
"plugins": [
"office-addins"
],
"extends": [
"plugin:office-addins/recommended"
]
}

2. In the root of the project, open the package.json file and be sure that the
scripts array has the following member.

JSON

"lint": "office-addin-lint check",

3. Run the linter with the following command either in the terminal of an editor,
such as Visual Studio Code, or in a command prompt. Problems found by the
linter appear in the terminal or prompt, and also appear directly in the code
when you're using an editor that supports linter messages, such as Visual
Studio Code.

command line

npm run lint

Install Script Lab


Script Lab is a tool for quickly prototyping code that calls the Office JavaScript Library
APIs. Script Lab is itself an Office Add-in and can be installed from AppSource at Script
Lab . There's a version for Excel, PowerPoint, and Word, and a separate version for
Outlook. For information about how to use Script Lab, see Explore Office JavaScript API
using Script Lab.

Next steps
Try creating your own add-in or use Script Lab to try built-in samples.

Create an Office Add-in


You can quickly create a basic add-in for Excel, OneNote, Outlook, PowerPoint, Project,
or Word by completing a 5-minute quick start. If you've previously completed a quick
start and want to create a slightly more complex add-in, you should try the tutorial.

Explore the APIs with Script Lab


Explore the library of built-in samples in Script Lab to get a sense for the capabilities of
the Office JavaScript APIs.

See also
Core concepts for Office Add-ins
Developing Office Add-ins
Design Office Add-ins
Test and debug Office Add-ins
Publish Office Add-ins
Learn about the Microsoft 365 Developer Program
Best practices for developing Office
Add-ins
Article • 05/20/2023

Effective add-ins offer unique and compelling functionality that extends Office
applications in a visually appealing way. To create a great add-in, provide an engaging
first-time experience for your users, design a first-class UI experience, and optimize your
add-in's performance. Apply the best practices described in this article to create add-ins
that help your users complete their tasks quickly and efficiently.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).

Provide clear value


Create add-ins that help users complete tasks quickly and efficiently. Focus on
scenarios that make sense for Office applications. For example:
Make core authoring tasks faster and easier, with fewer interruptions.
Enable new scenarios within Office.
Embed complementary services within Office applications.
Improve the Office experience to enhance productivity.
Make sure that the value of your add-in is clear to users right away by creating an
engaging first-run experience.
Create an effective AppSource listing. Make the benefits of your add-in clear in
your title and description. Don't rely on your brand to communicate what your
add-in does.

Create an engaging first-run experience


Engage new users with a highly usable and intuitive first experience. Note that
users are still deciding whether to use or abandon an add-in after they download it
from the store.
Make the steps that the user needs to take to engage with your add-in clear. Use
videos, placemats, paging panels, or other resources to entice users.

Reinforce the value proposition of your add-in on launch, rather than just asking
users to sign in.

Provide teaching UI to guide users and make your UI personal.

If your content add-in binds to data in the user's document, include sample data
or a template to show users the data format to use.
Offer free trials. If your add-in requires a subscription, make some functionality
available without a subscription.

Make the sign-up experience simple. Prefill information, such as email and display
name, and skip email verifications.

Avoid pop-up windows. If you have to use them, guide the user to enable your
pop-up window.

For patterns that you can apply as you develop your first-run experience, see UX design
patterns for Office Add-ins.

Use add-in commands


Provide relevant UI entry points for your add-in by using add-in commands. For
details, including design best practices, see add-in commands.

Apply UX design principles


Ensure that the look and feel and functionality of your add-in complements the
Office experience. See Design the UI of Office Add-ins.

Favor content over chrome. Avoid superfluous UI elements that don't add value to
the user experience.
Keep users in control. Ensure that users understand important decisions, and can
easily reverse actions the add-in performs.

Use branding to inspire trust and orient users. Do not use branding to overwhelm
or advertise to users.

Avoid scrolling. Optimize for 1366 x 768 resolution.

Don't include unlicensed images.

Use clear and simple language in your add-in.

Account for accessibility - make your add-in easy for all users to interact with, and
accommodate assistive technologies such as screen readers.

Design for all platforms and input methods, including mouse/keyboard and touch.
Ensure that your UI is responsive to different form factors.

Optimize for touch


Use the Context.touchEnabled property to detect whether the Office application
that your add-in runs on is touch enabled.

7 Note

This property isn't supported in Outlook.

Ensure that all controls are appropriately sized for touch interaction. For example,
buttons have adequate touch targets, and input boxes are large enough for users
to enter input.

Don't rely on non-touch input methods like hover or right-click.

Ensure that your add-in works in both portrait and landscape modes. Be aware that
on touch devices, part of your add-in might be hidden by the soft keyboard.

Test your add-in on a real device by using sideloading.

7 Note

If you're using Fluent UI React for your design elements, many of these elements
are built into the design system.
Optimize and monitor add-in performance
Create the perception of fast UI responses. Your add-in should load in 500 ms or
less.

Ensure that all user interactions respond in under one second.

Provide loading indicators for long-running operations.

Use a content delivery network (CDN) to host images, resources, and common
libraries. Load as much as you can from one place.

Follow standard web practices to optimize your web page. In production, use only
minified versions of libraries. Only load resources that you need, and optimize how
resources are loaded.

If operations take time to execute, provide feedback to users. Note the thresholds
listed in the following table. For additional information, see Resource limits and
performance optimization for Office Add-ins.

Interaction Target Upper Human perception


class bound

Instant <=50 100 ms No noticeable delay.


ms

Fast 50-100 200 ms Minimally noticeable delay. No feedback necessary.


ms

Typical 100-300 500 ms Quick, but too slow to be described as fast. No


ms feedback necessary.

Responsive 300-500 1 second Not fast, but still feels responsive. No feedback
ms necessary.

Continuous >500 5 seconds Medium wait, no longer feels responsive. Might need
ms feedback.

Captive >500 10 Long, but not long enough to do something else.


ms seconds Might need feedback.

Extended >500 >10 Long enough to do something else while waiting.


ms seconds Might need feedback.

Long >5 >1 Users will certainly do something else.


running seconds minute

Monitor your service health, and use telemetry to monitor user success.
Minimize data exchanges between the add-in and the Office document. For more
information, see Avoid using the context.sync method in loops.

Market your add-in


Publish your add-in to AppSource and promote it from your website. Create an
effective AppSource listing.

Use succinct and descriptive add-in titles. Include no more than 128 characters.

Write short, compelling descriptions of your add-in. Answer the question "What
problem does this add-in solve?".

Convey the value proposition of your add-in in your title and description. Don't
rely on your brand.

Create a website to help users find and use your add-in.

Support older Microsoft webviews and Office


versions (recommended but not required)
See Support older Microsoft webviews and Office versions.

See also
Office Add-ins platform overview
Learn about the Microsoft 365 Developer Program
Office versions and requirement sets
Article • 08/15/2023

There are many versions of Office on several platforms, and they don't all support every
API in Office JavaScript API (Office.js). Office 2013 on Windows was the earliest version
of Office that supported Office Add-ins. You may not always have control over the
version of Office your users have installed. To handle this situation, we provide a system
called requirement sets to help you determine whether an Office application supports
the capabilities you need in your Office Add-in.

7 Note

Office runs across multiple platforms, including Windows, in a browser, Mac,


and iPad.
Examples of Office applications are Office products: Excel, Word, PowerPoint,
Outlook, OneNote, and so forth.
Office is available by a Microsoft 365 subscription or perpetual license. The
perpetual version is available by volume-licensing agreement or retail.
A requirement set is a named group of API members, for example, ExcelApi
1.5 , WordApi 1.3 , and so on.

How to check your Office version


To identify the Office version that you're using, from within an Office application, select
the File menu, and then choose Account. The version of Office appears in the Product
Information section. For example, the following screenshot indicates Office Version
1802 (Build 9026.1000).

7 Note
If your version of Office is different from this, see What version of Outlook do I
have? or About Office: What version of Office am I using? to understand how
to get this information for your version.

Deployment
How your add-in is deployed can affect your add-in's availability on the various
platforms and clients. To learn more about deployment options, see Deploy and publish
Office Add-ins.

Office requirement sets availability


Office Add-ins can use API requirement sets to determine whether the Office application
supports the API members that it need to use. Requirement set support varies by Office
application and the Office application version (see earlier section How to check your
Office version).

Some Office applications have their own API requirement sets. For example, the first
requirement set for the Excel API was ExcelApi 1.1 and the first requirement set for the
Word API was WordApi 1.1 . Since then, multiple new ExcelApi requirement sets and
WordApi requirement sets have been added to provide additional API functionality.

In addition, other functionality such as add-in commands (ribbon extensibility) and the
ability to launch dialog boxes (Dialog API) were added to the Common API. Add-in
commands and Dialog API requirement sets are examples of API sets that various Office
applications share in common.

An add-in can only use APIs in requirement sets that are supported by the version of
Office application where the add-in is running. To know exactly which requirement sets
are available for a specific Office application version, refer to the following application-
specific requirement set articles.

Excel JavaScript API requirement sets (ExcelApi)


OneNote JavaScript API requirement sets (OneNoteApi)
Outlook JavaScript API requirement sets (Mailbox)
PowerPoint JavaScript API requirement sets (PowerPointApi)
Word JavaScript API requirement sets (WordApi)

Some requirement sets contain APIs that can be used by several Office applications. For
information about these requirement sets, refer to the following articles.
Office common requirement sets
Add-in commands requirement sets
Dialog API requirement sets
Dialog Origin requirement sets
Identity API requirement sets
Image Coercion requirement sets
Keyboard Shortcuts requirement sets
Open Browser Window requirement sets
Ribbon API requirement sets
Shared Runtime requirement sets

The version number of a requirement set, such as the "1.1" in ExcelApi 1.1 , is relative to
the Office application. The version number of a given requirement set (e.g., ExcelApi
1.1 ) does not correspond to the version number of Office.js or to requirement sets for

other Office applications (e.g., Word, Outlook, etc.). Requirement sets for the different
Office applications are released at different rates. For example, ExcelApi 1.5 was
released before the WordApi 1.3 requirement set.

The Office JavaScript API library (Office.js) includes all requirement sets that are currently
available. While there is such a thing as requirement sets ExcelApi 1.3 and WordApi
1.3 , there is no Office.js 1.3 requirement set. The latest release of Office.js is

maintained as a single Office endpoint delivered via the content delivery network (CDN).
For more details around the Office.js CDN, including how versioning and backward
compatibility is handled, see Understanding the Office JavaScript API.

Specify Office applications and requirement


sets
There are various ways to specify which Office applications and requirement sets are
required by an add-in. For detailed information, see Specify Office applications and API
requirements

See also
Specify Office applications and API requirements
Install the latest version of Office
Overview of update channels for Microsoft 365 Apps
Reimagine productivity with Microsoft 365 and Microsoft Teams
Requirements for running Office Add-
ins
Article • 05/20/2023

This article describes the software and device requirements for running Office Add-ins.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).

For a high-level view of where Office Add-ins are currently supported, see Office client
application and platform availability for Office Add-ins.

Server requirements
To be able to install and run any Office Add-in, you first need to deploy the manifest and
webpage files for the UI and code of your add-in to the appropriate server locations.

For all types of add-ins (content, Outlook, and task pane add-ins and add-in
commands), you need to deploy your add-in's webpage files to a web server, or web
hosting service, such as Microsoft Azure.

While not strictly required in all add-in scenarios, using an HTTPS endpoint for your
add-in is strongly recommended. Add-ins that are not SSL-secured (HTTPS) generate
unsecure content warnings and errors during use. If you plan to run your add-in in
Office on the web or publish your add-in to AppSource, it must be SSL-secured. If your
add-in accesses external data and services, it should be SSL-secured to protect data in
transit. Self-signed certificates can be used for development and testing, so long as the
certificate is trusted on the local machine.

 Tip

When you develop and debug an add-in in Visual Studio, Visual Studio deploys and
runs your add-in's webpage files locally with IIS Express, and doesn't require an
additional web server.
For content and task pane add-ins, in the supported Office client applications - Excel,
PowerPoint, Project, or Word - you also need either an app catalog on SharePoint to
upload the add-in's XML manifest file, or you need to deploy the add-in using
Integrated Apps.

To test and run an Outlook add-in, the user's Outlook email account must reside on
Exchange 2013 or later, which is available through Microsoft 365, Exchange Online, or
through an on-premises installation. The user or administrator installs manifest files for
Outlook add-ins on that server.

7 Note

POP and IMAP email accounts in Outlook don't support Office Add-ins.

Client requirements: Windows desktop and


tablet
The following software is required for developing an Office Add-in for the supported
Office desktop clients or web clients that run on Windows-based desktop, laptop, or
tablet devices.

For Windows x86 and x64 desktops, and tablets such as Surface Pro:
The 32- or 64-bit version of Office 2013 or a later version, running on Windows
7 or a later version.
Excel 2013, Outlook 2013, PowerPoint 2013, Project Professional 2013, Project
2013 SP1, Word 2013, or a later version of the Office client, if you're testing or
running an Office Add-in specifically for one of these Office desktop clients.
Office desktop clients can be installed on premises or via Click-to-Run on the
client computer.

If you have a valid Microsoft 365 subscription and you don't have access to the
Office client, you can download and install the latest version of Office .

Microsoft Edge must be installed, but doesn't have to be the default browser. To
support Office Add-ins, the Office client that acts as host uses webview
components that are part of Microsoft Edge.

7 Note
Strictly speaking, it's possible to develop add-ins on a machine that has
Internet Explorer 11 (IE11) installed, but not Microsoft Edge. However, IE11
is used to run add-ins only on certain older combinations of Windows and
Office versions. See Browsers and webview controls used by Office Add-
ins for more details. We don't recommend using such old environments as
your primary add-in development environment. However, if you're likely to
have customers of your add-in that are working in these older
combinations, we recommend that you support the Trident webview that's
provided by Internet Explorer. For more information, see Support older
Microsoft webviews and Office versions.
Internet Explorer's Enhanced Security Configuration (ESC) must be turned
off for Office Web Add-ins to work. If you are using a Windows Server
computer as your client when developing add-ins, note that ESC is turned
on by default in Windows Server.

One of the following as the default browser: Internet Explorer 11, or the latest
version of Microsoft Edge, Chrome, Firefox, or Safari (Mac OS).

An HTML and JavaScript editor such as Visual Studio Code , Visual Studio and the
Microsoft Developer Tools , or non-Microsoft web development tool.

Client requirements: OS X desktop


Outlook on Mac, which is distributed as part of Microsoft 365, supports Outlook add-ins.
Running Outlook add-ins in Outlook on Mac has the same requirements as Outlook on
Mac itself: the operating system must be at least OS X v10.10 "Yosemite". Because
Outlook on Mac uses WebKit as a layout engine to render the add-in pages, there is no
additional browser dependency.

The following are the minimum client versions of Office on Mac that support Office
Add-ins.

Word version 15.18 (160109)


Excel version 15.19 (160206)
PowerPoint version 15.24 (160614)

Client requirements: Browser support for Office


web clients and SharePoint
Any browser, except Internet Explorer, that supports ECMAScript 5.1, HTML5, and CSS3,
such as Microsoft Edge, Chrome, Firefox, or Safari (Mac OS).

Client requirements: Non-Windows


smartphone and tablet
Specifically for Outlook running on smartphones and non-Windows tablet devices, the
following software is required for testing and running Outlook add-ins.

Office Device Operating Exchange account Mobile browser


application system

Outlook on - Android - Android On the latest update of Browser not


Android tablets 4.4 KitKat Microsoft 365 Apps for applicable. Use the
- Android or later business or Exchange Online native app for
smartphones Android.1

Outlook on - iPad tablets - iOS 11 or On the latest update of Browser not


iOS - iPhone later Microsoft 365 Apps for applicable. Use the
smartphones business or Exchange Online native app for iOS.1

Outlook on - iPad 2 or - iOS 5 or On Microsoft 365, Exchange - Microsoft Edge


the web later later Online - Chrome
(modern)2 - Android - Android - Firefox
tablets 4.4 KitKat - Safari
or later

Outlook on - iPhone 4 or - iOS 5 or On on-premises Exchange - Safari


the web later later Server 2013 or later3
(classic) - iPad 2 or
later
- iPod Touch
4 or later

7 Note

1 OWA for Android, OWA for iPad, and OWA for iPhone native apps have been
deprecated .

2
Modern Outlook on the web on iPhone and Android smartphones is no longer
required or available for testing Outlook add-ins.

3
Add-ins aren't supported in Outlook on Android, on iOS, and modern mobile web
with on-premises Exchange accounts.
 Tip

You can distinguish between classic and modern Outlook in a web browser by
checking your mailbox toolbar.

modern

classic

See also
Office Add-ins platform overview
Office client application and platform availability for Office Add-ins
Browsers and webview controls used by Office Add-ins
Install the latest version of Office
Article • 05/11/2023

New developer features, including those still in preview, are delivered first to subscribers
who opt in to get the latest builds of Office.

Opt in to getting the latest builds of Office


If you're a Microsoft 365 Family, Personal, or University subscriber, see Be a
Microsoft 365 Insider .
If you're a Microsoft 365 Apps for business customer, see Install the First Release
build for Microsoft 365 Apps for business customers .
If you're running Office on a Mac:
Start an Office application.
Select Check for Updates on the Help menu.
In the Microsoft AutoUpdate box, check the box to join the Microsoft 365
Insider program.

Get the latest build of Office


1. Download the Office Deployment Tool .

2. Run the tool. This extracts a setup.exe and configuration files.

3. Create a new file named configuration.xml and add the following XML.

XML

<!-- Office 365 client configuration file sample. To be used for Office
365 ProPlus apps,
Office 365 Business apps, Project Pro for Office 365 and Visio Pro for
Office 365.

For detailed information regarding configuration options visit:


http://aka.ms/ODT.
To use the configuration file be sure to remove the comments

The following sample allows you to download and install the 32 bit
version of the Office 365 ProPlus apps
and Visio Pro for Office 365 directly from the Office CDN using the
First Release Branch
settings -->

<Configuration>
<Add OfficeClientEdition="32" Branch="FirstReleaseCurrent">
<Product ID="O365ProPlusRetail">
<Language ID="en-us" />
</Product>
</Add>

<Updates Enabled="TRUE" />


<Display Level="None" AcceptEULA="TRUE" />
<Logging Level="Standard" Path="%temp%" />

<!-- <Updates Enabled="TRUE" Branch="Current" /> -->

<!-- <Display Level="None" AcceptEULA="TRUE" /> -->

<!-- <Property Name="AUTOACTIVATE" Value="1" /> -->

</Configuration>

4. Run the following command as an administrator.

command line

setup.exe /configure configuration.xml

7 Note

The command might take a long time to run without indicating progress.

When the installation process finishes, you will have the latest Office applications
installed. To verify that you have the latest build, go to File > Account from any Office
application. Under Office Updates, you'll see the (Office Insiders) label above the version
number.
Minimum Office builds for Office JavaScript API
requirement sets
Excel JavaScript API requirement sets
OneNote JavaScript API requirement sets
Outlook JavaScript API requirement sets
PowerPoint JavaScript API requirement sets
Word JavaScript API requirement sets
Dialog API requirement sets
Office Common API requirement sets
Browsers and webview controls used by
Office Add-ins
Article • 05/30/2023

Office Add-ins are web applications that are displayed using iframes when running in
Office on the web. In Office for desktop and mobile clients, Office Add-ins use an
embedded browser control (also known as a webview). Add-ins also need a JavaScript
engine to run the JavaScript. Both the embedded browser and the engine are supplied
by a browser installed on the user's computer. In this article, "webview" refers to the
combination of a webview control and a JavaScript engine.

Which webview is used depends on:

The computer's operating system.


Whether the add-in is running in Office on the web, in Office downloaded from a
Microsoft 365 subscription, or in perpetual Office 2013 or later.
Within the perpetual versions of Office on Windows, whether the add-in is running
in the "retail" or "volume-licensed" variation.

7 Note

This article assumes that the add-in is running in a document that is not protected
with Windows Information Protection (WIP). For WIP-protected documents, there
are some exceptions to the information in this article. For more information, see
WIP-protected documents.

) Important

Webviews from Internet Explorer and Microsoft Edge Legacy are still used in
Office Add-ins

Some combinations of platforms and Office versions, including volume-licensed


perpetual versions through Office 2019, still use the webview controls that come
with Internet Explorer 11 (called "Trident") and Microsoft Edge Legacy (called
"EdgeHTML") to host add-ins, as explained in this article. Internet Explorer 11 was
disabled in Windows 10 and Windows 11 in February 2023, and the UI for
launching it was removed; but it's still installed on those operating systems. So,
Trident and other functionality from Internet Explorer can still be called
programmatically by Office.
We recommend (but don't require) that you continue to support these
combinations, at least in a minimal way, by providing users of your add-in a
graceful failure message when your add-in is launched in one of these webviews.
Keep these additional points in mind:

Office on the web no longer opens in Internet Explorer or Microsoft Edge


Legacy. Consequently, AppSource doesn't test add-ins in Office on the web
on these browsers.
AppSource still tests for combinations of platform and Office desktop versions
that use Trident or EdgeHTML. However, it only issues a warning when the
add-in doesn't support these webviews; the add-in isn't rejected by
AppSource.
The Script Lab tool no longer supports Trident.

For more information about supporting Trident or EdgeHTML, including


configuring a graceful failure message on your add-in, see Support older Microsoft
webviews and Office versions.

The following sections specify which browser is used for the various platforms and
operating systems.

Non-Windows platforms
For these platforms, the platform alone determines the browser that is used.

OS Office version Browser

any Office on the web The browser in which Office is opened.


(But note that Office on the web will not open in Internet Explorer.
Attempting to do so opens Office on the web in Edge.)

Mac any Safari with WKWebView

iOS any Safari with WKWebView

Android any Chrome

) Important

Conditional Access is not supported for Office Add-ins on iOS or Android. Those
add-ins use the Safari-based WKWebView or the Android-based WebView, not an
Edge-based browser control.
Windows
An add-in running on Windows might use any of three different webviews:

WebView2, which is provided by Microsoft Edge (Chromium-based).


EdgeHTML, which is provided by Microsoft Edge Legacy.
Trident+, which is provided by Internet Explorer 11. The "+" on the end indicates
that Office Add-ins use additional functionality from Internet Explorer 11 that isn't
built into Trident itself.

Perpetual versions of Office on Windows


For perpetual versions of Office on Windows, the browser that is used is determined by
the Office version, whether the license is retail or volume-licensed, and whether the
Edge WebView2 (Chromium-based) is installed. The version of Windows doesn't matter,
but note that Office Web Add-ins aren't supported on versions earlier than Windows 7
and Office 2021 isn't supported on versions earlier than Windows 10.

To determine whether Office 2016 or Office 2019 is retail or volume-licensed, use the
format of the Office version and build number. (For Office 2013 and Office 2021, the
distinction between volume-licensed and retail doesn't matter.)

Retail: For both Office 2016 and 2019, the format is YYMM (xxxxx.xxxxxx) , ending
with two blocks of five digits; for example, 2206 (Build 15330.20264 .
Volume-licensed:
For Office 2016, the format is 16.0.xxxx.xxxxx , ending with two blocks of four
digits; for example, 16.0.5197.1000 .
For Office 2019, the format is 1808 (xxxxx.xxxxxx) , ending with two blocks of
five digits; for example, 1808 (Build 10388.20027) . Note that the year and
month is always 1808 .

Office Retail vs. Volume- WebView2 Browser


version licensed installed?

Office Doesn't matter Yes1 WebView2 (Microsoft Edge2 Chromium-


2021 based)

Office Retail Yes1 WebView2 (Microsoft Edge2 Chromium-


2016, based)
Office
2019
Office Retail vs. Volume- WebView2 Browser
version licensed installed?

Office Retail No EdgeHTML (Microsoft Edge Legacy)2, 3


2016, If Edge isn't installed, Trident+ (Internet
Office Explorer 11) is used.
2019

Office Volume-licensed Doesn't matter Trident+ (Internet Explorer 11)


2019

Office Volume-licensed Doesn't matter Trident+ (Internet Explorer 11)


2016

Office Doesn't matter Doesn't matter Trident+ (Internet Explorer 11)


2013

1
On Windows versions prior to Windows 11, the WebView2 control must be installed so
that Office can embed it. It's installed with perpetual Office 2021 or later; but it isn't
automatically installed with Microsoft Edge. If you have an earlier version of perpetual
Office, use the instructions for installing the control at Microsoft Edge WebView2 /
Embed web content ... with Microsoft Edge WebView2.

2
When you use either EdgeHTML or WebView2, the Windows Narrator (sometimes
called a "screen reader") reads the <title> tag in the page that opens in the task pane.
In Trident+, the Narrator reads the title bar of the task pane, which comes from the add-
in name that is specified in the add-in's manifest.

3 If your add-in uses an XML manifest and includes the <Runtimes> element in the
manifest or it uses the unified manifest and it includes an "extensions.runtimes.lifetime"
property, then it won't use EdgeHTML. If the conditions for using WebView2 are met,
then the add-in uses WebView2. Otherwise, it uses Trident+. For more information, see
Runtimes and Configure your Outlook add-in for event-based activation.

Microsoft 365 subscription versions of Office on Windows


For subscription Office on Windows, the browser that is used is determined by the
operating system, the Office version, and whether the WebView2 control is installed.

OS Office version WebView2 Browser


installed?
OS Office version WebView2 Browser
installed?

Windows 11, Microsoft 365 ver. >= 16.0.13530.204241 Yes2 WebView2


Windows 10, (Microsoft Edge3
Windows 8.1, Chromium-based)
Windows
Server 2022,
Windows
Server 2019,
Windows
Server 2016

Window 11, Microsoft 365 ver. >= 16.0.13530.204241 No EdgeHTML


Windows 10 (Microsoft Edge
ver. >= 1903 Legacy)3, 4

Windows 11, Microsoft 365 Doesn't EdgeHTML


Windows 10 ver. >= 16.0.11629 AND < 16.0.13530.204241 matter (Microsoft Edge
ver. >= 1903 Legacy)3, 4

Windows 11, Microsoft 365 ver. < 16.0.116291 Doesn't Trident+ (Internet
Windows 10 matter Explorer 11)
ver. >= 1903

Windows 10 Microsoft 365 No Trident+ (Internet


ver. < 1903, Explorer 11)
Windows 8.1

Windows 7 Microsoft 365 Doesn't Trident+ (Internet


matter Explorer 11)

1 See the update history page and how to find your Office client version and update
channel for more details.

2
On Windows versions prior to Windows 11, the WebView2 control must be installed so
that Office can embed it. It's installed with Microsoft 365, Version 2101 or later, but it
isn't automatically installed with Microsoft Edge. If you have an earlier version of
Microsoft 365, use the instructions for installing the control at Microsoft Edge
WebView2 / Embed web content ... with Microsoft Edge WebView2. On Microsoft 365
builds before 16.0.14326.xxxxx, you must also create the registry key
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\Win32WebView2 and
set its value to dword:00000001 .

3
When you use either EdgeHTML or WebView2, the Windows Narrator (sometimes
called a "screen reader") reads the <title> tag in the page that opens in the task pane.
In Trident+, the Narrator reads the title bar of the task pane, which comes from the add-
in name that is specified in the add-in's manifest.

4
If your add-in uses an XML manifest and includes the <Runtimes> element in the
manifest or it uses the unified manifest and it includes an "extensions.runtimes.lifetime"
property, then it won't use EdgeHTML. If the conditions for using WebView2 are met,
then the add-in uses WebView2. Otherwise, it uses Trident+. For more information, see
Runtimes and Configure your Outlook add-in for event-based activation.

Working with Trident+ (Internet Explorer 11)


Trident+ doesn't support JavaScript versions later than ES5. If any of your add-in's users
have platforms that use Trident+, then to use the syntax and features of ECMAScript
2015 or later, you have two options.

Write your code in ECMAScript 2015 (also called ES6) or later JavaScript, or in
TypeScript, and then compile your code to ES5 JavaScript using a compiler such as
babel or tsc .
Write in ECMAScript 2015 or later JavaScript, but also load a polyfill library such
as core-js that enables IE to run your code.

For more information about these options, see Support older Microsoft webviews and
Office versions.

Also, Trident+ doesn't support some HTML5 features such as media, recording, and
location. To learn more, see Determine the webview the add-in is running in at runtime.

Troubleshoot EdgeHTML and WebView2


(Microsoft Edge) issues

Service Workers aren't working


Office Add-ins don't support Service Workers when EdgeHTML is used. They're
supported with WebView2.

Scroll bar doesn't appear in task pane


By default, scroll bars in EdgeHTML and WebView2 are hidden until hovered over. To
ensure that the scroll bar is always visible, the CSS styling that applies to the <body>
element of the pages in the task pane should include the -ms-overflow-style property
and it should be set to scrollbar .

When debugging with the Microsoft Edge DevTools, the


add-in crashes or reloads
Setting breakpoints in the Microsoft Edge DevTools for EdgeHTML can cause Office to
think that the add-in is hung. It will automatically reload the add-in when this happens.
To prevent this, add the following Registry key and value to the development computer:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\16.0\Wef]"AlertInterval"=dword:00000
000 .

When the add-in tries to open, get "ADD-IN ERROR We


can't open this add-in from the localhost" error
One known cause is that EdgeHTML requires that localhost be given a loopback
exemption on the development computer. Follow the instructions at Cannot open add-
in from localhost.

Get errors trying to download a PDF file


Directly downloading blobs as PDF files in an add-in isn't supported with EdgeHTML or
WebView2. The workaround is to create a simple web application that downloads blobs
as PDF files. In your add-in, call the Office.context.ui.openBrowserWindow(url) method
and pass the URL of the web application. This will open the web application in a browser
window outside of Office.

WIP-protected documents
Add-ins running in a WIP-protected document never use WebView2 (Microsoft Edge
Chromium-based). In the sections Perpetual versions of Office on Windows and
Microsoft 365 subscription versions of Office on Windows earlier in this article,
substitute EdgeHTML (Microsoft Edge Legacy) for WebView2 (Microsoft Edge
Chromium-based) wherever the latter appears.

To determine if a document is WIP-protected, follow these steps:

1. Open the file.


2. Select the File tab on the ribbon.
3. Select Info.
4. In the upper left of the Info page, just below the file name, a WIP-enable
document will have briefcase icon followed by Managed by Work (…).

See also
Requirements for Running Office Add-ins
Runtimes in Office Add-ins
Runtimes in Office Add-ins
Article • 07/21/2023

Office Add-ins execute in runtimes embedded in Office. As an interpreted language,


JavaScript must run in a JavaScript runtime. Node.js and modern browsers are
examples of such runtimes.

Types of runtimes
There are two types of runtimes used by Office Add-ins:

JavaScript-only runtime: A JavaScript engine supplemented with support for


WebSockets , Full CORS (Cross-Origin Resource Sharing) , and client-side
storage of data. It doesn't support local storage or cookies.
Browser runtime: Includes all the features of a JavaScript-only runtime and adds
support for local storage , a rendering engine that renders HTML, and cookies.

Details about these types are later in this article at JavaScript-only runtime and Browser
runtime.

The following table shows which possible features of an add-in use each type of
runtime.

Type of Add-in feature


runtime

JavaScript-only Excel custom functions


(except when the runtime is shared or the add-in is running in Office on the
web)

Outlook event-based task


(only when the add-in is running in Outlook on Windows)

Outlook integrated spam reporting feature (preview)


(only when the add-in is running in Outlook on Windows)

browser task pane

dialog

function command

Excel custom functions


(when the runtime is shared or the add-in is running in Office on the web)
Type of Add-in feature
runtime

Outlook event-based task


(when the add-in is running in Outlook on Mac or Outlook on the web)

The following table shows the same information organized by which type of runtime is
used for the various possible features of an add-in.

Add-in feature Type of runtime on Type of runtime on Type of runtime


Windows Mac on the web

Excel custom functions JavaScript-only JavaScript-only browser


(but browser when the (but browser when the
runtime is shared) runtime is shared)

Outlook event-based tasks JavaScript-only browser browser

Outlook integrated spam JavaScript-only Not available Not available


reporting feature (preview)

task pane browser browser browser

dialog browser browser browser

function command browser browser browser

In Office on the web, everything always runs in a browser type runtime. With one
exception, everything in an add-in on the web runs in the same browser process: the
browser process in which the user has opened Office on the web. The exception is when
a dialog is opened with a call of Office.ui.displayDialogAsync and the
DialogOptions.displayInIFrame option is not passed and set to true . When the option
isn't passed (so it has the default false value), the dialog opens in its own process. The
same principle applies to the OfficeRuntime.displayWebDialog method and the
OfficeRuntime.DisplayWebDialogOptions.displayInIFrame option.

When an add-in is running on a platform other than the web, the following principles
apply.

A dialog runs in its own runtime process.

An Outlook event-based or spam-reporting add-in runs in its own runtime process.

7 Note

The event-based activation and integrated spam reporting features in


Outlook must use the same runtime. Multiple runtimes aren't currently
supported in Outlook.

By default, task panes, function commands, and Excel custom functions each run in
their own runtime process. However, for some Office host applications, the add-in
manifest can be configured so that any two, or all three, can run in the same
runtime. See Shared runtime.

Depending on the host Office application and the features used in the add-in, there may
be many runtimes in an add-in. Each usually will run in its own process but not
necessarily simultaneously. The following are examples.

A PowerPoint or Word add-in that doesn't share any runtimes, and includes the
following features, has as many as three runtimes.

A task pane

A function command

A dialog (A dialog can be launched from either the task pane or the function
command.)

7 Note

It's not a good practice to have multiple dialogs open simultaneously, but
if the add-in enables the user to open one from the task pane and another
from the function command at the same time, this add-in would have four
runtimes. A task pane, and a given invocation of a function command can
have only one open dialog at a time; but if the function command is
invoked multiple times, a new dialog is opened on top of its predecessor
with each invocation, so there could be many runtimes. The remainder of
this list ignores the possibility of multiple open dialogs.

An Excel add-in that doesn't share any runtimes, and includes the following
features, has as many as four runtimes.
A task pane
A function command
A custom function
A dialog (A dialog can be launched from either the task pane, the function
command, or a custom function.)

An Excel add-in with the same features and is configured to share the same
runtime across the task pane, function command, and custom function, has two
runtimes. A shared runtime can open only one dialog at a time.

An Excel add-in with the same features, except that it has no dialog, and is
configured to share the same runtime across the task pane, function command,
and custom function, has one runtime.

An Outlook add-in that has the following features has as many as four runtimes.
Shared runtimes aren't supported in Outlook.
A task pane
A function command
An event-based task or integrated spam reporting feature
A dialog (A dialog can be launched from either the task pane or the function
command, but not from an event-based task.)

Share data across runtimes

7 Note

If you know that your add-in will only be used in Office on the web and that it
won't open any dialogs with the displayInIFrame option set to true , then
you can ignore this section. Since everything in your add-in runs in the same
runtime process, you can just use global variables to share data between
features.
As noted above in Types of runtimes, the type of runtime used by a feature
varies partly by platform. It's a good practice to avoid having add-in code that
branches based on platform, so the guidance in this section recommends
techniques that will work cross-platform. There is only one case, noted below,
in which branching code is required.

For Excel, PowerPoint, and Word add-ins, use a Shared runtime when any two or more
features, except dialogs, need to share data. In Outlook, or scenarios where sharing a
runtime isn't feasible, you need alternative methods. The parts of the add-in that are in
separate runtime processes don't share global data automatically and are treated by the
add-in's web application server as separate sessions, so Window.sessionStorage can't
be used to share data between them. The following guidance assumes that you're not
using a shared runtime.

Pass data between a dialog and its parent task pane, function command, or custom
function by using the Office.ui.messageParent and Dialog.messageChild methods.
7 Note

The OfficeRuntime.storage methods can't be called in a dialog, so this isn't an


option for sharing data between a dialog and another runtime.

To share data between a task pane and a function command, store data in
Window.localStorage , which is shared across all runtimes that access the same
specific origin .

7 Note

LocalStorage isn't accessible in a JavaScript-only runtime and, thus, it isn't


available in Excel custom functions. It also can't be used to share data with an
Outlook event-based tasks (since those tasks use a JavaScript-only runtime on
some platforms).

 Tip

Data in Window.localStorage persists between sessions of the add-in and is


shared by add-ins with the same origin. Both of these characteristics are often
undesirable for an add-in.
To ensure that each session of a given add-in starts fresh call the
Window.localStorage.clear method when the add-in starts.
To allow some stored values to persist, but reinitialize other values, use
Window.localStorage.setItem when the add-in starts for each item that
should be reset to an initial value.
To delete an item entirely, call Window.localStorage.removeItem .

To share data between an Excel custom function and any other runtime, use
OfficeRuntime.storage.

To share data between an Outlook event-based task and a task pane or function
command, you must branch your code by the value of the Office.context.platform
property.
When the value is PC (Windows), store and retrieve data using the
Office.sessionData APIs.
When the value is Mac , use Window.localStorage as described earlier in this list.

Other ways to share data include the following:


Store shared data in an online database that is accessible to all the runtimes.
Store shared data in a cookie for the add-in's domain to share it between browser
runtimes. JavaScript-only runtimes don't support cookies.

For more information, see Persist add-in state and settings and Get and set add-in
metadata for an Outlook add-in.

JavaScript-only runtime
The JavaScript-only runtime that is used in Office Add-ins is a modification of an open
source runtime originally created for React Native . It contains a JavaScript engine
supplemented with support for WebSockets , Full CORS (Cross-Origin Resource
Sharing) , and OfficeRuntime.storage. It doesn't have a rendering engine, and it
doesn't support cookies or local storage .

This type of runtime is used in event-based and spam-reporting add-ins in Outlook on


Windows only and in Excel custom functions except when the custom functions are
sharing a runtime.

When used for an Excel custom function, the runtime starts up when either the
worksheet recalculates or the custom function calculates. It doesn't shut down until
the workbook is closed.

When used in an Outlook event-based or spam-reporting add-in, the runtime


starts up when the event occurs. It ends when the first of the following occurs.
The event handler calls the completed method of its event parameter.
Five minutes have elapsed since the triggering event.
The user changes focus from the window where the event was triggered, such
as a message compose window (only applies to event-based add-ins).

7 Note

The event-based activation and integrated spam reporting features in


Outlook must use the same runtime. Multiple runtimes aren't currently
supported in Outlook.

A JavaScript-runtime uses less memory and starts up faster than a browser runtime, but
has fewer features.

Browser runtime
Office Add-ins use a different browser type runtime depending on the platform in which
Office is running (web, Mac, or Windows), and on the version and build of Windows and
Office. For example, if the user is running Office on the web in a FireFox browser, then
the Firefox runtime is used. If the user is running Office on Mac, then the Safari runtime
is used. If the user is running Office on Windows, then either an Edge or Internet
Explorer provides the runtime, depending on the version of Windows and Office. Details
can be found in Browsers and webview controls used by Office Add-ins.

All of these runtimes include an HTML rendering engine and provide support for
WebSockets , Full CORS (Cross-Origin Resource Sharing) , and local storage , and
cookies.

A browser runtime lifespan varies depending on the feature that it implements and on
whether it's being shared or not.

When an add-in with a task pane is launched, a browser runtime starts, unless it's a
shared runtime that is already running. If it's a shared runtime, it shuts down when
the document is closed. If it isn't a shared runtime, it shuts down when the task
pane is closed.

When a dialog is opened, a browser runtime starts. It shuts down when the dialog
is closed.

When a function command is executed (which happens when a user selects its
button or menu item), a browser runtime starts, unless it's a shared runtime that is
already running. If it's a shared runtime, it shuts down when the document is
closed. If it isn't a shared runtime, it shuts down when the first of the following
occurs.
The function command calls the completed method of its event parameter.
Five minutes have elapsed since the triggering event. (If a dialog was opened in
the function command and it's still open when the parent runtime times out, the
dialog runtime stays running until the dialog is closed.)

When an Excel custom function is using a shared runtime, then a browser-type


runtime starts when the custom function calculates if the shared runtime hasn't
already started for some other reason. It shuts down when the document is closed.

7 Note

When a runtime is being shared, it's possible for your code to close the task pane
without shutting down the add-in. See Show or hide the task pane of your Office
Add-in for more information.
A browser runtime has more features than a JavaScript-only runtime, but starts up
slower and uses more memory.

Shared runtime
A "shared runtime" isn't a type of runtime. It refers to a browser-type runtime that's
being shared by features of the add-in that would otherwise each have their own
runtime. Specifically, you have the option of configuring the add-in's task pane and
function commands to share a runtime. In an Excel add-in, you can also configure
custom functions to share the runtime of a task pane or function command or both.
When you do this, the custom functions are running in a browser-type runtime, instead
of a JavaScript-only runtime as it otherwise would. See Configure your add-in to use a
shared runtime for information about the benefits and limitations of sharing runtimes
and instructions for configuring the add-in to use a shared runtime. In brief, the
JavaScript-only runtime uses less memory and starts up faster, but has fewer features.

7 Note

You can share runtimes only in Excel, PowerPoint, and Word.


You can't configure a dialog to share a runtime. Each dialog always has its
own, except when the dialog is launched in Office on the web with the
displayInIFrame option set to true .

A shared runtime never uses the original Microsoft Edge WebView


(EdgeHTML) runtime. If the conditions for using Microsoft Edge with
WebView2 (Chromium-based) are met (as specified in Browsers and webview
controls used by Office Add-ins), then that runtime is used. Otherwise, the
Internet Explorer 11 runtime is used.
Support older Microsoft webviews and
Office versions
Article • 07/25/2023

Office Add-ins are web applications that are displayed inside iframes when running on
Office on the web. Office Add-ins are displayed using an embedded browser control
(also known as a webview) when running in Office on Windows or Office on the Mac.
The embedded browser controls are supplied by the operating system or by a browser
installed on the user's computer.

) Important

Webviews from Internet Explorer and Microsoft Edge Legacy are still used in
Office Add-ins

Some combinations of platforms and Office versions, including volume-licensed


perpetual versions through Office 2019, still use the webview controls that come
with Internet Explorer 11 (called "Trident") and Microsoft Edge Legacy (called
"EdgeHTML") to host add-ins, as explained in Browsers and webview controls used
by Office Add-ins. Internet Explorer 11 was disabled in Windows 10 and Windows
11 in February 2023, and the UI for launching it was removed; but it's still installed
on with those operating systems. So, Trident and other functionality from Internet
Explorer can still be called programmatically by Office.

We recommend (but don't require) that you support these combinations, at least in
a minimal way, by providing users of your add-in a graceful failure message when
your add-in is launched in these webviews. Keep these additional points in mind:

Office on the web no longer opens in Internet Explorer or Microsoft Edge


Legacy. Consequently, AppSource doesn't test add-ins in Office on the web
on these browsers.
AppSource still tests for combinations of platform and Office desktop versions
that use Trident or EdgeHTML. However, it only issues a warning when the
add-in doesn't support these webviews; the add-in isn't rejected by
AppSource.
The Script Lab tool no longer supports Trident.

If you plan to support older versions of Windows and Office, your add-in must work in
the embeddable browser controls used by these versions. For example, browser controls
based on Internet Explorer 11 (IE11) or Microsoft Edge Legacy (EdgeHTML-based). For
information about which combinations of Windows and Office use these browser
controls, see Browsers and webview controls used by Office Add-ins.

Determine the webview the add-in is running


in at runtime
Your add-in can discover the webview that it's running in by reading the
window.navigator.userAgent property. This enables the add-in to either provide an
alternate experience or gracefully fail. The following is an example that determines
whether the add-in is running in Trident or EdgeHTML.

JavaScript

if (navigator.userAgent.indexOf("Trident") !== -1) {


/*
Trident is the webview in use. Do one of the following:
1. Provide an alternate add-in experience that doesn't use any of
the HTML5
features that aren't supported in Trident (Internet Explorer 11).
2. Enable the add-in to gracefully fail by adding a message to the
UI that
says something similar to:
"This add-in won't run in your version of Office. Please upgrade
either to
perpetual Office 2021 (or later) or to a Microsoft 365 account."
*/
} else if (navigator.userAgent.indexOf("Edge") !== -1) {
/*
EdgeHTML is the browser in use. Do one of the following:
1. Provide an alternate add-in experience that's supported in
EdgeHTML (Microsoft Edge Legacy).
2. Enable the add-in to gracefully fail by adding a message to the
UI that
says something similar to:
"This add-in won't run in your version of Office. Please upgrade
either to
perpetual Office 2021 (or later) or to a Microsoft 365 account."
*/
} else {
/*
A webview other than Trident or EdgeHTML is in use.
Provide a full-featured version of the add-in here.
*/
}

7 Note
Microsoft Edge (Chromium) returns edg/ followed by one or more version digits
and zero or more . separators as the user agent; for example, edg/76.0.167.1 .
Note that the e isn't present at the end of name! It's "edg", not "edge".

This JavaScript should be as early in the add-in startup process as possible. The
following is an example of an add-in home page that advises users to upgrade Office
when Trident is detected.

HTML

<!doctype html>
<html lang="en" data-framework="typescript">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contoso Task Pane Add-in</title>

<!-- Office JavaScript API -->


<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
</head>

<body>
<div id="main">
<!--
The add-in UI is here.
-->
</div>

<!--
The script below makes the following div display if the
webview is Trident, and hides the regular div.
-->
<div id="tridentmessage" style="display: none; padding: 10;">
This add-in will not run in your version of Office. Please upgrade
either to
perpetual Office 2021 (or later) or to a Microsoft 365 account.
</div>
<script>
if (navigator.userAgent.indexOf("Trident") !== -1) {
var tridentMessage = document.getElementById("tridentmessage");
var normalUI = document.getElementById("main");
tridentMessage.style.display = "block";
normalUI.style.display = "none";
}
</script>
</body>
</html>
) Important

It's not always a good practice to read the userAgent property. Be sure you're
familiar with the article, Browser detection using the user agent , including the
recommendations and alternatives to reading userAgent . In particular, if you're
providing an alternate add-in experience to support the use of Trident, consider
using feature detection instead of testing for the user agent. But if you're just
providing a notification that the add-in doesn't work in Trident, as in this case,
using userAgent is appropriate.

As of July 24th, 2023, the non-English versions of the article are all out-of-date to
varying degrees; some are over 12 years out-of-date.

As of the same date, the text and tables in the section Which part of the user
agent contains the information you are looking for? of the English version of
the article no longer mention Trident or Internet Explorer 11. In the table for
Browser Name and version, the row for Internet Explorer 11 was the following:

Engine Must contain Must not contain

Internet Explorer 11 Trident/7.0; .*rv:xyz

In the table for Rendering engine, the row for Trident was the following:

Engin Must Comment


e contain

Tride Trident/xy Internet Explorer places this fragment in the comments section of the
nt z User-Agent string.

Review webview and Office version support


information
For more information on how to support specific webviews and Office versions, select
the applicable tab.

Trident (Internet Explorer)

) Important
Trident doesn't support some HTML5 features such as media, recording, and
location. If your add-in must support Trident, then you must either design the
add-in to avoid these unsupported features or the add-in must detect when
Trident is being used and provide an alternate experience that doesn't use the
unsupported features. For more information, see Determine the webview the
add-in is running in at runtime.

Support for recent versions of JavaScript


The JavaScript engine associated with Trident doesn't support JavaScript versions
later than ES5. If you want to use the syntax and features of ECMAScript 2015 or
later, or TypeScript, you must use a transpiler or a polyfill or both. A transpiler will
convert syntax or operators, such as the => operator, that is unknown in ES5, to
ES5. A polyfill replaces methods, types, and classes from ECMAScript 2015 (aka, ES6)
or later into equivalent functionality in ES5.

Use a transpiler
You can write your code in either TypeScript or modern JavaScript and then
transpile it at build-time into ES5 JavaScript. The resulting ES5 files are what you
upload to your add-in's web application.

There are two popular transpilers. Both of them can work with source files that are
TypeScript or post-ES5 JavaScript. They also work with React files (.jsx and .tsx).

babel
tsc

See the documentation for either of them for information about installing and
configuring the transpiler in your add-in project. We recommend that you use a
task runner, such as Grunt or WebPack to automate the transpilation. For a
sample add-in that uses tsc, see Office Add-in Microsoft Graph React . For a
sample that uses babel, see Offline Storage Add-in .

7 Note

If you're using Visual Studio (not Visual Studio Code), tsc is probably easiest to
use. You can install support for it with a nuget package. For more information,
see JavaScript and TypeScript in Visual Studio 2019. To use babel with Visual
Studio, create a build script or use the Task Runner Explorer in Visual Studio
with tools like the WebPack Task Runner or NPM Task Runner .

Use a polyfill
A polyfill is earlier-version JavaScript that duplicates functionality from more
recent versions of JavaScript. The polyfill works in webviews that don't support the
later JavaScript versions. For example, the string method startsWith wasn't part of
the ES5 version of JavaScript, and so it won't run in Trident (Internet Explorer 11).
There are polyfill libraries, written in ES5, that define and implement a startsWith
method. We recommend the core-js polyfill library.

To use a polyfill library, load it like any other JavaScript file or module. For example,
you can use a <script> tag in the add-in's home page HTML file (for example
<script src="/js/core-js.js"></script> ), or you can use an import statement in a

JavaScript file (for example, import 'core-js'; ). When the JavaScript engine sees a
method like startsWith , it will first look to see if there's a method of that name
built into the language. If there is, it will call the native method. If, and only if, the
method isn't built-in, the engine will look in all loaded files for it. So, the polyfilled
version isn't used in browsers that support the native version.

Importing the entire core-js library will import all core-js features. You can also
import only the polyfills that your Office Add-in requires. For instructions about
how to do this, see CommonJS APIs . The core-js library has most of the polyfills
that you need. There are a few exceptions detailed in the Missing Polyfills section
of the core-js documentation. For example, it doesn't support fetch , but you can
use the fetch polyfill.

For a sample add-in that uses core.js, see Word Add-in Angular2 StyleChecker .

Test an add-in on Trident (Internet Explorer)


See Trident testing.

See also
Browsers and webview controls used by Office Add-ins
ECMAScript 6 compatibility table
Can I use... Support tables for HTML5, CSS3, etc
Develop Office Add-ins
Article • 05/30/2023

 Tip

Please review Office Add-ins platform overview before reading this article.

All Office Add-ins are built upon the Office Add-ins platform. For any add-in you build,
you'll need to understand important concepts like application and platform availability,
Office JavaScript API programming patterns, how to specify an add-in's settings and
capabilities in the manifest file, how to design the UI and user experience, and more.
Core development concepts like these are covered here in the Development lifecycle >
Develop section of the documentation. Review the information here before exploring
the application-specific documentation that corresponds to the add-in you're building
(for example, Excel).

Create an Office Add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins,
Visual Studio, or Teams Toolkit.

Yeoman generator
The Yeoman generator for Office Add-ins can be used to create a Node.js Office Add-in
project that can be managed with Visual Studio Code or any other editor. The generator
can create Office Add-ins for any of the following:

Excel
OneNote
Outlook
PowerPoint
Project
Word
Excel custom functions

Create your project using HTML, CSS and JavaScript (or TypeScript), or using React. If
you choose React, you can choose between JavaScript and Typescript as well. For more
information about creating add-ins with the generator, see Yeoman generator for Office
Add-ins.
Visual Studio
Visual Studio can be used to create Office Add-ins for Excel, Outlook, Word, and
PowerPoint. An Office Add-in project gets created as part of a Visual Studio solution and
uses HTML, CSS, and JavaScript. For more information about creating add-ins with
Visual Studio, see Develop Office Add-ins with Visual Studio.

7 Note

Although it's possible to create Office Add-ins using Visual Studio, using the
Yeoman generator provides a better developer experience in some notable ways.

The Yeoman generator provides a wider range of options for project types,
frameworks, and languages than Visual Studio does for Office Add-in projects.

Project templates in the Yeoman generator are updated more frequently than
project templates in Visual Studio.

Teams Toolkit
The Teams Toolkit can be used to create almost any kind of Teams App, a term which
includes all extensions of Microsoft 365, including those that don't extend the Teams
application. For details about creating an add-in, see Create Office Add-in projects with
Teams Toolkit.

Understand the two parts of an Office Add-in


An Office Add-in consists of two parts.

The add-in manifest that defines the settings and capabilities of the add-in.

The web application that defines the UI and functionality of add-in components
such as task panes, content add-ins, and dialog boxes.

The web application uses the Office JavaScript API to interact with content in the Office
document where the add-in is running. Your add-in can also do other things that web
applications typically do, like call external web services, facilitate user authentication,
and more.

Define an add-in's settings and capabilities


An Office Add-in's manifest defines the settings and capabilities of the add-in. You'll
configure the manifest to specify things such as:

Metadata that describes the add-in (for example, ID, version, description, display
name, default locale).
Office applications where the add-in will run.
Permissions that the add-in requires.
How the add-in integrates with Office, including any custom UI that the add-in
creates (for example, a custom tab or custom ribbon buttons).
Location of images that the add-in uses for branding and command iconography.
Dimensions of the add-in (for example, dimensions for content add-ins, requested
height for Outlook add-ins).
Rules that specify when the add-in activates in the context of a message or
appointment (for Outlook add-ins only).
Keyboard shortcuts (for Excel only).

For detailed information about the manifest, see Office Add-ins manifest.

Interact with content in an Office document


An Office Add-in can use the Office JavaScript APIs to interact with content in the Office
document where the add-in is running.

Access the Office JavaScript API library


The Office JavaScript API library can be accessed via the Office JS content delivery
network (CDN) at: https://appsforoffice.microsoft.com/lib/1/hosted/office.js . To use
Office JavaScript APIs within any of your add-in's web pages, you must reference the
CDN in a <script> tag in the <head> tag of the page.

HTML

<head>
...
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>
</head>

7 Note

To use preview APIs, reference the preview version of the Office JavaScript API
library on the CDN:
https://appsforoffice.microsoft.com/lib/beta/hosted/office.js .

For more information about accessing the Office JavaScript API library, including how to
get IntelliSense, see Referencing the Office JavaScript API library from its content
delivery network (CDN).

API models

The Office JavaScript API includes two distinct models:

Application-specific APIs provide strongly-typed objects that can be used to


interact with objects that are native to a specific Office application. For example,
you can use the Excel JavaScript APIs to access worksheets, ranges, tables, charts,
and more. Application-specific APIs are currently available for the following Office
applications.
Excel
OneNote
PowerPoint
Word

This API model uses promises and allows you to specify multiple operations in
each request you send to the Office application. Batching operations in this
manner can significantly improve add-in performance in Office applications on the
web. Application-specific APIs were introduced with Office 2016.

7 Note

There's also an application-specific API for Visio, but you can use it only in
SharePoint Online pages to interact with Visio diagrams that have been
embedded in the page. Office Web Add-ins are not supported in Visio.

See Using the application-specific API model to learn more about this API model.

Common APIs can be used to access features such as UI, dialogs, and client
settings that are common across multiple types of Office applications. This API
model uses callbacks , which allow you to specify only one operation in each
request sent to the Office application. Common APIs were introduced with Office
2013 and can be used to interact with any supported Office applications. For
details about the Common API object model, which includes APIs for interacting
with Outlook, PowerPoint, and Project, see Common JavaScript API object model.
7 Note

Custom functions without a shared runtime run in a JavaScript-only runtime that


prioritizes execution of calculations. These functions use a slightly different
programming model.

API requirement sets

Requirement sets are named groups of API members. Requirement sets can be specific
to Office applications, such as the ExcelApi 1.7 requirement set (a set of APIs that can
only be used in Excel), or common to multiple applications, such as the DialogApi 1.1
requirement set (a set of APIs that can be used in any Office application that supports
the Dialog API).

Your add-in can use requirement sets to determine whether the Office application
supports the API members that it needs to use. For more information about this, see
Specify Office applications and API requirements.

Requirement set support varies by Office application, version, and platform. For detailed
information about the platforms, requirement sets, and Common APIs that each Office
application supports, see Office client application and platform availability for Office
Add-ins.

Explore APIs with Script Lab


Script Lab is an add-in that enables you to explore the Office JavaScript API and run
code snippets while you're working in an Office program such as Excel or Word. It's
available for free via AppSource and is a useful tool to include in your development
toolkit as you prototype and verify the functionality you want in your add-in. In Script
Lab, you can access a library of built-in samples to quickly try out APIs or even use a
sample as the starting point for your own code.

The following one-minute video shows Script Lab in action.


For more information about Script Lab, see Explore Office JavaScript APIs using Script
Lab.

Extend the Office UI


An Office Add-in can extend the Office UI by using add-in commands and HTML
containers such as task panes, content add-ins, or dialog boxes.

Add-in commands can be used to add a custom tab, custom buttons and menus
to the default ribbon in Office, or to extend the default context menu that appears
when users right-click text in an Office document or an object in Excel. When users
select an add-in command, they initiate the task that the add-in command
specifies, such as running JavaScript code, opening a task pane, or launching a
dialog box.

HTML containers like task panes, content add-ins, and dialog boxes can be used to
display custom UI and expose additional functionality within an Office application.
The content and functionality of each task pane, content add-in, or dialog box
derives from a web page that you specify. Those web pages can use the Office
JavaScript API to interact with content in the Office document where the add-in is
running, and can also do other things that web pages typically do, like call external
web services, facilitate user authentication, and more.

The following image shows an add-in command on the ribbon, a task pane to the right
of the document, and a dialog box or content add-in over the document.
For more information about extending the Office UI and designing the add-in's UX, see
Office UI elements for Office Add-ins.

Next steps
This article has outlined the different ways to create Office Add-ins, introduced the ways
that an add-in can extend the Office UI, described the API sets, and introduced Script
Lab as a valuable tool for exploring Office JavaScript APIs and prototyping add-in
functionality. Now that you've explored this introductory information, consider
continuing your Office Add-ins journey along the following paths.

Create an Office Add-in


You can quickly create a basic add-in for Excel, OneNote, Outlook, PowerPoint, Project,
or Word by completing a 5-minute quick start. If you've previously completed a quick
start and want to create a slightly more complex add-in, you should try the tutorial.
Learn more
Learn more about developing, testing, and publishing Office Add-ins by exploring this
documentation.

 Tip

For any add-in that you build, you'll use information in the Development lifecycle
section of this documentation, along with information in the application-specific
section that corresponds to the type of add-in you're building (for example, Excel).

See also
Office Add-ins platform overview
Learn about the Microsoft 365 Developer Program
Design Office Add-ins
Test and debug Office Add-ins
Publish Office Add-ins
Understanding the Office JavaScript API
Article • 05/18/2023

An Office Add-in can use the Office JavaScript APIs to interact with content in the Office
document where the add-in is running.

Accessing the Office JavaScript API library


The Office JavaScript API library can be accessed via the Office JS content delivery
network (CDN) at: https://appsforoffice.microsoft.com/lib/1/hosted/office.js . To use
Office JavaScript APIs within any of your add-in's web pages, you must reference the
CDN in a <script> tag in the <head> tag of the page.

HTML

<head>
...
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>
</head>

7 Note

To use preview APIs, reference the preview version of the Office JavaScript API
library on the CDN:
https://appsforoffice.microsoft.com/lib/beta/hosted/office.js .

For more information about accessing the Office JavaScript API library, including how to
get IntelliSense, see Referencing the Office JavaScript API library from its content
delivery network (CDN).

API models
The Office JavaScript API includes two distinct models:

Application-specific APIs provide strongly-typed objects that can be used to


interact with objects that are native to a specific Office application. For example,
you can use the Excel JavaScript APIs to access worksheets, ranges, tables, charts,
and more. Application-specific APIs are currently available for the following Office
applications.
Excel
OneNote
PowerPoint
Word

This API model uses promises and allows you to specify multiple operations in
each request you send to the Office application. Batching operations in this
manner can significantly improve add-in performance in Office applications on the
web. Application-specific APIs were introduced with Office 2016.

7 Note

There's also an application-specific API for Visio, but you can use it only in
SharePoint Online pages to interact with Visio diagrams that have been
embedded in the page. Office Web Add-ins are not supported in Visio.

See Using the application-specific API model to learn more about this API model.

Common APIs can be used to access features such as UI, dialogs, and client
settings that are common across multiple types of Office applications. This API
model uses callbacks , which allow you to specify only one operation in each
request sent to the Office application. Common APIs were introduced with Office
2013 and can be used to interact with any supported Office applications. For
details about the Common API object model, which includes APIs for interacting
with Outlook, PowerPoint, and Project, see Common JavaScript API object model.

7 Note

Custom functions without a shared runtime run in a JavaScript-only runtime that


prioritizes execution of calculations. These functions use a slightly different
programming model.

API requirement sets


Requirement sets are named groups of API members. Requirement sets can be specific
to Office applications, such as the ExcelApi 1.7 requirement set (a set of APIs that can
only be used in Excel), or common to multiple applications, such as the DialogApi 1.1
requirement set (a set of APIs that can be used in any Office application that supports
the Dialog API).
Your add-in can use requirement sets to determine whether the Office application
supports the API members that it needs to use. For more information about this, see
Specify Office applications and API requirements.

Requirement set support varies by Office application, version, and platform. For detailed
information about the platforms, requirement sets, and Common APIs that each Office
application supports, see Office client application and platform availability for Office
Add-ins.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).

See also
Office JavaScript API reference
Loading the DOM and runtime environment
Referencing the Office JavaScript API library
Initialize your Office Add-in
Runtimes in Office Add-ins
Application-specific API model
Article • 06/27/2023

This article describes how to use the API model for building add-ins in Excel, OneNote,
PowerPoint, Visio, and Word. It introduces core concepts that are fundamental to using
the promise-based APIs.

7 Note

This model isn't supported by Outlook or Project clients. Use the Common API
model to work with those applications. For full platform availability notes, see
Office client application and platform availability for Office Add-ins.

 Tip

The examples in this page use the Excel JavaScript APIs, but the concepts also apply
to OneNote, PowerPoint, Visio, and Word JavaScript APIs. For complete code
samples that show how you could use these and other concepts in various Office
applications, see Office Add-in code samples.

Asynchronous nature of the promise-based


APIs
Office Add-ins are websites which appear inside a webview control within Office
applications, such as Excel. This control is embedded within the Office application on
desktop-based platforms, such as Office on Windows, and runs inside an HTML iframe in
Office on the web. Due to performance considerations, the Office.js APIs cannot interact
synchronously with the Office applications across all platforms. Therefore, the sync()
API call in Office.js returns a Promise that is resolved when the Office application
completes the requested read or write actions. Also, you can queue up multiple actions,
such as setting properties or invoking methods, and run them as a batch of commands
with a single call to sync() , rather than sending a separate request for each action. The
following sections describe how to accomplish this using the run() and sync() APIs.

*.run function
Excel.run , OneNote.run , PowerPoint.run , and Word.run execute a function that specifies

the actions to perform against Excel, Word, and OneNote. *.run automatically creates a
request context that you can use to interact with Office objects. When *.run completes,
a promise is resolved, and any objects that were allocated at runtime are automatically
released.

The following example shows how to use Excel.run . The same pattern is also used with
OneNote, PowerPoint, Visio, and Word.

JavaScript

Excel.run(function (context) {
// Add your Excel JS API calls here that will be batched and sent to the
workbook.
console.log('Your code goes here.');
}).catch(function (error) {
// Catch and log any errors that occur within `Excel.run`.
console.log('error: ' + error);
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});

Request context
The Office application and your add-in run in different processes. Since they use
different runtime environments, add-ins require a RequestContext object in order to
connect your add-in to objects in Office such as worksheets, ranges, paragraphs, and
tables. This RequestContext object is provided as an argument when calling *.run .

Proxy objects
The Office JavaScript objects that you declare and use with the promise-based APIs are
proxy objects. Any methods that you invoke or properties that you set or load on proxy
objects are simply added to a queue of pending commands. When you call the sync()
method on the request context (for example, context.sync() ), the queued commands
are dispatched to the Office application and run. These APIs are fundamentally batch-
centric. You can queue up as many changes as you wish on the request context, and
then call the sync() method to run the batch of queued commands.

For example, the following code snippet declares the local JavaScript Excel.Range object,
selectedRange , to reference a selected range in the Excel workbook, and then sets some
properties on that object. The selectedRange object is a proxy object, so the properties
that are set and the method that is invoked on that object will not be reflected in the
Excel document until your add-in calls context.sync() .

JavaScript

const selectedRange = context.workbook.getSelectedRange();


selectedRange.format.fill.color = "#4472C4";
selectedRange.format.font.color = "white";
selectedRange.format.autofitColumns();

Performance tip: Minimize the number of proxy objects


created
Avoid repeatedly creating the same proxy object. Instead, if you need the same proxy
object for more than one operation, create it once and assign it to a variable, then use
that variable in your code.

JavaScript

// BAD: Repeated calls to .getRange() to create the same proxy object.


worksheet.getRange("A1").format.fill.color = "red";
worksheet.getRange("A1").numberFormat = "0.00%";
worksheet.getRange("A1").values = [[1]];

// GOOD: Create the range proxy object once and assign to a variable.
const range = worksheet.getRange("A1");
range.format.fill.color = "red";
range.numberFormat = "0.00%";
range.values = [[1]];

// ALSO GOOD: Use a "set" method to immediately set all the properties
// without even needing to create a variable!
worksheet.getRange("A1").set({
numberFormat: [["0.00%"]],
values: [[1]],
format: {
fill: {
color: "red"
}
}
});

sync()
Calling the sync() method on the request context synchronizes the state between proxy
objects and objects in the Office document. The sync() method runs any commands
that are queued on the request context and retrieves values for any properties that
should be loaded on the proxy objects. The sync() method executes asynchronously
and returns a Promise , which is resolved when the sync() method completes.

The following example shows a batch function that defines a local JavaScript proxy
object ( selectedRange ), loads a property of that object, and then uses the JavaScript
promises pattern to call context.sync() to synchronize the state between proxy objects
and objects in the Excel document.

JavaScript

await Excel.run(async (context) => {


const selectedRange = context.workbook.getSelectedRange();
selectedRange.load('address');
await context.sync();
console.log('The selected range is: ' + selectedRange.address);
});

In the previous example, selectedRange is set and its address property is loaded when
context.sync() is called.

Since sync() is an asynchronous operation, you should always return the Promise
object to ensure the sync() operation completes before the script continues to run. If
you're using TypeScript or ES6+ JavaScript, you can await the context.sync() call
instead of returning the promise.

Performance tip: Minimize the number of sync calls


In the Excel JavaScript API, sync() is the only asynchronous operation, and it can be
slow under some circumstances, especially for Excel on the web. To optimize
performance, minimize the number of calls to sync() by queueing up as many changes
as possible before calling it. For more information about optimizing performance with
sync() , see Avoid using the context.sync method in loops.

load()
Before you can read the properties of a proxy object, you must explicitly load the
properties to populate the proxy object with data from the Office document, and then
call context.sync() . For example, if you create a proxy object to reference a selected
range, and then want to read the selected range's address property, you need to load
the address property before you can read it. To request properties of a proxy object be
loaded, call the load() method on the object and specify the properties to load. The
following example shows the Range.address property being loaded for myRange .

JavaScript

await Excel.run(async (context) => {


const sheetName = 'Sheet1';
const rangeAddress = 'A1:B2';
const myRange =
context.workbook.worksheets.getItem(sheetName).getRange(rangeAddress);

myRange.load('address');
await context.sync();

console.log (myRange.address); // ok
//console.log (myRange.values); // not ok as it was not loaded

console.log('done');
});

7 Note

If you're only calling methods or setting properties on a proxy object, you don't
need to call the load() method. The load() method is only required when you
want to read properties on a proxy object.

Just like requests to set properties or invoke methods on proxy objects, requests to load
properties on proxy objects get added to the queue of pending commands on the
request context, which will run the next time you call the sync() method. You can queue
up as many load() calls on the request context as necessary.

Scalar and navigation properties

There are two categories of properties: scalar and navigational. Scalar properties are
assignable types such as strings, integers, and JSON structs. Navigation properties are
read-only objects and collections of objects that have their fields assigned, instead of
directly assigning the property. For example, name and position members on the
Excel.Worksheet object are scalar properties, whereas protection and tables are
navigation properties.

Your add-in can use navigational properties as a path to load specific scalar properties.
The following code queues up a load command for the name of the font used by an
Excel.Range object, without loading any other information.

JavaScript

someRange.load("format/font/name")

You can also set the scalar properties of a navigation property by traversing the path.
For example, you could set the font size for an Excel.Range by using
someRange.format.font.size = 10; . You don't need to load the property before you set
it.

Please be aware that some of the properties under an object may have the same name
as another object. For example, format is a property under the Excel.Range object, but
format itself is an object as well. So, if you make a call such as range.load("format") ,

this is equivalent to range.format.load() (an undesirable empty load() statement). To


avoid this, your code should only load the "leaf nodes" in an object tree.

Calling load without parameters (not recommended)


If you call the load() method on an object (or collection) without specifying any
parameters, all scalar properties of the object or the collection's objects will be loaded.
Loading unneeded data will slow down your add-in. You should always explicitly specify
which properties to load.

) Important

The amount of data returned by a parameter-less load statement can exceed the
size limits of the service. To reduce the risks to older add-ins, some properties are
not returned by load without explicitly requesting them. The following properties
are excluded from such load operations.

Excel.Range.numberFormatCategories

ClientResult
Methods in the promise-based APIs that return primitive types have a similar pattern to
the load / sync paradigm. As an example, Excel.TableCollection.getCount gets the
number of tables in the collection. getCount returns a ClientResult<number> , meaning
the value property in the returned ClientResult is a number. Your script can't access that
value until context.sync() is called.
The following code gets the total number of tables in an Excel workbook and logs that
number to the console.

JavaScript

const tableCount = context.workbook.tables.getCount();

// This sync call implicitly loads tableCount.value.


// Any other ClientResult values are loaded too.
await context.sync();

// Trying to log the value before calling sync would throw an error.
console.log (tableCount.value);

set()
Setting properties on an object with nested navigation properties can be cumbersome.
As an alternative to setting individual properties using navigation paths as described
above, you can use the object.set() method that is available on objects in the
promise-based JavaScript APIs. With this method, you can set multiple properties of an
object at once by passing either another object of the same Office.js type or a JavaScript
object with properties that are structured like the properties of the object on which the
method is called.

The following code sample sets several format properties of a range by calling the
set() method and passing in a JavaScript object with property names and types that
mirror the structure of properties in the Range object. This example assumes that there
is data in range B2:E2.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B2:E2");
range.set({
format: {
fill: {
color: '#4472C4'
},
font: {
name: 'Verdana',
color: 'white'
}
}
});
range.format.autofitColumns();
await context.sync();
});

Some properties cannot be set directly


Some properties cannot be set, despite being writable. These properties are part of a
parent property that must be set as a single object. This is because that parent property
relies on the subproperties having specific, logical relationships. These parent properties
must be set using object literal notation to set the entire object, instead of setting that
object's individual subproperties. One example of this is found in PageLayout. The zoom
property must be set with a single PageLayoutZoomOptions object, as shown here.

JavaScript

// PageLayout.zoom.scale must be set by assigning PageLayout.zoom to a


PageLayoutZoomOptions object.
sheet.pageLayout.zoom = { scale: 200 };

In the previous example, you would not be able to directly assign zoom a value:
sheet.pageLayout.zoom.scale = 200; . That statement throws an error because zoom is

not loaded. Even if zoom were to be loaded, the set of scale will not take effect. All
context operations happen on zoom , refreshing the proxy object in the add-in and
overwriting locally set values.

This behavior differs from navigational properties like Range.format. Properties of


format can be set using object navigation, as shown here.

JavaScript

// This will set the font size on the range during the next
`content.sync()`.
range.format.font.size = 10;

You can identify a property that cannot have its subproperties directly set by checking
its read-only modifier. All read-only properties can have their non-read-only
subproperties directly set. Writeable properties like PageLayout.zoom must be set with an
object at that level. In summary:

Read-only property: Subproperties can be set through navigation.


Writable property: Subproperties cannot be set through navigation (must be set as
part of the initial parent object assignment).
*OrNullObject methods and properties
Some accessor methods and properties throw an exception when the desired object
doesn't exist. For example, if you attempt to get an Excel worksheet by specifying a
worksheet name that isn't in the workbook, the getItem() method throws an
ItemNotFound exception. The application-specific libraries provide a way for your code to

test for the existence of document entities without requiring exception handling code.
This is accomplished by using the *OrNullObject variations of methods and properties.
These variations return an object whose isNullObject property is set to true , if the
specified item doesn't exist, rather than throwing an exception.

For example, you can call the getItemOrNullObject() method on a collection such as
Worksheets to retrieve an item from the collection. The getItemOrNullObject() method
returns the specified item if it exists; otherwise, it returns an object whose isNullObject
property is set to true . Your code can then evaluate this property to determine whether
the object exists.

7 Note

The *OrNullObject variations do not ever return the JavaScript value null . They
return ordinary Office proxy objects. If the the entity that the object represents
does not exist then the isNullObject property of the object is set to true . Do not
test the returned object for nullity or falsity. It is never null , false , or undefined .

The following code sample attempts to retrieve an Excel worksheet named "Data" by
using the getItemOrNullObject() method. If a worksheet with that name does not exist,
a new sheet is created. Note that the code does not load the isNullObject property.
Office automatically loads this property when context.sync is called, so you do not
need to explicitly load it with something like dataSheet.load('isNullObject') .

JavaScript

await Excel.run(async (context) => {


let dataSheet = context.workbook.worksheets.getItemOrNullObject("Data");

await context.sync();

if (dataSheet.isNullObject) {
dataSheet = context.workbook.worksheets.add("Data");
}

// Set `dataSheet` to be the second worksheet in the workbook.


dataSheet.position = 1;
});

Application undo stack


When an application-specific API is processed, the undo stack of the application is
cleared. This means that you can't undo changes made prior to any action done by an
add-in (unless that add-in only uses Common APIs or doesn't interact with the file). The
same is true for changes made by the add-in.

See also
Common JavaScript API object model
Resource limits and performance optimization for Office Add-ins
Common JavaScript API object model
Article • 03/21/2023

) Important

This article applies to the Common APIs, the Office JavaScript API model that was
introduced with Office 2013. These APIs include features such as UI, dialogs, and
client settings that are common across multiple types of Office applications.
Outlook add-ins exclusively use Common APIs, especially the subset of APIs
exposed through the Mailbox object.

You should only use Common APIs for scenarios that aren't supported by
application-specific APIs. To learn when to use Common APIs instead of
application-specific APIs, see Understanding the Office JavaScript API.

Office JavaScript APIs give access to the Office client application's underlying
functionality. Most of this access goes through a few important objects. The Context
object gives access to the runtime environment after initialization. The Document object
gives the user control over an Excel, PowerPoint, or Word document. The Mailbox object
gives an Outlook add-in access to messages, appointments, and user profiles.
Understanding the relationships between these high-level objects is the foundation of
an Office Add-in.

Context object
Applies to: All add-in types

When an add-in is initialized, it has many different objects that it can interact with in the
runtime environment. The add-in's runtime context is reflected in the API by the Context
object. The Context is the main object that provides access to the most important
objects of the API, such as the Document and Mailbox objects, which in turn provide
access to document and mailbox content.

For example, in task pane or content add-ins, you can use the document property of the
Context object to access the properties and methods of the Document object to
interact with the content of Word documents, Excel worksheets, or Project schedules.
Similarly, in Outlook add-ins, you can use the mailbox property of the Context object to
access the properties and methods of the Mailbox object to interact with the message,
meeting request, or appointment content.
The Context object also provides access to the contentLanguage and displayLanguage
properties that let you determine the locale (language) used in the document or item, or
by the Office application. The roamingSettings property lets you access the members of
the RoamingSettings object, which stores settings specific to your add-in for individual
users' mailboxes. Finally, the Context object provides a ui property that enables your
add-in to launch pop-up dialogs.

Document object
Applies to: Content and task pane add-in types

To interact with document data in Excel, PowerPoint, and Word, the API provides the
Document object. You can use Document object members to access data from the
following ways.

Read and write to active selections in the form of text, contiguous cells (matrices),
or tables.

Tabular data (matrices or tables).

Bindings (created with the "add" methods of the Bindings object).

Custom XML parts (only for Word).

Settings or add-in state persisted per add-in on the document.

You can also use the Document object to interact with data in Project documents. The
Project-specific functionality of the API is documented in the members
ProjectDocument abstract class. For more information about creating task pane add-ins
for Project, see Task pane add-ins for Project.

All these forms of data access start from an instance of the abstract Document object.

You can access an instance of the Document object when the task pane or content add-in
is initialized by using the document property of the Context object. The Document object
defines common data access methods shared across Word and Excel documents, and
also provides access to the CustomXmlParts object for Word documents.

The Document object supports four ways for developers to access document contents.

Selection-based access

Binding-based access
Custom XML part-based access (Word only)

Entire document-based access (PowerPoint and Word only)

To help you understand how selection- and binding-based data access methods work,
we will first explain how the data-access APIs provide consistent data access across
different Office applications.

Consistent data access across Office applications


Applies to: Content and task pane add-in types

To create extensions that seamlessly work across different Office documents, the Office
JavaScript API abstracts away the particularities of each Office application through
common data types and the ability to coerce different document contents into three
common data types.

Common data types


In both selection-based and binding-based data access, document contents are
exposed through data types that are common across all the supported Office
applications. Three main data types are supported.

Data Description Host application support


type

Text Provides a string In Excel, Project, and PowerPoint, only plain text is
representation of the data in supported. In Word, three text formats are supported:
the selection or binding. plain text, HTML, and Office Open XML (OOXML). When
text is selected in a cell in Excel, selection-based
methods read and write to the entire contents of the
cell, even if only a portion of the text is selected in the
cell. When text is selected in Word and PowerPoint,
selection-based methods read and write only to the run
of characters that are selected. Project and PowerPoint
support only selection-based data access.
Data Description Host application support
type

Matrix Provides the data in the Matrix data access is supported only in Excel and Word.
selection or binding as a two
dimensional Array, which in
JavaScript is implemented as
an array of arrays. For
example, two rows of string
values in two columns would
be [['a', 'b'], ['c', 'd']] ,
and a single column of three
rows would be [['a'],
['b'], ['c']] .

Table Provides the data in the Table data access is supported only in Excel and Word.
selection or binding as a
TableData object. The
TableData object exposes the
data through the headers
and rows properties.

Data type coercion

The data access methods on the Document and Binding objects support specifying the
desired data type using the coercionType parameter of these methods, and
corresponding CoercionType enumeration values. Regardless of the actual shape of the
binding, the different Office applications support the common data types by trying to
coerce the data into the requested data type. For example, if a Word table or paragraph
is selected, the developer can specify to read it as plain text, HTML, Office Open XML, or
a table, and the API implementation handles the necessary transformations and data
conversions.

 Tip

When should you use the matrix versus table coercionType for data access? If
you need your tabular data to grow dynamically when rows and columns are
added, and you must work with table headers, you should use the table data type
(by specifying the coercionType parameter of a Document or Binding object data
access method as "table" or Office.CoercionType.Table ). Adding rows and
columns within the data structure is supported in both table and matrix data, but
appending rows and columns is supported only for table data. If you aren't
planning on adding rows and columns, and your data doesn't require header
functionality, then you should use the matrix data type (by specifying the
coercionType parameter of the data access method as "matrix" or
Office.CoercionType.Matrix ), which provides a simpler model of interacting with

the data.

If the data can't be coerced to the specified type, the AsyncResult.status property in the
callback returns "failed" , and you can use the AsyncResult.error property to access an
Error object with information about why the method call failed.

Work with selections using the Document


object
The Document object exposes methods that let you to read and write to the user's
current selection in a "get and set" fashion. To do that, the Document object provides the
getSelectedDataAsync and setSelectedDataAsync methods.

For code examples that demonstrate how to perform tasks with selections, see Read and
write data to the active selection in a document or spreadsheet.

Work with bindings using the Bindings and


Binding objects
Binding-based data access enables content and task pane add-ins to consistently access
a particular region of a document or spreadsheet through an identifier associated with a
binding. The add-in first needs to establish the binding by calling one of the methods
that associates a portion of the document with a unique identifier:
addFromPromptAsync, addFromSelectionAsync, or addFromNamedItemAsync. After the
binding is established, the add-in can use the provided identifier to access the data
contained in the associated region of the document or spreadsheet. Creating bindings
provides the following value to your add-in.

Permits access to common data structures across supported Office applications,


such as: tables, ranges, or text (a contiguous run of characters).

Enables read/write operations without requiring the user to make a selection.

Establishes a relationship between the add-in and the data in the document.
Bindings are persisted in the document, and can be accessed at a later time.
Establishing a binding also allows you to subscribe to data and selection change events
that are scoped to that particular region of the document or spreadsheet. This means
that the add-in is only notified of changes that happen within the bound region as
opposed to general changes across the whole document or spreadsheet.

The Bindings object exposes a getAllAsync method that gives access to the set of all
bindings established on the document or spreadsheet. An individual binding can be
accessed by its ID using either the Bindings.getBindingByIdAsync method or
Office.select function. You can establish new bindings as well as remove existing ones by
using one of the following methods of the Bindings object: addFromSelectionAsync,
addFromPromptAsync, addFromNamedItemAsync, or releaseByIdAsync.

There are three different types of bindings that you specify with the bindingType
parameter when you create a binding with the addFromSelectionAsync ,
addFromPromptAsync or addFromNamedItemAsync methods.

Binding Description Host application support


type

Text Binds to a region of the document that can be In Word, most contiguous
binding represented as text. selections are valid, while in
Excel only single cell selections
can be the target of a text
binding. In Excel, only plain
text is supported. In Word,
three formats are supported:
plain text, HTML, and Open
XML for Office.

Matrix Binds to a fixed region of a document that contains In Excel, any contiguous
binding tabular data without headers. Data in a matrix selection of cells can be used
binding is written or read as a two dimensional Array, to establish a matrix binding. In
which in JavaScript is implemented as an array of Word, only tables support
arrays. For example, two rows of string values in two matrix binding.
columns can be written or read as [['a', 'b'],
['c', 'd']] , and a single column of three rows can
be written or read as [['a'], ['b'], ['c']] .

Table Binds to a region of a document that contains a table Any Excel or Word table can be
binding with headers. Data in a table binding is written or the basis for a table binding.
read as a TableData object. The TableData object After you establish a table
exposes the data through the headers and rows binding, each new row or
properties. column a user adds to the
table is automatically included
in the binding.
After a binding is created by using one of the three "add" methods of the Bindings
object, you can work with the binding's data and properties by using the methods of the
corresponding object: MatrixBinding, TableBinding, or TextBinding. All three of these
objects inherit the getDataAsync and setDataAsync methods of the Binding object that
enable to you interact with the bound data.

For code examples that demonstrate how to perform tasks with bindings, see Bind to
regions in a document or spreadsheet.

Work with custom XML parts using the


CustomXmlParts and CustomXmlPart objects
Applies to: Task pane add-ins for Word

The CustomXmlParts and CustomXmlPart objects of the API provide access to custom
XML parts in Word documents, which enable XML-driven manipulation of the contents
of the document. For demonstrations of working with the CustomXmlParts and
CustomXmlPart objects, see the Word-add-in-Work-with-custom-XML-parts code
sample.

Work with the entire document using the


getFileAsync method
Applies to: Task pane add-ins for Word and PowerPoint

The Document.getFileAsync method and members of the File and Slice objects to
provide functionality for getting entire Word and PowerPoint document files in slices
(chunks) of up to 4 MB at a time. For more information, see Get the whole document
from an add-in for PowerPoint or Word.

Mailbox object
Applies to: Outlook add-ins

Outlook add-ins primarily use a subset of the API exposed through the Mailbox object.
To access the objects and members specifically for use in Outlook add-ins, such as the
Item object, use the mailbox property of the Context object to access the Mailbox
object, as shown in the following line of code.

JavaScript
// Access the Item object.
const item = Office.context.mailbox.item;

Additionally, Outlook add-ins can use the following objects.

Office object: for initialization.

Context object: for access to content and display language properties.

RoamingSettings object: for saving Outlook add-in-specific custom settings to the


user's mailbox where the add-in is installed.

For information about using JavaScript in Outlook add-ins, see Outlook add-ins.
Asynchronous programming in Office
Add-ins
Article • 04/11/2023

) Important

This article applies to the Common APIs, the Office JavaScript API model that was
introduced with Office 2013. These APIs include features such as UI, dialogs, and
client settings that are common across multiple types of Office applications.
Outlook add-ins exclusively use Common APIs, especially the subset of APIs
exposed through the Mailbox object.

You should only use Common APIs for scenarios that aren't supported by
application-specific APIs. To learn when to use Common APIs instead of
application-specific APIs, see Understanding the Office JavaScript API.

Why does the Office Add-ins API use asynchronous programming? Because JavaScript is
a single-threaded language, if script invokes a long-running synchronous process, all
subsequent script execution will be blocked until that process completes. Because
certain operations against Office web clients (but rich clients as well) could block
execution if they are run synchronously, most of the Office JavaScript APIs are designed
to execute asynchronously. This makes sure that Office Add-ins are responsive and fast.
It also frequently requires you to write callback functions when working with these
asynchronous methods.

The names of all asynchronous methods in the API end with "Async", such as the
Document.getSelectedDataAsync , Binding.getDataAsync , or
Item.loadCustomPropertiesAsync methods. When an "Async" method is called, it

executes immediately and any subsequent script execution can continue. The optional
callback function you pass to an "Async" method executes as soon as the data or
requested operation is ready. This generally occurs promptly, but there can be a slight
delay before it returns.

The following diagram shows the flow of execution for a call to an "Async" method that
reads the data the user selected in a document open in the server-based Word or Excel.
At the point when the "Async" call is made, the JavaScript execution thread is free to
perform any additional client-side processing (although none are shown in the
diagram). When the "Async" method returns, the callback resumes execution on the
thread, and the add-in can the access data, do something with it, and display the result.
The same asynchronous execution pattern holds when working with Office client
applications on Windows or Mac.

Figure 1. Asynchronous programming execution flow

Support for this asynchronous design in both rich and web clients is part of the "write
once-run cross-platform" design goals of the Office Add-ins development model. For
example, you can create a content or task pane add-in with a single code base that will
run in both Excel on Windows and Excel on the web.

Write the callback function for an "Async"


method
The callback function you pass as the callback argument to an "Async" method must
declare a single parameter that the add-in runtime will use to provide access to an
AsyncResult object when the callback function executes. You can write:

An anonymous function that must be written and passed directly in line with the
call to the "Async" method as the callback parameter of the "Async" method.

A named function, passing the name of that function as the callback parameter of
an "Async" method.

An anonymous function is useful if you are only going to use its code once - because it
has no name, you can't reference it in another part of your code. A named function is
useful if you want to reuse the callback function for more than one "Async" method.
Write an anonymous callback function
The following anonymous callback function declares a single parameter named result
that retrieves data from the AsyncResult.value property when the callback returns.

JavaScript

function (result) {
write('Selected data: ' + result.value);
}

The following example shows how to pass this anonymous callback function in line in
the context of a full "Async" method call to the Document.getSelectedDataAsync method.

The first coercionType argument, Office.CoercionType.Text , specifies to return the


selected data as a string of text.

The second callback argument is the anonymous function passed in-line to the
method. When the function executes, it uses the result parameter to access the
value property of the AsyncResult object to display the data selected by the user
in the document.

JavaScript

Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
function (result) {
write('Selected data: ' + result.value);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

You can also use the parameter of your callback function to access other properties of
the AsyncResult object. Use the AsyncResult.status property to determine if the call
succeeded or failed. If your call fails you can use the AsyncResult.error property to
access an Error object for error information.

For more information about using the getSelectedDataAsync method, see Read and
write data to the active selection in a document or spreadsheet.

Write a named callback function


Alternatively, you can write a named function and pass its name to the callback
parameter of an "Async" method. For example, the previous example can be rewritten to
pass a function named writeDataCallback as the callback parameter like this.

JavaScript

Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
writeDataCallback);

// Callback to write the selected data to the add-in UI.


function writeDataCallback(result) {
write('Selected data: ' + result.value);
}

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

Differences in what's returned to the


AsyncResult.value property
The asyncContext , status , and error properties of the AsyncResult object return the
same kinds of information to the callback function passed to all "Async" methods.
However, what's returned to the AsyncResult.value property varies depending on the
functionality of the "Async" method.

For example, the addHandlerAsync methods (of the Binding, CustomXmlPart, Document,
RoamingSettings, and Settings objects) are used to add event handler functions to the
items represented by these objects. You can access the AsyncResult.value property
from the callback function you pass to any of the addHandlerAsync methods, but since
no data or object is being accessed when you add an event handler, the value property
always returns undefined if you attempt to access it.

On the other hand, if you call the Document.getSelectedDataAsync method, it returns the
data the user selected in the document to the AsyncResult.value property in the
callback. Or, if you call the Bindings.getAllAsync method, it returns an array of all of the
Binding objects in the document. And, if you call the Bindings.getByIdAsync method, it

returns a single Binding object.

For a description of what's returned to the AsyncResult.value property for an Async


method, see the "Callback value" section of that method's reference topic. For a
summary of all of the objects that provide Async methods, see the table at the bottom
of the AsyncResult object topic.

Asynchronous programming patterns


The Office JavaScript API supports two kinds of asynchronous programming patterns.

Using nested callbacks


Using the promises pattern

Asynchronous programming with callback functions frequently requires you to nest the
returned result of one callback within two or more callbacks. If you need to do so, you
can use nested callbacks from all "Async" methods of the API.

Using nested callbacks is a programming pattern familiar to most JavaScript developers,


but code with deeply nested callbacks can be difficult to read and understand. As an
alternative to nested callbacks, the Office JavaScript API also supports an
implementation of the promises pattern.

7 Note

In the current version of the Office JavaScript API, built-in support for the promises
pattern only works with code for bindings in Excel spreadsheets and Word
documents. However, you can wrap other functions that have callbacks inside your
own custom Promise-returning function. For more information, see Wrap Common
APIs in Promise-returning functions.

Asynchronous programming using nested callback


functions
Frequently, you need to perform two or more asynchronous operations to complete a
task. To accomplish that, you can nest one "Async" call inside another.

The following code example nests two asynchronous calls.

First, the Bindings.getByIdAsync method is called to access a binding in the


document named "MyBinding". The AsyncResult object returned to the result
parameter of that callback provides access to the specified binding object from the
AsyncResult.value property.

Then, the binding object accessed from the first result parameter is used to call
the Binding.getDataAsync method.
Finally, the result2 parameter of the callback passed to the Binding.getDataAsync
method is used to display the data in the binding.

JavaScript

function readData() {
Office.context.document.bindings.getByIdAsync("MyBinding", function
(result) {
result.value.getDataAsync({ coercionType: 'text' }, function
(result2) {
write(result2.value);
});
});
}

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

This basic nested callback pattern can be used for all asynchronous methods in the
Office JavaScript API.

The following sections show how to use either anonymous or named functions for
nested callbacks in asynchronous methods.

Use anonymous functions for nested callbacks

In the following example, two anonymous functions are declared inline and passed into
the getByIdAsync and getDataAsync methods as nested callbacks. Because the functions
are simple and inline, the intent of the implementation is immediately clear.

JavaScript

Office.context.document.bindings.getByIdAsync('myBinding', function
(bindingResult) {
bindingResult.value.getDataAsync(function (getResult) {
if (getResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
} else {
write('Data has been read successfully.');
}
});
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}
Use named functions for nested callbacks

In complex implementations, it may be helpful to use named functions to make your


code easier to read, maintain, and reuse. In the following example, the two anonymous
functions from the example in the previous section have been rewritten as functions
named deleteAllData and showResult . These named functions are then passed into the
getByIdAsync and deleteAllDataValuesAsync methods as callbacks by name.

JavaScript

Office.context.document.bindings.getByIdAsync('myBinding', deleteAllData);

function deleteAllData(asyncResult) {
asyncResult.value.deleteAllDataValuesAsync(showResult);
}

function showResult(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
} else {
write('Data has been deleted successfully.');
}
}

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

Asynchronous programming using the promises pattern


to access data in bindings
Instead of passing a callback function and waiting for the function to return before
execution continues, the promises programming pattern immediately returns a promise
object that represents its intended result. However, unlike true synchronous
programming, under the covers the fulfillment of the promised result is actually
deferred until the Office Add-ins runtime environment can complete the request. An
onError handler is provided to cover situations when the request can't be fulfilled.

The Office JavaScript API provides the Office.select function to support the promises
pattern for working with existing binding objects. The promise object returned to the
Office.select function supports only the four methods that you can access directly
from the Binding object: getDataAsync, setDataAsync, addHandlerAsync, and
removeHandlerAsync.

The promises pattern for working with bindings takes this form.

Office.select(selectorExpression, onError).BindingObjectAsyncMethod

The selectorExpression parameter takes the form "bindings#bindingId" , where bindingId


is the name ( id ) of a binding that you created previously in the document or
spreadsheet (using one of the "addFrom" methods of the Bindings collection:
addFromNamedItemAsync , addFromPromptAsync , or addFromSelectionAsync ). For example,

the selector expression bindings#cities specifies that you want to access the binding
with an id of 'cities'.

The onError parameter is an error handling function which takes a single parameter of
type AsyncResult that can be used to access an Error object, if the select function fails
to access the specified binding. The following example shows a basic error handler
function that can be passed to the onError parameter.

JavaScript

function onError(result){
const err = result.error;
write(err.name + ": " + err.message);
}

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

Replace the BindingObjectAsyncMethod placeholder with a call to any of the four


Binding object methods supported by the promise object: getDataAsync , setDataAsync ,
addHandlerAsync , or removeHandlerAsync . Calls to these methods don't support

additional promises. You must call them using the nested callback function pattern.

After a Binding object promise is fulfilled, it can be reused in the chained method call as
if it were a binding (the add-in runtime won't asynchronously retry fulfilling the
promise). If the Binding object promise can't be fulfilled, the add-in runtime will try
again to access the binding object the next time one of its asynchronous methods is
invoked.

The following code example uses the select function to retrieve a binding with the id
" cities " from the Bindings collection, and then calls the addHandlerAsync method to
add an event handler for the dataChanged event of the binding.

JavaScript

function addBindingDataChangedEventHandler() {
Office.select("bindings#cities", function onError(){/* error handling
code */}).addHandlerAsync(Office.EventType.BindingDataChanged,
function (eventArgs) {
doSomethingWithBinding(eventArgs.binding);
});
}

) Important

The Binding object promise returned by the Office.select function provides


access to only the four methods of the Binding object. If you need to access any of
the other members of the Binding object, instead you must use the
Document.bindings property and Bindings.getByIdAsync or Bindings.getAllAsync
methods to retrieve the Binding object. For example, if you need to access any of
the Binding object's properties (the document , id , or type properties), or need to
access the properties of the MatrixBinding or TableBinding objects, you must use
the getByIdAsync or getAllAsync methods to retrieve a Binding object.

Pass optional parameters to asynchronous


methods
The common syntax for all "Async" methods follows this pattern.

AsyncMethod ( RequiredParameters , [ OptionalParameters ], CallbackFunction );

All asynchronous methods support optional parameters, which are passed in as a


JavaScript object that contains one or more optional parameters. The object containing
the optional parameters is an unordered collection of key-value pairs with the ":"
character separating the key and the value. Each pair in the object is comma-separated,
and the entire set of pairs is enclosed in braces. The key is the parameter name, and
value is the value to pass for that parameter.

You can create the object that contains optional parameters inline, or by creating an
options object and passing that in as the options parameter.
Pass optional parameters inline
For example, the syntax for calling the Document.setSelectedDataAsync method with
optional parameters inline looks like this:

JavaScript

Office.context.document.setSelectedDataAsync(data, {coercionType:
'coercionType', asyncContext: 'asyncContext'},callback);

In this form of the calling syntax, the two optional parameters, coercionType and
asyncContext, are defined as an anonymous JavaScript object inline enclosed in braces.

The following example shows how to call to the Document.setSelectedDataAsync method


by specifying optional parameters inline.

JavaScript

Office.context.document.setSelectedDataAsync(
"<html><body>hello world</body></html>",
{coercionType: "html", asyncContext: 42},
function(asyncResult) {
write(asyncResult.status + " " + asyncResult.asyncContext);
}
)

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

7 Note

You can specify optional parameters in any order in the parameter object as long as
their names are specified correctly.

Pass optional parameters in an options object


Alternatively, you can create an object named options that specifies the optional
parameters separately from the method call, and then pass the options object as the
options argument.
The following example shows one way of creating the options object, where
parameter1 , value1 , and so on, are placeholders for the actual parameter names and
values.

JavaScript

const options = {
parameter1: value1,
parameter2: value2,
...
parameterN: valueN
};

Which looks like the following example when used to specify the ValueFormat and
FilterType parameters.

JavaScript

const options = {
valueFormat: "unformatted",
filterType: "all"
};

Here's another way of creating the options object.

JavaScript

const options = {};


options[parameter1] = value1;
options[parameter2] = value2;
...
options[parameterN] = valueN;

Which looks like the following example when used to specify the ValueFormat and
FilterType parameters:

JavaScript

const options = {};


options["ValueFormat"] = "unformatted";
options["FilterType"] = "all";

7 Note
When using either method of creating the options object, you can specify optional
parameters in any order as long as their names are specified correctly.

The following example shows how to call to the Document.setSelectedDataAsync method


by specifying optional parameters in an options object.

JavaScript

const options = {
coercionType: "html",
asyncContext: 42
};

document.setSelectedDataAsync(
"<html><body>hello world</body></html>",
options,
function(asyncResult) {
write(asyncResult.status + " " + asyncResult.asyncContext);
}
)

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

In both optional parameter examples, the callback parameter is specified as the last
parameter (following the inline optional parameters, or following the options argument
object). Alternatively, you can specify the callback parameter inside either the inline
JavaScript object, or in the options object. However, you can pass the callback
parameter in only one location: either in the options object (inline or created
externally), or as the last parameter, but not both.

Wrap Common APIs in Promise-returning


functions
The Common API (and Outlook API) methods do not return Promises . Therefore, you
cannot use await to pause the execution until the asynchronous operation completes.
If you need await behavior, you can wrap the method call in an explicitly created
Promise.

The basic pattern is to create an asynchronous method that returns a Promise object
immediately and resolves that Promise object when the inner method completes, or
rejects the object if the method fails. The following is a simple example.
JavaScript

function getDocumentFilePath() {
return new OfficeExtension.Promise(function (resolve, reject) {
try {
Office.context.document.getFilePropertiesAsync(function
(asyncResult) {
resolve(asyncResult.value.url);
});
}
catch (error) {
reject(WordMarkdownConversion.errorHandler(error));
}
})
}

When this function needs to be awaited, it can be either called with the await keyword
or passed to a then function.

7 Note

This technique is especially useful when you need to call a Common API inside a
call of the run function in an application-specific object model. For an example of
the getDocumentFilePath function being used in this way, see the file Home.js in
the sample Word-Add-in-JavaScript-MDConversion .

The following is an example using TypeScript.

TypeScript

readDocumentFileAsync(): Promise<any> {
return new Promise((resolve, reject) => {
const chunkSize = 65536;
const self = this;

Office.context.document.getFileAsync(Office.FileType.Compressed, {
sliceSize: chunkSize }, (asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
reject(asyncResult.error);
} else {
// `getAllSlices` is a Promise-wrapped implementation of
File.getSliceAsync.
self.getAllSlices(asyncResult.value).then(result => {
if (result.IsSuccess) {
resolve(result.Data);
} else {
reject(asyncResult.error);
}
});
}
});
});
}

See also
Understanding the Office JavaScript API
Office JavaScript API
Runtimes in Office Add-ins
Office JavaScript API support for
content and task pane add-ins
Article • 04/11/2023

) Important

This article applies to the Common APIs, the Office JavaScript API model that was
introduced with Office 2013. These APIs include features such as UI, dialogs, and
client settings that are common across multiple types of Office applications.
Outlook add-ins exclusively use Common APIs, especially the subset of APIs
exposed through the Mailbox object.

You should only use Common APIs for scenarios that aren't supported by
application-specific APIs. To learn when to use Common APIs instead of
application-specific APIs, see Understanding the Office JavaScript API.

You can use the Office JavaScript API to create task pane or content add-ins for Office
client applications. The objects and methods that content and task pane add-ins
support are categorized as follows:

1. Common objects shared with other Office Add-ins. These objects include Office,
Context, and AsyncResult. The Office object is the root object of the Office
JavaScript API. The Context object represents the add-in's runtime environment.
Both Office and Context are the fundamental objects for any Office Add-in. The
AsyncResult object represents the results of an asynchronous operation, such as

the data returned to the getSelectedDataAsync method, which reads what a user
has selected in a document.

2. The Document object. The majority of the API available to content and task pane
add-ins is exposed through the methods, properties, and events of the Document
object. A content or task pane add-in can use the Office.context.document
property to access the Document object, and through it, can access the key
members of the API for working with data in documents, such as the Bindings and
CustomXmlParts objects, and the getSelectedDataAsync, setSelectedDataAsync,
and getFileAsync methods. The Document object also provides the mode property
for determining whether a document is read-only or in edit mode, the url property
to get the URL of the current document, and access to the Settings object. The
Document object also supports adding event handlers for the SelectionChanged

event, so you can detect when a user changes their selection in the document.
A content or task pane add-in can access the Document object only after the DOM
and runtime environment has been loaded, typically in the event handler for the
Office.initialize event. For information about the flow of events when an add-in is
initialized, and how to check that the DOM and runtime and loaded successfully,
see Loading the DOM and runtime environment.

3. Objects for working with specific features. To work with specific features of the
API, use the following objects and methods.

The methods of the Bindings object to create or get bindings, and the
methods and properties of the Binding object to work with data.

The CustomXmlParts, CustomXmlPart and associated objects to create and


manipulate custom XML parts in Word documents.

The File and Slice objects to create a copy of the entire document, break it
into chunks or "slices", and then read or transmit the data in those slices.

The Settings object to save custom data, such as user preferences, and add-in
state.

) Important

Some of the API members aren't supported across all Office applications that can
host content and task pane add-ins. To determine which members are supported,
see any of the following:

For a summary of Office JavaScript API support across Office client applications, see
Understanding the Office JavaScript API.

Read and write to an active selection in a


document, spreadsheet, or presentation
You can read or write to the user's current selection in a document, spreadsheet, or
presentation. Depending on the Office application for your add-in, you can specify the
type of data structure to read or write as a parameter in the getSelectedDataAsync and
setSelectedDataAsync methods of the Document object. For example, you can specify
any type of data (text, HTML, tabular data, or Office Open XML) for Word, text and
tabular data for Excel, and text for PowerPoint and Project. You can also create event
handlers to detect changes to the user's selection. The following example gets data from
the selection as text using the getSelectedDataAsync method.
JavaScript

Office.context.document.getSelectedDataAsync(
Office.CoercionType.Text, function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
}
else {
write('Selected data: ' + asyncResult.value);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

For more details and examples, see Read and write data to the active selection in a
document or spreadsheet.

Bind to a region in a document or spreadsheet


You can use the getSelectedDataAsync and setSelectedDataAsync methods to read or
write to the user's current selection in a document, spreadsheet, or presentation.
However, if you would like to access the same region in a document across sessions of
running your add-in without requiring the user to make a selection, you should first
bind to that region. You can also subscribe to data and selection change events for that
bound region.

You can add a binding by using addFromNamedItemAsync, addFromPromptAsync, or


addFromSelectionAsync methods of the Bindings object. These methods return an
identifier that you can use to access data in the binding, or to subscribe to its data
change or selection change events.

The following is an example that adds a binding to the currently selected text in a
document, by using the Bindings.addFromSelectionAsync method.

JavaScript

Office.context.document.bindings.addFromSelectionAsync(
Office.BindingType.Text, { id: 'myBinding' }, function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
} else {
write('Added new binding with type: ' +
asyncResult.value.type + ' and id: ' + asyncResult.value.id);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

For more details and examples, see Bind to regions in a document or spreadsheet.

Get entire documents


If your task pane add-in runs in PowerPoint or Word, you can use the
Document.getFileAsync, File.getSliceAsync, and File.closeAsync methods to get an entire
presentation or document.

When you call Document.getFileAsync you get a copy of the document in a File object.
The File object provides access to the document in "chunks" represented as Slice
objects. When you call getFileAsync , you can specify the file type (text or compressed
Open Office XML format), and size of the slices (up to 4MB). To access the contents of
the File object, you then call File.getSliceAsync which returns the raw data in the
Slice.data property. If you specified compressed format, you will get the file data as a
byte array. If you are transmitting the file to a web service, you can transform the
compressed raw data to a base64-encoded string before submission. Finally, when you
are finished getting slices of the file, use the File.closeAsync method to close the
document.

For more details, see how to get the whole document from an add-in for PowerPoint or
Word.

Read and write custom XML parts of a Word


document
Using the Open Office XML file format and content controls, you can add custom XML
parts to a Word document and bind elements in the XML parts to content controls in
that document. When you open the document, Word reads and automatically populates
bound content controls with data from the custom XML parts. Users can also write data
into the content controls, and when the user saves the document, the data in the
controls will be saved to the bound XML parts. Task pane add-ins for Word, can use the
Document.customXmlParts property,CustomXmlParts, CustomXmlPart, and
CustomXmlNode objects to read and write data dynamically to the document.
Custom XML parts may be associated with namespaces. To get data from custom XML
parts in a namespace, use the CustomXmlParts.getByNamespaceAsync method.

You can also use the CustomXmlParts.getByIdAsync method to access custom XML parts
by their GUIDs. After getting a custom XML part, use the CustomXmlPart.getXmlAsync
method to get the XML data.

To add a new custom XML part to a document, use the Document.customXmlParts


property to get the custom XML parts that are in the document, and call the
CustomXmlParts.addAsync method.

For detailed information about how to manage custom XML parts with a task pane add-
in, see Understand when and how to use Office Open XML in your Word add-in.

Persisting add-in settings


Often you need to save custom data for your add-in, such as a user's preferences or the
add-in's state, and access that data the next time the add-in is opened. You can use
common web programming techniques to save that data, such as browser cookies or
HTML 5 web storage. Alternatively, if your add-in runs in Excel, PowerPoint, or Word, you
can use the methods of the Settings object. Data created with the Settings object is
stored in the spreadsheet, presentation, or document that the add-in was inserted into
and saved with. This data is available to only the add-in that created it.

To avoid roundtrips to the server where the document is stored, data created with the
Settings object is managed in memory at run time. Previously saved settings data is

loaded into memory when the add-in is initialized, and changes to that data are only
saved back to the document when you call the Settings.saveAsync method. Internally,
the data is stored in a serialized JSON object as name/value pairs. You use the get, set,
and remove methods of the Settings object, to read, write, and delete items from the in-
memory copy of the data. The following line of code shows how to create a setting
named themeColor and set its value to 'green'.

JavaScript

Office.context.document.settings.set('themeColor', 'green');

Because settings data created or deleted with the set and remove methods is acting on
an in-memory copy of the data, you must call saveAsync to persist changes to settings
data into the document your add-in is working with.
For more details about working with custom data using the methods of the Settings
object, see Persisting add-in state and settings.

Read properties of a project document


If your task pane add-in runs in Project, your add-in can read data from some of the
project fields, resource, and task fields in the active project. To do that, you use the
methods and events of the ProjectDocument object, which extends the Document object
to provide additional Project-specific functionality.

For examples of reading Project data, see Create your first task pane add-in for Project.

Permissions model and governance


Your add-in uses the Permissions element in its manifest to request permission to
access the level of functionality it requires from the Office JavaScript API. For example, if
your add-in requires read/write access to the document, its manifest must specify
ReadWriteDocument as the text value in its Permissions element. Because permissions

exist to protect a user's privacy and security, as a best practice you should request the
minimum level of permissions it needs for its features. The following example shows
how to request the ReadDocument permission in a task pane's manifest.

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
???<!-- Other manifest elements omitted. -->
<Permissions>ReadDocument</Permissions>
???
</OfficeApp>

For more information, see Requesting permissions for API use in add-ins.

See also
Office JavaScript API
Schema reference for Office Add-ins manifests
Troubleshoot user errors with Office Add-ins
Runtimes in Office Add-ins
Persist add-in state and settings
Article • 06/08/2023

) Important

This article applies to the Common APIs, the Office JavaScript API model that was
introduced with Office 2013. These APIs include features such as UI, dialogs, and
client settings that are common across multiple types of Office applications.
Outlook add-ins exclusively use Common APIs, especially the subset of APIs
exposed through the Mailbox object.

You should only use Common APIs for scenarios that aren't supported by
application-specific APIs. To learn when to use Common APIs instead of
application-specific APIs, see Understanding the Office JavaScript API.

Office Add-ins are essentially web applications running in the stateless environment of a
browser iframe or a webview control. (For brevity hereafter, this article uses "browser
control" to mean "browser or webview control".) When in use, your add-in may need to
persist data to maintain the continuity of certain operations or features across sessions.
For example, your add-in may have custom settings or other values that it needs to save
and reload the next time it's initialized, such as a user's preferred view or default
location. To do that, you can:

Use members of the Office JavaScript API that store data as either:
Name/value pairs in a property bag stored in a location that depends on add-in
type.
Custom XML stored in the document.

Use techniques provided by the underlying browser control: browser cookies, or


HTML5 web storage (localStorage or sessionStorage ).

7 Note

Some browsers or the user's browser settings may block browser-based


storage techniques. You should test for availability as documented in Using
the Web Storage API .

This article focuses on how to use the Office JavaScript API to persist add-in state to the
current document. It's recommended that you use the application-specific object if it's
available for your selected Office client instead of the Common Office JavaScript version.
If you need to persist state across documents, such as tracking user preferences across
any documents they open, you'll need to use a different approach. For example, you
could use SSO to obtain the user identity, and then save the user ID and their settings to
an online database.

Persist add-in state and settings with the Office


JavaScript API
The Office JavaScript API provides objects, such as Settings, RoamingSettings, and
CustomProperties, to save add-in state across sessions as described in the following
table. In all cases, the saved settings values are associated with the Id of the add-in that
created them.

Object Add-in type Storage location Office application support


support
Object Add-in type Storage location Office application support
support

Settings content The document, Excel


task spreadsheet, or PowerPoint
pane presentation the Word
add-in is working
with. Content and
task pane add-in Note: Task pane add-ins for Project
settings are don't support the Settings API for
available only to the storing add-in state or settings.
add-in that created However, for add-ins running in
them from the Project and other Office client
document where applications, you can use techniques
they're saved. such as browser cookies or web
storage. For more information on
Important: Don't these techniques, see the Excel-Add-
store passwords and in-JavaScript-
other sensitive PersistCustomSettings .
personally
identifiable
information (PII)
with the Settings
object. The data
saved isn't visible to
end users, but it's
stored as part of the
document, which is
accessible by
reading the
document's file
format directly. You
should limit your
add-in's use of PII
and store any PII
required by your
add-in only on the
server hosting your
add-in as a user-
secured resource.
Object Add-in type Storage location Office application support
support

RoamingSettings mail The user's Exchange Outlook


mailbox where the
add-in is installed.
Because these
settings are stored
in the user's
mailbox, they can
"roam" with the user
and are available to
the add-in when it's
running in the
context of any
supported Office
client application or
browser accessing
that user's mailbox.

Outlook add-in
roaming settings are
available only to the
add-in that created
them, and only from
the mailbox where
the add-in is
installed.

CustomProperties mail The message, Outlook


appointment, or
meeting request Note: A version of this object is
item the add-in is available for Excel and Word. Task
working with. pane and content add-ins for Excel
Outlook add-in item support Excel.CustomProperty. Task
custom properties pane add-ins for Word support
are available only to Word.CustomProperty. Any add-in
the add-in that can access any custom properties
created them, and saved in the document. The key and
only from the item value of a custom property are each
where they're saved. limited to 255 characters.
Object Add-in type Storage location Office application support
support

InternetHeaders mail The message, Outlook


appointment, or
meeting request
item the add-in is
working with.
Custom internet
headers persist after
the mail item leaves
Exchange and are
available to the
item's recipients.

CustomXmlParts task pane The document or Word (using the application-


spreadsheet the specific Word JavaScript API
add-in is working Word.CustomXmlPartCollection
with. Task pane add- (recommended) or using the
in custom XML parts Office JavaScript Common API)
are available to any Excel (using the application-
add-in in the specific Excel JavaScript API
document where Excel.CustomXmlPartCollection)
they're saved.

Important: Don't
store passwords and
other sensitive
personally
identifiable
information (PII) in a
custom XML part.
The data saved isn't
visible to end users,
but it's stored as
part of the
document, which is
accessible by
reading the
document's file
format directly. You
should limit your
add-in's use of PII
and store any PII
required by your
add-in only on the
server hosting your
add-in as a user-
secured resource.
Settings data is managed in memory at
runtime
The following two sections discuss settings in the context of the Office Common
JavaScript API. The application-specific JavaScript APIs for Excel and for Word also
provide access to the custom settings. The application-specific APIs and programming
patterns are somewhat different from the Common version. For more information, see
Excel.SettingCollection and Word.SettingCollection.

Internally, the data in the property bag accessed with the Settings , CustomProperties ,
or RoamingSettings objects is stored as a serialized JavaScript Object Notation (JSON)
object that contains name/value pairs. The name (key) for each value must be a string ,
and the stored value can be a JavaScript string , number , date , or object , but not a
function.

This example of the property bag structure contains three defined string values named
firstName , location , and defaultView .

JSON

{
"firstName":"Erik",
"location":"98052",
"defaultView":"basic"
}

After the settings property bag is saved during the previous add-in session, it can be
loaded when the add-in is initialized or at any point after that during the add-in's
current session. During the session, the settings are managed in entirely in memory
using the get , set , and remove methods of the object that corresponds to the kind of
settings you're creating (Settings, CustomProperties, or RoamingSettings).

) Important

To persist any additions, updates, or deletions made during the add-in's current
session to the storage location, you must call the saveAsync method of the
corresponding object used to work with that kind of settings. The get , set , and
remove methods operate only on the in-memory copy of the settings property bag.

If your add-in is closed without calling saveAsync , any changes made to settings
during that session will be lost.
How to save add-in state and settings per
document for content and task pane add-ins
To persist state or custom settings of a content or task pane add-in for Word, Excel, or
PowerPoint, use the Settings object and its methods. The property bag created with the
methods of the Settings object are available only to the instance of the content or task
pane add-in that created it, and only from the document in which it is saved.

The Settings object is automatically loaded as part of the Document object, and is
available when the task pane or content add-in is activated. After the Document object is
instantiated, you can access the Settings object with the settings property of the
Document object. During the lifetime of the session, you can use the Settings.get ,

Settings.set , and Settings.remove methods to read, write, or remove persisted


settings and add-in state from the in-memory copy of the property bag.

Because the set and remove methods operate against only the in-memory copy of the
settings property bag, to save new or changed settings back to the document the add-
in is associated with, you must call the Settings.saveAsync method.

Create or update a setting value


The following code example shows how to use the Settings.set method to create a
setting called 'themeColor' with a value 'green' . The first parameter of the set method
is the case-sensitive name (Id) of the setting to set or create. The second parameter is
the value of the setting.

JavaScript

Office.context.document.settings.set('themeColor', 'green');

The setting with the specified name is created if it doesn't already exist, or its value is
updated if it does exist. Use the Settings.saveAsync method to persist the new or
updated settings to the document.

Get the value of a setting


The following example shows how use the Settings.get method to get the value of a
setting called "themeColor". The only parameter of the get method is the case-sensitive
name of the setting.
JavaScript

write('Current value for mySetting: ' +


Office.context.document.settings.get('themeColor'));

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

The get method returns the value that was previously saved for the setting name that
was passed in. If the setting doesn't exist, the method returns null.

Remove a setting
The following example shows how to use the Settings.remove method to remove a
setting with the name "themeColor". The only parameter of the remove method is the
case-sensitive name of the setting.

JavaScript

Office.context.document.settings.remove('themeColor');

Nothing will happen if the setting doesn't exist. Use the Settings.saveAsync method to
persist removal of the setting from the document.

Save your settings


To save any additions, changes, or deletions your add-in made to the in-memory copy
of the settings property bag during the current session, you must call the
Settings.saveAsync method to store them in the document. The only parameter of the
saveAsync method is callback, which is a callback function with a single parameter.

JavaScript

Office.context.document.settings.saveAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Settings save failed. Error: ' + asyncResult.error.message);
} else {
write('Settings saved.');
}
});
// Function that writes to a div with id='message' on the page.
function write(message){
document.getElementById('message').innerText += message;
}

The anonymous function passed into the saveAsync method as the callback parameter is
executed when the operation is completed. The asyncResult parameter of the callback
provides access to an AsyncResult object that contains the status of the operation. In
the example, the function checks the AsyncResult.status property to see if the save
operation succeeded or failed, and then displays the result in the add-in's page.

How to save custom XML to the document


This section discusses custom XML parts in the context of the Office Common JavaScript
API which is supported in Word. The application-specific JavaScript APIs for Excel and for
Word also provide access to the custom XML parts. The application-specific APIs and
programming patterns are somewhat different from the Common version. For more
information, see Excel.CustomXmlPart and Word.CustomXmlPart.

A custom XML part is an available storage option for when you want to store
information that has a structured character or need the data to be accessible across
instances of your add-in. Note that data stored this way can also be accessed by other
add-ins. You can persist custom XML markup in a task pane add-in for Word (and for
Excel and Word using application-specific API as mentioned in the previous paragraph).
In Word, you can use the CustomXmlPart object and its methods. The following code
creates a custom XML part and displays its ID and then its content in divs on the page.
Note that there must be an xmlns attribute in the XML string.

JavaScript

function createCustomXmlPart() {
const xmlString = "<Reviewers
xmlns='http://schemas.contoso.com/review/1.0'><Reviewer>Juan</Reviewer>
<Reviewer>Hong</Reviewer><Reviewer>Sally</Reviewer></Reviewers>";
Office.context.document.customXmlParts.addAsync(xmlString,
(asyncResult) => {
$("#xml-id").text("Your new XML part's ID: " +
asyncResult.value.id);
asyncResult.value.getXmlAsync(
(asyncResult) => {
$("#xml-blob").text(asyncResult.value);
}
);
}
);
}
To retrieve a custom XML part, use the getByIdAsync method, but the ID is a GUID that
is generated when the XML part is created, so you can't know when coding what the ID
is. For that reason, it's a good practice when creating an XML part to immediately store
the ID of the XML part as a setting and give it a memorable key. The following method
shows how to do this.

JavaScript

function createCustomXmlPartAndStoreId() {
const xmlString = "<Reviewers
xmlns='http://schemas.contoso.com/review/1.0'><Reviewer>Juan</Reviewer>
<Reviewer>Hong</Reviewer><Reviewer>Sally</Reviewer></Reviewers>";
Office.context.document.customXmlParts.addAsync(xmlString,
(asyncResult) => {
Office.context.document.settings.set('ReviewersID',
asyncResult.id);
Office.context.document.settings.saveAsync();
}
);
}

The following code shows how to retrieve the XML part by first getting its ID from a
setting.

JavaScript

function getReviewers() {
const reviewersXmlId =
Office.context.document.settings.get('ReviewersID');
Office.context.document.customXmlParts.getByIdAsync(reviewersXmlId,
(asyncResult) => {
asyncResult.value.getXmlAsync(
(asyncResult) => {
$("#xml-blob").text(asyncResult.value);
}
);
}
);
}

How to save settings in an Outlook add-in


For information about how to save settings in an Outlook add-in, see Get and set add-in
metadata for an Outlook add-in and Get and set internet headers on a message in an
Outlook add-in.
See also
Understanding the Office JavaScript API
Outlook add-ins
Get and set add-in metadata for an Outlook add-in
Get and set internet headers on a message in an Outlook add-in
Excel-Add-in-JavaScript-PersistCustomSettings
Bind to regions in a document or
spreadsheet
Article • 04/11/2023

Binding-based data access enables content and task pane add-ins to consistently access
a particular region of a document or spreadsheet through an identifier. The add-in first
needs to establish the binding by calling one of the methods that associates a portion of
the document with a unique identifier: addFromPromptAsync, addFromSelectionAsync,
or addFromNamedItemAsync. After the binding is established, the add-in can use the
provided identifier to access the data contained in the associated region of the
document or spreadsheet. Creating bindings provides the following value to your add-
in.

Permits access to common data structures across supported Office applications,


such as: tables, ranges, or text (a contiguous run of characters).
Enables read/write operations without requiring the user to make a selection.
Establishes a relationship between the add-in and the data in the document.
Bindings are persisted in the document, and can be accessed at a later time.

Establishing a binding also allows you to subscribe to data and selection change events
that are scoped to that particular region of the document or spreadsheet. This means
that the add-in is only notified of changes that happen within the bound region as
opposed to general changes across the whole document or spreadsheet.

The Bindings object exposes a getAllAsync method that gives access to the set of all
bindings established on the document or spreadsheet. An individual binding can be
accessed by its ID using either the Bindings.getByIdAsync method or Office.select
function. You can establish new bindings as well as remove existing ones by using one
of the following methods of the Bindings object: addFromSelectionAsync,
addFromPromptAsync, addFromNamedItemAsync, or releaseByIdAsync.

Binding types
There are three different types of bindings that you specify with the bindingType
parameter when you create a binding with the addFromSelectionAsync,
addFromPromptAsync or addFromNamedItemAsync methods.

1. Text Binding - Binds to a region of the document that can be represented as text.
In Word, most contiguous selections are valid, while in Excel only single cell
selections can be the target of a text binding. In Excel, only plain text is supported.
In Word, three formats are supported: plain text, HTML, and Open XML for Office.

2. Matrix Binding - Binds to a fixed region of a document that contains tabular data
without headers.Data in a matrix binding is written or read as a two dimensional
Array, which in JavaScript is implemented as an array of arrays. For example, two
rows of string values in two columns can be written or read as [['a', 'b'], ['c',
'd']] , and a single column of three rows can be written or read as [['a'], ['b'],

['c']] .

In Excel, any contiguous selection of cells can be used to establish a matrix


binding. In Word, only tables support matrix binding.

3. Table Binding - Binds to a region of a document that contains a table with


headers.Data in a table binding is written or read as a TableData object. The
TableData object exposes the data through the headers and rows properties.

Any Excel or Word table can be the basis for a table binding. After you establish a
table binding, each new row or column a user adds to the table is automatically
included in the binding.

After a binding is created by using one of the three "addFrom" methods of the Bindings
object, you can work with the binding's data and properties by using the methods of the
corresponding object: MatrixBinding, TableBinding, or TextBinding. All three of these
objects inherit the getDataAsync and setDataAsync methods of the Binding object that
enable you to interact with the bound data.

7 Note

When should you use matrix versus table bindings? When the tabular data you
are working with contains a total row, you must use a matrix binding if your add-
in's script needs to access values in the total row or detect that the user's selection
is in the total row. If you establish a table binding for tabular data that contains a
total row, the TableBinding.rowCount property and the rowCount and startRow
properties of the BindingSelectionChangedEventArgs object in event handlers
won't reflect the total row in their values. To work around this limitation, you must
use establish a matrix binding to work with the total row.

Add a binding to the user's current selection


The following example shows how to add a text binding called myBinding to the current
selection in a document by using the addFromSelectionAsync method.

JavaScript

Office.context.document.bindings.addFromSelectionAsync(Office.BindingType.Te
xt, { id: 'myBinding' }, function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
} else {
write('Added new binding with type: ' + asyncResult.value.type + '
and id: ' + asyncResult.value.id);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

In this example, the specified binding type is text. This means that a TextBinding will be
created for the selection. Different binding types expose different data and operations.
Office.BindingType is an enumeration of available binding type values.

The second optional parameter is an object that specifies the ID of the new binding
being created. If an ID is not specified, one is generated automatically.

The anonymous function that is passed into the method as the final callback parameter
is executed when the creation of the binding is complete. The function is called with a
single parameter, asyncResult , which provides access to an AsyncResult object that
provides the status of the call. The AsyncResult.value property contains a reference to a
Binding object of the type that is specified for the newly created binding. You can use
this Binding object to get and set data.

Add a binding from a prompt


The following example shows how to add a text binding called myBinding by using the
addFromPromptAsync method. This method lets the user specify the range for the
binding by using the application's built-in range selection prompt.

JavaScript

function bindFromPrompt() {

Office.context.document.bindings.addFromPromptAsync(Office.BindingType.Text,
{ id: 'myBinding' }, function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
} else {
write('Added new binding with type: ' + asyncResult.value.type +
' and id: ' + asyncResult.value.id);
}
});
}

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

In this example, the specified binding type is text. This means that a TextBinding will be
created for the selection that the user specifies in the prompt.

The second parameter is an object that contains the ID of the new binding being
created. If an ID is not specified, one is generated automatically.

The anonymous function passed into the method as the third callback parameter is
executed when the creation of the binding is complete. When the callback function
executes, the AsyncResult object contains the status of the call and the newly created
binding.

Figure 1 shows the built-in range selection prompt in Excel.

Figure 1. Excel Select Data UI

Add a binding to a named item


The following example shows how to add a binding to the existing myRange named item
as a "matrix" binding by using the addFromNamedItemAsync method, and assigns the
binding's id as "myMatrix".

JavaScript

function bindNamedItem() {
Office.context.document.bindings.addFromNamedItemAsync("myRange",
"matrix", {id:'myMatrix'}, function (result) {
if (result.status == 'succeeded'){
write('Added new binding with type: ' + result.value.type + '
and id: ' + result.value.id);
}
else
write('Error: ' + result.error.message);
});
}

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

For Excel, the itemName parameter of the addFromNamedItemAsync method can refer
to an existing named range, a range specified with the A1 reference style ("A1:A3") , or
a table. By default, adding a table in Excel assigns the name "Table1" for the first table
you add, "Table2" for the second table you add, and so on. To assign a meaningful name
for a table in the Excel UI, use the Table Name property on the Table Tools | Design tab
of the ribbon.

7 Note

In Excel, when specifying a table as a named item, you must fully qualify the name
to include the worksheet name in the name of the table in this format:
"Sheet1!Table1"

The following example creates a binding in Excel to the first three cells in column A (
"A1:A3" ), assigns the id "MyCities" , and then writes three city names to that binding.

JavaScript

function bindingFromA1Range() {
Office.context.document.bindings.addFromNamedItemAsync("A1:A3",
"matrix", {id: "MyCities" },
function (asyncResult) {
if (asyncResult.status == "failed") {
write('Error: ' + asyncResult.error.message);
}
else {
// Write data to the new binding.
Office.select("bindings#MyCities").setDataAsync([['Berlin'],
['Munich'], ['Duisburg']], { coercionType: "matrix" },
function (asyncResult) {
if (asyncResult.status == "failed") {
write('Error: ' + asyncResult.error.message);
}
});
}
});
}
// Function that writes to a div with id='message' on the page.
function write(message){
document.getElementById('message').innerText += message;
}

For Word, the itemName parameter of the addFromNamedItemAsync method refers to


the Title property of a Rich Text content control. (You can't bind to content controls
other than the Rich Text content control.)

By default, a content control has no Title* value assigned. To assign a meaningful name
in the Word UI, after inserting a Rich Text content control from the Controls group on
the Developer tab of the ribbon, use the Properties command in the Controls group to
display the Content Control Properties dialog box. Then set the Title property of the
content control to the name you want to reference from your code.

The following example creates a text binding in Word to a rich text content control
named "FirstName" , assigns the id "firstName" , and then displays that information.

JavaScript

function bindContentControl() {
Office.context.document.bindings.addFromNamedItemAsync('FirstName',
Office.BindingType.Text, {id:'firstName'},
function (result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
write('Control bound. Binding.id: '
+ result.value.id + ' Binding.type: ' +
result.value.type);
} else {
write('Error:', result.error.message);
}
});
}
// Function that writes to a div with id='message' on the page.
function write(message){
document.getElementById('message').innerText += message;
}

Get all bindings


The following example shows how to get all bindings in a document by using the
Bindings.getAllAsync method.
JavaScript

Office.context.document.bindings.getAllAsync(function (asyncResult) {
let bindingString = '';
for (let i in asyncResult.value) {
bindingString += asyncResult.value[i].id + '\n';
}
write('Existing bindings: ' + bindingString);
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

The anonymous function that is passed into the method as the callback parameter is
executed when the operation is complete. The function is called with a single parameter,
asyncResult , which contains an array of the bindings in the document. The array is

iterated to build a string that contains the IDs of the bindings. The string is then
displayed in a message box.

Get a binding by ID using the getByIdAsync


method of the Bindings object
The following example shows how to use the getByIdAsync method to get a binding in a
document by specifying its ID. This example assumes that a binding named 'myBinding'
was added to the document using one of the methods described earlier in this topic.

JavaScript

Office.context.document.bindings.getByIdAsync('myBinding', function
(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
}
else {
write('Retrieved binding with type: ' + asyncResult.value.type + '
and id: ' + asyncResult.value.id);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}
In the example, the first id parameter is the ID of the binding to retrieve.

The anonymous function that is passed into the method as the second callback
parameter is executed when the operation is completed. The function is called with a
single parameter, asyncResult, which contains the status of the call and the binding with
the ID "myBinding".

Get a binding by ID using the select function of


the Office object
The following example shows how to use the Office.select function to get a Binding
object promise in a document by specifying its ID in a selector string. It then calls the
Binding.getDataAsync method to get data from the specified binding. This example
assumes that a binding named 'myBinding' was added to the document using one of
the methods described earlier in this topic.

JavaScript

Office.select("bindings#myBinding", function onError()


{}).getDataAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
} else {
write(asyncResult.value);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

7 Note

If the select function promise successfully returns a Binding object, that object
exposes only the following four methods of the object: getDataAsync,
setDataAsync, addHandlerAsync, and removeHandlerAsync. If the promise cannot
return a Binding object, the onError callback can be used to access an
asyncResult.error object to get more information. If you need to call a member of
the Binding object other than the four methods exposed by the Binding object
promise returned by the select function, instead use the getByIdAsync method by
using the Document.bindings property and Bindings.getByIdAsync method to
retrieve the Binding object.
Release a binding by ID
The following example shows how use the releaseByIdAsync method to release a
binding in a document by specifying its ID.

JavaScript

Office.context.document.bindings.releaseByIdAsync('myBinding', function
(asyncResult) {
write('Released myBinding!');
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

In the example, the first id parameter is the ID of the binding to release.

The anonymous function that is passed into the method as the second parameter is a
callback that is executed when the operation is complete. The function is called with a
single parameter, asyncResult, which contains the status of the call.

Read data from a binding


The following example shows how to use the getDataAsync method to get data from an
existing binding.

JavaScript

myBinding.getDataAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
} else {
write(asyncResult.value);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}
myBinding is a variable that contains an existing text binding in the document.

Alternatively, you could use the Office.select to access the binding by its ID, and start
your call to the getDataAsync method, like this:

JavaScript

Office.select("bindings#myBindingID").getDataAsync

The anonymous function that is passed into the method is a callback that is executed
when the operation is complete. The AsyncResult.value property contains the data
within myBinding . The type of the value depends on the binding type. The binding in
this example is a text binding. Therefore, the value will contain a string. For additional
examples of working with matrix and table bindings, see the getDataAsync method
topic.

Write data to a binding


The following example shows how to use the setDataAsync method to set data in an
existing binding.

JavaScript

myBinding.setDataAsync('Hello World!', function (asyncResult) { });

myBinding is a variable that contains an existing text binding in the document.

In the example, the first parameter is the value to set on myBinding . Because this is a
text binding, the value is a string . Different binding types accept different types of
data.

The anonymous function that is passed into the method is a callback that is executed
when the operation is complete. The function is called with a single parameter,
asyncResult , which contains the status of the result.

Detect changes to data or the selection in a


binding
The following example shows how to attach an event handler to the DataChanged event
of a binding with an id of "MyBinding".

JavaScript
function addHandler() {
Office.select("bindings#MyBinding").addHandlerAsync(
Office.EventType.BindingDataChanged, dataChanged);
}
function dataChanged(eventArgs) {
write('Bound data changed in binding: ' + eventArgs.binding.id);
}
// Function that writes to a div with id='message' on the page.
function write(message){
document.getElementById('message').innerText += message;
}

The myBinding is a variable that contains an existing text binding in the document.

The first eventType parameter of the addHandlerAsync method specifies the name of the
event to subscribe to. Office.EventType is an enumeration of available event type values.
Office.EventType.BindingDataChanged evaluates to the string "bindingDataChanged".

The dataChanged function that is passed into the method as the second handler
parameter is an event handler that is executed when the data in the binding is changed.
The function is called with a single parameter, eventArgs, which contains a reference to
the binding. This binding can be used to retrieve the updated data.

Similarly, you can detect when a user changes selection in a binding by attaching an
event handler to the SelectionChanged event of a binding. To do that, specify the
eventType parameter of the addHandlerAsync method as

Office.EventType.BindingSelectionChanged or "bindingSelectionChanged" .

You can add multiple event handlers for a given event by calling the addHandlerAsync
method again and passing in an additional event handler function for the handler
parameter. This will work correctly as long as the name of each event handler function is
unique.

Remove an event handler


To remove an event handler for an event, call the removeHandlerAsync method passing
in the event type as the first eventType parameter, and the name of the event handler
function to remove as the second handler parameter. For example, the following
function will remove the dataChanged event handler function added in the previous
section's example.

JavaScript
function removeEventHandlerFromBinding() {
Office.select("bindings#MyBinding").removeHandlerAsync(
Office.EventType.BindingDataChanged, {handler:dataChanged});
}

) Important

If the optional handler parameter is omitted when the removeHandlerAsync


method is called, all event handlers for the specified eventType will be removed.

See also
Understanding the Office JavaScript API
Asynchronous programming in Office Add-ins
Read and write data to the active selection in a document or spreadsheet
Referencing the Office JavaScript API
library
Article • 05/04/2023

The Office JavaScript API library provides the APIs that your add-in can use to interact
with the Office application. The simplest way to reference the library is to use the
content delivery network (CDN) by adding the following <script> tag within the <head>
section of your HTML page.

HTML

<head>
...
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>
</head>

This will download and cache the Office JavaScript API files the first time your add-in
loads to make sure that it is using the most up-to-date implementation of Office.js and
its associated files for the specified version.

) Important

You must reference the Office JavaScript API from inside the <head> section of the
page to ensure that the API is fully initialized prior to any body elements.

API versioning and backward compatibility


In the previous HTML snippet, the /1/ in front of office.js in the CDN URL specifies
the latest incremental release within version 1 of Office.js. Because the Office JavaScript
API maintains backward compatibility, the latest release will continue to support API
members that were introduced earlier in version 1. If you need to upgrade an existing
project, see Update the version of your Office JavaScript API and manifest schema files.

If you plan to publish your Office Add-in from AppSource, you must use this CDN
reference. Local references are only appropriate for internal, development, and
debugging scenarios.

7 Note
To use preview APIs, reference the preview version of the Office JavaScript API
library on the CDN:
https://appsforoffice.microsoft.com/lib/beta/hosted/office.js .

Enabling IntelliSense for a TypeScript project


In addition to referencing the Office JavaScript API as described previously, you can also
enable IntelliSense for TypeScript add-in project by using the type definitions from
DefinitelyTyped . To do so, run the following command in a Node-enabled system
prompt (or git bash window) from the root of your project folder. You must have
Node.js installed (which includes npm).

command line

npm install --save-dev @types/office-js

Preview APIs
New JavaScript APIs are first introduced in "preview" and later become part of a specific
numbered requirement set after sufficient testing occurs and user feedback is acquired.

7 Note

Preview APIs are subject to change and are not intended for use in a production
environment. We recommend that you try them out in test and development
environments only. Do not use preview APIs in a production environment or within
business-critical documents.

To use preview APIs:

You must use the preview version of the Office JavaScript API library from the
Office.js content delivery network (CDN) . The type definition file for
TypeScript compilation and IntelliSense is found at the CDN and
DefinitelyTyped . You can install these types with npm install --save-dev
@types/office-js-preview .

You may need to join the Microsoft 365 Insider program for access to
more recent Office builds.
CDN references for other Microsoft 365
environments
21Vianet operates and manages an Office 365 service powered by licensed Microsoft
technologies to provide Office 365 services for China compliant with local laws and
regulations. Add-ins developed for use within this cloud environment should use
corresponding CDN. Use
https://appsforoffice.cdn.partner.office365.cn/appsforoffice/lib/1/hosted/office.j

s instead of the standard CDN reference. This ensures continued compliance and

provides better add-in performance.

See also
Understanding the Office JavaScript API
Office JavaScript API
Guidance for deploying Office Add-ins on government clouds
Microsoft software license terms for the Microsoft Office JavaScript (Office.js) API
library
Specify Office applications and API
requirements
Article • 03/21/2023

Your Office Add-in might depend on a specific Office application (also called an Office
host) or on specific members of the Office JavaScript API (office.js). For example, your
add-in might:

Run in a single Office application (e.g., Word or Excel), or several applications.


Make use of Office JavaScript APIs that are only available in some versions of
Office. For example, the volume-licensed perpetual version of Excel 2016 doesn't
support all Excel-related APIs in the Office JavaScript library.

In these situations, you need to ensure that your add-in is never installed on Office
applications or Office versions in which it cannot run.

There are also scenarios in which you want to control which features of your add-in are
visible to users based on their Office application and Office version. Two examples are:

Your add-in has features that are useful in both Word and PowerPoint, such as text
manipulation, but it has some additional features that only make sense in
PowerPoint, such as slide management features. You need to hide the PowerPoint-
only features when the add-in is running in Word.
Your add-in has a feature that requires an Office JavaScript API method that is
supported in some versions of an Office application, such as Microsoft 365
subscription Excel, but is not supported in others, such as volume-licensed
perpetual Excel 2016. But your add-in has other features that require only Office
JavaScript API methods that are supported in volume-licensed perpetual Excel
2016. In this scenario, you need the add-in to be installable on that version of Excel
2016, but the feature that requires the unsupported method should be hidden
from those users.

This article helps you understand which options you should choose to ensure that your
add-in works as expected and reaches the broadest audience possible.

7 Note

For a high-level view of where Office Add-ins are currently supported, see the
Office client application and platform availability for Office Add-ins page.
 Tip

Many of the tasks described in this article are done for you, in whole or in part,
when you create your add-in project with a tool, such as the Yeoman generator for
Office Add-ins or one of the Office Add-in templates in Visual Studio. In such
cases, please interpret the task as meaning that you should verify that it has been
done.

Use the latest Office JavaScript API library


Your add-in should load the most current version of the Office JavaScript API library
from the content delivery network (CDN). To do this, be sure you have the following
script tag in the first HTML file your add-in opens. Using /1/ in the CDN URL ensures

that you reference the most recent version of Office.js.

HTML

<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>

Specify which Office applications can host your


add-in
By default, an add-in is installable in all Office applications supported by the specified
add-in type (that is, Mail, Task pane, or Content). For example, a task pane add-in is
installable by default on Access, Excel, OneNote, PowerPoint, Project, and Word.

To ensure that your add-in is installable in a subset of Office applications, use the Hosts
and Host elements in the manifest.

For example, the following <Hosts> and <Host> declaration specifies that the add-in
can install on any release of Excel, which includes Excel on the web, Windows, and iPad,
but cannot be installed on any other Office application.

XML

<Hosts>
<Host Name="Workbook" />
</Hosts>
The <Hosts> element can contain one or more <Host> elements. There should be a
separate <Host> element for each Office application on which the add-in should be
installable. The Name attribute is required and can be set to one of the following values.

Name Office client applications Available add-in types

Database Access web apps Task pane

Document Word on the web, Windows, Mac, iPad Task pane

Mailbox Outlook on the web, Windows, Mac, Android, iOS Mail

Notebook OneNote on the web Task pane, Content

Presentation PowerPoint on the web, Windows, Mac, iPad Task pane, Content

Project Project on Windows Task pane

Workbook Excel on the web, Windows, Mac, iPad Task pane, Content

7 Note

Office applications are supported on different platforms and run on desktops, web
browsers, tablets, and mobile devices. You usually can't specify which platform can
be used to run your add-in. For example, if you specify Workbook , both Excel on the
web and on Windows can be used to run your add-in. However, if you specify
Mailbox , your add-in won't run on Outlook mobile clients unless you define the
mobile extension point.

7 Note

It isn't possible for an add-in manifest to apply to more than one type: Mail, Task
pane, or Content. This means that if you want your add-in to be installable on
Outlook and on one of the other Office applications, you must create two add-ins,
one with a Mail type manifest and the other with a Task pane or Content type
manifest.

) Important

We no longer recommend that you create and use Access web apps and databases
in SharePoint. As an alternative, we recommend that you use Microsoft
PowerApps to build no-code business solutions for web and mobile devices.
Specify which Office versions and platforms
can host your add-in
You can't explicitly specify the Office versions and builds or the platforms on which your
add-in should be installable, and you wouldn't want to because you would have to
revise your manifest whenever support for the add-in features that your add-in uses is
extended to a new version or platform. Instead, specify in the manifest the APIs that
your add-in needs. Office prevents the add-in from being installed on combinations of
Office version and platform that don't support the APIs and ensures that the add-in
won't appear in My Add-ins.

) Important

Only use the base manifest to specify the API members that your add-in must have
to be of any significant value at all. If your add-in uses an API for some features but
has other useful features that don't require the API, you should design the add-in
so that it's installable on platform and Office version combinations that don't
support the API but provides a diminished experience on those combinations. For
more information, see Design for alternate experiences.

Requirement sets
To simplify the process of specifying the APIs that your add-in needs, Office groups
most APIs together in requirement sets. The APIs in the Common API Object Model are
grouped by the development feature that they support. For example, all the APIs
connected to table bindings are in the requirement set called "TableBindings 1.1". The
APIs in the Application specific object models are grouped by when they were released
for use in production add-ins.

Requirement sets are versioned. For example, the APIs that support Dialog Boxes are in
the requirement set DialogApi 1.1. When additional APIs that enable messaging from a
task pane to a dialog were released, they were grouped into DialogApi 1.2, along with
all the APIs in DialogApi 1.1. Each version of a requirement set is a superset of all earlier
versions.

Requirement set support varies by Office application, the version of the Office
application, and the platform on which it is running. For example, DialogApi 1.2 is not
supported on volume-licensed perpetual versions of Office before Office 2021, but
DialogApi 1.1 is supported on all perpetual versions back to Office 2013. You want your
add-in to be installable on every combination of platform and Office version that
supports the APIs that it uses, so you should always specify in the manifest the minimum
version of each requirement set that your add-in requires. Details about how to do this
are later in this article.

 Tip

For more information about requirement set versioning, see Office requirement
sets availability, and for the complete lists of requirement sets and information
about the APIs in each, start with Office Add-in requirement sets. The reference
topics for most Office.js APIs also specify the requirement set they belong to (if
any).

7 Note

Some requirement sets also have manifest elements associated with them. See
Specifying requirements in a VersionOverrides element for information about
when this fact is relevant to your add-in design.

APIs not in a requirement set


All APIs in the application specific models are in requirement sets, but some of those in
the Common API model are not. There is also a way that you can specify one of these
setless APIs in the manifest when your add-in requires one. Details are later in this
article.

Requirements element
Use the Requirements element and its child elements Sets and Methods to specify the
minimum requirement sets or API members that must be supported by the Office
application to install your add-in.

If the Office application or platform doesn't support the requirement sets or API
members specified in the <Requirements> element, the add-in won't run in that
application or platform, and won't display in My Add-ins.

7 Note

The <Requirements> element is optional for all add-ins, except for Outlook add-
ins. When the xsi:type attribute of the root OfficeApp element is MailApp , there
must be a <Requirements> element that specifies the minimum version of the
Mailbox requirement set that the add-in requires. For more information, see
Outlook JavaScript API requirement sets.

The following code example shows how to configure an add-in that is installable in all
Office applications that support the following:

TableBindings requirement set, which has a minimum version of "1.1".

OOXML requirement set, which has a minimum version of "1.1".

Document.getSelectedDataAsync method.

XML

<OfficeApp ... >


...
<Requirements>
<Sets DefaultMinVersion="1.1">
<Set Name="TableBindings" MinVersion="1.1"/>
<Set Name="OOXML" MinVersion="1.1"/>
</Sets>
<Methods>
<Method Name="Document.getSelectedDataAsync"/>
</Methods>
</Requirements>
...
</OfficeApp>

Note the following about this example.

The <Requirements> element contains the <Sets> and <Methods> child


elements.
The <Sets> element can contain one or more <Set> elements. DefaultMinVersion
specifies the default MinVersion value of all child <Set> elements.
A Set element specifies a requirement set that the Office application must support
to make the add-in installable. The Name attribute specifies the name of the
requirement set. The MinVersion specifies the minimum version of the requirement
set. MinVersion overrides the value of the DefaultMinVersion attribute in the
parent <Sets>.
The <Methods> element can contain one or more Method elements. You can't use
the <Methods> element with Outlook add-ins.
The <Method> element specifies an individual method that the Office application
must support to make the add-in installable. The Name attribute is required and
specifies the name of the method qualified with its parent object.
Design for alternate experiences
The extensibility features that the Office Add-in platform provides can be usefully
divided into three kinds:

Extensibility features that are available immediately after the add-in is installed.
You can make use of this kind of feature by configuring a VersionOverrides
element in the manifest. An example of this kind of feature is Add-in Commands,
which are custom ribbon buttons and menus.
Extensibility features that are available only when the add-in is running and that
are implemented with Office.js JavaScript APIs; for example, Dialog Boxes.
Extensibility features that are available only at runtime but are implemented with a
combination of Office.js JavaScript and configuration in a <VersionOverrides>
element. Examples of these are Excel custom functions, single sign-on, and custom
contextual tabs.

If your add-in uses a specific extensibility feature for some of its functionality but has
other useful functionality that doesn't require the extensibility feature, you should
design the add-in so that it's installable on platform and Office version combinations
that don't support the extensibility feature. It can provide a valuable, albeit diminished,
experience on those combinations.

You implement this design differently depending on how the extensibility feature is
implemented:

For features implemented entirely with JavaScript, see Runtime checks for method
and requirement set support.
For features that require you to configure a <VersionOverrides> element, see
Specifying requirements in a VersionOverrides element.

Runtime checks for method and requirement set support


You test at runtime to discover whether the user's Office supports a requirement set
with the isSetSupported method. Pass the requirement set's name and the minimum
version as parameters. If the requirement set is supported, isSetSupported returns true .
The following code shows an example.

JavaScript

if (Office.context.requirements.isSetSupported('WordApi', '1.2'))
{
// Code that uses API members from the WordApi 1.2 requirement set.
} else {
// Provide diminished experience here. E.g., run alternate code when the
user's Word is volume-licensed perpetual Word 2016 (which doesn't support
WordApi 1.2).
}

About this code, note:

The first parameter is required. It's a string that represents the name of the
requirement set. For more information about available requirement sets, see Office
Add-in requirement sets.
The second parameter is optional. It's a string that specifies the minimum
requirement set version that the Office application must support in order for the
code within the if statement to run (e.g., "1.9"). If not used, version "1.1" is
assumed.

2 Warning

When calling the isSetSupported method, the value of the second parameter (if
specified) should be a string not a number. The JavaScript parser cannot
differentiate between numeric values such as 1.1 and 1.10, whereas it can for string
values such as "1.1" and "1.10".

The following table shows the requirement set names for the application specific API
models.

Office application RequirementSetName

Excel ExcelApi

OneNote OneNoteApi

Outlook Mailbox

PowerPoint PowerPointApi

Word WordApi

The following is an example of using the method with one of the Common API model
requirement sets.

JavaScript

if (Office.context.requirements.isSetSupported('CustomXmlParts'))
{
// Run code that uses API members from the CustomXmlParts requirement
set.
}
else
{
// Run alternate code when the user's Office application doesn't support
the CustomXmlParts requirement set.
}

7 Note

The isSetSupported method and the requirement sets for these applications are
available in the latest Office.js file on the CDN. If you don't use Office.js from the
CDN, your add-in might generate exceptions if you are using an old version of the
library in which isSetSupported is undefined. For more information, see Use the
latest Office JavaScript API library.

When your add-in depends on a method that isn't part of a requirement set, use the
runtime check to determine whether the method is supported by the Office application,
as shown in the following code example. For a complete list of methods that don't
belong to a requirement set, see Office Add-in requirement sets.

7 Note

We recommend that you limit the use of this type of runtime check in your add-in's
code.

The following code example checks whether the Office application supports
document.setSelectedDataAsync .

JavaScript

if (Office.context.document.setSelectedDataAsync)
{
// Run code that uses `document.setSelectedDataAsync`.
}

Specify requirements in a VersionOverrides element


The VersionOverrides element was added to the manifest schema primarily, but not
exclusively, to support features that must be available immediately after an add-in is
installed, such as add-in commands (custom ribbon buttons and menus). Office must
know about these features when it parses the add-in manifest.
Suppose your add-in uses one of these features, but the add-in is valuable, and should
be installable, even on Office versions that don't support the feature. In this scenario,
identify the feature using a Requirements element (and its child Sets and Methods
elements) that you include as a child of the <VersionOverrides> element itself instead
of as a child of the base OfficeApp element. The effect of doing this is that Office will
allow the add-in to be installed, but Office will ignore certain of the child elements of
the <VersionOverrides> element on Office versions where the feature isn't supported.

Specifically, the child elements of the <VersionOverrides> that override elements in the
base manifest, such as a <Hosts> element, are ignored and the corresponding elements
of the base manifest are used instead. However, there can be child elements in a
<VersionOverrides> that actually implement additional features rather than override
settings in the base manifest. Two examples are the WebApplicationInfo and
EquivalentAddins . These parts of the <VersionOverrides> will not be ignored, assuming

the platform and version of Office support the corresponding feature.

For information about the descendent elements of the <Requirements> element, see
Requirements element earlier in this article.

The following is an example.

XML

<VersionOverrides ... >


...
<Requirements>
<Sets DefaultMinVersion="1.1">
<Set Name="WordApi" MinVersion="1.2"/>
</Sets>
</Requirements>
<Hosts>

<!-- ALL MARKUP INSIDE THE HOSTS ELEMENT IS IGNORED WHEREVER WordApi
1.2 IS NOT SUPPORTED -->

<Host xsi:type="Workbook">
<!-- markup for custom add-in commands -->
</Host>
</Hosts>
</VersionOverrides>

2 Warning

Use great care before including a <Requirements> element in a


<VersionOverrides>, because on platform and version combinations that don't
support the requirement, none of the add-in commands will be installed, even those
that invoke functionality that doesn't need the requirement. Consider, for example,
an add-in that has two custom ribbon buttons. One of them calls Office JavaScript
APIs that are available in requirement set ExcelApi 1.4 (and later). The other calls
APIs that are only available in ExcelApi 1.9 (and later). If you put a requirement for
ExcelApi 1.9 in the <VersionOverrides>, then when 1.9 is not supported neither
button will appear on the ribbon. A better strategy in this scenario would be to use
the technique described in Runtime checks for method and requirement set
support. The code invoked by the second button first uses isSetSupported to
check for support of ExcelApi 1.9. If it isn't supported, the code gives the user a
message saying that this feature of the add-in is not available on their version of
Office.

 Tip

There's no point to repeating a Requirement element in a <VersionOverrides> that


already appears in the base manifest. If the requirement is specified in the base
manifest, then the add-in can't install where the requirement isn't supported so
Office doesn't even parse the <VersionOverrides> element.

See also
Office Add-ins manifest
Office Add-in requirement sets
Word-Add-in-Get-Set-EditOpen-XML
Load the DOM and runtime
environment
Article • 05/20/2023

Before running its own custom logic, an add-in must ensure that both the DOM and the
Office Add-ins runtime environment are loaded.

Startup of a content or task pane add-in


The following figure shows the flow of events involved in starting a content or task pane
add-in in Excel, PowerPoint, Project, or Word.

The following events occur when a content or task pane add-in starts.

1. The user opens a document that already contains an add-in or inserts an add-in in
the document.
2. The Office client application reads the add-in's XML manifest from AppSource, an
app catalog on SharePoint, or the shared folder catalog it originates from.

3. The Office client application opens the add-in's HTML page in a webview control.

The next two steps, steps 4 and 5, occur asynchronously and in parallel. For this
reason, your add-in's code must make sure that both the DOM and the add-in
runtime environment have finished loading before proceeding.

4. The webview control loads the DOM and HTML body, and calls the event handler
for the window.onload event.

5. The Office client application loads the runtime environment, which downloads and
caches the Office JavaScript API library files from the content distribution network
(CDN) server, and then calls the add-in's event handler for the initialize event of
the Office object, if a handler has been assigned to it. At this time it also checks to
see if any callbacks (or chained then() method) have been passed (or chained) to
the Office.onReady handler. For more information about the distinction between
Office.initialize and Office.onReady , see Initialize your add-in.

6. When the DOM and HTML body finish loading and the add-in finishes initializing,
the main function of the add-in can proceed.

Startup of an Outlook add-in


The following figure shows the flow of events involved in starting an Outlook add-in
running on the desktop, tablet, or smartphone.
The following events occur when an Outlook add-in starts.

1. When Outlook starts, Outlook reads the XML manifests for Outlook add-ins that
have been installed for the user's email account.

2. The user selects an item in Outlook.

3. If the selected item satisfies the activation conditions of an Outlook add-in,


Outlook activates the add-in and makes its button visible in the UI.

4. If the user clicks the button to start the Outlook add-in, Outlook opens the HTML
page in a webview control. The next two steps, steps 5 and 6, occur in parallel.

5. The webview control loads the DOM and HTML body, and calls the event handler
for the onload event.

6. Outlook loads the runtime environment, which downloads and caches the
JavaScript API for JavaScript library files from the content distribution network
(CDN) server, and then calls the event handler for the initialize event of the Office
object of the add-in, if a handler has been assigned to it. At this time it also checks
to see if any callbacks (or chained then() methods) have been passed (or chained)
to the Office.onReady handler. For more information about the distinction
between Office.initialize and Office.onReady , see Initialize your add-in.

7. When the DOM and HTML body finish loading and the add-in finishes initializing,
the main function of the add-in can proceed.

See also
Understanding the Office JavaScript API
Initialize your Office Add-in
Runtimes in Office Add-ins
Initialize your Office Add-in
Article • 07/21/2022

Office Add-ins often have start-up logic to do things such as:

Check that the user's version of Office supports all the Office APIs that your code
calls.

Ensure the existence of certain artifacts, such as a worksheet with a specific name.

Prompt the user to select some cells in Excel, and then insert a chart initialized with
those selected values.

Establish bindings.

Use the Office Dialog API to prompt the user for default add-in settings values.

However, an Office Add-in cannot successfully call any Office JavaScript APIs until the
library has been loaded. This article describes the two ways your code can ensure that
the library has been loaded.

Initialize with Office.onReady() .


Initialize with Office.initialize .

 Tip

We recommend that you use Office.onReady() instead of Office.initialize .


Although Office.initialize is still supported, Office.onReady() provides more
flexibility. You can assign only one handler to Office.initialize and it's called only
once by the Office infrastructure. You can call Office.onReady() in different places
in your code and use different callbacks.

For information about the differences in these techniques, see Major differences
between Office.initialize and Office.onReady().

For more details about the sequence of events when an add-in is initialized, see Loading
the DOM and runtime environment.

Initialize with Office.onReady()


Office.onReady() is an asynchronous function that returns a Promise object while it
checks to see if the Office.js library is loaded. When the library is loaded, it resolves the
Promise as an object that specifies the Office client application with an Office.HostType
enum value ( Excel , Word , etc.) and the platform with an Office.PlatformType enum
value ( PC , Mac , OfficeOnline , etc.). The Promise resolves immediately if the library is
already loaded when Office.onReady() is called.

One way to call Office.onReady() is to pass it a callback function. Here's an example.

JavaScript

Office.onReady(function(info) {
if (info.host === Office.HostType.Excel) {
// Do Excel-specific initialization (for example, make add-in task
pane's
// appearance compatible with Excel "green").
}
if (info.platform === Office.PlatformType.PC) {
// Make minor layout changes in the task pane.
}
console.log(`Office.js is now ready in ${info.host} on
${info.platform}`);
});

Alternatively, you can chain a then() method to the call of Office.onReady() , instead of
passing a callback. For example, the following code checks to see that the user's version
of Excel supports all the APIs that the add-in might call.

JavaScript

Office.onReady()
.then(function() {
if (!Office.context.requirements.isSetSupported('ExcelApi', '1.7'))
{
console.log("Sorry, this add-in only works with newer versions
of Excel.");
}
});

Here is the same example using the async and await keywords in TypeScript.

TypeScript

(async () => {
await Office.onReady();
if (!Office.context.requirements.isSetSupported('ExcelApi', '1.7')) {
console.log("Sorry, this add-in only works with newer versions of
Excel.");
}
})();

If you are using additional JavaScript frameworks that include their own initialization
handler or tests, these should usually be placed within the response to
Office.onReady() . For example, JQuery's $(document).ready() method would be

referenced as follows:

JavaScript

Office.onReady(function() {
// Office is ready
$(document).ready(function () {
// The document is ready
});
});

However, there are exceptions to this practice. For example, suppose you want to open
your add-in in a browser (instead of sideload it in an Office application) in order to
debug your UI with browser tools. In this scenario, once Office.js determines that it is
running outside of an Office host application, it will call the callback and resolve the
promise with null for both the host and platform.

Another exception would be if you want a progress indicator to appear in the task pane
while the add-in is loading. In this scenario, your code should call the jQuery ready and
use its callback to render the progress indicator. Then the Office.onReady callback can
replace the progress indicator with the final UI.

Initialize with Office.initialize


An initialize event fires when the Office.js library is loaded and ready for user interaction.
You can assign a handler to Office.initialize that implements your initialization logic.
The following is an example that checks to see that the user's version of Excel supports
all the APIs that the add-in might call.

JavaScript

Office.initialize = function () {
if (!Office.context.requirements.isSetSupported('ExcelApi', '1.7')) {
console.log("Sorry, this add-in only works with newer versions of
Excel.");
}
};
If you are using additional JavaScript frameworks that include their own initialization
handler or tests, these should usually be placed within the Office.initialize event (the
exceptions described in the Initialize with Office.onReady() section earlier apply in this
case also). For example, JQuery's $(document).ready() method would be referenced
as follows:

JavaScript

Office.initialize = function () {
// Office is ready
$(document).ready(function () {
// The document is ready
});
};

For task pane and content add-ins, Office.initialize provides an additional reason
parameter. This parameter specifies how an add-in was added to the current document.
You can use this to provide different logic for when an add-in is first inserted versus
when it already existed within the document.

JavaScript

Office.initialize = function (reason) {


$(document).ready(function () {
switch (reason) {
case 'inserted': console.log('The add-in was just inserted.');
case 'documentOpened': console.log('The add-in is already part
of the document.');
}
});
};

For more information, see Office.initialize Event and InitializationReason Enumeration.

Major differences between Office.initialize and


Office.onReady
You can assign only one handler to Office.initialize and it's called only once by
the Office infrastructure; but you can call Office.onReady() in different places in
your code and use different callbacks. For example, your code could call
Office.onReady() as soon as your custom script loads with a callback that runs

initialization logic; and your code could also have a button in the task pane, whose
script calls Office.onReady() with a different callback. If so, the second callback
runs when the button is clicked.

The Office.initialize event fires at the end of the internal process in which
Office.js initializes itself. And it fires immediately after the internal process ends. If
the code in which you assign a handler to the event executes too long after the
event fires, then your handler doesn't run. For example, if you are using the
WebPack task manager, it might configure the add-in's home page to load polyfill
files after it loads Office.js but before it loads your custom JavaScript. By the time
your script loads and assigns the handler, the initialize event has already
happened. But it is never "too late" to call Office.onReady() . If the initialize event
has already happened, the callback runs immediately.

7 Note

Even if you have no start-up logic, you should either call Office.onReady() or
assign an empty function to Office.initialize when your add-in JavaScript loads.
Some Office application and platform combinations won't load the task pane until
one of these happens. The following examples show these two approaches.

JavaScript

Office.onReady();

JavaScript

Office.initialize = function () {};

Debug initialization
For information about debugging the Office.initialize and Office.onReady()
functions, see Debug the initialize and onReady functions.

See also
Understanding the Office JavaScript API
Loading the DOM and runtime environment
Automatically open a task pane with a
document
Article • 05/05/2023

You can use add-in commands in your Office Add-in to extend the Office UI by adding
buttons to the Office app ribbon. When users click your command button, an action
occurs, such as opening a task pane.

Some scenarios require that a task pane open automatically when a document opens,
without explicit user interaction. You can use the autoopen task pane feature, introduced
in the AddInCommands 1.1 requirement set, to automatically open a task pane when
your scenario requires it.

7 Note

To configure a task pane to open immediately when the add-in is installed, but not
necessarily whenever the document is opened later, see Automatically open a task
pane when an add-in is installed.

How is the autoopen feature different from


inserting a task pane?
When a user launches add-ins that don't use add-in commands, the add-ins are inserted
into the document, and persist in that document. As a result, when other users open the
document, they're prompted to install the add-in, and the task pane opens. The
challenge with this model is that in many cases, users don't want the add-in to persist in
the document. For example, a student who uses a dictionary add-in in a Word document
might not want their classmates or teachers to be prompted to install that add-in when
they open the document.

With the autoopen feature, you can explicitly define or allow the user to define whether
a specific task pane add-in persists in a specific document.

Support and availability


The autoopen feature is currently supported in the following products and platforms.

Products Platforms
Products Platforms

Word Supported platforms for all supported products:


Excel Office on Windows Desktop. Build 16.0.8121.1000+
PowerPoint Office on Mac. Build 15.34.17051500+
Office on the web

Best practices
Apply the following best practices when you use the autoopen feature.

Use the autoopen feature when it will help make your add-in users more efficient,
such as:
When the document needs the add-in in order to function properly. For
example, a spreadsheet that includes stock values that are periodically refreshed
by an add-in. The add-in should open automatically when the spreadsheet is
opened to keep the values up to date.
When the user will most likely always use the add-in with a particular document.
For example, an add-in that helps users fill in or change data in a document by
pulling information from a backend system.

Allow users to turn on or turn off the autoopen feature. Include an option in your
UI for users to choose to no longer automatically open the add-in task pane.

Use requirement set detection to determine whether the autoopen feature is


available, and provide a fallback behavior if it isn't.

Don't use the autoopen feature to artificially increase usage of your add-in. If it
doesn't make sense for your add-in to open automatically with certain documents,
this feature can annoy users.

7 Note

If Microsoft detects abuse of the autoopen feature, your add-in might be


rejected from AppSource.

Don't use this feature to pin multiple task panes. You can only set one pane of your
add-in to open automatically with a document.

Implement the autoopen feature


Specify the task pane to be opened automatically.
Tag the document to automatically open the task pane.

) Important

The pane that you designate to open automatically will only open if the add-in is
already installed on the user's device. If the user does not have the add-in installed
when they open a document, the autoopen feature will not work and the setting
will be ignored. If you also require the add-in to be distributed with the document
you need to set the visibility property to 1; this can only be done using OpenXML,
an example is provided later in this article.

Step 1: Specify the task pane to open


To specify the task pane to open automatically, set the TaskpaneId value to
Office.AutoShowTaskpaneWithDocument. You can only set this value on one task pane.
If you set this value on multiple task panes, the first occurrence of the value will be
recognized and the others will be ignored.

The following example shows the TaskPaneId value set to


Office.AutoShowTaskpaneWithDocument.

XML

<Action xsi:type="ShowTaskpane">
<TaskpaneId>Office.AutoShowTaskpaneWithDocument</TaskpaneId>
<SourceLocation resid="Contoso.Taskpane.Url" />
</Action>

Step 2: Tag the document to automatically open the task


pane
You can tag the document to trigger the autoopen feature in one of two ways. Pick the
alternative that works best for your scenario.

Tag the document on the client side


Use the Office.js settings.set method to set Office.AutoShowTaskpaneWithDocument to
true , as shown in the following example.

JavaScript
Office.context.document.settings.set("Office.AutoShowTaskpaneWithDocument",
true);
Office.context.document.settings.saveAsync();

Use this method if you need to tag the document as part of your add-in interaction (for
example, as soon as the user creates a binding, or chooses an option to indicate that
they want the pane to open automatically).

Use Open XML to tag the document


You can use Open XML to create or modify a document and add the appropriate Open
Office XML markup to trigger the autoopen feature. For a sample that shows you how to
do this, see Office-OOXML-EmbedAddin .

Add two Open XML parts to the document.

A webextension part
A taskpane part

The following example shows how to add the webextension part.

XML

<we:webextension
xmlns:we="http://schemas.microsoft.com/office/webextensions/webextension/201
0/11" id="[ADD-IN ID PER MANIFEST]">
<we:reference id="[GUID or AppSource asset ID]" version="[your add-in
version]" store="[Pointer to store or catalog]" storeType="[Store or catalog
type]"/>
<we:alternateReferences/>
<we:properties>
<we:property name="Office.AutoShowTaskpaneWithDocument" value="true"/>
</we:properties>
<we:bindings/>
<we:snapshot
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships
"/>
</we:webextension>

The webextension part includes a property bag and a property named


Office.AutoShowTaskpaneWithDocument that must be set to true .

The webextension part also includes a reference to the store or catalog with attributes
for id , storeType , store , and version . Of the storeType values, only four are relevant
to the autoopen feature. The values for the other three attributes depend on the value
for storeType , as shown in the following table.

storeType id value store value version


value value

OMEX The The locale of AppSource; for example, "en-us". The version
(AppSource) AppSource in the
asset ID of AppSource
the add-in catalog (see
(see Note). Note).

WOPICatalog The "wopicatalog". Use this value for add-ins that are The version
(partner AppSource published in App Source and are installed in in the add-in
WOPI hosts) asset ID of WOPI hosts. For more information, see manifest.
the add-in Integrating with Office Online.
(see Note).

FileSystem (a The GUID of The path of the network share; for example, The version
network the add-in in "\\MyComputer\MySharedFolder". in the add-in
share) the add-in manifest.
manifest.

EXCatalog The GUID of "EXCatalog". EXCatalog row is the row to use with The version
(deployment the add-in in add-ins that use Centralized Deployment in the in the add-in
via the the add-in Microsoft 365 admin center. manifest.
Exchange manifest.
server)

Registry The GUID of "developer" The version


(System the add-in in in the add-in
registry) the add-in manifest.
manifest.

7 Note

To find the asset ID and version of an add-in in AppSource, go to the AppSource


landing page for the add-in. The asset ID appears in the address bar in the browser.
The version is listed in the Details section of the page.

For more information about the webextension markup, see [MS-OWEXML] 2.2.5.
WebExtensionReference.

The following example shows how to add the taskpane part.

XML
<wetp:taskpane dockstate="right" visibility="0" width="350" row="4"
xmlns:wetp="http://schemas.microsoft.com/office/webextensions/taskpanes/2010
/11">
<wetp:webextensionref
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships
" r:id="rId1" />
</wetp:taskpane>

Note that in this example, the visibility attribute is set to "0". This means that after
the webextension and taskpane parts are added, the first time the document is opened,
the user has to install the add-in from the Add-in button on the ribbon. Thereafter, the
add-in task pane opens automatically when the file is opened. Also, when you set
visibility to "0", you can use Office.js to enable users to turn on or turn off the

autoopen feature. Specifically, your script sets the


Office.AutoShowTaskpaneWithDocument document setting to true or false . (For
details, see Tag the document on the client side.)

If visibility is set to "1", the task pane opens automatically the first time the
document is opened. The user is prompted to trust the add-in, and when trust is
granted, the add-in opens. Thereafter, the add-in task pane opens automatically when
the file is opened. However, when visibility is set to "1", you can't use Office.js to
enable users to turn on or turn off the autoopen feature.

Setting visibility to "1" is a good choice when the add-in and the template or content
of the document are so closely integrated that the user would not opt out of the
autoopen feature.

7 Note

If you want to distribute your add-in with the document, so that users are
prompted to install it, you must set the visibility property to 1. You can only do this
via Open XML.

An easy way to write the XML is to first run your add-in and tag the document on the
client side to write the value, and then save the document and inspect the XML that is
generated.Office will detect and provide the appropriate attribute values. You can also
use the Open XML SDK Productivity Tool to generate C# code to programmatically
add the markup based on the XML you generate.

Test and verify opening task panes


You can deploy a test version of your add-in that will automatically open a task pane
using Centralized Deployment via the Microsoft 365 admin center. The following
example shows how add-ins are inserted from the Centralized Deployment catalog
using the EXCatalog store version.

XML

<we:webextension
xmlns:we="http://schemas.microsoft.com/office/webextensions/webextension/201
0/11" id="{52811C31-4593-43B8-A697-EB873422D156}">
<we:reference id="af8fa5ba-4010-4bcc-9e03-a91ddadf6dd3"
version="1.0.0.0" store="EXCatalog" storeType="EXCatalog"/>
<we:alternateReferences/>
<we:properties/>
<we:bindings/>
<we:snapshot
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships
"/>
</we:webextension>

You can test the previous example by using your Microsoft 365 subscription to try out
Centralized Deployment and verify that your add-in works as expected. If you don't
already have a Microsoft 365 subscription, you can get a free, 90-day renewable
Microsoft 365 subscription by joining the Microsoft 365 developer program .

See also
For a sample that shows you how to use the autoopen feature, see Auto-open a
task pane with a document .
Automatically open a task pane when an add-in is installed
Join the Microsoft 365 developer program.
Automatically open a task pane when an
add-in is installed
Article • 03/14/2023

You can configure your add-in's task pane to launch immediately after it's installed. This
feature increases usage.

By default, task pane add-ins that do not include any add-in commands open the task
pane immediately upon installation. However, when an add-in has one or more add-in
commands, then the user is notified of new add-in, but the add-in doesn't launch
automatically. This historic default behavior is changing so add-ins that do have add-in
commands will launch automatically in some situations. In addition, if the add-in has
more than one task pane page, it's possible for you to control whether the add-in
launches upon installation and, if so, which page opens in the task pane.

7 Note

This feature is currently available only in Office on the web. We're working to
bring this behavior to other platforms, but currently they still exhibit the
historic default behavior described previously.
This feature applies only to add-ins installed by an end-user, not to centrally
deployed add-ins.
This feature doesn't apply to Content add-ins or Mail (Outlook) add-ins.
This feature applies only to add-ins that have at least one add-in command of
the type "task pane command".

New behavior
The new behavior is as follows:

If the add-in has just one task pane command, then the add-in's ribbon tab is
selected and the task pane opens automatically upon installation. You don't need
to configure anything.
If the add-in has multiple task pane commands, and one is configured to be the
default (see Configure default task pane), then the add-in's ribbon tab is selected
and the default task pane opens automatically upon installation.
If the add-in has multiple task pane commands, but none is configured to be the
default, then the add-in's ribbon tab is selected automatically upon installation and
a callout appears near it notifying the user of the new add-in, but no task pane is
opened. This is the same as the historic default behavior.

7 Note

If for any reason, the add-in command that launches the task pane cannot be
manually selected by a user at start up, such as when it's configured to be disabled
at start up, then it won't be automatically opened regardless of configuration.

Configure default task pane


To designate a task pane as the default, add a TaskpaneId element as the first child of
the <Action> element and set its value to Office.AutoShowTaskpaneWithDocument.
The following is an example.

XML

<Action xsi:type="ShowTaskpane">
<TaskpaneId>Office.AutoShowTaskpaneWithDocument</TaskpaneId>
<SourceLocation resid="Contoso.Taskpane.Url" />
</Action>

 Tip

If you want your add-in to automatically launch whenever the user reopens the
document, you need to take further configuration steps. For details and advice
about when to use this feature, see Automatically open a task pane with a
document.

See also
Automatically open a task pane with a document
Configure your Office Add-in to use a
shared runtime
Article • 05/20/2023

) Important

The shared runtime is only supported in some Office applications. For more
information, see Shared runtime requirement sets.

You can configure your Office Add-in to run all of its code in a single shared runtime.
This enables better coordination across your add-in and access to the DOM and CORS
from all parts of your add-in. It also enables additional features such as running code
when the document opens, or enabling or disabling ribbon buttons. To configure your
add-in to use a shared runtime, follow the instructions in this article.

Create the add-in project


If you are starting a new project, use the Yeoman generator for Office Add-ins to create
an Excel, PowerPoint, or Word add-in project.

Run the command yo office --projectType taskpane --name "my office add in" --host
<host> --js true , where <host> is one of the following values.

excel
powerpoint
word

) Important

The --name argument value must be in double quotation marks, even if it has no
spaces.

You can use different options for the --projecttype, --name, and --js command-line
options. For the full list of options, see Yeoman generator for Office Add-ins .

The generator will create the project and install supporting Node components. You can
also use the steps in this article to update a Visual Studio project to use the shared
runtime. However, you may need to update the XML schemas for the manifest. For more
information, see Troubleshoot development errors with Office Add-ins.
Configure the manifest
Follow these steps for a new or existing project to configure it to use a shared runtime.
These steps assume you have generated your project using the Yeoman generator for
Office Add-ins.

1. Start Visual Studio Code and open your add-in project.

2. Open the manifest.xml file.

3. For an Excel or PowerPoint add-in, update the requirements section to include the
shared runtime. Be sure to remove the CustomFunctionsRuntime requirement if it is
present. The XML should appear as follows.

XML

<Hosts>
<Host Name="Workbook"/>
</Hosts>
<Requirements>
<Sets DefaultMinVersion="1.1">
<Set Name="SharedRuntime" MinVersion="1.1"/>
</Sets>
</Requirements>
<DefaultSettings>

4. Find the <VersionOverrides> section and add the following <Runtimes> section.
The lifetime needs to be long so that your add-in code can run even when the task
pane is closed. The resid value is Taskpane.Url, which references the
taskpane.html file location specified in the <bt:Urls> section near the bottom of
the manifest.xml file.

) Important

The shared runtime won't load if the resid uses different values in the
manifest. If you change the value to something other than Taskpane.Url, be
sure to also change the value in all locations shown in the following steps in
this article.

Also, the <Runtimes> section must be entered after the <Host> element in
the exact order shown in the following XML.

XML
<VersionOverrides ...>
<Hosts>
<Host ...>
<Runtimes>
<Runtime resid="Taskpane.Url" lifetime="long" />
</Runtimes>
...
</Host>

5. If you generated an Excel add-in with custom functions, find the <Page> element.
Then change the source location from Functions.Page.Url to Taskpane.Url.

XML

<AllFormFactors>
...
<Page>
<SourceLocation resid="Taskpane.Url"/>
</Page>
...

6. Find the <FunctionFile> tag and change the resid from Commands.Url to
Taskpane.Url. Note that if you don't have action commands, you won't have a
<FunctionFile> entry, and can skip this step.

XML

</GetStarted>
...
<FunctionFile resid="Taskpane.Url"/>
...

7. Save the manifest.xml file.

Configure the webpack.config.js file


The webpack.config.js will build multiple runtime loaders. You need to modify it to load
only the shared runtime via the taskpane.html file.

1. Start Visual Studio Code and open the add-in project you generated.

2. Open the webpack.config.js file.

3. If your webpack.config.js file has the following functions.html plugin code,


remove it.
JavaScript

new HtmlWebpackPlugin({
filename: "functions.html",
template: "./src/functions/functions.html",
chunks: ["polyfill", "functions"]
})

4. If your webpack.config.js file has the following commands.html plugin code,


remove it.

JavaScript

new HtmlWebpackPlugin({
filename: "commands.html",
template: "./src/commands/commands.html",
chunks: ["polyfill", "commands"]
})

5. If your project used either the functions or commands chunks, add them to the
chunks list as shown next (the following code is for if your project used both
chunks).

JavaScript

new HtmlWebpackPlugin({
filename: "taskpane.html",
template: "./src/taskpane/taskpane.html",
chunks: ["polyfill", "taskpane", "commands", "functions"]
})

6. Save your changes and rebuild the project.

command line

npm run build

7 Note

If your project has a functions.html file or commands.html file, they can be


removed. The taskpane.html will load the functions.js and commands.js code into
the shared runtime via the webpack updates you just made.
Test your Office Add-in changes
You can confirm that you are using the shared runtime correctly by using the following
instructions.

1. Open the taskpane.js file.

2. Replace the entire contents of the file with the following code. This will display a
count of how many times the task pane has been opened. Adding the
onVisibilityModeChanged event is only supported in a shared runtime.

JavaScript

/*global document, Office*/

let _count = 0;

Office.onReady(() => {
document.getElementById("sideload-msg").style.display = "none";
document.getElementById("app-body").style.display = "flex";

updateCount(); // Update count on first open.


Office.addin.onVisibilityModeChanged(function (args) {
if (args.visibilityMode === "Taskpane") {
updateCount(); // Update count on subsequent opens.
}
});
});

function updateCount() {
_count++;
document.getElementById("run").textContent = "Task pane opened " +
_count + " times.";
}

3. Save your changes and run the project.

command line

npm start

Each time you open the task pane, the count of how many times it has been opened will
be incremented. The value of _count will not be lost because the shared runtime keeps
your code running even when the task pane is closed.

Runtime lifetime
When you add the <Runtime> element, you also specify a lifetime with a value of long
or short . Set this value to long to take advantage of features such as starting your add-
in when the document opens, continuing to run code after the task pane is closed, or
using CORS and DOM from custom functions.

7 Note

The default lifetime value is short , but we recommend using long in Excel,
PowerPoint, and Word add-ins. If you set your runtime to short in this example,
your add-in will start when one of your ribbon buttons is pressed, but it may shut
down after your ribbon handler is done running. Similarly, your add-in will start
when the task pane is opened, but it may shut down when the task pane is closed.

XML

<Runtimes>
<Runtime resid="Taskpane.Url" lifetime="long" />
</Runtimes>

7 Note

If your add-in includes the <Runtimes> element in the manifest (required for a
shared runtime) and the conditions for using WebView2 (Microsoft Edge
Chromium-based) are met, it uses that control. If the conditions are not met, then it
uses the Trident (Internet Explorer 11) webview control regardless of the Windows
or Microsoft 365 version. For more information, see Runtimes and Browsers and
webview controls used by Office Add-ins.

About the shared runtime


On Windows or Mac, your add-in will run code for ribbon buttons, custom functions,
and the task pane in separate runtime environments. This creates limitations such as not
being able to easily share global data, and not being able to access all CORS
functionality from a custom function.

However, you can configure your Office Add-in to share code in the same runtime (also
referred to as a shared runtime). This enables better coordination across your add-in
and access to the task pane DOM and CORS from all parts of your add-in.

Configuring a shared runtime enables the following scenarios.


Your Office Add-in can use additional UI features.
Enable and Disable Add-in Commands
Run code in your Office Add-in when the document opens
Show or hide the task pane of your Office Add-in
The following are available for Excel add-ins only.
Add Custom keyboard shortcuts to your Office Add-ins (preview)
Create custom contextual tabs in Office Add-ins (preview)
Custom functions will have full CORS support.
Custom functions can call Office.js APIs to read spreadsheet document data.

For Office on Windows, the shared runtime uses WebView2 (Microsoft Edge Chromium-
based) if the conditions for using it are met as explained in Browsers and webview
controls used by Office Add-ins. Otherwise, it uses Trident (Internet Explorer 11).
Additionally, any buttons that your add-in displays on the ribbon will run in the same
shared runtime. The following image shows how custom functions, the ribbon UI, and
the task pane code will all run in the same runtime.

Debug
When using a shared runtime, you can't use Visual Studio Code to debug custom
functions in Excel on Windows at this time. You'll need to use developer tools instead.
For more information, see Debug add-ins using developer tools for Internet Explorer or
Debug add-ins using developer tools in Microsoft Edge (Chromium-based).

Multiple task panes


Don't design your add-in to use multiple task panes if you are planning to use a shared
runtime. A shared runtime only supports the use of one task pane. Note that any task
pane without a <TaskpaneID> is considered a different task pane.

See also
Call Excel APIs from a custom function
Add custom keyboard shortcuts to your Office Add-ins (preview)
Create custom contextual tabs in Office Add-ins (preview)
Enable and Disable Add-in Commands
Run code in your Office Add-in when the document opens
Show or hide the task pane of your Office Add-in
Tutorial: Share data and events between Excel custom functions and the task pane
Runtimes in Office Add-ins
Run code in your Office Add-in when
the document opens
Article • 07/11/2023

) Important

The shared runtime is only supported in some Office applications. For more
information, see Shared runtime requirement sets.

You can configure your Office Add-in to load and run code as soon as the document is
opened. This is useful if you need to register event handlers, pre-load data for the task
pane, synchronize UI, or perform other tasks before the add-in is visible.

7 Note

The configuration is implemented with a method that your code calls at runtime.
This means that the add-in won't run the first time a user opens the document. The
add-in must be opened manually for the first time on any document. After the
method runs, either in Office.initialize, Office.onReady, or because the user takes a
code path that runs it; then whenever the document is reopened, the add-in loads
immediately and any code in the Office.initialize or Office.onReady method
runs.

7 Note

This article requires that your Office Add-in is configured to use a shared runtime.
For more information, see Configure your Office Add-in to use a shared runtime.

Configure your add-in to load when the


document opens
The following code configures your add-in to load and start running when the
document is opened.

JavaScript
Office.addin.setStartupBehavior(Office.StartupBehavior.load);

7 Note

The setStartupBehavior method is asynchronous.

Place startup code in Office.initialize or


Office.onReady
When your add-in is configured to load on document open, it will run immediately. The
Office.initialize event handler will be called. Place your startup code in the

Office.initialize or Office.onReady event handler.

The following Excel add-in code shows how to register an event handler for change
events from the active worksheet. If you configure your add-in to load on document
open, this code will register the event handler when the document is opened. You can
handle change events before the task pane is opened.

JavaScript

// This is called as soon as the document opens.


// Put your startup code here.
Office.initialize = () => {
// Add the event handler.
Excel.run(async context => {
let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.onChanged.add(onChange);

await context.sync();
console.log("A handler has been registered for the onChanged event.");
});
};

/**
* Handle the changed event from the worksheet.
*
* @param event The event information from Excel
*/
async function onChange(event) {
await Excel.run(async (context) => {
await context.sync();
console.log("Change type of event: " + event.changeType);
console.log("Address of event: " + event.address);
console.log("Source of event: " + event.source);
});
}

The following PowerPoint add-in code shows how to register an event handler for
selection change events from the PowerPoint document. If you configure your add-in to
load on document open, this code will register the event handler when the document is
opened. You can handle change events before the task pane is opened.

JavaScript

// This is called as soon as the document opens.


// Put your startup code here.
Office.onReady(info => {
if (info.host === Office.HostType.PowerPoint) {

Office.context.document.addHandlerAsync(Office.EventType.DocumentSelectionCh
anged, onChange);
console.log("A handler has been registered for the onChanged event.");
}
});

/**
* Handle the changed event from the PowerPoint document.
*
* @param event The event information from PowerPoint
*/
async function onChange(event) {
console.log("Change type of event: " + event.type);
}

Configure your add-in for no load behavior on


document open
There may be scenarios when you want to turn off the "run on document open"
behavior. The following code configures your add-in not to start when the document is
opened. Instead, it will start when the user engages it in some way, such as choosing a
ribbon button or opening the task pane. This code has no effect if the method hasn't
previously been called on the current document, with Office.StartupBehavior.load as
the parameter.

7 Note

If add-in calls the method, with Office.StartupBehavior.load as the parameter, in


Office.initialize or Office.onReady , the behavior is turned on again. So, in this
scenario, turning it off only applies to the next time the document is opened, not all
subsequent openings.

JavaScript

Office.addin.setStartupBehavior(Office.StartupBehavior.none);

Get the current load behavior


There may be scenarios in which your add-in needs to know if it's configured to start
automatically the next time the current document is opened. To determine what the
current startup behavior is, run the following method, which returns an
Office.StartupBehavior value.

JavaScript

let behavior = await Office.addin.getStartupBehavior();

See also
Configure your Office Add-in to use a shared runtime
Share data and events between Excel custom functions and task pane tutorial
Work with Events using the Excel JavaScript API
Runtimes in Office Add-ins
Office Add-ins manifest
Article • 07/27/2023

Every Office add-in has a manifest. There are two types of manifests:

XML manifest: This is the only type of manifest that is currently supported for
production add-ins. As the name indicates, it is XML format. This type of manifest
can't be used for an app that combines an add-in with some other kind of Teams
App; that is, some other kind of extension of the Microsoft 365 platform.
unified manifest for Microsoft 365: This is a JSON-formatted manifest that has
been used for years as the manifest for Teams Apps. It is supported for add-ins
only as a preview currently, and only on Outlook for Windows. It shouldn't be used
with a production add-in. When it releases to general availability, add-ins that use
this manifest can be combined with other kinds of Teams Apps in a single app that
is installable as a unit whole.

The remainder of this article is applicable to both types of manifest.

 Tip

For an overview that is specific to the XML manifest, see Office Add-ins XML
manifest.
For an overview that is specific to the unified manifest, see Office Add-ins
with the unified manifest for Microsoft 365 (preview).
If you have some familiarity with the XML manifest, the article Compare the
XML manifest with the unified manifest for Microsoft 365 explains the
unified manifest by comparing it with the XML manifest.

The manifest file of an Office Add-in describes how your add-in should be activated
when an end user installs and uses it with Office documents and applications.

A manifest file enables an Office Add-in to do the following:

Describe itself by providing an ID, version, description, display name, and default
locale.

Specify the images used for branding the add-in and iconography used for add-in
commands in the Office app ribbon.

Specify how the add-in integrates with Office, including any custom UI, such as
ribbon buttons the add-in creates.
Specify the requested default dimensions for content add-ins, and requested
height for Outlook add-ins.

Declare permissions that the Office Add-in requires, such as reading or writing to
the document.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).

Hosting requirements
All image URIs, such as those used for add-in commands, must support caching in
production. The server hosting the image shouldn't return a Cache-Control header
specifying no-cache , no-store , or similar options in the HTTP response. However, when
you're developing the add-in and making changes to image files, the caching can
prevent you from seeing your changes, so using Cache-Control headers is advisable in
development.

All URLs to code or content files in the add-in should be SSL-secured (HTTPS). While
not strictly required in all add-in scenarios, using an HTTPS endpoint for your add-in is
strongly recommended. Add-ins that are not SSL-secured (HTTPS) generate unsecure
content warnings and errors during use. If you plan to run your add-in in Office on the
web or publish your add-in to AppSource, it must be SSL-secured. If your add-in
accesses external data and services, it should be SSL-secured to protect data in transit.
Self-signed certificates can be used for development and testing, so long as the
certificate is trusted on the local machine.

Best practices for submitting to AppSource


Make sure that the add-in ID is a valid and unique GUID. Various GUID generator tools
are available on the web that you can use to create a unique GUID.

Add-ins submitted to AppSource must also include a support URL in the manifest. For
more information, see Validation policies for apps and add-ins submitted to AppSource.
Specify domains you want to open in the add-
in window
When running in Office on the web, your task pane can be navigated to any URL.
However, in desktop platforms, if your add-in tries to go to a URL in a domain other
than the domain that hosts the start page (as specified in the manifest file), that URL
opens in a new browser window outside the add-in pane of the Office application.

To override this (desktop Office) behavior, specify each domain you want to open in the
add-in window in the manifest. If the add-in tries to go to a URL in a domain that is in
the list, then it opens in the task pane in both Office on the web and desktop. If it tries
to go to a URL that isn't in the list, then, in desktop Office, that URL opens in a new
browser window (outside the add-in pane).

7 Note

There are two exceptions to this behavior.

It applies only to the root pane of the add-in. If there is an iframe embedded
in the add-in page, the iframe can be directed to any URL regardless of
whether it is listed in the manifest, even in desktop Office.
When a dialog is opened with the displayDialogAsync API, the URL that is
passed to the method must be in the same domain as the add-in, but the
dialog can then be directed to any URL regardless of whether it is listed in the
manifest, even in desktop Office.

Specify domains from which Office.js API calls


are made
Your add-in can make Office.js API calls from the ad-in's domain referenced in the
manifest file. If you have other iframes within your add-in that need to access Office.js
APIs, add the domain of that source URL to the manifest file. If an iframe with a source
not listed in the manifest attempts to make an Office.js API call, then the add-in will
receive a permission denied error.

See also
Specify Office applications and API requirements
Localization for Office Add-ins
Office Add-ins XML manifest
Article • 06/06/2023

This article introduces the XML-formatted manifest for Office Add-ins. It assumes that
you're familiar with Office Add-ins manifest. In addition to the purposes described in
that article, the XML-formatted manifest also supports certain Outlook Add-in features
that aren't supported in the unified manifest for Microsoft 365. For example, an XML
manifest for Outlook add-ins, can define the rule or rules that specify the context in
which the add-in is activated.

 Tip

For an overview of the unified manifest for Microsoft 365, see Office Add-ins with
the unified manifest for Microsoft 365 (preview).

Schema versions
Not all Office clients support the latest features, and some Office users will have an
older version of Office. Having schema versions lets developers build add-ins that are
backwards compatible, using the newest features where they are available but still
functioning on older versions.

The <VersionOverrides> element in the manifest is an example of this. All elements


defined inside <VersionOverrides> will override the same element in the other part of
the manifest. This means that, whenever possible, Office will use what is in the
<VersionOverrides> section to set up the add-in. However, if the version of Office
doesn't support a certain version of <VersionOverrides>, Office will ignore it and
depend on the information in the rest of the manifest.

This approach means that developers don't have to create multiple individual manifests,
but rather keep everything defined in one file.

The current versions of the schema are:

Version Description

v1.0 Supports version 1.0 of the Office JavaScript API. For example, in Outlook
add-ins, this supports the read form.

v1.1 Supports version 1.1 of the Office JavaScript API and <VersionOverrides>.
For example, in Outlook add-ins, this adds support for the compose form.
Version Description

<VersionOverrides> Supports later versions of the Office JavaScript API. This supports add-in
1.0 commands.

<VersionOverrides> Supported by Outlook only. This version of <VersionOverrides> adds


1.1 support for newer features, such as pinnable task panes and mobile add-
ins.

Even if your add-in manifest uses the <VersionOverrides> element, it is still important
to include the v1.1 manifest elements to allow your add-in to work with older clients
that do not support <VersionOverrides>.

7 Note

Office uses a schema to validate manifests. The schema requires that elements in
the manifest appear in a specific order. If you include elements out of the required
order, you may get errors when sideloading your add-in. See How to find the
proper order of manifest elements elements in the required order.

Required elements
The following table specifies the elements that are required for the three types of Office
Add-ins.

7 Note

There is also a mandatory order in which elements must appear within their parent
element. For more information see How to find the proper order of XML manifest
elements.

Required elements by Office Add-in type

Element Content Task pane Mail


(Outlook)

OfficeApp Required Required Required

Id Required Required Required

Version Required Required Required


Element Content Task pane Mail
(Outlook)

ProviderName Required Required Required

DefaultLocale Required Required Required

DisplayName Required Required Required

Description Required Required Required

SupportUrl** Required Required Required

DefaultSettings (ContentApp) Required Required Not available


DefaultSettings (TaskPaneApp)

SourceLocation (ContentApp) Required Required Required


SourceLocation (TaskPaneApp)
SourceLocation (MailApp)

DesktopSettings Not available Not available Required

Permissions (ContentApp) Required Required Required


Permissions (TaskPaneApp)
Permissions (MailApp)

Rule (RuleCollection) Not available Not available Required


Rule (MailApp)

Requirements (MailApp)* Not applicable Not available Required

Set* Required Required Required


Sets (Requirements)*
Sets (MailAppRequirements)*

Form* Not available Not available Required


FormSettings*

Hosts* Required Required Optional

*Added in the Office Add-in Manifest Schema version 1.1.

** SupportUrl is only required for add-ins that are distributed through AppSource.

Root element
The root element for the Office add-in manifest is <OfficeApp>. This element also
declares the default namespace, schema version and the type of add-in. Place all other
elements in the manifest within its open and close tags. The following is an example of
the root element.

XML

<OfficeApp
xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0"

xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides
/1.0"
xsi:type="MailApp">

<!-- the rest of the manifest -->

</OfficeApp>

Version
This is the version of the specific add-in. If a developer updates something in the
manifest, the version must be incremented as well. This way, when the new manifest is
installed, it will overwrite the existing one and the user will get the new functionality. If
this add-in was submitted to the store, the new manifest will have to be re-submitted
and re-validated. Then, users of this add-in will get the new updated manifest
automatically in a few hours, after it is approved.

If the add-in's requested permissions change, users will be prompted to upgrade and
re-consent to the add-in. If the admin installed this add-in for the entire organization,
the admin will have to re-consent first. Users will be unable to use the add-in until
consent is granted.

Hosts
Office add-ins specify the <Hosts> element like the following:

XML

<OfficeApp>
...
<Hosts>
<Host Name="Mailbox" />
</Hosts>
...
</OfficeApp>
This is separate from the <Hosts> element inside the <VersionOverrides> element,
which is discussed in Create add-in commands with the XML manifest.

Specify safe domains with the AppDomains


element
There is an AppDomains element of the XML manifest file that is used to tell Office
which domains your add-in should be allowed to navigate to. As noted in Specify
domains you want to open in the add-in window, when running in Office on the web,
your task pane can be navigated to any URL. However, in desktop platforms, if your
add-in tries to go to a URL in a domain other than the domain that hosts the start page
(as specified in the SourceLocation element), that URL opens in a new browser window
outside the add-in pane of the Office application.

To override this (desktop Office) behavior, add each domain you want to open in the
add-in window in the list of domains specified in the <AppDomains> element. If the
add-in tries to go to a URL in a domain that is in the list, then it opens in the task pane
in both Office on the web and desktop. If it tries to go to a URL that isn't in the list, then
in desktop Office that URL opens in a new browser window (outside the add-in pane).

The following table describes browser behavior when your add-in attempts to navigate
to a URL outside of the add-in's default domain.

Office client Domain Browser behavior


defined
in
AppDomains?

All clients Yes Link opens in add-in task pane.

- Office 2016 on Windows (volume-licensed No Link opens in Internet Explorer


perpetual) 11.
- Office 2013 on Windows (perpetual)

Other clients No Link opens in user's default


browser.

The following XML manifest example hosts its main add-in page in the
https://www.contoso.com domain as specified in the <SourceLocation> element. It also

specifies the https://www.northwindtraders.com domain in an AppDomain element


within the <AppDomains> element list. If the add-in goes to a page in the
www.northwindtraders.com domain, that page opens in the add-in pane, even in Office
desktop.
XML

<?xml version="1.0" encoding="UTF-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<!--IMPORTANT! Id must be unique for each add-in. If you copy this
manifest ensure that you change this id to your own GUID. -->
<Id>c6890c26-5bbb-40ed-a321-37f07909a2f0</Id>
<Version>1.0</Version>
<ProviderName>Contoso, Ltd</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Northwind Traders Excel" />
<Description DefaultValue="Search Northwind Traders data from Excel"/>
<SupportUrl DefaultValue="[Insert the URL of a page that provides support
information for the app]" />
<AppDomains>
<AppDomain>https://www.northwindtraders.com</AppDomain>
</AppDomains>
<DefaultSettings>
<SourceLocation
DefaultValue="https://www.contoso.com/search_app/Default.aspx" />
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
</OfficeApp>

Version overrides in the manifest


The optional VersionOverrides element contains child markup that enables additional
add-in features. Some of these are:

Customizing the Office ribbon and menus.


Customizing how Office works with the embedded runtimes in which add-ins run.
Configuring how the add-in interacts with Azure Active Directory and Microsoft
Graph for Single Sign-on.

Some descendant elements of VersionOverrides have values that override values of the
parent OfficeApp element. For example, the Hosts element in VersionOverrides
overrides the Hosts element in OfficeApp .

The VersionOverrides element has its own schema, actually four of them, depending on
the type of add-in and the features it uses. The schemas are:

Task pane 1.0


Content 1.0
Mail 1.0
Mail 1.1
When a VersionOverrides element is used, then the OfficeApp element must have a
xmlns attribute that identifies the appropriate schema. The possible values of the
attribute are the following:

http://schemas.microsoft.com/office/taskpaneappversionoverrides
http://schemas.microsoft.com/office/contentappversionoverrides

http://schemas.microsoft.com/office/mailappversionoverrides

The VersionOverrides element itself must also have an xmlns attribute specifying the
schema. The possible values are the three above and the following:

http://schemas.microsoft.com/office/mailappversionoverrides/1.1

The VersionOverrides element also must have an xsi:type attribute that specifies the
schema version. The possible values are the following:

VersionOverridesV1_0
VersionOverridesV1_1

The following are examples of VersionOverrides used, respectively, in a task pane add-
in and a mail add-in. Note that when a mail VersionOverrides with version 1.1 is used, it
must be the last child of a parent VersionOverrides of type 1.0. The values of child
elements in the inner VersionOverrides override the values of the same-named
elements in the parent VersionOverrides and the grandparent OfficeApp element.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides"
xsi:type="VersionOverridesV1_0">
<!-- child elements omitted -->
</VersionOverrides>

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<!-- other child elements omitted -->
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<!-- child elements omitted -->
</VersionOverrides>
</VersionOverrides>
For an example of a manifest that includes a VersionOverrides element, see Manifest
v1.1 XML file examples and schemas.

Requirements
The <Requirements> element specifies the set of APIs available to the add-in. For
detailed information about requirement sets, see Office requirement sets availability. For
example, for an Outlook add-in, the requirement set must be Mailbox and a value of 1.1
or above.

The <Requirements> element can also appear in the <VersionOverrides> element,


allowing the add-in to specify a different requirement when loaded in clients that
support <VersionOverrides>.

The following example uses the DefaultMinVersion attribute of the <Sets> element to
require office.js version 1.1 or higher, and the MinVersion attribute of the <Set>
element to require the Mailbox requirement set version 1.1.

XML

<OfficeApp>
...
<Requirements>
<Sets DefaultMinVersion="1.1">
<Set Name="MailBox" MinVersion="1.1" />
</Sets>
</Requirements>
...
</OfficeApp>

Localization
Some aspects of the add-in need to be localized for different locales, such as the name,
description and the URL that's loaded. These elements can easily be localized by
specifying the default value and then locale overrides in the <Resources> element
within the <VersionOverrides> element. The following shows how to override an image,
a URL, and a string.

XML

<Resources>
<bt:Images>
<bt:Image id="icon1_16x16"
DefaultValue="https://contoso.com/images/app_icon_small.png" >
<bt:Override Locale="ar-sa"
Value="https://contoso.com/images/app_icon_small_arsa.png" />
<!-- add information for other locales -->
</bt:Image>
</bt:Images>

<bt:Urls>
<bt:Url id="residDesktopFuncUrl"
DefaultValue="https://contoso.com/urls/page_appcmdcode.html" >
<bt:Override Locale="ar-sa"
Value="https://contoso.com/urls/page_appcmdcode.html?lcid=ar-sa" />
<!-- add information for other locales -->
</bt:Url>
</bt:Urls>

<bt:ShortStrings>
<bt:String id="residViewTemplates" DefaultValue="Launch My Add-in">
<bt:Override Locale="ar-sa" Value="<add localized value here>" />
<!-- add information for other locales -->
</bt:String>
</bt:ShortStrings>
</Resources>

The schema reference contains full information on which elements can be localized.

Manifest v1.1 XML file examples and schemas


The following sections show examples of manifest v1.1 XML files for content, task pane,
and mail (Outlook) add-ins.

Task pane

Add-in manifest schemas

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0"
xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverride
s" xsi:type="TaskPaneApp">

<!-- See https://github.com/OfficeDev/Office-Add-in-Commands-Samples


for documentation-->

<!-- BeginBasicSettings: Add-in metadata, used for all versions of


Office unless override provided -->

<!--IMPORTANT! Id must be unique for your add-in. If you copy this


manifest ensure that you change this id to your own GUID. -->
<Id>e504fb41-a92a-4526-b101-542f357b7acb</Id>
<Version>1.0.0.0</Version>
<ProviderName>Contoso</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<!-- The display name of your add-in. Used on the store and various
placed of the Office UI such as the add-ins dialog -->
<DisplayName DefaultValue="Add-in Commands Sample" />
<Description DefaultValue="Sample that illustrates add-in commands
basic control types and actions" />
<!--Icon for your add-in. Used on installation screens and the add-ins
dialog -->
<IconUrl DefaultValue="https://contoso.com/assets/icon-32.png" />
<HighResolutionIconUrl DefaultValue="https://contoso.com/assets/hi-
res-icon.png" />
<SupportUrl DefaultValue="[Insert the URL of a page that provides
support information for the app]" />
<!--BeginTaskpaneMode integration. Any client that doesn't understand
commands will use this section.
This section will also be used if there are no VersionOverrides -->
<Hosts>
<Host Name="Document"/>
</Hosts>
<DefaultSettings>
<SourceLocation
DefaultValue="https://commandsimple.azurewebsites.net/Taskpane.html" />
</DefaultSettings>
<!--EndTaskpaneMode integration -->

<Permissions>ReadWriteDocument</Permissions>

<!--BeginAddinCommandsMode integration-->
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides"
xsi:type="VersionOverridesV1_0">
<Hosts>
<!--Each host can have a different set of commands. Cool huh!? -->
<!-- Workbook=Excel Document=Word Presentation=PowerPoint -->
<!-- Make sure the hosts you override match the hosts declared in
the top section of the manifest -->
<Host xsi:type="Document">
<!-- Form factor. Currently only DesktopFormFactor is supported.
We will add TabletFormFactor and PhoneFormFactor in the future-->
<DesktopFormFactor>
<!--Function file is an html page that includes the javascript
where functions for ExecuteAction will be called.
Think of the FunctionFile as the "code behind"
ExecuteFunction-->
<FunctionFile resid="Contoso.FunctionFile.Url" />

<!--PrimaryCommandSurface==Main Office app ribbon-->


<ExtensionPoint xsi:type="PrimaryCommandSurface">
<!--Use OfficeTab to extend an existing Tab. Use CustomTab
to create a new tab -->
<!-- Documentation includes all the IDs currently tested to
work -->
<CustomTab id="Contoso.Tab1">
<!--Group ID-->
<Group id="Contoso.Tab1.Group1">
<!--Label for your group. resid must point to a
ShortString resource -->
<Label resid="Contoso.Tab1.GroupLabel" />
<Icon>
<!-- Sample Todo: Each size needs its own icon
resource or it will look distorted when resized -->
<!--Icons. Required sizes: 16, 32, 80; optional: 20,
24, 40, 48, 64. You should provide as many sizes as possible for a great
user experience. -->
<!--Use PNG icons and remember that all URLs on the
resources section must use HTTPS -->
<bt:Image size="16"
resid="Contoso.TaskpaneButton.Icon16" />
<bt:Image size="32"
resid="Contoso.TaskpaneButton.Icon32" />
<bt:Image size="80"
resid="Contoso.TaskpaneButton.Icon80" />
</Icon>

<!--Control. It can be of type "Button" or "Menu" -->


<Control xsi:type="Button" id="Contoso.FunctionButton">
<!--Label for your button. resid must point to a
ShortString resource -->
<Label resid="Contoso.FunctionButton.Label" />
<Supertip>
<!--ToolTip title. resid must point to a ShortString
resource -->
<Title resid="Contoso.FunctionButton.Label" />
<!--ToolTip description. resid must point to a
LongString resource -->
<Description resid="Contoso.FunctionButton.Tooltip"
/>
</Supertip>
<Icon>
<bt:Image size="16"
resid="Contoso.FunctionButton.Icon16" />
<bt:Image size="32"
resid="Contoso.FunctionButton.Icon32" />
<bt:Image size="80"
resid="Contoso.FunctionButton.Icon80" />
</Icon>
<!--This is what happens when the command is triggered
(E.g. click on the Ribbon). Supported actions are ExecuteFunction or
ShowTaskpane-->
<!--Look at the FunctionFile.html page for reference
on how to implement the function -->
<Action xsi:type="ExecuteFunction">
<!--Name of the function to call. This function
needs to exist in the global DOM namespace of the function file-->
<FunctionName>writeText</FunctionName>
</Action>
</Control>

<Control xsi:type="Button" id="Contoso.TaskpaneButton">


<Label resid="Contoso.TaskpaneButton.Label" />
<Supertip>
<Title resid="Contoso.TaskpaneButton.Label" />
<Description resid="Contoso.TaskpaneButton.Tooltip"
/>
</Supertip>
<Icon>
<bt:Image size="16"
resid="Contoso.TaskpaneButton.Icon16" />
<bt:Image size="32"
resid="Contoso.TaskpaneButton.Icon32" />
<bt:Image size="80"
resid="Contoso.TaskpaneButton.Icon80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>Button2Id1</TaskpaneId>
<!--Provide a url resource id for the location that
will be displayed on the task pane -->
<SourceLocation resid="Contoso.Taskpane1.Url" />
</Action>
</Control>
<!-- Menu example -->
<Control xsi:type="Menu" id="Contoso.Menu">
<Label resid="Contoso.Dropdown.Label" />
<Supertip>
<Title resid="Contoso.Dropdown.Label" />
<Description resid="Contoso.Dropdown.Tooltip" />
</Supertip>
<Icon>
<bt:Image size="16"
resid="Contoso.TaskpaneButton.Icon16" />
<bt:Image size="32"
resid="Contoso.TaskpaneButton.Icon32" />
<bt:Image size="80"
resid="Contoso.TaskpaneButton.Icon80" />
</Icon>
<Items>
<Item id="Contoso.Menu.Item1">
<Label resid="Contoso.Item1.Label"/>
<Supertip>
<Title resid="Contoso.Item1.Label" />
<Description resid="Contoso.Item1.Tooltip" />
</Supertip>
<Icon>
<bt:Image size="16"
resid="Contoso.TaskpaneButton.Icon16" />
<bt:Image size="32"
resid="Contoso.TaskpaneButton.Icon32" />
<bt:Image size="80"
resid="Contoso.TaskpaneButton.Icon80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>MyTaskPaneID1</TaskpaneId>
<SourceLocation resid="Contoso.Taskpane1.Url" />
</Action>
</Item>

<Item id="Contoso.Menu.Item2">
<Label resid="Contoso.Item2.Label"/>
<Supertip>
<Title resid="Contoso.Item2.Label" />
<Description resid="Contoso.Item2.Tooltip" />
</Supertip>
<Icon>
<bt:Image size="16"
resid="Contoso.TaskpaneButton.Icon16" />
<bt:Image size="32"
resid="Contoso.TaskpaneButton.Icon32" />
<bt:Image size="80"
resid="Contoso.TaskpaneButton.Icon80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>MyTaskPaneID2</TaskpaneId>
<SourceLocation resid="Contoso.Taskpane2.Url" />
</Action>
</Item>

</Items>
</Control>

</Group>

<!-- Label of your tab -->


<!-- If validating with XSD it needs to be at the end -->
<Label resid="Contoso.Tab1.TabLabel" />
</CustomTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Contoso.TaskpaneButton.Icon16"
DefaultValue="https://myCDN/Images/Button16x16.png" />
<bt:Image id="Contoso.TaskpaneButton.Icon32"
DefaultValue="https://myCDN/Images/Button32x32.png" />
<bt:Image id="Contoso.TaskpaneButton.Icon80"
DefaultValue="https://myCDN/Images/Button80x80.png" />
<bt:Image id="Contoso.FunctionButton.Icon"
DefaultValue="https://myCDN/Images/ButtonFunction.png" />
</bt:Images>
<bt:Urls>
<bt:Url id="Contoso.FunctionFile.Url"
DefaultValue="https://commandsimple.azurewebsites.net/FunctionFile.html"
/>
<bt:Url id="Contoso.Taskpane1.Url"
DefaultValue="https://commandsimple.azurewebsites.net/Taskpane.html" />
<bt:Url id="Contoso.Taskpane2.Url"
DefaultValue="https://commandsimple.azurewebsites.net/Taskpane2.html" />
</bt:Urls>
<bt:ShortStrings>
<bt:String id="Contoso.FunctionButton.Label"
DefaultValue="Execute Function" />
<bt:String id="Contoso.TaskpaneButton.Label" DefaultValue="Show
Taskpane" />
<bt:String id="Contoso.Dropdown.Label" DefaultValue="Dropdown"
/>
<bt:String id="Contoso.Item1.Label" DefaultValue="Show Taskpane
1" />
<bt:String id="Contoso.Item2.Label" DefaultValue="Show Taskpane
2" />
<bt:String id="Contoso.Tab1.GroupLabel" DefaultValue="Test
Group" />
<bt:String id="Contoso.Tab1.TabLabel" DefaultValue="Test Tab"
/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="Contoso.FunctionButton.Tooltip"
DefaultValue="Click to Execute Function" />
<bt:String id="Contoso.TaskpaneButton.Tooltip"
DefaultValue="Click to Show a Taskpane" />
<bt:String id="Contoso.Dropdown.Tooltip" DefaultValue="Click to
Show Options on this Menu" />
<bt:String id="Contoso.Item1.Tooltip" DefaultValue="Click to
Show Taskpane1" />
<bt:String id="Contoso.Item2.Tooltip" DefaultValue="Click to
Show Taskpane2" />
</bt:LongStrings>
</Resources>
</VersionOverrides>
</OfficeApp>

Validate an Office Add-in's manifest


For information about validating a manifest against the XML Schema Definition (XSD),
see Validate an Office Add-in's manifest.

See also
How to find the proper order of XML manifest elements
Create add-in commands with the XML manifest
Specify Office applications and API requirements
Localization for Office Add-ins
Schema reference for XML Office Add-ins manifests
Update API and manifest version
Identify an equivalent COM add-in
Requesting permissions for API use in add-ins
Validate an Office Add-in's manifest
Office Add-ins with the unified manifest
for Microsoft 365 (preview)
Article • 05/25/2023

This article introduces the unified manifest for Microsoft 365 in preview. It assumes that
you're familiar with Office Add-ins manifest.

 Tip

For an overview of the XML manifest, see Office Add-ins XML manifest.
If you're familiar with the XML manifest, you might get a grasp on the JSON-
formatted unified manifest easier by reading Compare the XML manifest with
the unified manifest for Microsoft 365.

Microsoft is making a number of improvements to the Microsoft 365 developer


platform. These improvements provide more consistency in the development,
deployment, installation, and administration of all types of extensions of Microsoft 365,
including Office Add-ins. These changes are compatible with existing add-ins.

One important improvement we're working on is the ability to create a single unit of
distribution for all your Microsoft 365 extensions (Teams Apps) by using the same
manifest format and schema, based on the JSON-formatted unified manifest for
Microsoft 365.

7 Note

Any extension of any part of the Microsoft 365 platform is now called a "Teams
App" even if it doesn't extend the Teams application itself.

We've taken an important first step toward these goals by making it possible for you to
create Outlook add-ins, running on Windows only, with a unified manifest for Microsoft
365.

7 Note

The unified manifest is available for preview and is subject to change based
on feedback. We encourage experienced add-in developers to experiment
with it. The unified manifest shouldn't be used in production add-ins.
The preview version of the unified manifest only supports Outlook add-ins
and only in Office downloaded from a Microsoft 365 subscription and
installed on Windows. We're working on extending support to Excel,
PowerPoint, and Word, as well as other platforms.
The unified manifest requires Office Version 2304 (Build 16320.00000) or later.
Your Microsoft 365 subscription channel must be "Beta".

 Tip

Ready to get started with the preview unified manifest? Begin with Build an
Outlook add-in with the unified manifest for Microsoft 365 (preview).

Key properties of the unified manifest


The main reference documentation for the preview version of the unified manifest is at
Public developer preview manifest schema for Teams. That article provides information
about the critical base manifest properties, but may not include any documentation of
the "extensions" property, which is the property where Office Add-ins are configured in
the unified manifest. So, in this article, we provide a brief description of the meaning of
base properties when the Teams App is (or includes) an Office add-in. This is followed by
some basic documentation for the "extensions" property and its descendent properties.
There is a full sample manifest for an add-in at Sample preview unified manifest.

Base properties
Each of the base properties listed in the following table has more extensive
documentation at Public developer preview manifest schema for Teams. Base properties
not included in this table have no meaning for Office Add-ins.

JSON property Purpose

"$schema" Identifies the manifest schema.

"manifestVersion" Version of the manifest schema.

"id" GUID of the Teams app/add-in.

"version" Version of the Teams app/add-in.

"name" Public short and long names of the Teams app/add-in. The short name
appears at the top of an add-in's task pane.
JSON property Purpose

"description" Public short and long descriptions of the Teams app/add-in.

"developer" Information about the developer of the Teams app/add-in.

"localizationInfo" Configures the default locale and other supported locales.

"validDomains" See Specify safe domains.

"webApplicationInfo" Identifies the Teams app/add-in's web app as it is known in Azure Active
Directory.

"authorization" Identifies any Microsoft Graph permissions that the add-in needs.

"extensions" property
We're working hard to complete reference documentation for the "extensions" property
and its descendent properties. In the meantime, the following provides some basic
documentation. Most, but not all, of the properties have an equivalent element (or
attribute) in the XML manifest for add-ins. For the most part, the description, and
restrictions, that apply to the XML element or attribute also apply to its JSON property
equivalent in the unified manifest. The tables in the '"extensions" property' section of
Compare the XML manifest with the unified manifest for Microsoft 365 can help you
determine the XML equivalent of a JSON property.

JSON property Purpose

"requirements.capabilities" Identifies the requirement sets that the add-in needs to be


installable.

"requirements.scopes" Identifies the Office applications in which the add-in can be installed.
For example, "mail" means the add-in can be installed in Outlook.

"ribbons" The ribbons that the add-in customizes.

"ribbons.contexts" Specifies the command surfaces that the add-in customizes. For
example, "mailRead" or "mailCompose".

"ribbons.tabs" Configures custom ribbon tabs.

"alternatives" Specifies backwards compatibility with an equivalent COM add-in,


XLL, or both.

"runtimes" Configures the embedded runtimes that the add-in uses, including
various kinds of add-ins that have little or no UI, such as custom
function-only add-ins and function commands.
JSON property Purpose

"autoRunEvents" Configures an event handler for a specified event.

Specify safe domains


There is a "validDomains" array in the manifest file that is used to tell Office which
domains your add-in should be allowed to navigate to. As noted in Specify domains you
want to open in the add-in window, when running in Office on the web, your task pane
can be navigated to any URL. However, in desktop platforms, if your add-in tries to go
to a URL in a domain other than the domain that hosts the start page, that URL opens in
a new browser window outside the add-in pane of the Office application.

To override this behavior in desktop platforms, add each domain you want to open in
the add-in window to the list of domains specified in the "validDomains" array. If the
add-in tries to go to a URL in a domain that is in the list, then it opens in the task pane
in both Office on the web and desktop. If it tries to go to a URL that isn't in the list, then
in Office on desktop, that URL opens in a new browser window (outside the add-in task
pane).

Sample preview unified manifest


The following is an example of a preview unified manifest for an add-in.

JSON

{
"$schema": "https://raw.githubusercontent.com/OfficeDev/microsoft-teams-
app-schema/op/extensions/MicrosoftTeams.schema.json",
"id": "00000000-0000-0000-0000-000000000000",
"version": "1.0.0",
"manifestVersion": "devPreview",
"name": {
"short": "Name of your app (<=30 chars)",
"full": "Full name of app, if longer than 30 characters (<=100 chars)"
},
"description": {
"short": "Short description of your app (<= 80 chars)",
"full": "Full description of your app (<= 4000 chars)"
},
"icons": {
"outline": "outline.png",
"color": "color.png"
},
"accentColor": "#230201",
"developer": {
"name": "Contoso",
"websiteUrl": "https://www.contoso.com",
"privacyUrl": "https://www.contoso.com/privacy",
"termsOfUseUrl": "https://www.contoso.com/servicesagreement"
},
"localizationInfo": {
"defaultLanguageTag": "en-us",
"additionalLanguages": [
{
"languageTag": "es-es",
"file": "es-es.json"
}
]
},
"webApplicationInfo": {
"id": "00000000-0000-0000-0000-000000000000",
"resource": "api://www.contoso.com/prodapp"
},
"authorization": {
"permissions": {
"resourceSpecific": [
{
"name": "Mailbox.ReadWrite.User",
"type": "Delegated"
}
]
}
},
"extensions": [
{
"requirements": {
"scopes": [ "mail" ],
"capabilities": [
{
"name": "Mailbox", "minVersion": "1.1"
}
]
},
"runtimes": [
{
"requirements": {
"capabilities": [
{
"name": "MailBox", "minVersion": "1.10"
}
]
},
"id": "eventsRuntime",
"type": "general",
"code": {
"page": "https://contoso.com/events.html",
"script": "https://contoso.com/events.js"
},
"lifetime": "short",
"actions": [
{
"id": "onMessageSending",
"type": "executeFunction"
},
{
"id": "onNewMessageComposeCreated",
"type": "executeFunction"
}
]
},
{
"requirements": {
"capabilities": [
{
"name": "MailBox", "minVersion": "1.1"
}
]
},
"id": "commandsRuntime",
"type": "general",
"code": {
"page": "https://contoso.com/commands.html",
"script": "https://contoso.com/commands.js"
},
"lifetime": "short",
"actions": [
{
"id": "action1",
"type": "executeFunction"
},
{
"id": "action2",
"type": "executeFunction"
},
{
"id": "action3",
"type": "executeFunction"
}
]
}
],
"ribbons": [
{
"contexts": [
"mailCompose"
],
"tabs": [
{
"builtInTabId": "TabDefault",
"groups": [
{
"id": "dashboard",
"label": "Controls",
"controls": [
{
"id": "control1",
"type": "button",
"label": "Action 1",
"icons": [
{
"size": 16,
"file": "test_16.png"
},
{
"size": 32,
"file": "test_32.png"
},
{
"size": 80,
"file": "test_80.png"
}
],
"supertip": {
"title": "Action 1 Title",
"description": "Action 1 Description"
},
"actionId": "action1"
},
{
"id": "menu1",
"type": "menu",
"label": "My Menu",
"icons": [
{
"size": 16,
"file": "test_16.png"
},
{
"size": 32,
"file": "test_32.png"
},
{
"size": 80,
"file": "test_80.png"
}
],
"supertip": {
"title": "My Menu",
"description": "Menu with 2 actions"
},
"items": [
{
"id": "menuItem1",
"type": "menuItem",
"label": "Action 2",
"supertip": {
"title": "Action 2 Title",
"description": "Action 2 Description"
},
"actionId": "action2"
},
{
"id": "menuItem2",
"type": "menuItem",
"label": "Action 3",
"icons": [
{
"size": 16,
"file": "test_16.png"
},
{
"size": 32,
"file": "test_32.png"
},
{
"size": 80,
"file": "test_80.png"
}
],
"supertip": {
"title": "Action 3 Title",
"description": "Action 3 Description"
},
"actionId": "action3"
}
]
}
]
}
]
}
]
},
{
"contexts": [ "mailRead" ],
"tabs": [
{
"builtInTabId": "TabDefault",
"groups": [
{
"id": "dashboard",
"label": "Controls",
"controls": [
{
"id": "control1",
"type": "button",
"label": "Action 1",
"icons": [
{
"size": 16,
"file": "test_16.png"
},
{
"size": 32,
"file": "test_32.png"
},
{
"size": 80,
"file": "test_80.png"
}
],
"supertip": {
"title": "Action 1 Title",
"description": "Action 1 Description"
},
"actionId": "action1"
}
]
}
]
}
]
}
],
"autoRunEvents": [
{
"requirements": {
"capabilities": [
{
"name": "MailBox", "minVersion": "1.10"
}
]
},
"events": [
{
"type": "newMessageComposeCreated",
"actionId": "onNewMessageComposeCreated"
},
{
"type": "messageSending",
"actionId": "onMessageSending",
"options": {
"sendMode": "promptUser"
}
}
]
}
],
"alternates": [
{
"requirements": {
"scopes": [ "mail" ]
},
"prefer": {
"comAddin": {
"progId": "ContosoExtension"
}
},
"hide": {
"storeOfficeAddin": {
"officeAddinId": "00000000-0000-0000-0000-000000000000",
"assetId": "WA000000000"
}
}
}
]
}
]
}

See also
Create add-in commands with the unified manifest for Microsoft 365
Preview schema for the unified manifest
Compare the XML manifest with the
unified manifest for Microsoft 365
Article • 07/27/2023

This article is intended to help readers who are familiar with the XML manifest
understand the unified manifest by comparing the two. Readers should also see Office
Add-ins with the unified manifest for Microsoft 365 (preview).

7 Note

The unified manifest is a preview feature of Office Add-ins and is only supported
for Outlook on Windows.

Schemas and general points


There is just one schema for the preview unified manifest , in contrast to the current
XML manifest which has a total of seven schemas.

Conceptual mapping of the preview unified


and current XML manifests
This section describes the preview unified manifest for readers who are familiar with the
current XML manifest. Some points to keep in mind:

The unified manifest is JSON-formatted.

JSON doesn't distinguish between attribute and element value like XML does.
Typically, the JSON that maps to an XML element makes both the element value
and each of the attributes a child property. The following example shows some
XML markup and its JSON equivalent.

XML

<MyThing color="blue">Some text</MyThing>

JSON

"myThing" : {
"color": "blue",
"text": "Some text"
}

There are many places in the current XML manifest where an element with a plural
name has children with the singular version of the same name. For example, the
markup to configure a custom menu includes an <Items> element which can have
multiple <Item> element children. The JSON equivalent of these plural elements is
a property with an array as its value. The members of the array are anonymous
objects, not properties named "item" or "item1", "item2", etc. The following is an
example.

JSON

"items": [
{
-- markup for a menu item is here --
},
{
-- markup for another menu item is here --
}
]

Top-level structure
The root level of the preview unified manifest, which roughly corresponds to the
<OfficeApp> element in the current XML manifest, is an anonymous object.

The children of <OfficeApp> are commonly divided into two notional categories. The
<VersionOverrides> element is one category. The other consists of all the other
children of <OfficeApp>, which are collectively referred to as the base manifest. So too,
the preview unified manifest has a similar division. There is a top-level "extensions"
property that roughly corresponds in its purposes and child properties to the
<VersionOverrides> element. The preview unified manifest also has over 10 other top-
level properties that collectively serve the same purposes as the base manifest of the
XML manifest. These other properties can be thought of collectively as the base
manifest of the unified manifest.

Base manifest
The base manifest properties specify characteristics of the add-in that any type of
extension of Microsoft 365 is expected to have. This includes Teams tabs and message
extensions, not just Office add-ins. These characteristics include a public name and a
unique ID. The following table shows a mapping of some critical top-level properties in
the preview unified manifest to the XML elements in the current manifest, where the
mapping principle is the purpose of the markup.

JSON property Purpose XML elements Comments

"$schema" Identifies the attributes of None.


manifest <OfficeApp> and
schema. <VersionOverrides>

"id" GUID of the <Id> None.


add-in.

"version" Version of the <Version> None.


add-in.

"manifestVersion" Version of the attributes of None.


manifest <OfficeApp>
schema.

"name" Public name <DisplayName> None.


of the add-in.

"description" Public <Description> None.


description of
the add-in.

"accentColor" None. None. This property has no


equivalent in the current
XML manifest and isn't used
in the preview of the unified
manifest. But it must be
present.

"developer" Identifies the <ProviderName> None.


developer of
the add-in.

"localizationInfo" Configures the <DefaultLocale> and None.


default locale <Override>
and other
supported
locales.

"webApplicationInfo" Identifies the <WebApplicationInfo> In the current XML manifest,


add-in's web the <WebApplicationInfo>
app as it is element is inside
known in <VersionOverrides>, not the
Azure Active base manifest.
Directory.
JSON property Purpose XML elements Comments

"authorization" Identifies any <WebApplicationInfo> In the current XML manifest,


Microsoft the <WebApplicationInfo>
Graph element is inside
permissions <VersionOverrides>, not the
that the add- base manifest.
in needs.

The <Hosts>, <Requirements>, and <ExtendedOverrides> elements are part of the


base manifest in the current XML manifest. But concepts and purposes associated with
these elements are configured inside the "extensions" property of the preview unified
manifest.

"extensions" property
The "extensions" property in the preview unified manifest primarily represents
characteristics of the add-in that wouldn't be relevant to other kinds of Microsoft 365
extensions. For example, the Office applications that the add-in extends (such as, Excel,
PowerPoint, Word, and Outlook) are specified inside the "extensions" property, as are
customizations of the Office application ribbon. The configuration purposes of the
"extensions" property closely match those of the <VersionOverrides> element in the
current XML manifest.

7 Note

The <VersionOverrides> section of the current XML manifest has a "double jump"
system for many string resources. Strings, including URLs, are specified and
assigned an ID in the <Resources> child of <VersionOverrides>. Elements that
require a string have a resid attribute that matches the ID of a string in the
<Resources> element. The "extensions" property of the preview unified manifest
simplifies things by defining strings directly as property values. There is nothing in
the unified manifest that is equivalent to the <Resources> element.

The following table shows a mapping of some high level child properties of the
"extensions" property in the preview unified manifest to XML elements in the current
manifest. Dot notation is used to reference child properties.

JSON property Purpose XML elements Comments

"requirements.capabilities" Identifies the <Requirements> and None.


requirement sets <Sets>
JSON property Purpose XML elements Comments

that the add-in


needs to be
installable. that the
add-in needs to
be installable.

"requirements.scopes" Identifies the <Hosts> None.


Office applications
in which the add-
in can be installed.

"ribbons" The ribbons that <Hosts>, The "ribbons"


the add-in ExtensionPoints, and property is an array
customizes. various *FormFactor of anonymous
elements objects that each
merge the purposes
of the these three
elements. See
"ribbons" table.

"alternatives" Specifies <EquivalentAddins> See the


backwards EquivalentAddins -
compatibility with See also for
an equivalent background
COM add-in, XLL, information.
or both.

"runtimes" Configures the <Runtimes>. None.


embedded <FunctionFile>, and
runtimes that the <ExtensionPoint> (of
add-in uses, type CustomFunctions)
including various
kinds of add-ins
that have little or
no UI, such as
custom function-
only add-ins and
function
commands.

"autoRunEvents" Configures an <ExtensionPoint> (of None.


event handler for a type LaunchEvent)
specified event.

"ribbons" table
The following table maps the child properties of the anonymous child objects in the
"ribbons" array onto XML elements in the current manifest.

JSON Purpose XML elements Comments


property

"contexts" Specifies the various *CommandSurface elements, None.


command such as PrimaryCommandSurface and
surfaces that MessageReadCommandSurface
the add-in
customizes.

"tabs" Configures <CustomTab> The names and


custom ribbon hierarchy of the
tabs. descendant properties
of "tabs" closely match
the descendants of
<CustomTab>.

For a full sample unified manifest, see Sample preview unified manifest.

Next steps
Build an Outlook add-in with the unified manifest for Microsoft 365 (preview).
Convert an add-in to use the unified
manifest for Microsoft 365 (preview)
Article • 07/27/2023

To add Teams capabilities to an add-in that uses the XML manifest, or to just future
proof the add-in, you need to convert it to use the unified manifest for Microsoft 365.

There are three basic tasks to converting an add-in project from the XML manifest to the
unified manifest.

Ensure that you have 64x64 pixel and 128x128 pixel images files to serve as icons
for the add-in.
Convert the XML manifest itself to the JSON format of the unified manifest.
Package the new manifest and main icon and high resolution icon image files into
a zip file for sideloading or deployment.

7 Note

The unified manifest is a preview feature for Office Add-ins and is currently
supported only for Outlook on Windows.
Add-ins that use the unified manifest can be sideloaded only on Office
version 16.0.16501.10000 or later.
Projects created in Visual Studio, as distinct from Visual Studio Code, can't be
converted at this time.
If you created the project with Teams Toolkit or with the "unified manifest"
option in the Office Yeoman Generator, it already uses the unified manifest.

Ensure that you have the two image files


It's highly likely that you have the two image files already in your add-in. They're
specified in the IconUrl and HighResolutionIconUrl elements in the XML manifest.
Because these are both optional elements, your add-in project mignt not have one or
both. These are required for an add-in that uses the unified manifest, so you need to
create them if the project doesn't already have them. For guidance, see Create an icon
for your add-in.

When you've added the files to the project, add <IconUrl> and
<HighResolutionIconUrl> (in that order) to the XML manifest just below the
<Description> element. The following is an example.

XML

<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="MailApp">
<Id>01234567-89ab-cdef-0123-4567-89abcdef0123</Id>
<Version>1.0</Version>
<ProviderName>Contoso</ProviderName>
<DefaultLocale>en-us</DefaultLocale>
<DisplayName DefaultValue="Great Add-in"/>
<Description DefaultValue="A great add-in."/>
<IconUrl DefaultValue="https://localhost:3000/assets/icon-64.png" />
<HighResolutionIconUrl DefaultValue="https://localhost:300/assets/icon-
128.png" />

<!-- Other markup omitted -->

Conversion tools and options


There are several ways to carry out the remaining tasks, depending on the IDE and other
tools you want to use for your project, and on the tool you used to create the project.

Convert the project with Teams Toolkit


Projects created with the Office Yeoman Generator (aka "Yo Office")
NodeJS and npm projects that aren't created with Yeoman Generator

Convert the project with Teams Toolkit


The easiest way to convert is to use Teams Toolkit.

Prerequisites

Install Visual Studio Code


Install Teams Toolkit

Import the add-in project to Teams Toolkit

1. Open Visual Studio Code and select the Teams Toolkit icon on the Activity Bar.

2. Select Create a new app.


3. In the New Project drop down, select Outlook Add-in.

4. In the App Features Using an Outlook Add-in drop down, select Import an
Existing Outlook Add-in.

5. In the Existing add-in project folder drop down, browse to the root folder of the
add-in project.

6. In the Select import project manifest file drop down, browse to the XML manifest
file.

7. In the Workspace folder dialog, select the folder where you want to put the
converted project.

8. In the Application name dialog, give a name to the project (with no spaces). Teams
Toolkit creates the project with your source files and scaffolding. It then opens the
project in a second Visual Studio Code window. Close the original Visual Studio
Code window.

Sideload the add-in in Visual Studio Code

You can sideload the add-in using the Teams Toolkit or in a command prompt, bash
shell, or terminal.
Sideload with the Teams Toolkit

1. First, make sure Outlook desktop is closed.


2. In Visual Studio Code, open the Teams Toolkit.
3. In the ACCOUNTS section, verify that you're signed into Microsoft 365.
4. Select View | Run in Visual Studio Code. In the RUN AND DEBUG drop down
menu, select the option, Outlook Desktop (Edge Chromium), and then press F5.
The project builds and a Node dev-server window opens. This process may take a
couple of minutes and then Outlook desktop opens.
5. You can now work with your add-in. Be sure you're working in the Inbox of your
Microsoft 365 account identity.

Sideload with a system prompt, bash shell, or terminal

1. First, make sure Outlook desktop is closed.


2. Open a system prompt, bash shell, or the Visual Studio Code TERMINAL, and
navigate to the root of the project.
3. Run the command npm run start:desktop . The project builds and a Node dev-
server window opens. This process may take a couple of minutes then Outlook
desktop opens.
4. You can now work with your add-in.

Projects created with the Office Yeoman Generator (aka


"Yo Office")
If the project was created with the Office Yeoman Generator (using any option except
the "unified manifest" option) and you don't want to use the Teams Toolkit, convert it
using the following steps.

1. In the root of the project, open a command prompt or bash shell and run the
following command. This converts the manifest and updates the package.json to
specify current tooling packages. The new unified manifest is in the root of the
project and the old XML manifest is in a backup.zip file. For details about this
command, see Office-Addin-Project .

command line

npx office-addin-project convert -m <relative-path-to-XML-manifest>

2. Run npm install .


3. To sideload the add-in, run npm run start:desktop . This command puts the unified
manifest and the two image files into a zip file and sideloads it to the Office
application. It also starts server in a separate NodeJS window to host the add-in
files on localhost.

NodeJS and npm projects that aren't created with


Yeoman Generator
If you don't want to use the Teams Toolkit and your project wasn't created with the
Office Yeoman generator, use the office-addin-manifest-converter tool.

In the root of the project, open a command prompt or bash shell and run the following
command. This command puts the unified manifest in a subfolder with the same name
as the filename stem of the original XML manifest. For example, if the manifest is named
MyManifest.xml, the unified manifest is created at .\MyManifest\MyManifest.json. For
more details about this command, see Office-Addin-Manifest-Converter .

command line

npx office-addin-manifest-converter convert -m <relative-path-to-XML-


manifest>

Once you have the unified manifest created, there are two ways to create the zip file and
sideload it. They are described in the next two subsections.

Sideload with the Office-Addin-Debugging tool

1. To sideload the add-in, run the following command. This command puts the
unified manifest and two default icon image files into a zip file and sideloads it to
the Office application. It also starts a server in a separate NodeJS window to host
the add-in files on localhost. Note that you pass the path to the unified manifest
that you created in the previous step. For more details about this command, see
Office-Addin-Debugging .

command line

npx office-addin-debugging start <relative-path-to-unified-manifest>


desktop

2. When you use office-addin-debugging to start an add-in, always stop the session
with the following command. Closing the server window doesn't reliably stop the
server and closing the Office application doesn't reliably cause Office to unacquire
the add-in.

command line

npx office-addin-debugging stop <relative-path-to-unified-manifest>

Sideload with the Teams Toolkit CLI (command-line interface)

1. Manually create the zip package using the following steps.


a. Open the unified manifest and scroll to the "icons" property. Note the relative
path of the two image files.
b. Use any zip utility to create a zip file that contains the unified manifest and the
two image files. The image files must have the same relative path in the zip file as
they do in the project. For example, if the relative path is "assets/icon-64.png"
and "assets/icon-128.png", then you must include the "assets" folder with the
two files in the zip package.
c. If the folder contains other files, such as image files used in the Office ribbon,
remove these from the zip package. It should have only the two image files
specified in the "icons" property (in addition to the manifest in the root of the
zip package).

2. In the root of the project, open a command prompt or bash shell and run the
following commands.

command line

npm install -g @microsoft/teamsfx-cli

teamsfx m365 sideloading --file-path <relative-path-to-zip-file>


Office Add-ins manifest reference
Article • 05/02/2023

This section contains information about every element used by an Office Add-in's XML
manifest. To learn more about how the manifest describes your add-in to an Office
application, visit Office Add-ins XML manifest.

Elements
Name Category

Action VersionOverrides

AllFormFactors VersionOverrides

AllowSnapshot General

AlternateId General

AppDomain General

AppDomains General

CitationText General

Control VersionOverrides

Control (Button) VersionOverrides

Control (Menu) VersionOverrides

Control (MobileButton) VersionOverrides

CustomTab VersionOverrides

DefaultLocale General

DefaultSettings General

Description General

DesktopFormFactor VersionOverrides

DesktopSettings General

Dictionary General

DictionaryHomePage General
Name Category

DisableEntityHighlighting General

DisplayName General

Enabled VersionOverrides

EquivalentAddin General

EquivalentAddins General

Event VersionOverrides

ExtendedPermission VersionOverrides

ExtendedPermissions VersionOverrides

ExtensionPoint VersionOverrides

ExtendedOverrides General

FileName General

Form General

FormSettings General

FunctionFile VersionOverrides

GetStarted VersionOverrides

Group VersionOverrides

HighResolutionIconUrl General

Host General

Hosts General

Icon VersionOverrides

IconUrl General

Id General

Image VersionOverrides

Images VersionOverrides

Item VersionOverrides

Items VersionOverrides
Name Category

LaunchEvent VersionOverrides

LaunchEvents VersionOverrides

LongStrings VersionOverrides

Metadata General

Method General

Methods General

MobileFormFactor VersionOverrides

Namespace General

OfficeApp General

OfficeMenu VersionOverrides

OfficeTab VersionOverrides

OverriddenByRibbonApi VersionOverrides

Override General

Page VersionOverrides

Permissions General

PhoneSettings General

ProgId General

ProviderName General

QueryUri General

RequestedHeight General

RequestedWidth General

Requirements General

Resources VersionOverrides

Rule General

Runtime VersionOverrides

Runtimes VersionOverrides
Name Category

Scopes VersionOverrides

Script VersionOverrides

Set General

Sets General

ShortStrings VersionOverrides

SourceLocation General

SourceLocation (custom functions) VersionOverrides

String VersionOverrides

Supertip VersionOverrides

SupportsSharedFolders VersionOverrides

SupportUrl General

TabletSettings General

TargetDialect General

TargetDialects General

Tokens General

Token General

Type General

Url VersionOverrides

Urls VersionOverrides

Version General

VersionOverrides General

VersionOverrides 1.0 Content VersionOverrides

VersionOverrides 1.0 Mail VersionOverrides

VersionOverrides 1.1 Mail VersionOverrides

VersionOverrides 1.0 TaskPane VersionOverrides

WebApplicationInfo VersionOverrides
Work with Extended Overrides of the
manifest
Article • 03/14/2023

Some extensibility features of Office Add-ins are configured with JSON files that are
hosted on your server, instead of with the add-in's XML manifest.

7 Note

This article assumes that you're familiar with Office Add-in manifests and their role
in add-ins. Please read Office Add-ins manifest, if you haven't recently.

The following table specifies the extensibility features that require an extended override
along with links to documentation of the feature.

Feature Development Instructions

Keyboard shortcuts Add Custom keyboard shortcuts to your Office Add-ins

The schema that defines the JSON format is extended-manifest schema.

 Tip

This article is somewhat abstract. Consider reading one of the articles in the table
to add clarity to the concepts.

Tell Office where to find the JSON file


Use the manifest to tell Office where to find the JSON file. Immediately below (not
inside) the <VersionOverrides> element in the manifest, add an ExtendedOverrides
element. Set the Url attribute to the full URL of a JSON file. The following is an example
of the simplest possible <ExtendedOverrides> element.

XML

...
</VersionOverrides>
<ExtendedOverrides Url="https://contoso.com/addin/extended-
overrides.json"></ExtendedOverrides>
</OfficeApp>
The following is an example of a very simple extended overrides JSON file. It assigns
keyboard shortcut CTRL+SHIFT+A to a function (defined elsewhere) that opens the add-
in's task pane.

JSON

{
"actions": [
{
"id": "SHOWTASKPANE",
"type": "ExecuteFunction",
"name": "Show task pane for add-in"
}
],
"shortcuts": [
{
"action": "SHOWTASKPANE",
"key": {
"default": "CTRL+SHIFT+A"
}
}
]
}

Localize the extended overrides file


If your add-in supports multiple locales, you can use the ResourceUrl attribute of the
<ExtendedOverrides> element to point Office to a file of localized resources. The
following is an example.

XML

...
</VersionOverrides>
<ExtendedOverrides Url="https://contoso.com/addin/extended-
overrides.json"
ResourceUrl="https://contoso.com/addin/my-
resources.json">
</ExtendedOverrides>
</OfficeApp>

For more details about how to create and use the resources file, how to refer to its
resources in the extended overrides file, and for additional options not discussed here,
see Localize extended overrides.
How to find the proper order of
manifest elements
Article • 03/23/2023

The XML elements in the manifest of an Office Add-in must be under the proper parent
element and in a specific order, relative to each other, under the parent.

The required ordering is specified in the XSD files in the Schemas folder. The XSD files
are categorized into subfolders for taskpane, content, and mail add-ins.

For example, in the <OfficeApp> element, the <Id>, <Version>, <ProviderName>


must appear in that order. If an <AlternateId> element is added, it must be between
the <Id> and <Version> element. Your manifest will not be valid and your add-in will
not load, if any element is in the wrong order.

7 Note

The validator within office-addin-manifest uses the same error message when an
element is out-of-order as it does when an element is under the wrong parent. The
error says the child element is not a valid child of the parent element. If you get
such an error but the reference documentation for the child element indicates that
it is valid for the parent, then the problem is likely that the child has been placed in
the wrong order.

The following sections show the manifest elements in the order in which they must
appear. There are differences depending on whether the type attribute of the
<OfficeApp> element is TaskPaneApp , ContentApp , or MailApp . To keep these sections
from becoming too unwieldy, the highly complex <VersionOverrides> element is
broken out into separate sections.

7 Note

Not all of the elements shown are mandatory. If the minOccurs value for a element
is 0 in the schema, the element is optional.

Basic task pane add-in element ordering


XML
<OfficeApp xsi:type="TaskPaneApp">
<Id>
<AlternateID>
<Version>
<ProviderName>
<DefaultLocale>
<DisplayName>
<Override>
<Description>
<Override>
<IconUrl>
<Override>
<HighResolutionIconUrl>
<Override>
<SupportUrl>
<AppDomains>
<AppDomain>
<Hosts>
<Host>
<Requirements>
<Sets>
<Set>
<Methods>
<Method>
<DefaultSettings>
<SourceLocation>
<Override>
<Permissions>
<Dictionary>
<TargetDialects>
<QueryUri>
<CitationText>
<DictionaryName>
<DictionaryHomePage>
<VersionOverrides>*
<ExtendedOverrides>

*See Task pane add-in element ordering within VersionOverrides for the ordering of
children elements of VersionOverrides.

Basic mail add-in element ordering


XML

<OfficeApp xsi:type="MailApp">
<Id>
<AlternateId>
<Version>
<ProviderName>
<DefaultLocale>
<DisplayName>
<Override>
<Description>
<Override>
<IconUrl>
<Override>
<HighResolutionIconUrl>
<Override>
<SupportUrl>
<AppDomains>
<AppDomain>
<Hosts>
<Host>
<Requirements>
<Sets>
<Set>
<FormSettings>
<Form>
<DesktopSettings>
<SourceLocation>
<RequestedHeight>
<TabletSettings>
<SourceLocation>
<RequestedHeight>
<PhoneSettings>
<SourceLocation>
<Permissions>
<Rule>
<DisableEntityHighlighting>
<VersionOverrides>*

*See Mail add-in element ordering within VersionOverrides Ver. 1.0 and Mail add-in
element ordering within VersionOverrides Ver. 1.1 for the ordering of children elements
of VersionOverrides.

Basic content add-in element ordering


XML

<OfficeApp xsi:type="ContentApp">
<Id>
<AlternateId>
<Version>
<ProviderName>
<DefaultLocale>
<DisplayName>
<Override>
<Description>
<Override>
<IconUrl >
<Override>
<HighResolutionIconUrl>
<Override>
<SupportUrl>
<AppDomains>
<AppDomain>
<Hosts>
<Host>
<Requirements>
<Sets>
<Set>
<Methods>
<Method>
<DefaultSettings>
<SourceLocation>
<Override>
<RequestedWidth>
<RequestedHeight>
<Permissions>
<AllowSnapshot>
<VersionOverrides>*

*See Content add-in element ordering within VersionOverrides for the ordering of
children elements of VersionOverrides.

Task pane add-in element ordering within


VersionOverrides
XML

<VersionOverrides>
<Description>
<Requirements>
<Sets>
<Set>
<Hosts>
<Host>
<Runtimes>
<Runtime>
<AllFormFactors>
<ExtensionPoint>
<Script>
<SourceLocation>
<Page>
<SourceLocation>
<Metadata>
<SourceLocation>
<Namespace>
<DesktopFormFactor>
<GetStarted>
<Title>
<Description>
<LearnMoreUrl>
<FunctionFile>
<ExtensionPoint>
<OfficeTab>
<Group>
<Label>
<Icon>
<Image>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Enabled>
<Items>
<Item>
<Label>
<Supertip>
<Title>
<Description>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<CustomTab>
<Group> (can be below <ControlGroup>)
<OverriddenByRibbonApi>
<Label>
<Icon>
<Image>
<Control>
<OverriddenByRibbonApi>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Enabled>
<Items>
<Item>
<OverriddenByRibbonApi>
<Label>
<Supertip>
<Title>
<Description>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<ControlGroup> (can be above <Group>)
<Label>
<InsertAfter> (or <InsertBefore>)
<OfficeMenu>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Enabled>
<Items>
<Item>
<Label>
<Supertip>
<Title>
<Description>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Resources>
<Images>
<Image>
<Override>
<Urls>
<Url>
<Override>
<ShortStrings>
<String>
<Override>
<LongStrings>
<String>
<Override>
<WebApplicationInfo>
<Id>
<Resource>
<Scopes>
<Scope>
<EquivalentAddins>
<EquivalentAddin>
<ProgId>
<DisplayName>
<FileName>
<Type>

Mail add-in element ordering within


VersionOverrides Ver. 1.0
XML

<VersionOverrides>
<Description>
<Requirements>
<Sets>
<Set>
<Hosts>
<Host>
<DesktopFormFactor>
<ExtensionPoint>
<OfficeTab>
<Group>
<Label>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<SourceLocation>
<FunctionName>
<CustomTab>
<Group>
<Label>
<Icon>
<Image>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Items>
<Item>
<Label>
<Supertip>
<Title>
<Description>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Label>
<OfficeMenu>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Items>
<Item>
<Label>
<Supertip>
<Title>
<Description>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Resources>
<Images>
<Image>
<Override>
<Urls>
<Url>
<Override>
<ShortStrings>
<String>
<Override>
<LongStrings>
<String>
<Override>
<VersionOverrides>*

* A VersionOverrides with type value VersionOverridesV1_1 , instead of


VersionOverridesV1_0 , can be nested at the end of the outer VersionOverrides. See Mail
add-in element ordering within VersionOverrides Ver. 1.1 for the ordering of elements in
VersionOverridesV1_1 .

Mail add-in element ordering within


VersionOverrides Ver. 1.1
XML

<VersionOverrides>
<Description>
<Requirements>
<Sets>
<Set>
<Hosts>
<Host>
<DesktopFormFactor>
<ExtensionPoint>
<OfficeTab>
<Group>
<Label>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<SourceLocation>
<FunctionName>
<CustomTab>
<Group>
<Label>
<Icon>
<Image>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Items>
<Item>
<Label>
<Supertip>
<Title>
<Description>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Label>
<OfficeMenu>
<Control>
<Label>
<Supertip>
<Title>
<Description>
<Icon>
<Image>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<Items>
<Item>
<Label>
<Supertip>
<Title>
<Description>
<Action>
<TaskpaneId>
<SourceLocation>
<Title>
<FunctionName>
<SourceLocation>
<Label>
<CommandSurface>
<MobileFormFactor>
<ExtensionPoint>
<Group>
<Label>
<Control>
<Label>
<Icon>
<Image>
<Action>
<SourceLocation>
<FunctionName>
<Control>
<Label>
<Icon>
<Image>
<Action>
<SourceLocation>
<FunctionName>
<Resources>
<Images>
<Image>
<Override>
<Urls>
<Url>
<Override>
<ShortStrings>
<String>
<Override>
<LongStrings>
<String>
<Override>
<WebApplicationInfo>
<Id>
<Resource>
<Scopes>
<Scope>

Content add-in element ordering within


VersionOverrides
XML

<VersionOverrides>
<WebApplicationInfo>
<Id>
<Resource>
<Scopes>
<Scope>

See also
Reference for Office Add-ins manifests (v1.1)
Official schema definitions
Update to the latest Office JavaScript
API library and version 1.1 add-in
manifest schema
Article • 04/11/2023

This article describes how to update your JavaScript files (Office.js and app-specific .js
files) and add-in manifest validation file in your Office Add-in project to version 1.1.

7 Note

Projects created in Visual Studio 2019 or later will already use version 1.1. However,
there are occasional minor updates to version 1.1 that you can apply by using the
techniques in this article.

Use the most up-to-date project files


If you use Visual Studio to develop your add-in, to use the newest API members of the
Office JavaScript API and the v1.1 features of the add-in XML manifest (which is
validated against offappmanifest-1.1.xsd), you need to download the latest version of
Visual Studio. To download Visual Studio, see the Visual Studio IDE page . During
installation you'll need to select the Office/SharePoint development workload.

If you use a text editor or IDE other than Visual Studio to develop your add-in, you need
to update the references to the content delivery network (CDN) for Office.js and the
version of schema referenced in your add-in's manifest.

Updating an Office Add-in project created with


Visual Studio
For projects created before the release of v1.1 of the Office JavaScript API and add-in
manifest schema, you can update a project's files using the NuGet Package Manager,
and then update your add-in's HTML pages to reference them.

Note that the update process is applied on a per-project basis - you'll need to repeat the
updating process for each add-in project in which you want to use v1.1 of Office.js and
add-in manifest schema.
Update the Office JavaScript API library files in your
project to the newest release
The following steps will update your Office.js library files to the latest version. The steps
use Visual Studio 2019, but they are similar for previous versions of Visual Studio.

1. In Visual Studio 2019, open or create a new Office Add-in project.


2. Choose Tools > NuGet Package Manager > Manage Nuget Packages for
Solution.
3. Choose the Updates tab.
4. Select Microsoft.Office.js. Ensure the package source is from nuget.org.
5. In the left pane, choose Install and complete the package update process.

You'll need to take a few additional steps to complete the update. In the head tag of
your add-in's HTML pages, comment out or delete any existing office.js script
references, and reference the updated Office JavaScript API library as follows:

HTML

<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>

7 Note

The /1/ in the office.js in the CDN URL specifies to use the latest incremental
release within version 1 of Office.js.

Update the manifest file in your project to use schema


version 1.1
In your add-in's manifest file, update the xmlns attribute of the <OfficeApp> element
changing the version value to 1.1 (leaving attributes other than the xmlns attribute
unchanged).

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xsi:type="ContentApp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.microsoft.com/office/appforoffice/1.1">

<!-- manifest contents -->


</OfficeApp>

7 Note

After updating the version of the add-in manifest schema to 1.1, you will need to
remove the Capabilities and Capability elements, and replace them with either the
Hosts and Host elements or the Requirements and Requirement elements.

Updating an Office Add-in project created with


a text editor or other IDE
For projects created before the release of v1.1 of the Office JavaScript API and add-in
manifest schema, you need to update your add-in's HTML pages to reference CDN of
the v1.1 library, and update your add-in's manifest file to use schema v1.1.

The update process is applied on a per-project basis - you'll need to repeat the updating
process for each add-in project in which you want to use v1.1 of Office.js and add-in
manifest schema.

You don't need local copies of the Office JavaScript API files (Office.js and app-specific
.js files) to develop anOffice Add-in (referencing the CDN for Office.js downloads the
necessary files at runtime), but if you want a local copy of the library files you can use
the NuGet Command-Line Utility and the Install-Package Microsoft.Office.js
command to download them.

7 Note

To get a copy of the XSD (XML Schema Definition) for the v1.1 add-in manifest, see
the listing in Schema reference for Office Add-ins manifests.

Update the Office JavaScript API library files in your


project to use the newest release
1. Open the HTML pages for your add-in in your text editor or IDE.

2. In the head tag of your add-in's HTML pages, comment out or delete any existing
office.js script references, and reference the updated Office JavaScript API library as
follows:
HTML

<script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>

7 Note

The /1/ in front of office.js in the CDN URL specifies to use the latest
incremental release within version 1 of Office.js.

Update the manifest file in your project to use schema


version 1.1
In your add-in's manifest file, update the xmlns attribute of the <OfficeApp> element
changing the version value to 1.1 (leaving attributes other than the xmlns attribute
unchanged).

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xsi:type="ContentApp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.microsoft.com/office/appforoffice/1.1">

<!-- manifest contents -->

</OfficeApp>

7 Note

After updating the version of the add-in manifest schema to 1.1, you will need to
remove the Capabilities and Capability elements, and replace them with either the
Hosts and Host elements or the Requirements and Requirement elements.

See also
Specify Office applications and API requirements ]
Understanding the Office JavaScript API
Office JavaScript API
Schema reference for Office Add-ins manifests
Privacy and security for Office Add-ins
Article • 04/11/2023

Process security
Office Add-ins are secured by an add-in runtime environment, a multiple-tier
permissions model, and performance governors. This framework protects the user's
experience in the following ways.

Access to the Office client application's UI frame is managed.

Only indirect access to the Office client application's UI thread is allowed.

Modal interactions aren't allowed - for example, calls to JavaScript alert , confirm ,
and prompt methods aren't allowed because they're modal.

Further, the runtime framework provides the following benefits to ensure that an Office
Add-in can't damage the user's environment.

Isolates the process the add-in runs in.

Doesn't require .dll or .exe replacement or ActiveX components.

Makes add-ins easy to install and uninstall.

Also, the use of memory, CPU, and network resources by Office Add-ins is governable to
ensure that good performance and reliability are maintained.

7 Note

In some scenarios, different features of an add-in run in separate runtimes. For


simplicity, this article uses the singular "runtime." For more information, see
Runtimes in Office Add-ins.

The following sections briefly describe how the runtime architecture supports running
add-ins in Office clients on Windows-based devices, on Mac OS X devices, and in web
browsers.

Clients on Windows and OS X devices


In supported clients for desktop and tablet devices, such as Excel on Windows, and
Outlook on Windows and Mac, Office Add-ins are supported by integrating an in-
process component, the Office Add-ins runtime, which manages the add-in lifecycle and
enables interoperability between the add-in and the client application. The add-in
webpage itself is hosted out-of-process. As shown in figure 1, on a Windows desktop or
tablet device, the add-in webpage is hosted inside an Internet Explorer or Microsoft
Edge control which, in turn, is hosted inside an add-in runtime process that provides
security and performance isolation.

On Windows desktops, Protected Mode in Internet Explorer must be enabled for the
Restricted Site Zone. This is typically enabled by default. If it is disabled, an error will
occur when you try to launch an add-in.

Figure 1. Office Add-ins runtime environment in Windows-based desktop and tablet clients

As shown in the following figure, on a Mac OS X desktop, the add-in web page is hosted
inside a sandboxed WebKit runtime host process which helps provide similar level of
security and performance protection.

Figure 2. Office Add-ins runtime environment in Mac OS X clients


The Office Add-ins runtime manages interprocess communication, the translation of
JavaScript API calls and events into native ones, as well as UI remoting support to enable
the add-in to be rendered inside the document, in a task pane, or adjacent to an email
message, meeting request, or appointment.

Web clients
In supported web clients, Office Add-ins are hosted in an iframe that runs using the
HTML5 sandbox attribute. ActiveX components or navigating the main page of the web
client are not allowed. Office Add-ins support is enabled in the web clients by the
integration of the JavaScript API for Office. In a similar way to the desktop client
applications, the JavaScript API manages the add-in lifecycle and interoperability
between the add-in and the web client. This interoperability is implemented by using a
special cross-frame post message communication infrastructure. The same JavaScript
library (Office.js) that is used on desktop clients is available to interact with the web
client. The following figure shows the infrastructure that supports add-ins in Office
running in the browser, and the relevant components (the web client, iframe, Office
Add-ins runtime, and JavaScript API for Office) that are required to support them.

Figure 3. Infrastructure that supports Office Add-ins in Office web clients


Add-in integrity in AppSource
You can make your Office Add-ins available to the public by publishing them to
AppSource. AppSource enforces the following measures to maintain the integrity of
add-ins.

Requires the host server of an Office Add-in to always use Secure Sockets Layer
(SSL) to communicate.

Requires a developer to provide proof of identity, a contractual agreement, and a


compliant privacy policy to submit add-ins.

Supports a user-review system for available add-ins to promote a self-policing


community.

Optional connected experiences


End users and IT admins can turn off optional connected experiences in Office desktop
and mobile clients. For Office Add-ins, the impact of disabling the Optional connected
experiences setting is that users can no longer access add-ins or the Office Store
through these clients. However, certain Microsoft add-ins that are considered essential
or business-critical, and add-ins deployed by an organization's IT admin through
Centralized Deployment will still be available. Additionally, add-ins and the Store remain
available in Outlook on the web, regardless of the status of the setting.
For more about Outlook-specific behavior, see Privacy, permissions, and security for
Outlook add-ins.

Note that if an IT admin disables the use of connected experiences in Office, it has the
same effect on add-ins as turning off just optional connected experiences.

Addressing end users' privacy concerns


This section describes the protection offered by the Office Add-ins platform from the
customer's (end user's) perspective, and provides guidelines for how to support users'
expectations and how to securely handle users' personally identifiable information (PII).

End users' perspective


Office Add-ins are built using web technologies that run in a browser control or iframe.
Because of this, using add-ins is similar to browsing to web sites on the Internet or
intranet. Add-ins can be external to an organization (if you acquire the add-in from
AppSource) or internal (if you acquire the add-in from an Exchange Server add-in
catalog, SharePoint app catalog, or file share on an organization's network). Add-ins
have limited access to the network and most add-ins can read or write to the active
document or mail item. The add-in platform applies certain constraints before a user or
administrator installs or starts an add-in. But as with any extensibility model, users
should be cautious before starting an unknown add-in.

7 Note

Users may see a security prompt to trust the domain the first time an add-in is
loaded. This will happen if the add-in's domain host is outside of the domain of
Exchange on-premise or Office Online Server.

The add-in platform addresses end users' privacy concerns in the following ways.

Data communicated with the web server that hosts a content, Outlook or task pane
add-in as well as communication between the add-in and any web services it uses
must be encrypted using the Secure Socket Layer (SSL) protocol.

Before a user installs an add-in from AppSource, the user can view the privacy
policy and requirements of that add-in. In addition, Outlook add-ins that interact
with users' mailboxes surface the specific permissions that they require; the user
can review the terms of use, requested permissions and privacy policy before
installing an Outlook add-in.
When sharing a document, users also share add-ins that have been inserted in or
associated with that document. If a user opens a document that contains an add-in
that the user hasn't used before, the Office client application prompts the user to
grant permission for the add-in to run in the document. In an organizational
environment, the Office client application also prompts the user if the document
comes from an external source.

Users can enable or disable the access to AppSource. For content and task pane
add-ins, users manage access to trusted add-ins and catalogs from the Trust
Center on the host Office client (opened from File > Options > Trust Center >
Trust Center Settings > Trusted Add-in Catalogs). In Outlook, access to manage
add-ins depends on the user's Outlook client.
For instructions on how to manage add-ins in Outlook on Windows or on Mac,
see Get an Office Add-in for Outlook .
In Outlook on the web, select Get Add-ins from the ribbon. To learn more, see
Using add-ins in Outlook on the web .

Administrators can also manage access to AppSource through the admin center.

The design of the add-in platform provides security and performance for end users
in the following ways.

An Office Add-in runs in a web browser control that is hosted in an add-in


runtime environment separate from the Office client application. This design
provides both security and performance isolation from the client application.

Running in a web browser control allows the add-in to do almost anything a


regular web page running in a browser can do but, at the same time, restricts
the add-in to observe the same-origin policy for domain isolation and security
zones.

Outlook add-ins provide additional security and performance features through Outlook
add-in specific resource usage monitoring. For more information, see Privacy,
permissions, and security for Outlook add-ins.

Developer guidelines to handle PII


The following lists some specific PII protection guidelines for you as a developer of
Office Add-ins.

The Settings object is intended for persisting add-in settings and state data across
sessions for a content or task pane add-in, but don't store passwords and other
sensitive PII in the Settings object. The data in the Settings object isn't visible to
end users, but it is stored as part of the document's file format which is readily
accessible. You should limit your add-in's use of PII and store any PII required by
your add-in on the server hosting your add-in as a user-secured resource.

Using some applications can reveal PII. Make sure that you securely store data for
your users' identity, location, access times, and any other credentials so that data
won't become available to other users of the add-in.

If your add-in is available in AppSource, the AppSource requirement for HTTPS


protects PII transmitted between your web server and the client computer or
device. However, if you re-transmit that data to other servers, make sure you
observe the same level of protection.

If you store users' PII, make sure you reveal that fact, and provide a way for users
to inspect and delete it. If you submit your add-in to AppSource, you can outline
the data you collect and how it's used in the privacy statement.

Developers' permission choices and security


practices
Follow these general guidelines to support the security model of Office Add-ins, and
drill down on more details for each add-in type.

Permissions choices
The add-in platform provides a permissions model that your add-in uses to declare the
level of access to a user's data that it requires for its features. Each permission level
corresponds to the subset of the JavaScript API for Office your add-in is allowed to use
for its features. For example, the WriteDocument permission for content and task pane
add-ins allows access to the Document.setSelectedDataAsync method that lets an add-
in write to the user's document, but doesn't allow access to any of the methods for
reading data from the document. This permission level makes sense for add-ins that
only need to write to a document, such as an add-in where the user can query for data
to insert into their document.

As a best practice, you should request permissions based on the principle of least
privilege. That is, you should request permission to access only the minimum subset of
the API that your add-in requires to function correctly. For example, if your add-in needs
only to read data in a user's document for its features, you should request no more than
the ReadDocument permission. (But, keep in mind that requesting insufficient
permissions will result in the add-in platform blocking your add-in's use of some APIs
and will generate errors at run time.)

You specify permissions in the manifest of your add-in, as shown in the example in this
section below, and end users can see the requested permission level of an add-in before
they decide to install or activate the add-in for the first time. Additionally, Outlook add-
ins that request the ReadWriteMailbox permission require explicit administrator
privilege to install.

The following example shows how a task pane add-in specifies the ReadDocument
permission in its manifest. To keep permissions as the focus, other elements in the
manifest aren't displayed.

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ver="http://schemas.microsoft.com/office/appforoffice/1.0"
xsi:type="TaskPaneApp">

... <!-- To keep permissions as the focus, not displaying other elements. --
>
<Permissions>ReadDocument</Permissions>
...
</OfficeApp>

For more information about permissions for task pane and content add-ins, see
Requesting permissions for API use in add-ins.

For more information about permissions for Outlook add-ins, see the following topics.

Privacy, permissions, and security for Outlook add-ins

Understanding Outlook add-in permissions

Same origin policy


Because Office Add-ins are webpages that run in a web browser control, they must
follow the same-origin policy enforced by the browser. By default, a webpage in one
domain can't make XmlHttpRequest web service calls to another domain other than
the one where it is hosted.

One way to overcome this limitation is to use JSON/P -- provide a proxy for the web
service by including a script tag with a src attribute that points to some script hosted on
another domain. You can programmatically create the script tags, dynamically creating
the URL to which to point the src attribute, and passing parameters to the URL via URI
query parameters. Web service providers create and host JavaScript code at specific
URLs, and return different scripts depending on the URI query parameters. These scripts
then execute where they are inserted and work as expected.

The following is an example of JSON/P in the Outlook add-in example.

JavaScript

// Dynamically create an HTML SCRIPT element that obtains the details for
the specified video.
function loadVideoDetails(videoIndex) {
// Dynamically create a new HTML SCRIPT element in the webpage.
const script = document.createElement("script");
// Specify the URL to retrieve the indicated video from a feed of a
current list of videos,
// as the value of the src attribute of the SCRIPT element.
script.setAttribute("src", "https://gdata.youtube.com/feeds/api/videos/"
+
videos[videoIndex].Id + "?alt=json-in-
script&amp;callback=videoDetailsLoaded");
// Insert the SCRIPT element at the end of the HEAD section.
document.getElementsByTagName('head')[0].appendChild(script);
}

Exchange and SharePoint provide client-side proxies to enable cross-domain access. In


general, same origin policy on an intranet isn't as strict as on the Internet. For more
information, see Same Origin Policy Part 1: No Peeking and Addressing same-origin
policy limitations in Office Add-ins.

Tips to prevent malicious cross-site scripting


An ill-intentioned user could attack the origin of an add-in by entering malicious script
through the document or fields in the add-in. A developer should process user input to
avoid executing a malicious user's JavaScript within their domain. The following are
some good practices to follow to handle user input from a document or mail message,
or via fields in an add-in.

Instead of the DOM property innerHTML , use the innerText and textContent
properties where appropriate. Do the following for Internet Explorer and Firefox
cross-browser support.

JavaScript

var text = x.innerText || x.textContent


For information about the differences between innerText and textContent, see
Node.textContent . For more information about DOM compatibility across
common browsers, see W3C DOM Compatibility - HTML .

If you must use innerHTML, make sure the user's input doesn't contain malicious
content before passing it to innerHTML. For more information and an example of
how to use innerHTML safely, see innerHTML property.

If you are using jQuery, use the .text() method instead of the .html() method.

Use the toStaticHTML method to remove any dynamic HTML elements and
attributes in users' input before passing it to innerHTML.

Use the encodeURIComponent or encodeURI function to encode text that is


intended to be a URL that comes from or contains user input.

See Developing secure add-ins for more best practices to create more secure web
solutions.

Tips to prevent "Clickjacking"


Because Office Add-ins are rendered in an iframe when running in a browser with Office
client applications, use the following tips to minimize the risk of clickjacking -- a
technique used by hackers to fool users into revealing confidential information.

First, identify sensitive actions that your add-in can perform. These include any actions
that an unauthorized user could use with malicious intent, such as initiating a financial
transaction or publishing sensitive data. For example, your add-in might let the user
send a payment to a user-defined recipient.

Second, for sensitive actions, your add-in should confirm with the user before it
executes the action. This confirmation should detail what effect the action will have. It
should also detail how the user can prevent the action, if necessary, whether by
choosing a specific button marked "Don't Allow" or by ignoring the confirmation.

Third, to ensure that no potential attacker can hide or mask the confirmation, you
should display it outside the context of the add-in (that is, not in an HTML dialog box).

The following are some examples of how you could get confirmation.

Send an email to the user that contains a confirmation link.

Send a text message to the user that includes a confirmation code that the user
can enter in the add-in.
Open a confirmation dialog in a new browser window to a page that cannot be
iframed. This is typically the pattern that is used by login pages. Use the dialog api
to create a new dialog.

Also, ensure that the address you use for contacting the user couldn't have been
provided by a potential attacker. For example, for payment confirmations use the
address on file for the authorized user's account.

Other security practices


Developers should also take note of the following security practices.

Developers shouldn't use ActiveX controls in Office Add-ins as ActiveX controls


don't support the cross-platform nature of the add-in platform.

Content and task pane add-ins assume the same SSL settings that the browser
uses by default, and allows most content to be delivered only by SSL. Outlook add-
ins require all content to be delivered by SSL. Developers must specify in the
<SourceLocation> element of the add-in manifest a URL that uses HTTPS, to
identify the location of the HTML file for the add-in.

To make sure add-ins aren't delivering content by using HTTP, when testing add-
ins, developers should make sure the following settings are selected in Internet
Options in Control Panel and no security warnings appear in their test scenarios.

Make sure the security setting, Display mixed content, for the Internet zone is
set to Prompt. You can do that by selecting the following in Internet Options:
on the Security tab, select the Internet zone, select Custom level, scroll to look
for Display mixed content, and select Prompt if it isn't already selected.

Make sure Warn if Changing between Secure and not secure mode is selected
in the Advanced tab of the Internet Options dialog box.

To make sure that add-ins don't use excessive CPU core or memory resources and
cause any denial of service on a client computer, the add-in platform establishes
resource usage limits. As part of testing, developers should verify whether an add-
in performs within the resource usage limits.

Before publishing an add-in, developers should make sure that any personal
identifiable information that they expose in their add-in files is secure.

Developers shouldn't embed keys that they use to access APIs or services from
Microsoft and others (such as Bing, Google, or Facebook) directly in the HTML
pages of their add-in. Instead, they should create a custom web service or store
the keys in some other form of secure web storage that they can then call to pass
the key value to their add-in.

Developers should do the following when submitting an add-in to AppSource.


Host the add-in they are submitting on a web server that supports SSL.
Produce a statement outlining a compliant privacy policy.
Be ready to sign a contractual agreement upon submitting the add-in.

Other than resource usage rules, developers for Outlook add-ins should also make sure
their add-ins observe limits for specifying activation rules and using the JavaScript API.
For more information, see Limits for activation and JavaScript API for Outlook add-ins.

IT administrators' control
In a corporate setting, IT administrators have ultimate authority over enabling or
disabling access to AppSource and any private catalogs.

The management and enforcement of Office settings is done with group policy settings.
These are configurable through the Office Deployment Tool, in conjunction with the
Office Customization Tool.

Setting name Description

Allow Unsecure Allows users to run non-secure Office Add-ins, which are Office Add-ins that
web add-ins and have webpage or catalog locations that are not SSL-secured (https://) and are
Catalogs not in users' Internet zones.

Block Web Add- Allows you to prevent users from running Office Add-ins that use web
ins technologies.

Block the Office Allows you to prevent users from getting or running Office Add-ins that come
Store from the Office Store.

See also
Requesting permissions for API use in add-ins
Privacy, permissions, and security for Outlook add-ins
Understanding Outlook add-in permissions
Limits for activation and JavaScript API for Outlook add-ins
Addressing same-origin policy limitations in Office Add-ins
Same Origin Policy
Same Origin Policy Part 1: No Peeking
Same origin policy for JavaScript
IE Protect Mode
Privacy controls for Microsoft 365 Apps
Requesting permissions for API use in
add-ins
Article • 05/18/2023

This article describes the different permission levels that you can declare in your content
or task pane add-in's manifest to specify the level of JavaScript API access your add-in
requires for its features.

7 Note

To learn about permission levels for mail (Outlook) add-ins, see Outlook
permissions model.

Permissions model
A five-level JavaScript API access-permissions model provides the basis for privacy and
security for users of your content and task pane add-ins. Figure 1 shows the five levels
of API permissions you can declare in your add-in's manifest.

Figure 1. The five-level permission model for content and task pane add-ins
These permissions specify the subset of the API that the add-in runtime will allow your
content or task pane add-in to use when a user inserts, and then activates (trusts) your
add-in. To declare the permission level your content or task pane add-in requires,
specify one of the permission text values in the Permissions element of your add-in's
manifest. The following example requests the WriteDocument permission, which will
allow only methods that can write to (but not read) the document.

XML

<Permissions>WriteDocument</Permissions>

As a best practice, you should request permissions based on the principle of least
privilege. That is, you should request permission to access only the minimum subset of
the API that your add-in requires to function correctly. For example, if your add-in needs
only to read data in a user's document for its features, you should request no more than
the ReadDocument permission.
The following table describes the subset of the JavaScript API that is enabled by each
permission level.

Permission Enabled subset of the API

Restricted The methods of the Settings object, and the


Document.getActiveViewAsync method. This is the minimum permission
level that can be requested by a content or task pane add-in.

ReadDocument In addition to the API allowed by the Restricted permission, adds access
to the API members necessary to read the document and manage
bindings. This includes the use of:

The Document.getSelectedDataAsync method to get the selected


text, HTML (Word only), or tabular data, but not the underlying
Open Office XML (OOXML) code that contains all of the data in the
document.
The Document.getFileAsync method to get all of the text in the
document, but not the underlying OOXML binary copy of the
document.
The Binding.getDataAsync method for reading bound data in the
document.
The addFromNamedItemAsync, addFromPromptAsync,
addFromSelectionAsync methods of the Bindings object for
creating bindings in the document.
The getAllAsync, getByIdAsync, and releaseByIdAsync methods of
the Bindings object for accessing and removing bindings in the
document.
The Document.getFilePropertiesAsync method to access document
file properties, such as the URL of the document.
The Document.goToByIdAsync method to navigate to named
objects and locations in the document.
For task pane add-ins for Project, all of the "get" methods of the
ProjectDocument object.

ReadAllDocument In addition to the API allowed by the Restricted and ReadDocument


permissions, allows the following additional access to document data.

The Document.getSelectedDataAsync and Document.getFileAsync


methods can access the underlying OOXML code of the document
(which in addition to the text may include formatting, links,
embedded graphics, comments, revisions, and so forth).

WriteDocument In addition to the API allowed by the Restricted permission, adds access
to the following API members.

The Document.setSelectedDataAsync method to write to the user's


selection in the document.
Permission Enabled subset of the API

ReadWriteDocument In addition to the API allowed by the Restricted, ReadDocument,


ReadAllDocument, and WriteDocument permissions, includes access to
all remaining API supported by content and task pane add-ins, including
methods for subscribing to events. You must declare the
ReadWriteDocument permission to access these additional API members:

The Binding.setDataAsync method for writing to bound regions of


the document.
The TableBinding.addRowsAsync method for adding rows to bound
tables.
The TableBinding.addColumnsAsync method for adding columns to
bound tables.
The TableBinding.deleteAllDataValuesAsync method for deleting all
data in a bound table.
The setFormatsAsync, clearFormatsAsync, and
setTableOptionsAsync methods of the TableBinding object for
setting formatting and options on bound tables.
All of the members of the CustomXmlNode, CustomXmlPart,
CustomXmlParts, and CustomXmlPrefixMappings objects.
All of the methods for subscribing to the events supported by
content and task pane add-ins, specifically the addHandlerAsync
and removeHandlerAsync methods of the Binding, CustomXmlPart,
Document, ProjectDocument, and Settings objects.

See also
Privacy and security for Office Add-ins
Develop your Office Add-in to work
with ITP when using third-party cookies
Article • 08/23/2022

If your Office Add-in requires third-party cookies, those cookies are blocked if the
Runtime that loaded your add-in uses Intelligent Tracking Prevention (ITP). You may be
using third-party cookies to authenticate users, or for other scenarios, such as storing
settings.

If your Office Add-in and website must rely on third-party cookies, use the following
steps to work with ITP.

1. Set up OAuth 2.0 Authorization so that the authenticating domain (in your case,
the third-party that expects cookies) forwards an authorization token to your
website. Use the token to establish a first-party login session with a server-
set Secure and HttpOnly cookie .
2. Use the Storage Access API so that the third-party can request permission to get
access to its first-party cookies. Current versions of Office on Mac and Office on
the web both support this API.

7 Note

If you're using cookies for purposes other than authentication, then consider
using localStorage for your scenario.

The following code sample shows how to use the Storage Access API.

JavaScript

function displayLoginButton() {
const button = createLoginButton();
button.addEventListener("click", function(ev) {
document.requestStorageAccess().then(function() {
authenticateWithCookies();
}).catch(function() {
// User must have previously interacted with this domain loaded in a
top frame
// Also you should have previously written a cookie when domain was
loaded in the top frame
console.error("User cancelled or requirements were not met.");
});
});
}
if (document.hasStorageAccess) {
document.hasStorageAccess().then(function(hasStorageAccess) {
if (!hasStorageAccess) {
displayLoginButton();
} else {
authenticateWithCookies();
}
});
} else {
authenticateWithCookies();
}

About ITP and third-party cookies


Third-party cookies are cookies that are loaded in an iframe, where the domain is
different from the top level frame. ITP could affect complex authentication scenarios,
where a popup dialog is used to enter credentials and then the cookie access is needed
by an add-in iframe to complete the authentication flow. ITP could also affect silent
authentication scenarios, where you have previously used a popup dialog to
authenticate, but subsequent use of the add-in tries to authenticate through a hidden
iframe.

When developing Office Add-ins on Mac, access to third-party cookies is blocked by the
MacOS Big Sur SDK. This is because WKWebView ITP is enabled by default on the Safari
browser, and WKWebView blocks all third-party cookies. Office on Mac version 16.44 or
later is integrated with the MacOS Big Sur SDK.

In the Safari browser, end users can toggle the Prevent cross-site tracking checkbox
under Preference > Privacy to turn off ITP. However, ITP cannot be turned off for the
embedded WKWebView control.

See also
Handle ITP in Safari and other browsers where third-party cookies are blocked
Tracking Prevention in WebKit
Chrome’s “Privacy Sandbox”
Introducing the Storage Access API
Addressing same-origin policy
limitations in Office Add-ins
Article • 08/02/2023

The same-origin policy enforced by the browser or webview control prevents a script
loaded from one domain from getting or manipulating properties of a webpage from
another domain. This means that, by default, the domain of a requested URL must be
the same as the domain of the current webpage. For example, this policy will prevent a
webpage in one domain from making XmlHttpRequest web-service calls to a domain
other than the one where it is hosted.

Because Office Add-ins are hosted in a webview control, the same-origin policy applies
to script running in their web pages as well.

The same-origin policy can be an unnecessary handicap in many situations, such as


when a web application hosts content and APIs across multiple subdomains. There are a
few common techniques for securely overcoming same-origin policy enforcement. This
article can only provide the briefest introduction to some of them. Please use the links
provided to get started in your research of these techniques.

Use JSONP for anonymous access


One way to overcome same-origin policy limitations is to use JSONP to provide a
proxy for the web service. You do this by including a script tag with a src attribute
that points to some script hosted on any domain. You can programmatically create the
script tags, dynamically create the URL to point the src attribute to, and then pass
parameters to the URL via URI query parameters. Web service providers create and host
JavaScript code at specific URLs, and return different scripts depending on the URI query
parameters. These scripts then execute where they are inserted and work as expected.

The following is an example of JSONP that uses a technique that will work in any Office
Add-in.

JavaScript

// Dynamically create an HTML SCRIPT element that obtains the details for
the specified video.
function loadVideoDetails(videoIndex) {
// Dynamically create a new HTML SCRIPT element in the webpage.
const script = document.createElement("script");
// Specify the URL to retrieve the indicated video from a feed of a
current list of videos,
// as the value of the src attribute of the SCRIPT element.
script.setAttribute("src", "https://gdata.youtube.com/feeds/api/videos/"
+
videos[videoIndex].Id + "?alt=json-in-
script&amp;callback=videoDetailsLoaded");
// Insert the SCRIPT element at the end of the HEAD section.
document.getElementsByTagName('head')[0].appendChild(script);
}

Implement server-side code using a token-


based authorization scheme
Another way to address same-origin policy limitations is to provide server-side code
that uses OAuth 2.0 flows to enable one domain to get authorized access to resources
hosted on another.

Use cross-origin resource sharing (CORS)


To learn more about cross-origin resource sharing, see the many resources available on
the web, such as Cross-Origin Resource Sharing (CORS) .

Build your own proxy using IFRAME and POST


MESSAGE (Cross-Window Messaging)
For an example of how to build your own proxy using IFRAME and POST MESSAGE, see
Cross-Window Messaging .

See also
Privacy and security for Office Add-ins
Overview of authentication and
authorization in Office Add-ins
Article • 06/23/2023

Office Add-ins allow anonymous access by default, but you can require users to sign in
to use your add-in with a Microsoft account, a Microsoft 365 Education or work account,
or other common account. This task is called user authentication because it enables the
add-in to know who the user is.

Your add-in can also get the user's consent to access their Microsoft Graph data (such as
their Microsoft 365 profile, OneDrive files, and SharePoint data) or data in other external
sources such as Google, Facebook, LinkedIn, SalesForce, and GitHub. This task is called
add-in (or app) authorization, because it's the add-in that is being authorized, not the
user.

Key resources for authentication and


authorization
This documentation explains how to build and configure Office Add-ins to successfully
implement authentication and authorization. However, many concepts and security
technologies mentioned are outside the scope of this documentation. For example,
general security concepts such as OAuth flows, token caching, or identity management
are not explained here. This documentation also doesn't document anything specific to
Microsoft Azure or the Microsoft identity platform. We recommend you refer to the
following resources if you need more information in those areas.

Microsoft identity platform


Microsoft identity platform support and help options for developers
OAuth 2.0 and OpenID Connect protocols on the Microsoft identity platform

SSO scenarios
Using Single Sign-on (SSO) is convenient for the user because they only have to sign in
once to Office. They don't need to sign in separately to your add-in. SSO isn't supported
on all versions of Office, so you'll still need to implement an alternative sign-in
approach, by using the Microsoft identity platform. For more information on supported
Office versions, see Identity API requirement sets
Get the user's identity through SSO
Often your add-in only needs the user's identity. For example, you may just want to
personalize your add-in and display the user's name on the task pane. Or you might
want a unique ID to associate the user with their data in your database. This can be
accomplished by just getting the access token for the user from Office.

To get the user's identity through SSO, call the getAccessToken method. The method
returns an access token that is also an identity token containing several claims that are
unique to the current signed in user, including preferred_username , name , sub , and oid .
For more information on these properties, see Microsoft identity platform ID tokens. For
an example of the token returned by getAccessToken, see Example access token.

If the user isn't signed in, Office will open a dialog box and use the Microsoft identity
platform to request the user to sign in. Then the method will return an access token, or
throw an error if unable to sign in the user.

In a scenario where you need to store data for the user, refer to Microsoft identity
platform ID tokens for information about how to get a value from the token to uniquely
identify the user. Use that value to look up the user in a user table or user database that
you maintain. Use the database to store user-relative information such as the user's
preferences or the state of the user's account. Since you're using SSO, your users don't
sign-in separately to your add-in, so you don't need to store a password for the user.

Before you begin implementing user authentication with SSO, be sure that you're
thoroughly familiar with the article Enable single sign-on for Office Add-ins.

Access your Web APIs through SSO


If your add-in has server-side APIs that require an authorized user, call the
getAccessToken method to get an access token. The access token provides access to
your own web server (configured through a Microsoft Azure app registration). When you
call APIs on your web server, you also pass the access token to authorize the user.

The following code shows how to construct an HTTPS GET request to the add-in's web
server API to get some data. The code runs on the client side, such as in a task pane. It
first gets the access token by calling getAccessToken . Then it constructs an AJAX call
with the correct authorization header and URL for the server API.

JavaScript

function getOneDriveFileNames() {
let accessToken = await Office.auth.getAccessToken();

$.ajax({
url: "/api/data",
headers: { "Authorization": "Bearer " + accessToken },
type: "GET"
})
.done(function (result) {
//... work with data from the result...
});
}

The following code shows an example /api/data handler for the REST call from the
previous code example. The code is ASP.NET code running on a web server. The
[Authorize] attribute will require that a valid access token is passed from the client, or

it'll return an error to the client.

C#

[Authorize]
// GET api/data
public async Task<HttpResponseMessage> Get()
{
//... obtain and return data to the client-side code...
}

Access Microsoft Graph through SSO


In some scenarios, not only do you need the user's identity, but you also need to access
Microsoft Graph resources on behalf of the user. For example, you may need to send an
email, or create a chat in Teams on behalf of the user. These actions and more can be
accomplished through Microsoft Graph. You'll need to follow these steps:

1. Get the access token for the current user through SSO by calling getAccessToken. If
the user isn't signed in, Office will open a dialog box and sign in the user with the
Microsoft identity platform. After the user signs in, or if the user is already signed
in, the method returns an access token.
2. Pass the access token to your server-side code.
3. On the server-side, use the OAuth 2.0 On-Behalf-Of flow to exchange the access
token for a new access token containing the necessary delegated user identity and
permissions to call Microsoft Graph.

7 Note
For best security to avoid leaking the access token, always perform the On-Behalf-
Of flow on the server-side. Call Microsoft Graph APIs from your server, not the
client. Don't return the access token to the client-side code.

Before you begin implementing SSO to access Microsoft Graph in your add-in, be sure
that you're thoroughly familiar with the following articles.

Enable single sign-on for Office Add-ins


Authorize to Microsoft Graph with SSO

You should also read at least one of the following articles that'll walk you through
building an Office Add-in to use SSO and access Microsoft Graph. Even if you don't
carry out the steps, they contain valuable information about how you implement SSO
and the On-Behalf-Of flow.

Create an ASP.NET Office Add-in that uses single sign-on which walks you through
the sample at Office Add-in ASP.NET SSO .
Create an Node.js Office Add-in that uses single sign-on which walks you through
the sample at Office Add-in NodeJS SSO .

Non-SSO scenarios
In some scenarios, you may not want to use SSO. For example, you may need to
authenticate using a different identity provider than the Microsoft identity platform.
Also, SSO isn't supported in all scenarios. For example, older versions of Office don't
support SSO. In this case, you'd need to fall back to an alternate authentication system
for your add-in.

Authenticate with the Microsoft identity platform


Your add-in can sign in users using the Microsoft identity platform as the authentication
provider. Once you've signed in the user, you can then use the Microsoft identity
platform to authorize the add-in to Microsoft Graph or other services managed by
Microsoft. Use this approach as an alternate sign-in method when SSO through Office is
unavailable. Also, there are scenarios in which you want to have your users sign in to
your add-in separately even when SSO is available; for example, if you want them to
have the option of signing in to the add-in with a different ID from the one with which
they're currently signed in to Office.

It's important to note that the Microsoft identity platform doesn't allow its sign-in page
to open in an iframe. When an Office Add-in is running in Office on the web, the task
pane is an iframe. This means that you'll need to open the sign-in page by using a
dialog box opened with the Office dialog API. This affects how you use authentication
helper libraries. For more information, see Authentication with the Office dialog API.

For information about implementing authentication with the Microsoft identity platform,
see Microsoft identity platform (v2.0) overview. The documentation contains many
tutorials and guides, as well as links to relevant samples and libraries. As explained in
Authentication with the Office dialog API, you may need to adjust the code in the
samples to run in the Office dialog box.

Access to Microsoft Graph without SSO


You can get authorization to Microsoft Graph data for your add-in by obtaining an
access token to Microsoft Graph from the Microsoft identity platform. You can do this
without relying on SSO through Office (or if SSO failed or isn't supported). For more
information, see Access to Microsoft Graph without SSO which has more details and
links to samples.

Access to non-Microsoft data sources


Popular online services, including Google, Facebook, LinkedIn, SalesForce, and GitHub,
let developers give users access to their accounts in other applications. This gives you
the ability to include these services in your Office Add-in. For an overview of the ways
that your add-in can do this, see Authorize external services in your Office Add-in.

) Important

Before you begin coding, find out if the data source allows its sign-in page to open
in an iframe. When an Office Add-in is running in Office on the web, the task pane is
an iframe. If the data source doesn't allow its sign-in page to open in an iframe,
then you'll need to open the sign-in page in a dialog box opened with the Office
dialog API. For more information, see Authentication with the Office dialog API.

See also
Microsoft identity platform documentation
Microsoft identity platform access tokens
OAuth 2.0 and OpenID Connect protocols on the Microsoft identity platform
Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow
JSON web token (JWT)
JSON web token viewer
Enable single sign-on (SSO) in an Office
Add-in
Article • 08/14/2023

Users sign in to Office using either their personal Microsoft account or their Microsoft
365 Education or work account. Take advantage of this and use single sign-on (SSO) to
authenticate and authorize the user to your add-in without requiring them to sign in a
second time.

How SSO works at runtime


The following diagram shows how the SSO process works. The blue elements represent
Office or the Microsoft identity platform. The gray elements represent the code you
write and include the client-side code (task pane) and the server-side code for your add-
in.
Sign and consent UI Microso
iden ty
pla orm
Redirect to sign in and
consent

ken
t to d
ues rne
Open dialog

Req retu
en
Tok

Office host (Word, Excel, PowerPoint, Outlook

Add-in client-side

Call getAccessToken

Token returned

Read iden ty info

Access server-side APIs


Add-in server-side

1. In the add-in, your JavaScript code calls the Office.js API getAccessToken. If the
user is already signed in to Office, the Office host will return the access token with
the claims of the signed in user.
2. If the user is not signed in, the Office host application opens a dialog box for the
user to sign in. Office redirects to the Microsoft identity platform to complete the
sign-in process.
3. If this is the first time the current user has used your add-in, they are prompted to
consent.
4. The Office host application requests the access token from the Microsoft identity
platform for the current user.
5. The Microsoft identity platform returns the access token to Office. Office will cache
the token on your behalf so that future calls to getAccessToken simply return the
cached token.
6. The Office host application returns the access token to the add-in as part of the
result object returned by the getAccessToken call.
7. The token is both an access token and an identity token. You can use it as an
identity token to parse and examine claims about the user, such as the user's name
and email address.
8. Optionally, the add-in can use the token as an access token to make authenticated
HTTPS requests to APIs on the server-side. Because the access token contains
identity claims, the server can store information associated with the user's identity;
such as the user's preferences.

Requirements and Best Practices

Don't cache the access token


Never cache or store the access token in your client-side code. Always call
getAccessToken when you need an access token. Office will cache the access token (or
request a new one if it expired.) This will help to avoid accidentally leaking the token
from your add-in.

Enable modern authentication for Outlook


If you're working with an Outlook add-in, be sure to enable Modern Authentication for
the Microsoft 365 tenancy. For information about how to do this, see Enable or disable
modern authentication for Outlook in Exchange Online.

Implement a fallback authentication system


You should not rely on SSO as your add-in's only method of authentication. You should
implement an alternate authentication system that your add-in can fall back to in certain
error situations. For example, if your add-in is loaded on an older version of Office that
does not support SSO, the getAccessToken call will fail.

For Excel, Word, and PowerPoint add-ins you will typically want to fall back to using the
Microsoft identity platform. For more information, see Authenticate with the Microsoft
identity platform.

For Outlook add-ins, there is a recommended fallback system. For more information, see
Scenario: Implement single sign-on to your service in an Outlook add-in.

You can also use a system of user tables and authentication, or you can leverage one of
the social login providers. For more information about how to do this with an Office
Add-in, see Authorize external services in your Office Add-in.

For code samples that use the Microsoft identity platform as the fallback system, see
Office Add-in NodeJS SSO and Office Add-in ASP.NET SSO .

Develop an SSO add-in


This section describes the tasks involved in creating an Office Add-in that uses SSO.
These tasks are described here independently of language or framework. For step-by-
step instructions, see:

Create a Node.js Office Add-in that uses single sign-on


Create an ASP.NET Office Add-in that uses single sign-on

Register your add-in with the Microsoft identity platform


To work with SSO you need to register your add-in with the Microsoft identity platform.
This will enable the Microsoft identity platform to provide authentication and
authorization services for your add-in. Creating the app registration includes the
following tasks.

Get an application (client) ID to identify your add-in to the Microsoft identity


platform.
Generate a client secret to act as a password for your add-in when requesting a
token.
Specify the permissions that your add-in requires. The Microsoft Graph "profile"
and "openid" permissions are always required. You may need additional
permissions depending on what your add-in needs to do.
Grant the Office applications trust to the add-in.
Pre-authorize the Office applications to the add-in with the default scope
access_as_user.

For more details about this process, see Register an Office Add-in that uses SSO with the
Microsoft identity platform.

Configure the add-in


Add new markup to the add-in manifest.

<WebApplicationInfo> - The parent of the following elements.


<Id> - The application (client) ID you received when you registered the add-in
with the Microsoft identity platform. For more information, see Register an Office
Add-in that uses SSO with the Microsoft identity platform.
<Resource> - The URI of the add-in. This is the same URI (including the api:
protocol) that you used when registering the add-in with the Microsoft identity
platform. The domain part of this URI must match the domain, including any
subdomains, used in the URLs in the <Resources> section of the add-in's manifest
and the URI must end with the client ID specified in the <Id> element.
<Scopes> - The parent of one or more <Scope> elements.
<Scope> - Specifies a permission that the add-in needs. The profile and openID
permissions are always needed and may be the only permissions needed. If your
add-in needs access to Microsoft Graph or other Microsoft 365 resources, you'll
need additional <Scope> elements. For example, for Microsoft Graph permissions
you might request the User.Read and Mail.Read scopes. Libraries that you use in
your code to access Microsoft Graph may need additional permissions. For more
information, see Authorize to Microsoft Graph from an Office Add-in.

For Word, Excel, and PowerPoint add-ins, add the markup to the end of the
<VersionOverrides ... xsi:type="VersionOverridesV1_0"> section. For Outlook add-ins,

add the markup to the end of the <VersionOverrides ...


xsi:type="VersionOverridesV1_1"> section.

The following is an example of the markup.

XML

<WebApplicationInfo>
<Id>5661fed9-f33d-4e95-b6cf-624a34a2f51d</Id>
<Resource>api://addin.contoso.com/5661fed9-f33d-4e95-b6cf-
624a34a2f51d</Resource>
<Scopes>
<Scope>openid</Scope>
<Scope>user.read</Scope>
<Scope>files.read</Scope>
<Scope>profile</Scope>
</Scopes>
</WebApplicationInfo>

) Important

If your add-in is deployed by one or more admins to their organizations, adding


new scopes to the manifest will require the admin to consent to the updates. Users
will be blocked from the add-in until consent is granted.

7 Note

If you don't follow the format requirements in the manifest for SSO, your add-in
will be rejected from AppSource until it meets the required format.

Include the Identity API requirement set


To use SSO your add-in requires the Identity API 1.3 requirement set. For more
information, see IdentityAPI.

Add client-side code


Add JavaScript to the add-in to:

Call getAccessToken.
Parse the access token or pass it to the add-in’s server-side code.

The following code shows a simple example of calling getAccessToken and parsing the
token for the user name and other credentials.

7 Note

This example handles only one kind of error explicitly. For examples of more
elaborate error handling, see Office Add-in NodeJS SSO and Office Add-in
ASP.NET SSO .

JavaScript

async function getUserData() {


try {
let userTokenEncoded = await OfficeRuntime.auth.getAccessToken();
let userToken = jwt_decode(userTokenEncoded); // Using the
https://www.npmjs.com/package/jwt-decode library.
console.log(userToken.name); // user name
console.log(userToken.preferred_username); // email
console.log(userToken.oid); // user id
}
catch (exception) {
if (exception.code === 13003) {
// SSO is not supported for domain user accounts, only
// Microsoft 365 Education or work account, or a Microsoft
account.
} else {
// Handle error
}
}
}

When to call getAccessToken

If your add-in requires a signed in user, then you should call getAccessToken from inside
Office.initialize . You should also pass allowSignInPrompt: true in the options
parameter of getAccessToken . For example; OfficeRuntime.auth.getAccessToken( {
allowSignInPrompt: true }); This will ensure that if the user is not yet signed in, that

Office prompts the user through the UI to sign in now.

If the add-in has some functionality that doesn't require a signed in user, then you can
call getAccessToken when the user takes an action that requires a signed in user. There is
no significant performance degradation with redundant calls of getAccessToken because
Office caches the access token and will reuse it, until it expires, without making another
call to the Microsoft identity platform whenever getAccessToken is called. So you can
add calls of getAccessToken to all functions and handlers that initiate an action where
the token is needed.

) Important

As a best security practice, always call getAccessToken when you need an access
token. Office will cache it for you. Don't cache or store the access token using your
own code.

Pass the access token to server-side code


If you need to access web APIs on your server, or additional services such as Microsoft
Graph, you'll need to pass the access token to your server-side code. The access token
provides access (for the authenticated user) to your web APIs. Also the server-side code
can parse the token for identity information if it needs it. (See Use the access token as
an identity token below.) There are many libraries available for different languages and
platforms that can help simplify the code you write. For more information, see Overview
of the Microsoft Authentication Library (MSAL).

If you need to access Microsoft Graph data, your server-side code should do the
following:

Validate the access token (see Validate the access token below).
Initiate the OAuth 2.0 On-Behalf-Of flow with a call to the Microsoft identity
platform that includes the access token, some metadata about the user, and the
credentials of the add-in (its ID and secret). The Microsoft identity platform will
return a new access token that can be used to access Microsoft Graph.
Get data from Microsoft Graph by using the new token.
If you need to cache the new access token for multiple calls, we recommend using
Token cache serialization in MSAL.NET.
) Important

As a best security practice, always use the server-side code to make Microsoft
Graph calls, or other calls that require passing an access token. Never return the
OBO token to the client to enable the client to make direct calls to Microsoft Graph.
This helps protect the token from being intercepted or leaked. For more
information on the proper protocol flow, see the OAuth 2.0 protocol diagram

The following code shows an example of passing the access token to the server-side.
The token is passed in an Authorization header when sending a request to a server-side
web API. This example sends JSON data, so it uses the POST method, but GET is
sufficient to send the access token when you are not writing to the server.

JavaScript

$.ajax({
type: "POST",
url: "/api/DoSomething",
headers: {
"Authorization": "Bearer " + accessToken
},
data: { /* some JSON payload */ },
contentType: "application/json; charset=utf-8"
}).done(function (data) {
// Handle success
}).fail(function (error) {
// Handle error
}).always(function () {
// Cleanup
});

For more details about getting authorized access to the user's Microsoft Graph data, see
Authorize to Microsoft Graph in your Office Add-in.

Validate the access token


Web APIs on your server must validate the access token if it is sent from the client. The
token is a JSON Web Token (JWT), which means that validation works just like token
validation in most standard OAuth flows. There are a number of libraries available that
can handle JWT validation, but the basics include:

Checking that the token is well-formed


Checking that the token was issued by the intended authority
Checking that the token is targeted to the Web API
Keep in mind the following guidelines when validating the token.

Valid SSO tokens will be issued by the Azure authority,


https://login.microsoftonline.com . The iss claim in the token should start with

this value.
The token's aud parameter will be set to the application ID of the add-in's Azure
app registration.
The token's scp parameter will be set to access_as_user .

For more information on token validation, see Microsoft identity platform access tokens.

Use the access token as an identity token


If your add-in needs to verify the user's identity, the access token returned from
getAccessToken() contains information that can be used to establish the identity. The

following claims in the token relate to identity.

name - The user's display name.


preferred_username - The user's email address.

oid - A GUID representing the ID of the user in the Microsoft identity system.
tid - A GUID representing the tenant tha the user is signing in to.

For more details on these and other claims, see Microsoft identity platform ID tokens. If
you need to construct a unique ID to represent the user in your system, refer to Using
claims to reliably identify a user for more information.

Example access token


The following is a typical decoded payload of an access token. For information about
the properties, see Microsoft identity platform access tokens.

JavaScript

{
aud: "2c3caa80-93f9-425e-8b85-0745f50c0d24",
iss: "https://login.microsoftonline.com/fec4f964-8bc9-4fac-b972-
1c1da35adbcd/v2.0",
iat: 1521143967,
nbf: 1521143967,
exp: 1521147867,
aio:
"ATQAy/8GAAAA0agfnU4DTJUlEqGLisMtBk5q6z+6DB+sgiRjB/Ni73q83y0B86yBHU/WFJnlMQJ
8",
azp: "e4590ed6-62b3-5102-beff-bad2292ab01c",
azpacr: "0",
e_exp: 262800,
name: "Mila Nikolova",
oid: "6467882c-fdfd-4354-a1ed-4e13f064be25",
preferred_username: "milan@contoso.com",
scp: "access_as_user",
sub: "XkjgWjdmaZ-_xDmhgN1BMP2vL2YOfeVxfPT_o8GRWaw",
tid: "fec4f964-8bc9-4fac-b972-1c1da35adbcd",
uti: "MICAQyhrH02ov54bCtIDAA",
ver: "2.0"
}

Using SSO with an Outlook add-in


There are some small, but important differences in using SSO in an Outlook add-in from
using it in an Excel, PowerPoint, or Word add-in. Be sure to read Authenticate a user with
a single sign-on token in an Outlook add-in and Scenario: Implement single sign-on to
your service in an Outlook add-in.

See also
Microsoft identity platform documentation
Requirement sets
IdentityAPI
Single sign-on (SSO) quick start
Article • 05/02/2023

In this article, you'll use the Yeoman generator for Office Add-ins to create an Office
Add-in for Excel, Outlook, Word, or PowerPoint that uses single sign-on (SSO).

7 Note

The SSO template provided by the Yeoman generator for Office Add-ins only runs
on localhost and cannot be deployed. If you're building a new Office Add-in with
SSO for production purposes, follow the instructions in Create a Node.js Office
Add-in that uses single sign-on.

Prerequisites
Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

If you're using a Mac and don't have the Azure CLI installed on your machine, you
must install Homebrew . The SSO configuration script that you'll run during this
quick start will use Homebrew to install the Azure CLI, and will then use the Azure
CLI to configure SSO within Azure.

Create the add-in project

 Tip
The Yeoman generator can create an SSO-enabled Office Add-in for Excel, Outlook,
Word, or PowerPoint with script type of JavaScript or TypeScript. The following
instructions specify JavaScript and Excel , but you should choose the script type
and Office client application that best suits your scenario.

Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project supporting single sign-
on (localhost)
Choose a script type: JavaScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Choose Excel ,
Outlook , Word , or Powerpoint .

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip
You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains code for an
SSO-enabled task pane add-in.

Configuration
The following files specify configuration settings for the add-in.

The ./manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in.

The ./.ENV file in the root directory of the project defines constants that are used
by the add-in project.

Task pane
The following files define the add-in's task pane UI and functionality.

The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.

The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in


the task pane.

In a JavaScript project, the ./src/taskpane/taskpane.js file contains code to


initialize the add-in. In a TypeScript project, the ./src/taskpane/taskpane.ts file
contains code to initialize the add-in and also code that uses the Office JavaScript
API library to add the data from Microsoft Graph to the Office document.

Authentication
The following files facilitate the SSO process and write data to the Office document.

In a JavaScript project, the ./src/helpers/documentHelper.js file contains code that


uses the Office JavaScript API library to add the data from Microsoft Graph to the
Office document. There is no such file in a TypeScript project; the code that uses
the Office JavaScript API library to add the data from Microsoft Graph to the Office
document exists in ./src/taskpane/taskpane.ts instead.

The ./src/helpers/fallbackauthdialog.html file is the UI-less page that loads the


JavaScript for the fallback authentication strategy.

The ./src/helpers/fallbackauthdialog.js file contains the JavaScript for the fallback


authentication strategy that signs in the user with msal.js.

The ./src/helpers/fallbackauthhelper.js file contains the task pane JavaScript that


invokes the fallback authentication strategy in scenarios when SSO authentication
is not supported.

The ./src/middle-tier/ssoauth-helper.js file contains the JavaScript call to the SSO


API, getAccessToken , receives the access token, initiates the swap of the access
token for a new access token with permissions to Microsoft Graph, and calls to
Microsoft Graph for the data.

Configure SSO
Now that your add-in project is created and contains the code that's necessary to
facilitate the SSO process, complete the following steps to configure SSO for your add-
in.

1. Go to the root folder of the project.

command line

cd "My Office Add-in"

2. Run the following command to configure SSO for the add-in.

command line

npm run configure-sso

2 Warning

This command will fail if your tenant is configured to require two-factor


authentication. In this scenario, you'll need to manually complete the Azure
app registration and SSO configuration steps by following all the steps in the
Create a Node.js Office Add-in that uses single sign-on tutorial.
3. A web browser window will open and prompt you to sign in to Azure. Sign in to
Azure using your Microsoft 365 administrator credentials. These credentials will be
used to register a new application in Azure and configure the settings required by
SSO.

7 Note

If you sign in to Azure using non-administrator credentials during this step,


the configure-sso script won't be able to provide administrator consent for
the add-in to users within your organization. SSO will therefore not be
available to users of the add-in and they'll be prompted to sign-in.

4. After you enter your credentials, close the browser window and return to the
command prompt. As the SSO configuration process continues, you'll see status
messages being written to the console. As described in the console messages, files
within the add-in project that the Yeoman generator created are automatically
updated with data that's required by the SSO process.

Test your add-in


If you've created an Excel, Word, or PowerPoint add-in, complete the steps in the
following section to try it. If you've created an Outlook add-in, complete the steps in the
Outlook section instead.

Excel, Word, and PowerPoint


Complete the following steps to test an Excel, Word, or PowerPoint add-in.

1. When the SSO configuration process completes, run the following command to
build the project, start the local web server, and sideload your add-in in the
previously selected Office client application.

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.
command line

npm start

2. When Excel, Word, or PowerPoint opens when you run the previous command,
make sure you're signed in with a user account that's a member of the same
Microsoft 365 organization as the Microsoft 365 administrator account that you
used to connect to Azure while configuring SSO in step 3 of the previous section.
Doing so establishes the appropriate conditions for SSO to succeed.

3. In the Office client application, choose the Home tab, and then choose Show
Taskpane to open the add-in task pane.

4. At the bottom of the task pane, choose the Get My User Profile Information
button to initiate the SSO process.

5. If a dialog window appears to request permissions on behalf of the add-in, this


means that SSO is not supported for your scenario and the add-in has instead
fallen back to an alternate method of user authentication. This may occur when the
tenant administrator hasn't granted consent for the add-in to access Microsoft
Graph, or when the user isn't signed in to Office with a valid Microsoft account or
Microsoft 365 Education or Work account. Choose Accept to continue.
7 Note

After a user accepts this permissions request, they won't be prompted again
in the future.

6. The add-in retrieves profile information for the signed-in user and writes it to the
document. The following image shows an example of profile information written to
an Excel worksheet.
Outlook
Complete the following steps to try out an Outlook add-in.

1. When the SSO configuration process completes, run the following command to
build the project and start the local web server.

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

command line

npm start

2. Follow the instructions in Sideload Outlook add-ins for testing to sideload the add-
in in Outlook. Make sure that you're signed in to Outlook with a user that's a
member of the same Microsoft 365 organization as the Microsoft 365
administrator account that you used to connect to Azure while configuring SSO in
step 3 of the previous section. Doing so establishes the appropriate conditions for
SSO to succeed.

3. In Outlook, compose a new message.

4. In the message compose window, choose the Show Taskpane button to open the
add-in task pane.

5. At the bottom of the task pane, choose the Get My User Profile Information
button to initiate the SSO process.
6. If a dialog window appears to request permissions on behalf of the add-in, this
means that SSO is not supported for your scenario and the add-in has instead
fallen back to an alternate method of user authentication. This may occur when the
tenant administrator hasn't granted consent for the add-in to access Microsoft
Graph, or when the user isn't signed in to Office with a valid Microsoft account or
Microsoft 365 Education or Work account. Choose Accept to continue.

7 Note

After a user accepts this permissions request, they won't be prompted again
in the future.

7. The add-in retrieves profile information for the signed-in user and writes it to the
body of the email message.
Next steps
Congratulations, you've successfully created a task pane add-in that uses SSO when
possible, and uses an alternate method of user authentication when SSO is not
supported. To learn about customizing your add-in to add new functionality that
requires different permissions, see Customize your Node.js SSO-enabled add-in.

See also
Enable single sign-on for Office Add-ins
Customize your Node.js SSO-enabled add-in
Create a Node.js Office Add-in that uses single sign-on
Troubleshoot error messages for single sign-on (SSO)
Using Visual Studio Code to publish
Register an Office Add-in that uses
single sign-on (SSO) with the Microsoft
identity platform
Article • 04/18/2023

This article explains how to register an Office Add-in with the Microsoft identity platform
so that you can use SSO. Register the add-in when you begin developing it so that when
you progress to testing or production, you can change the existing registration or create
separate registrations for development, testing, and production versions of the add-in.

The following table itemizes the information that you need to carry out this procedure
and the corresponding placeholders that appear in the instructions.

Information Examples Placeholder

A human readable name for the add-in. (Uniqueness Contoso Marketing <add-in-
recommended, but not required.) Excel Add-in name>
(Prod)

An application ID which Azure generates for you as part of c6c1f32b-5e55- <app-id>


the registration process. 4997-881a-
753cc1d563b7

The fully qualified domain name (except for protocol) of the localhost:6789 , <fully-
add-in. You must use a domain that you own. For this reason, addins.contoso.com qualified-
you cannot use certain well-known domains such as domain-
azurewebsites.net or cloudapp.net . The domain must be name>
the same, including any subdomains, as is used in the URLs
in the <Resources> section of the add-in's manifest.

The permissions to the Microsoft identity platform and profile , N/A


Microsoft Graph that your add-in needs. ( profile is always Files.Read.All
required.)

Register the add-in with Microsoft identity


platform
You need to create an app registration in Azure that represents your web server. This
enables authentication support so that proper access tokens can be issued to the client
code in JavaScript. This registration supports both SSO in the client, and fallback
authentication using the Microsoft Authentication Library (MSAL).
1. Sign in to the Azure portal with the admin credentials to your Microsoft 365
tenancy. For example, MyName@contoso.onmicrosoft.com.

2. Select App registrations. If you don't see the icon, search for "app registration" in
the search bar.

The App registrations page appears.

3. Select New registration.

The Register an application page appears.

4. On the Register an application page, set the values as follows.


Set Name to <add-in-name> .
Set Supported account types to Accounts in any organizational directory
(any Azure AD directory - multitenant) and personal Microsoft accounts
(e.g. Skype, Xbox).
Set Redirect URI to use the platform Single-page application (SPA) and the
URI to https://<fully-qualified-domain-name>/dialog.html .

5. Select Register. A message is displayed stating that the application registration


was created.

6. Copy and save the values for the Application (client) ID and the Directory (tenant)
ID. You'll use both of them in later procedures.
Add a client secret
Sometimes called an application password, a client secret is a string value your app can
use in place of a certificate to identity itself.

1. From the left pane, select Certificates & secrets. Then on the Client secrets tab,
select New client secret.

The Add a client secret pane appears.

2. Add a description for your client secret.

3. Select an expiration for the secret or specify a custom lifetime.

Client secret lifetime is limited to two years (24 months) or less. You can't
specify a custom lifetime longer than 24 months.
Microsoft recommends that you set an expiration value of less than 12
months.

4. Select Add. The new secret is created and the value is temporarily displayed.

) Important

Record the secret's value for use in your client application code. This secret value is
never displayed again after you leave this pane.

Expose a web API


1. From the left pane, select Expose an API.

The Expose an API pane appears.

2. Select Set to generate an application ID URI.


The section for setting the application ID URI appears with a generated Application
ID URI in the form api://<app-id> .

3. Update the application ID URI to api://<fully-qualified-domain-name>/<app-id> .

The Application ID URI is pre-filled with app ID (GUID) in the format


api://<app-id> .

The application ID URI format should be: api://<fully-qualified-domain-


name>/<app-id>
Insert the fully-qualified-domain-name between api:// and <app-id>
(which is a GUID). For example, api://contoso.com/<app-id> .
If you're using localhost, then the format should be api://localhost:
<port>/<app-id> . For example, api://localhost:3000/c6c1f32b-5e55-4997-
881a-753cc1d563b7 .

For additional application ID URI details, see Application manifest identifierUris


attribute.

7 Note
If you get an error saying that the domain is already owned but you own it,
follow the procedure at Quickstart: Add a custom domain name to Azure
Active Directory to register it, and then repeat this step. (This error can also
occur if you are not signed in with credentials of an admin in the Microsoft
365 tenancy. See step 2. Sign out and sign in again with admin credentials
and repeat the process from step 3.)

Add a scope
1. On the Expose an API page, select Add a scope.

The Add a scope pane opens.

2. In the Add a scope pane, specify the scope's attributes. The following table shows
example values for and Outlook add-in requiring the profile , openid ,
Files.ReadWrite , and Mail.Read permissions. Modify the text to match the

permissions your add-in needs.

Field Description Values

Scope The name of your scope. A For SSO this must be set to
name common scope naming access_as_user .
convention is
resource.operation.constraint .
Field Description Values

Who can Determines if admin consent is For learning SSO and samples, we
consent required or if users can consent recommend you set this to Admins and
without an admin approval. users.

Select Admins only for higher-privileged


permissions.

Admin A short description of the scope's Read/write permissions to user files.


consent purpose visible to admins only. Read permissions to user mail and
display profiles.
name

Admin A more detailed description of Allow Office to have read/write


consent the permission granted by the permissions to all user files and read
description scope that only admins see. permissions to all user mail. Office
can call the app's web APIs as the
current user.

User A short description of the scope's Read/write permissions to your files.


consent purpose. Shown to users only if Read permissions to your mail and
display you set Who can consent to profile.
name Admins and users.

User A more detailed description of Allow Office to have read/write


consent the permission granted by the permissions to your files, and read
description scope. Shown to users only if you permissions to your mail and profile.
set Who can consent to Admins
and users.

3. Set the State to Enabled, and then select Add scope.


The new scope you defined displays on the pane.
7 Note

The domain part of the Scope name displayed just below the text field should
automatically match the Application ID URI set in the previous step, with
/access_as_user appended to the end; for example,
api://localhost:6789/c6c1f32b-5e55-4997-881a-753cc1d563b7/access_as_user .

4. Select Add a client application.


The Add a client application pane appears.

5. In the Client ID enter ea5a67f6-b6f3-4338-b240-c655ddc3cc8e . This value pre-


authorizes all Microsoft Office application endpoints.

7 Note

The ea5a67f6-b6f3-4338-b240-c655ddc3cc8e ID pre-authorizes Office on all the


following platforms. Alternatively, you can enter a proper subset of the
following IDs if, for any reason, you want to deny authorization to Office on
some platforms. If you do so, leave out the IDs of the platforms from which
you want to withhold authorization. Users of your add-in on those platforms
will not be able to call your Web APIs, but other functionality in your add-in
will still work.

d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft Office)

93d53678-613d-4013-afc1-62e9e444a0a5 (Office on the web)

bc59ab01-8403-45c6-8796-ac3ef710b3e3 (Outlook on the web)

6. In Authorized scopes, select the api://<fully-qualified-domain-name>/<app-


id>/access_as_user checkbox.

7. Select Add application.

Add Microsoft Graph permissions


1. From the left pane, select API permissions.
The API permissions pane opens.

2. Select Add a permission.

The Request API permissions pane opens.

3. Select Microsoft Graph.


4. Select Delegated permissions.

5. In the Select permissions search box, search for the permissions your add-in
needs. For example, for an Outlook add-in, you might use profile , openid ,
Files.ReadWrite , and Mail.Read .

7 Note

The User.Read permission may already be listed by default. It's a good


practice to only request permissions that are needed, so we recommend that
you uncheck the box for this permission if your add-in doesn't actually need
it.

6. Select the checkbox for each permission as it appears. Note that the permissions
will not remain visible in the list as you select each one. After selecting the
permissions that your add-in needs, select Add permissions.
7. Select Grant admin consent for [tenant name]. Select Yes for the confirmation
that appears.

Configure access token version


You must define the access token version that is acceptable for your app. This
configuration is made in the Azure Active Directory application manifest.

Define the access token version


The access token version can change if you chose an account type other than Accounts
in any organizational directory (Any Azure AD directory - Multitenant) and personal
Microsoft accounts (e.g. Skype, Xbox). Use the following steps to ensure the access
token version is correct for Office SSO usage.
1. From the left pane, select Manifest.

The Azure Active Directory application manifest appears.

2. Enter 2 as the value for the accessTokenAcceptedVersion property.

3. Select Save.

A message pops up on the browser stating that the manifest was updated
successfully.
Congratulations! You've completed the app registration to enable SSO for your Office
add-in.
Grant administrator consent to the add-
in
Article • 06/23/2023

7 Note

This procedure is only needed when you're developing the add-in. When your
production add-in is deployed to AppSource or the Microsoft 365 admin center,
users will individually trust it or an admin will consent for the organization at
installation.

Carry out this procedure after you have registered the add-in.

1. Browse to the Azure portal - App registrations page to view your app
registration.

2. Sign in with the admin credentials to your Microsoft 365 tenancy. For example,
MyName@contoso.onmicrosoft.com.

3. Select the app with display name $ADD-IN-NAME$.

4. On the $ADD-IN-NAME$ page, select API permissions then, under the


Configured permissions section, choose Grant admin consent for [tenant name].
Select Yes for the confirmation that appears.

7 Note

We recommend this procedure as a best practice if you're using a Microsoft 365


developer account. However, if you prefer, it's possible to sideload an SSO add-in
under development and prompt the user with a consent form. For more
information, see Sideload on Windows and Sideload on Office on the web.
Use SSO to get the identity of the
signed-in user
Article • 06/23/2023

Use the getAccessToken API to get an access token that contains the identity for the
current user signed in to Office. The access token is also an ID token because it contains
identity claims about the signed-in user, such as their name and email. You can also use
the ID token to identify the user when calling your own web services. To call
getAccessToken , you must configure your Office Add-in to use SSO with Office.

In this article, you'll create an Office Add-in that gets the ID token, and displays the
user's name, email, and unique ID in the task pane.

7 Note

SSO with Office and the getAccessToken API don't work in all scenarios. Always
implement a fallback dialog to sign in the user when SSO is unavailable. For more
information, see Authenticate and authorize with the Office dialog API.

Create an app registration


To use SSO with Office, you need to create an app registration in the Azure portal so the
Microsoft identity platform can provide authentication and authorization services for
your Office Add-in and its users.

1. To register your app, go to the Azure portal - App registrations page.

2. Sign in with the admin credentials to your Microsoft 365 tenancy. For example,
MyName@contoso.onmicrosoft.com.

3. Select New registration. On the Register an application page, set the values as
follows.

Set Name to Office-Add-in-SSO .


Set Supported account types to Accounts in any organizational directory
and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com).
Set the application type to Web and then set Redirect URI to
https://localhost:[port]/dialog.html . Replace [port] with the correct port

number for your web application. If you created the add-in using yo office,
the port number is typically 3000 and found in the package.json file. If you
created the add-in with Visual Studio 2019, the port is found in the SSL URL
property of the web project.
Choose Register.

4. On the Office-Add-in-SSO page, copy and save the values for the Application
(client) ID and the Directory (tenant) ID. You'll use both of them in later
procedures.

7 Note

This Application (client) ID is the "audience" value when other applications,


such as the Office client application (e.g., PowerPoint, Word, Excel), seek
authorized access to the application. It's also the "client ID" of the application
when it, in turn, seeks authorized access to Microsoft Graph.

5. Select Authentication under Manage. In the Implicit grant section, enable the
checkboxes for both Access token and ID token.

6. Select Save at the top of the form.

7. Select Expose an API under Manage. Select the Set link. This will generate the
Application ID URI in the form api://[app-id-guid] , where [app-id-guid] is the
Application (client) ID.

8. In the generated ID, insert localhost:[port]/ (note the forward slash "/"
appended to the end) between the double forward slashes and the GUID. Replace
[port] with the correct port number for your web application. If you created the
add-in using yo office, the port number is typically 3000 and found in the
package.json file. If you created the add-in with Visual Studio 2019, the port is
found in the SSL URL property of the web project.

When you're finished, the entire ID should have the form api://localhost:
[port]/[app-id-guid] ; for example api://localhost:44355/c6c1f32b-5e55-4997-
881a-753cc1d563b7 .

9. Select the Add a scope button. In the panel that opens, enter access_as_user as
the <Scope> name.

10. Set Who can consent? to Admins and users.

11. Fill in the fields for configuring the admin and user consent prompts with values
that are appropriate for the access_as_user scope which enables the Office client
application to use your add-in's web APIs with the same rights as the current user.
Suggestions:

Admin consent display name: Office can act as the user.


Admin consent description: Enable Office to call the add-in's web APIs with
the same rights as the current user.
User consent display name: Office can act as you.
User consent description: Enable Office to call the add-in's web APIs with the
same rights that you have.

12. Ensure that State is set to Enabled.

13. Select Add scope .

7 Note

The domain part of the <Scope> name displayed just below the text field
should automatically match the Application ID URI that you set earlier, with
/access_as_user appended to the end; for example,
api://localhost:6789/c6c1f32b-5e55-4997-881a-753cc1d563b7/access_as_user .

14. In the Authorized client applications section, enter the following ID to pre-
authorize all Microsoft Office application endpoints.

ea5a67f6-b6f3-4338-b240-c655ddc3cc8e (All Microsoft Office application

endpoints)

7 Note

The ea5a67f6-b6f3-4338-b240-c655ddc3cc8e ID pre-authorizes Office on all the


following platforms. Alternatively, you can enter a proper subset of the
following IDs if for any reason you want to deny authorization to Office on
some platforms. Just leave out the IDs of the platforms from which you want
to withhold authorization. Users of your add-in on those platforms will not be
able to call your Web APIs, but other functionality in your add-in will still work.

d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft Office)

93d53678-613d-4013-afc1-62e9e444a0a5 (Office on the web)

bc59ab01-8403-45c6-8796-ac3ef710b3e3 (Outlook on the web)


15. Select the Add a client application button and then, in the panel that opens, set
the [app-id-guid] to the Application (client) ID and check the box for
api://localhost:44355/[app-id-guid]/access_as_user .

16. Select Add application.

17. Select API permissions under Manage and select Add a permission. On the panel
that opens, choose Microsoft Graph and then choose Delegated permissions.

18. Use the Select permissions search box to search for the permissions your add-in
needs. Search for and select the profile permission. The profile permission is
required for the Office application to get a token to your add-in web application.

profile

7 Note

The User.Read permission may already be listed by default. It's a good


practice not to ask for permissions that aren't needed, so we recommend that
you uncheck the box for this permission if your add-in doesn't actually need
it.

19. Select the Add permissions button at the bottom of the panel.

20. On the same page, choose the Grant admin consent for <tenant-name> button,
and then select Yes for the confirmation that appears.

Create the Office Add-in


Visual Studio 2019

1. Start Visual Studio 2019 and choose to Create a new project.


2. Search for and select the Excel Web Add-in project template. Then choose
Next. Note: SSO works with any Office application, but Excel is the application
being used with this article.
3. Enter a project name, such as sso-display-user-info, and choose Create. You
can leave the other fields at default values.
4. In the Choose the add-in type dialog box, select Add new functionality to
Excel, and choose Finish.

The project is created and will contain two projects in the solution.
sso-display-user-info: Contains the manifest and details for sideloading the
add-in to Excel.
sso-display-user-infoWeb: The ASP.NET project that hosts the web pages for
the add-in.

Configure the manifest


Visual Studio 2019

In Solution Explorer, open sso-display-user-info > sso-display-user-infoManifest


> sso-display-user-info.xml.

1. Near the bottom of the manifest is a closing </Resources> element. Insert the
following XML just below the </Resources> element but before the closing
</VersionOverrides> element. For Office applications other than Outlook, add the
markup to the end of the <VersionOverrides ...
xsi:type="VersionOverridesV1_0"> section. For Outlook, add the markup to the

end of the <VersionOverrides ... xsi:type="VersionOverridesV1_1"> section.

XML

<WebApplicationInfo>
<Id>[application-id]</Id>
<Resource>api://localhost:[port]/[application-id]</Resource>
<Scopes>
<Scope>openid</Scope>
<Scope>user.read</Scope>
<Scope>profile</Scope>
</Scopes>
</WebApplicationInfo>

2. Replace [port] with the correct port number for your project. If you created the
add-in using yo office, the port number is typically 3000 and found in the
package.json file. If you created the add-in with Visual Studio 2019, the port is
found in the SSL URL property of the web project.

3. Replace both [application-id] placeholders with the actual application ID from


your app registration.

4. Save the file.


The XML you inserted contains the following elements and information.

<WebApplicationInfo> - The parent of the following elements.


<Id> - The client ID of the add-in This is an application ID that you obtain as part
of registering the add-in. See Register an Office Add-in that uses SSO with the
Azure AD v2.0 endpoint.
<Resource> - The URL of the add-in. This is the same URI (including the api:
protocol) that you used when registering the add-in in AAD. The domain part of
this URI must match the domain, including any subdomains, used in the URLs in
the <Resources> section of the add-in's manifest and the URI must end with the
client ID in the <Id>.
<Scopes> - The parent of one or more <Scope> elements.
<Scope> - Specifies a permission that the add-in needs to AAD. The profile and
openID permissions are always needed and may be the only permissions needed, if

your add-in doesn't access Microsoft Graph. If it does, you also need <Scope>
elements for the required Microsoft Graph permissions; for example, User.Read ,
Mail.Read . Libraries that you use in your code to access Microsoft Graph may need

additional permissions. For example, Microsoft Authentication Library (MSAL) for


.NET requires the offline_access permission. For more information, see Authorize
to Microsoft Graph from an Office Add-in.

Add the jwt-decode package


You can call the getAccessToken API to get the ID token from Office. First, let's add the
jwt-decode package to make it easier to decode and view the ID token.

Visual Studio 2019

1. Open the Visual Studio solution.

2. On the menu, choose Tools > NuGet Package Manager > Package Manager
Console.

3. Enter the following command in the Package Manager Console.

Install-Package jwt-decode -Projectname sso-display-user-infoWeb

Add UI to the task pane


Modify the task pane so that it can display the user information you'll get from the ID
token.

Visual Studio 2019

1. Open the Home.html file.

2. Add the following script tag to the <head> section of the page. This will
include the jwt-decode package was added earlier.

HTML

<script src="Scripts/jwt-decode-2.2.0.js" type="text/javascript">


</script>

3. Replace the <body> section with the following HTML.

HTML

<body>
<h1>Welcome</h1>
<p>
Sign in to Office, then choose the <b>Get ID Token</b> button
to see your
ID token information.
</p>
<button id="getIDToken">Get ID Token</button>
<div>
<span id="userInfo"></span>
</div>
</body>

Call the getAccessToken API


The final step is to get the ID token by calling getAccessToken .

Visual Studio 2019

1. Open the Home.js file.

2. Replace the entire contents of the file with the following code.
JavaScript

(function () {
"use strict";

// The initialize function must be run each time a new page is


loaded.
Office.initialize = function (reason) {
$(document).ready(function () {
$("#getIDToken").click(getIDToken);
});
};

async function getIDToken() {


try {
let userTokenEncoded = await
OfficeRuntime.auth.getAccessToken({
allowSignInPrompt: true,
});
let userToken = jwt_decode(userTokenEncoded);
document.getElementById("userInfo").innerHTML =
"name: " +
userToken.name +
"<br>email: " +
userToken.preferred_username +
"<br>id: " +
userToken.oid;
console.log(userToken);
} catch (error) {
document.getElementById("userInfo").innerHTML =
"An error occurred. <br>Name: " +
error.name +
"<br>Code: " +
error.code +
"<br>Message: " +
error.message;
console.log(error);
}
}
})();

3. Save the file.

Run the add-in


Visual Studio 2019

Choose Debug > Start Debugging, or press F5.


1. When Excel starts, sign in to Office with the same tenant account you used to
create the app registration.
2. On the Home ribbon, choose Show Taskpane to open the add-in.
3. In the add-in's task pane, choose Get ID token.

The add-in will display the name, email, and ID of the account you signed in with.

7 Note

If you encounter any errors, review the registration steps in this article for the app
registration. Missing a detail when setting up the app registration is a common
cause of issues working with SSO. If you still can't get the add-in to run
successfully, see Troubleshoot error messages for single sign-on (SSO).

See also
Using claims to reliably identify a user (Subject and Object ID)
Authorize to Microsoft Graph with SSO
Article • 08/14/2023

Users sign in to Office using either their personal Microsoft account or their Microsoft
365 Education or work account. The best way for an Office Add-in to get authorized
access to Microsoft Graph is to use the credentials from the user's Office sign on. This
enables them to access their Microsoft Graph data without needing to sign in a second
time.

Add-in architecture for SSO and Microsoft


Graph
In addition to hosting the pages and JavaScript of the web application, the add-in must
also host, at the same fully qualified domain name, one or more web APIs that will get
an access token to Microsoft Graph and make requests to it.

The add-in manifest contains a <WebApplicationInfo> element that provides important


Azure app registration information to Office, including the permissions to Microsoft
Graph that the add-in requires.

How it works at runtime


The following diagram shows the steps involved to sign in and access Microsoft Graph.
The entire process uses OAuth 2.0 and JWT access tokens.
Microso
Office Add-in Microso Graph
Office host iden ty
Office Add-in server-side code API
pla orm
client-side code

Add-in calls
1 getAccessToken() Office host requests
access token

Microso iden ty
Office host returns pla orm returns
access token A SAML token access token A
A
SAML token
A

Request access
Add-in passes access
token A to web server API SAML token
6 token for
A Microso Graph
A

Return access
token B 7
SAML token
B

Call Microso Graph


8 with access token B
SAML token
B

Return data 9
Return data 10

1. The client-side code of the add-in calls the Office.js API getAccessToken. This tells
the Office host to obtain an access token for the add-in.

If the user is not signed in, the Office host in conjunction with the Microsoft
identity platform provides UI for the user to sign in and consent.

2. The Office host request an access token from the Microsoft identity platform.

3. The Microsoft identity platform returns access token A to the Office host. Access
token A only provides access to the add-in's own server-side APIs. It does not
provide access to Microsoft Graph.

4. The Office host returns access token A to the add-in's client-side code. Now the
client-side code can make authenticated calls to the server-side APIs.

5. The client-side code makes an HTTP request to a web API on the server-side that
requires authentication. It includes access token A as authorization proof. Server-
side code validates access token A.

6. The server-side code uses the OAuth 2.0 On-Behalf-Of flow (OBO) to request a
new access token with permissions to Microsoft Graph.
7. The Microsoft identity platform returns the new access token B with permissions to
Microsoft Graph (and a refresh token, if the add-in requests offline_access
permission). The server can optionally cache access token B.

8. The server-side code makes a request to a Microsoft Graph API and includes
access token B with permissions to Microsoft Graph.

9. Microsoft Graph returns data back to the server-side code.

10. The server-side code returns the data back to the client-side code.

On subsequent requests the client code will always pass access token A when making
authenticated calls to server-side code. The server-side code can cache token B so that it
does not need to request it again on future API calls.

Develop an SSO add-in that accesses Microsoft


Graph
You develop an add-in that accesses Microsoft Graph just as you would any other
application that uses SSO. For a thorough description, see Enable single sign-on for
Office Add-ins. The difference is that it is mandatory that the add-in have a server-side
Web API.

Depending on your language and framework, libraries might be available that will
simplify the server-side code you have to write. Your code should do the following:

Validate the access token A every time it is passed from the client-side code. For
more information, see Validate the access token.
Initiate the OAuth 2.0 On-Behalf-Of flow (OBO) with a call to the Microsoft identity
platform that includes the access token, some metadata about the user, and the
credentials of the add-in (its ID and secret). For more information about the OBO
flow, see Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow.
Optionally, after the flow completes, cache the returned access token B with
permissions to Microsoft Graph. You'll want to do this if the add-in makes more
than one call to Microsoft Graph. For more information, see Acquire and cache
tokens using the Microsoft Authentication Library (MSAL)
Create one or more Web API methods that get Microsoft Graph data by passing
the (possibly cached) access token B to Microsoft Graph.

For examples of detailed walkthroughs and scenarios, see:

Create a Node.js Office Add-in that uses single sign-on


Create an ASP.NET Office Add-in that uses single sign-on
Scenario: Implement single sign-on to your service in an Outlook add-in

Distributing SSO-enabled add-ins in Microsoft


AppSource
When a Microsoft 365 admin acquires an add-in from AppSource , the admin can
redistribute it through Integrated Apps and grant admin consent to the add-in to access
Microsoft Graph scopes. It's also possible, however, for the end user to acquire the add-
in directly from AppSource, in which case the user must grant consent to the add-in.
This can create a potential performance problem for which we've provided a solution.

If your code passes the allowConsentPrompt option in the call of getAccessToken , like
OfficeRuntime.auth.getAccessToken( { allowConsentPrompt: true } ); , then Office can

prompt the user for consent if the Microsoft identity platform reports to Office that
consent has not yet been granted to the add-in. However, for security reasons, Office
can only prompt the user to consent to the Microsoft Graph profile scope. Office
cannot prompt for consent to other Microsoft Graph scopes, not even User.Read . This
means that if the user grants consent on the prompt, Office returns an access token. But
the attempt to exchange the access token for a new access token with additional
Microsoft Graph scopes fails with error AADSTS65001, which means consent (to
Microsoft Graph scopes) has not been granted.

7 Note

The request for consent with { allowConsentPrompt: true } could still fail even for
the profile scope if the administrator has turned off end-user consent. For more
information, see Configure how end-users consent to applications using Azure
Active Directory.

Your code can, and should, handle this error by falling back to an alternate system of
authentication, which prompts the user for consent to Microsoft Graph scopes. For code
examples, see Create a Node.js Office Add-in that uses single sign-on and Create an
ASP.NET Office Add-in that uses single sign-on and the samples they link to. The entire
process requires multiple round trips to the Microsoft identity platform. To avoid this
performance penalty, include the forMSGraphAccess option in the call of getAccessToken ;
for example, OfficeRuntime.auth.getAccessToken( { forMSGraphAccess: true } ) . This
signals to Office that your add-in needs Microsoft Graph scopes. Office will ask the
Microsoft identity platform to verify that consent to Microsoft Graph scopes has already
been granted to the add-in. If it has, the access token is returned. If it hasn't, then the
call of getAccessToken returns error 13012. Your code can handle this error by falling
back to an alternate system of authentication immediately, without making a doomed
attempt to exchange tokens with the Microsoft identity platform.

As a best practice, always pass forMSGraphAccess to getAccessToken when your add-in


will be distributed in AppSource and needs Microsoft Graph scopes.

Details on SSO with an Outlook add-in


If you develop an Outlook add-in that uses SSO and you sideload it for testing, Office
will always return error 13012 when forMSGraphAccess is passed to getAccessToken even
if administrator consent has been granted. For this reason, you should comment out the
forMSGraphAccess option when developing an Outlook add-in. Be sure to uncomment

the option when you deploy for production. The bogus 13012 only happens when you
are sideloading in Outlook.

For Outlook add-ins, be sure to enable Modern Authentication for the Microsoft 365
tenancy. For information about how to do this, see Enable or disable modern
authentication for Outlook in Exchange Online.

See also
OAuth2 Token Exchange
Microsoft identity platform and OAuth 2.0 On-Behalf-Of flow
IdentityAPI requirement sets
Create an ASP.NET Office Add-in that
uses single sign-on
Article • 05/20/2023

Users can sign in to Office, and your Office Web Add-in can take advantage of this sign-
in process to authorize users to your add-in and to Microsoft Graph without requiring
users to sign in a second time. This article walks you through the process of enabling
single sign-on (SSO) in an add-in.

The sample shows you how to build the following parts:

Client-side code that provides a task pane that loads in Microsoft Excel, Word, or
PowerPoint. The client-side code calls the Office JS API getAccessToken() to get
the SSO access token to call server-side REST APIs.
Server-side code that uses ASP.NET Core to provide a single REST API /api/files .
The server-side code uses the Microsoft Authentication Library for .NET
(MSAL.NET) for all token handling, authentication, and authorization.

The sample uses SSO and the On-Behalf-Of (OBO) flow to obtain correct access tokens
and call Microsoft Graph APIs. If you are unfamiliar with how this flow works, see How
SSO works at runtime for more detail.

Prerequisites
Visual Studio 2019 or later.

The Office/SharePoint development workload when configuring Visual Studio.

At least a few files and folders stored on OneDrive for Business in your Microsoft
365 subscription.

A build of Microsoft 365 that supports the IdentityAPI 1.3 requirement set. You can
get a free developer sandbox that provides a renewable 90-day Microsoft 365 E5
developer subscription. The developer sandbox includes a Microsoft Azure
subscription that you can use for app registrations in later steps in this article. If
you prefer, you can use a separate Microsoft Azure subscription for app
registrations. Get a trial subscription at Microsoft Azure .

Set up the starter project


Clone or download the repo at Office Add-in ASPNET SSO .

7 Note

There are two versions of the sample.

The Begin folder is a starter project. The UI and other aspects of the add-in
that are not directly connected to SSO or authorization are already done.
Later sections of this article walk you through the process of completing it.
The Complete folder contains the same sample with all coding steps from this
article completed. To use the completed version, just follow the instructions in
this article, but replace "Begin" with "Complete" and skip the sections Code
the client side and Code the server side.

Use the following values for placeholders for the subsequent app registration steps.

Placeholder Value

<add-in-name> Office-Add-in-ASPNET-SSO

<fully-qualified-domain-name> localhost:44355

Microsoft Graph permissions profile, openid, Files.Read

Register the add-in with Microsoft identity


platform
You need to create an app registration in Azure that represents your web server. This
enables authentication support so that proper access tokens can be issued to the client
code in JavaScript. This registration supports both SSO in the client, and fallback
authentication using the Microsoft Authentication Library (MSAL).

1. Sign in to the Azure portal with the admin credentials to your Microsoft 365
tenancy. For example, MyName@contoso.onmicrosoft.com.

2. Select App registrations. If you don't see the icon, search for "app registration" in
the search bar.
The App registrations page appears.

3. Select New registration.

The Register an application page appears.

4. On the Register an application page, set the values as follows.

Set Name to <add-in-name> .


Set Supported account types to Accounts in any organizational directory
(any Azure AD directory - multitenant) and personal Microsoft accounts
(e.g. Skype, Xbox).
Set Redirect URI to use the platform Single-page application (SPA) and the
URI to https://<fully-qualified-domain-name>/dialog.html .
5. Select Register. A message is displayed stating that the application registration
was created.

6. Copy and save the values for the Application (client) ID and the Directory (tenant)
ID. You'll use both of them in later procedures.
Add a client secret
Sometimes called an application password, a client secret is a string value your app can
use in place of a certificate to identity itself.

1. From the left pane, select Certificates & secrets. Then on the Client secrets tab,
select New client secret.

The Add a client secret pane appears.

2. Add a description for your client secret.

3. Select an expiration for the secret or specify a custom lifetime.

Client secret lifetime is limited to two years (24 months) or less. You can't
specify a custom lifetime longer than 24 months.
Microsoft recommends that you set an expiration value of less than 12
months.

4. Select Add. The new secret is created and the value is temporarily displayed.

) Important

Record the secret's value for use in your client application code. This secret value is
never displayed again after you leave this pane.

Expose a web API


1. From the left pane, select Expose an API.

The Expose an API pane appears.

2. Select Set to generate an application ID URI.


The section for setting the application ID URI appears with a generated Application
ID URI in the form api://<app-id> .

3. Update the application ID URI to api://<fully-qualified-domain-name>/<app-id> .

The Application ID URI is pre-filled with app ID (GUID) in the format


api://<app-id> .

The application ID URI format should be: api://<fully-qualified-domain-


name>/<app-id>
Insert the fully-qualified-domain-name between api:// and <app-id>
(which is a GUID). For example, api://contoso.com/<app-id> .
If you're using localhost, then the format should be api://localhost:
<port>/<app-id> . For example, api://localhost:3000/c6c1f32b-5e55-4997-
881a-753cc1d563b7 .

For additional application ID URI details, see Application manifest identifierUris


attribute.

7 Note
If you get an error saying that the domain is already owned but you own it,
follow the procedure at Quickstart: Add a custom domain name to Azure
Active Directory to register it, and then repeat this step. (This error can also
occur if you are not signed in with credentials of an admin in the Microsoft
365 tenancy. See step 2. Sign out and sign in again with admin credentials
and repeat the process from step 3.)

Add a scope
1. On the Expose an API page, select Add a scope.

The Add a scope pane opens.

2. In the Add a scope pane, specify the scope's attributes. The following table shows
example values for and Outlook add-in requiring the profile , openid ,
Files.ReadWrite , and Mail.Read permissions. Modify the text to match the

permissions your add-in needs.

Field Description Values

Scope The name of your scope. A For SSO this must be set to
name common scope naming access_as_user .
convention is
resource.operation.constraint .
Field Description Values

Who can Determines if admin consent is For learning SSO and samples, we
consent required or if users can consent recommend you set this to Admins and
without an admin approval. users.

Select Admins only for higher-privileged


permissions.

Admin A short description of the scope's Read/write permissions to user files.


consent purpose visible to admins only. Read permissions to user mail and
display profiles.
name

Admin A more detailed description of Allow Office to have read/write


consent the permission granted by the permissions to all user files and read
description scope that only admins see. permissions to all user mail. Office
can call the app's web APIs as the
current user.

User A short description of the scope's Read/write permissions to your files.


consent purpose. Shown to users only if Read permissions to your mail and
display you set Who can consent to profile.
name Admins and users.

User A more detailed description of Allow Office to have read/write


consent the permission granted by the permissions to your files, and read
description scope. Shown to users only if you permissions to your mail and profile.
set Who can consent to Admins
and users.

3. Set the State to Enabled, and then select Add scope.


The new scope you defined displays on the pane.
7 Note

The domain part of the Scope name displayed just below the text field should
automatically match the Application ID URI set in the previous step, with
/access_as_user appended to the end; for example,
api://localhost:6789/c6c1f32b-5e55-4997-881a-753cc1d563b7/access_as_user .

4. Select Add a client application.


The Add a client application pane appears.

5. In the Client ID enter ea5a67f6-b6f3-4338-b240-c655ddc3cc8e . This value pre-


authorizes all Microsoft Office application endpoints.

7 Note

The ea5a67f6-b6f3-4338-b240-c655ddc3cc8e ID pre-authorizes Office on all the


following platforms. Alternatively, you can enter a proper subset of the
following IDs if, for any reason, you want to deny authorization to Office on
some platforms. If you do so, leave out the IDs of the platforms from which
you want to withhold authorization. Users of your add-in on those platforms
will not be able to call your Web APIs, but other functionality in your add-in
will still work.

d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft Office)

93d53678-613d-4013-afc1-62e9e444a0a5 (Office on the web)

bc59ab01-8403-45c6-8796-ac3ef710b3e3 (Outlook on the web)

6. In Authorized scopes, select the api://<fully-qualified-domain-name>/<app-


id>/access_as_user checkbox.

7. Select Add application.

Add Microsoft Graph permissions


1. From the left pane, select API permissions.
The API permissions pane opens.

2. Select Add a permission.

The Request API permissions pane opens.

3. Select Microsoft Graph.


4. Select Delegated permissions.

5. In the Select permissions search box, search for the permissions your add-in
needs. For example, for an Outlook add-in, you might use profile , openid ,
Files.ReadWrite , and Mail.Read .

7 Note

The User.Read permission may already be listed by default. It's a good


practice to only request permissions that are needed, so we recommend that
you uncheck the box for this permission if your add-in doesn't actually need
it.

6. Select the checkbox for each permission as it appears. Note that the permissions
will not remain visible in the list as you select each one. After selecting the
permissions that your add-in needs, select Add permissions.
7. Select Grant admin consent for [tenant name]. Select Yes for the confirmation
that appears.

Configure access token version


You must define the access token version that is acceptable for your app. This
configuration is made in the Azure Active Directory application manifest.

Define the access token version


The access token version can change if you chose an account type other than Accounts
in any organizational directory (Any Azure AD directory - Multitenant) and personal
Microsoft accounts (e.g. Skype, Xbox). Use the following steps to ensure the access
token version is correct for Office SSO usage.
1. From the left pane, select Manifest.

The Azure Active Directory application manifest appears.

2. Enter 2 as the value for the accessTokenAcceptedVersion property.

3. Select Save.

A message pops up on the browser stating that the manifest was updated
successfully.
Congratulations! You've completed the app registration to enable SSO for your Office
add-in.

Configure the solution


1. In the root of the Begin folder, open the solution (.sln) file in Visual Studio. Right-
click the top node in Solution Explorer (the Solution node, not either of the project
nodes), and then select Set startup projects.

2. Under Common Properties, select Startup Project, and then Multiple startup
projects. Ensure that the Action for both projects is set to Start, and that the
Office-Add-in-ASPNETCoreWebAPI project is listed first. Close the dialog.

3. In Solution Explorer, choose the Office-Add-in-ASPNET-SSO-manifest project


and open the add-in manifest file “Office-Add-in-ASPNET-SSO.xml” and then scroll
to the bottom of the file. Just above the end </VersionOverrides> tag, you'll find
the following markup.

XML

<WebApplicationInfo>
<Id>Enter_client_ID_here</Id>
<Resource>api://localhost:44355/Enter_client_ID_here</Resource>
<Scopes>
<Scope>Files.Read</Scope>
<Scope>profile</Scope>
<Scope>openid</Scope>
</Scopes>
</WebApplicationInfo>

4. Replace the placeholder "Enter_client_ID_here" in both places in the markup with


the Application ID that you copied when you created the Office-Add-in-ASPNET-
SSO app registration. This is the same ID you used for the application ID in the
appsettings.json file.

7 Note

The <Resource> value is the Application ID URI you set when you registered
the add-in. The <Scopes> section is used only to generate a consent dialog
box if the add-in is sold through AppSource.

5. Save and close the manifest file.

6. In Solution Explorer, choose the Office-Add-in-ASPNET-SSO-web project and


open the appsettings.json file.

7. Replace the placeholder Enter_client_id_here with the Application (client) ID value


you saved previously.

8. Replace the placeholder Enter_client_secret_here with the client secret value you
saved previously.

7 Note

You must also change the TenantId to support single-tenant if you configured
your app registration for single-tenant. Replace the Common value with the
Application (client) ID for single-tenant support.

9. Save and close the appsettings.json file.

Code the client side

Get the access token and call the application server REST
API
1. In the Office-Add-in-ASPNETCore-WebAPI project, open the
wwwroot\js\HomeES6.js file. It already has code that ensures that Promises are
supported, even in the Trident (Internet Explorer 11) webview control, and an
Office.onReady call to assign a handler to the add-in's only button.

7 Note

As the name suggests, the HomeES6.js uses JavaScript ES6 syntax because
using async and await best shows the essential simplicity of the SSO API.
When the localhost server is started, this file is transpiled to ES5 syntax so that
the sample will support Trident.

2. In the getUserFileNames function, replace TODO 1 with the following code. About
this code, note:
It calls Office.auth.getAccessToken to get the access token from Office using
SSO. This token will contain the user's identity as well as access permission to
the application server.
The access token is passed to callRESTApi which makes the actual call to the
application server. The application server then uses the OBO flow to call
Microsoft Graph.
Any errors from calling getAccessToken will be handled by
handleClientSideErrors .

JavaScript

let fileNameList = null;


try {
let accessToken = await Office.auth.getAccessToken(options);
fileNameList = await callRESTApi("/api/files", accessToken);
}
catch (exception) {
if (exception.code) {
handleClientSideErrors(exception);
}
else {
showMessage("EXCEPTION: " + exception);
}
}

3. In the getUserFileNames function, replace TODO 2 with the following code. This will
write the list of file names to the document.

JavaScript

try {
await writeFileNamesToOfficeDocument(fileNameList);
showMessage("Your data has been added to the document.");
} catch (error) {
// The error from writeFileNamesToOfficeDocument will begin
// "Unable to add filenames to document."
showMessage(error);
}

4. In the callRESTApi function, replace TODO 3 with the following code. About this
code, note:

It constructs an authorization header containing the access token. This


confirms to the application server that this client code has access permissions
to the REST APIs.
It request JSON return types, so that all return values are handled in JSON.
Any errors are passed to handleServerSideErrors for processing.

JavaScript

try {
let result = await $.ajax({
url: relativeUrl,
headers: { "Authorization": "Bearer " + accessToken },
type: "GET",
dataType: "json",
contentType: "application/json; charset=utf-8"
});
return result;
} catch (error) {
handleServerSideErrors(error);
}

Handle SSO errors and application REST API errors


1. In the handleSSOErrors function, replace TODO 4 with the following code. For more
information about these errors, see Troubleshoot SSO in Office Add-ins.

JavaScript

switch (error.code) {
case 13001:
// No one is signed into Office. If the add-in cannot be
effectively used when no one
// is logged into Office, then the first call of
getAccessToken should pass the
// `allowSignInPrompt: true` option.
showMessage("No one is signed into Office. But you can use
many of the add-ins functions anyway. If you want to log in, press the
Get OneDrive File Names button again.");
break;
case 13002:
// The user aborted the consent prompt. If the add-in cannot
be effectively used when consent
// has not been granted, then the first call of getAccessToken
should pass the `allowConsentPrompt: true` option.
showMessage("You can use many of the add-ins functions even
though you have not granted consent. If you want to grant consent,
press the Get OneDrive File Names button again.");
break;
case 13006:
// Only seen in Office on the web.
showMessage("Office on the web is experiencing a problem.
Please sign out of Office, close the browser, and then start again.");
break;
case 13008:
// Only seen in Office on the web.
showMessage("Office is still working on the last operation.
When it completes, try this operation again.");
break;
case 13010:
// Only seen in Office on the web.
showMessage("Follow the instructions to change your browser's
zone configuration.");
break;
default:
// For all other errors, including 13000, 13003, 13005, 13007,
13012, and 50001, fall back
// to non-SSO sign-in by using MSAL authentication.
showMessage("SSO failed. In these cases you should implement a
falback to MSAL authentication.");
break;
}

2. In the handleServerSideErrors function, replace TODO 5 with the following code.

JavaScript

// Check headers to see if admin has not consented.


const header = errorResponse.getResponseHeader('WWW-Authenticate');
if (header !== null && header.includes('proposedAction=\"consent\"')) {
showMessage("MSAL ERROR: " + "Admin consent required. Be sure admin
consent is granted on all scopes in the Azure app registration.");
return;
}

3. In the handleServerSideErrors function, replace TODO 6 with the following code.


About this code, note:

In some cases, additional consent is required, such as 2FA. Microsoft identity


returns the additional claims that are required to complete consent. This code
adds the authChallenge property with the additional claims and calls
getUserfileNames again. When getAccessToken is called again with the

additional claims, the user gets a prompt for all required forms of
authentication.

JavaScript

// Check if Microsoft Graph requires an additional form of


authentication. Have the Office host
// get a new token using the Claims string, which tells Microsoft
identity to prompt the user for all
// required forms of authentication.
const errorDetails =
JSON.parse(errorResponse.responseJSON.value.details);
if (errorDetails) {
if (errorDetails.error.message.includes("AADSTS50076")) {
const claims = errorDetails.message.Claims;
const claimsAsString = JSON.stringify(claims);
getUserFileNames({ authChallenge: claimsAsString });
return;
}
}

4. In the handleServerSideErrors function, replace TODO 7 with the following code.


About this code, note:

In the rare case the original SSO token is expired, it will detect this error
condition and call getUserFilenames again. This results in another call to
getAccessToken which returns a refreshed access token. The

retryGetAccessToken variable counts the retries and is currently configured to


only retry once.
Finally, if an error cannot be handled, the default is to display the error in the
task pane.

JavaScript

// Results from other errors (other than AADSTS50076) will have an


ExceptionMessage property.
const exceptionMessage =
JSON.parse(errorResponse.responseText).ExceptionMessage;
if (exceptionMessage) {
// On rare occasions the access token is unexpired when Office
validates it,
// but expires by the time it is sent to Microsoft identity in the
OBO flow. Microsoft identity will respond
// with "The provided value for the 'assertion' is not valid. The
assertion has expired."
// Retry the call of getAccessToken (no more than once). This time
Office will return a
// new unexpired access token.
if ((exceptionMessage.includes("AADSTS500133"))
&& (retryGetAccessToken <= 0)) {
retryGetAccessToken++;
getUserFileNames();
return;
}
else {
showMessage("MSAL error from application server: " +
JSON.stringify(exceptionMessage));
return;
}
}
// Default error handling if previous checks didn't apply.
showMessage(errorResponse.responseJSON.value);

5. Save the file.

Code the server side


The server-side code is an ASP.NET Core server that provides REST APIs for the client to
call. For example, the REST API /api/files gets a list of filenames from the user's
OneDrive folder. Each REST API call requires an access token by the client to ensure the
correct client is accessing their data. The access token is exchanged for a Microsoft
Graph token through the On-Behalf-Of flow (OBO). The new Microsoft Graph token is
cached by the MSAL.NET library for subsequent API calls. It's never sent outside of the
server-side code. Microsoft identity documentation refers to this server as the middle-
tier server because it is in the middle of the flow from client-side code to Microsoft
services. For more information, see Middle-tier access token request

Configure Microsoft Graph and OBO flow


1. Open the Program.cs file and replace TODO 8 with the following code. About this
code, note:

It adds required services to handle token validation that is required for the
REST APIs.
It adds Microsoft Graph and OBO flow support in the call to
EnableTokenAcquisitionToCallDownstreamApi().AddMicrosoftGraph(...) . The

OBO flow is handled automatically for you, and the Microsoft Graph SDK is
provided to your REST API controllers.
The DownstreamApi configuration is specified in the appsettings.json file.

C#

// Add services to the container.


builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Confi
guration)
.EnableTokenAcquisitionToCallDownstreamApi()

.AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();

Create the /api/filenames REST API


1. In the Controllers folder, open the FilesController.cs file. replace TODO 9 with the
following code. About this code, note:

It specifies the [Authorize] attribute to ensure the access token is validated


for each call to any REST APIs in the FilesController class. For more
information, see Validating tokens.
It specifies the [RequiredScope("access_as_user")] attribute to ensure the
client has the correct access_as_user scope in the access token.
The constructor initializes the _graphServiceClient object to make calling
Microsoft Graph REST APIs easier.

C#

[Authorize]
[Route("api/[controller]")]
[RequiredScope("access_as_user")]
public class FilesController : Controller
{
public FilesController(ITokenAcquisition tokenAcquisition,
GraphServiceClient graphServiceClient, IOptions<MicrosoftGraphOptions>
graphOptions)
{
_tokenAcquisition = tokenAcquisition;
_graphServiceClient = graphServiceClient;
_graphOptions = graphOptions;

private readonly ITokenAcquisition _tokenAcquisition;


private readonly GraphServiceClient _graphServiceClient;
private readonly IOptions<MicrosoftGraphOptions> _graphOptions;

// TODO 10: Add the REST API to get filenames.

2. Replace TODO 10 with the following code. About this code, note:

It creates the /api/files REST API.


It handles exceptions from MSAL through MsalException class.
It handles exceptions from Microsoft Graph API calls through the
ServiceException class.

C#

// GET api/files
[HttpGet]
[Produces("application/json")]
public async Task<IActionResult> Get()
{
List<DriveItem> result = new List<DriveItem>();
try
{
var files = await
_graphServiceClient.Me.Drive.Root.Children.Request()
.Top(10)
.Select(m => new { m.Name })
.GetAsync();

result = files.ToList();
}
catch (MsalException ex)
{
var errorResponse = new
{
message = "An authentication error occurred while
acquiring a token for downstream API",
details = ex.Message
};

return StatusCode((int)HttpStatusCode.Unauthorized,
Json(errorResponse));
}
catch (ServiceException ex)
{
if (ex.InnerException is
MicrosoftIdentityWebChallengeUserException challengeException)
{

_tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(_graphOptions
.Value.Scopes.Split(' '),
challengeException.MsalUiRequiredException);
}
else
{
var errorResponse = new
{
message = "An error occurred calling Microsoft
Graph",
details = ex.RawResponseBody
};
return StatusCode((int)HttpStatusCode.BadRequest,
Json(errorResponse));
}
}
catch (Exception ex)
{
var errorResponse = new
{
message = "An error occurred while calling the
downstream API",
details = ex.Message
};
return StatusCode((int)HttpStatusCode.BadRequest,
Json(errorResponse));

}
return Json(result);
}

Run the solution


1. In Visual Studio, on the Build menu, select Clean Solution. When it finishes, open
the Build menu again and select Build Solution.

2. In Solution Explorer, select the Office-Add-in-ASPNET-SSO-manifest project


node.

3. In the Properties pane, open the Start Document drop down and choose one of
the three options (Excel, Word, or PowerPoint).

4. Press F5. Or select Debug > Start Debugging.

5. In the Office application, select the Show Add-in in the SSO ASP.NET group to
open the task pane add-in.

6. Select Get OneDrive File Names. If you're logged into Office with either a
Microsoft 365 Education or work account, or a Microsoft account, and SSO is
working as expected, the first 10 file and folder names in your OneDrive for
Business are displayed on the task pane. If you are not logged in, or you are in a
scenario that does not support SSO, or SSO is not working for any reason, you will
be prompted to sign in. After you sign in, the file and folder names appear.
Deploy the add-in
When you're ready to deploy to a staging or production server, be sure to update the
following areas in the project solution.

In the appsettings.json file, change the domain to your staging or production


domain name.
Update any references to localhost:7080 throughout your project to use your
staging or production URL.
Update any references to localhost:7080 in your Azure App registration, or create
a new registration for use in staging or production.

For more information, see Host and deploy ASP.NET Core.

See also
Create a Node.js Office Add-in that uses single sign-on.
Authorize to Microsoft Graph with SSO.
Create a Node.js Office Add-in that uses
single sign-on
Article • 05/20/2023

Users can sign in to Office, and your Office Web Add-in can take advantage of this sign-
in process to authorize users to your add-in and to Microsoft Graph without requiring
users to sign in a second time. For an overview, see Enable SSO in an Office Add-in.

This article walks you through the process of enabling single sign-on (SSO) in an add-in.
The sample add-in you create has two parts; a task pane that loads in Microsoft Excel,
and a middle-tier server that handles calls to Microsoft Graph for the task pane. The
middle-tier server is built with Node.js and Express and exposes a single REST API,
/getuserfilenames , that returns a list of the first 10 file names in the user's OneDrive

folder. The task pane uses the getAccessToken() method to get an access token for the
signed in user to the middle-tier server. The middle-tier server uses the On-Behalf-Of
flow (OBO) to exchange the access token for a new one with access to Microsoft Graph.
You can extend this pattern to access any Microsoft Graph data. The task pane always
calls a middle-tier REST API (passing the access token) when it needs Microsoft Graph
services. The middle-tier uses the token obtained via OBO to call Microsoft Graph
services and return the results to the task pane.

This article works with an add-in that uses Node.js and Express. For a similar article
about an ASP.NET-based add-in, see Create an ASP.NET Office Add-in that uses single
sign-on.

Prerequisites
Node.js (the latest LTS version)

Git Bash (or another git client)

A code editor - we recommend Visual Studio Code

At least a few files and folders stored on OneDrive for Business in your Microsoft
365 subscription

A build of Microsoft 365 that supports the IdentityAPI 1.3 requirement set. You can
get a free developer sandbox that provides a renewable 90-day Microsoft 365 E5
developer subscription. The developer sandbox includes a Microsoft Azure
subscription that you can use for app registrations in later steps in this article. If
you prefer, you can use a separate Microsoft Azure subscription for app
registrations. Get a trial subscription at Microsoft Azure .

Set up the starter project


1. Clone or download the repo at Office Add-in NodeJS SSO .

7 Note

There are two versions of the sample:

The Begin folder is a starter project. The UI and other aspects of the
add-in that are not directly connected to SSO or authorization are
already done. Later sections of this article walk you through the process
of completing it.
The Complete folder contains the same sample with all coding steps
from this article completed. To use the completed version, just follow the
instructions in this article, but replace "Begin" with "Complete" and skip
the sections Code the client side and Code the middle-tier server side.

2. Open a command prompt in the Begin folder.

3. Enter npm install in the console to install all of the dependencies itemized in the
package.json file.

4. Run the command npm run install-dev-certs . Select Yes to the prompt to install
the certificate.

Use the following values for placeholders for the subsequent app registration steps.

Placeholder Value

<add-in-name> Office-Add-in-NodeJS-SSO

<fully-qualified-domain-name> localhost:3000

Microsoft Graph permissions profile, openid, Files.Read

Register the add-in with Microsoft identity


platform
You need to create an app registration in Azure that represents your web server. This
enables authentication support so that proper access tokens can be issued to the client
code in JavaScript. This registration supports both SSO in the client, and fallback
authentication using the Microsoft Authentication Library (MSAL).

1. Sign in to the Azure portal with the admin credentials to your Microsoft 365
tenancy. For example, MyName@contoso.onmicrosoft.com.

2. Select App registrations. If you don't see the icon, search for "app registration" in
the search bar.

The App registrations page appears.

3. Select New registration.


The Register an application page appears.

4. On the Register an application page, set the values as follows.

Set Name to <add-in-name> .


Set Supported account types to Accounts in any organizational directory
(any Azure AD directory - multitenant) and personal Microsoft accounts
(e.g. Skype, Xbox).
Set Redirect URI to use the platform Single-page application (SPA) and the
URI to https://<fully-qualified-domain-name>/dialog.html .

5. Select Register. A message is displayed stating that the application registration


was created.
6. Copy and save the values for the Application (client) ID and the Directory (tenant)
ID. You'll use both of them in later procedures.

Add a client secret


Sometimes called an application password, a client secret is a string value your app can
use in place of a certificate to identity itself.

1. From the left pane, select Certificates & secrets. Then on the Client secrets tab,
select New client secret.

The Add a client secret pane appears.

2. Add a description for your client secret.

3. Select an expiration for the secret or specify a custom lifetime.


Client secret lifetime is limited to two years (24 months) or less. You can't
specify a custom lifetime longer than 24 months.
Microsoft recommends that you set an expiration value of less than 12
months.

4. Select Add. The new secret is created and the value is temporarily displayed.

) Important

Record the secret's value for use in your client application code. This secret value is
never displayed again after you leave this pane.

Expose a web API


1. From the left pane, select Expose an API.

The Expose an API pane appears.

2. Select Set to generate an application ID URI.


The section for setting the application ID URI appears with a generated Application
ID URI in the form api://<app-id> .

3. Update the application ID URI to api://<fully-qualified-domain-name>/<app-id> .

The Application ID URI is pre-filled with app ID (GUID) in the format


api://<app-id> .

The application ID URI format should be: api://<fully-qualified-domain-


name>/<app-id>
Insert the fully-qualified-domain-name between api:// and <app-id>
(which is a GUID). For example, api://contoso.com/<app-id> .
If you're using localhost, then the format should be api://localhost:
<port>/<app-id> . For example, api://localhost:3000/c6c1f32b-5e55-4997-
881a-753cc1d563b7 .

For additional application ID URI details, see Application manifest identifierUris


attribute.

7 Note
If you get an error saying that the domain is already owned but you own it,
follow the procedure at Quickstart: Add a custom domain name to Azure
Active Directory to register it, and then repeat this step. (This error can also
occur if you are not signed in with credentials of an admin in the Microsoft
365 tenancy. See step 2. Sign out and sign in again with admin credentials
and repeat the process from step 3.)

Add a scope
1. On the Expose an API page, select Add a scope.

The Add a scope pane opens.

2. In the Add a scope pane, specify the scope's attributes. The following table shows
example values for and Outlook add-in requiring the profile , openid ,
Files.ReadWrite , and Mail.Read permissions. Modify the text to match the

permissions your add-in needs.

Field Description Values

Scope The name of your scope. A For SSO this must be set to
name common scope naming access_as_user .
convention is
resource.operation.constraint .
Field Description Values

Who can Determines if admin consent is For learning SSO and samples, we
consent required or if users can consent recommend you set this to Admins and
without an admin approval. users.

Select Admins only for higher-privileged


permissions.

Admin A short description of the scope's Read/write permissions to user files.


consent purpose visible to admins only. Read permissions to user mail and
display profiles.
name

Admin A more detailed description of Allow Office to have read/write


consent the permission granted by the permissions to all user files and read
description scope that only admins see. permissions to all user mail. Office
can call the app's web APIs as the
current user.

User A short description of the scope's Read/write permissions to your files.


consent purpose. Shown to users only if Read permissions to your mail and
display you set Who can consent to profile.
name Admins and users.

User A more detailed description of Allow Office to have read/write


consent the permission granted by the permissions to your files, and read
description scope. Shown to users only if you permissions to your mail and profile.
set Who can consent to Admins
and users.

3. Set the State to Enabled, and then select Add scope.


The new scope you defined displays on the pane.
7 Note

The domain part of the Scope name displayed just below the text field should
automatically match the Application ID URI set in the previous step, with
/access_as_user appended to the end; for example,
api://localhost:6789/c6c1f32b-5e55-4997-881a-753cc1d563b7/access_as_user .

4. Select Add a client application.


The Add a client application pane appears.

5. In the Client ID enter ea5a67f6-b6f3-4338-b240-c655ddc3cc8e . This value pre-


authorizes all Microsoft Office application endpoints.

7 Note

The ea5a67f6-b6f3-4338-b240-c655ddc3cc8e ID pre-authorizes Office on all the


following platforms. Alternatively, you can enter a proper subset of the
following IDs if, for any reason, you want to deny authorization to Office on
some platforms. If you do so, leave out the IDs of the platforms from which
you want to withhold authorization. Users of your add-in on those platforms
will not be able to call your Web APIs, but other functionality in your add-in
will still work.

d3590ed6-52b3-4102-aeff-aad2292ab01c (Microsoft Office)

93d53678-613d-4013-afc1-62e9e444a0a5 (Office on the web)

bc59ab01-8403-45c6-8796-ac3ef710b3e3 (Outlook on the web)

6. In Authorized scopes, select the api://<fully-qualified-domain-name>/<app-


id>/access_as_user checkbox.

7. Select Add application.

Add Microsoft Graph permissions


1. From the left pane, select API permissions.
The API permissions pane opens.

2. Select Add a permission.

The Request API permissions pane opens.

3. Select Microsoft Graph.


4. Select Delegated permissions.

5. In the Select permissions search box, search for the permissions your add-in
needs. For example, for an Outlook add-in, you might use profile , openid ,
Files.ReadWrite , and Mail.Read .

7 Note

The User.Read permission may already be listed by default. It's a good


practice to only request permissions that are needed, so we recommend that
you uncheck the box for this permission if your add-in doesn't actually need
it.

6. Select the checkbox for each permission as it appears. Note that the permissions
will not remain visible in the list as you select each one. After selecting the
permissions that your add-in needs, select Add permissions.
7. Select Grant admin consent for [tenant name]. Select Yes for the confirmation
that appears.

Configure access token version


You must define the access token version that is acceptable for your app. This
configuration is made in the Azure Active Directory application manifest.

Define the access token version


The access token version can change if you chose an account type other than Accounts
in any organizational directory (Any Azure AD directory - Multitenant) and personal
Microsoft accounts (e.g. Skype, Xbox). Use the following steps to ensure the access
token version is correct for Office SSO usage.
1. From the left pane, select Manifest.

The Azure Active Directory application manifest appears.

2. Enter 2 as the value for the accessTokenAcceptedVersion property.

3. Select Save.

A message pops up on the browser stating that the manifest was updated
successfully.
Congratulations! You've completed the app registration to enable SSO for your Office
add-in.

Configure the add-in


1. Open the \Begin folder in the cloned project in your code editor.

2. Open the .ENV file and use the values that you copied earlier from the Office-Add-
in-NodeJS-SSO app registration. Set the values as follows:

Name Value

CLIENT_ID Application (client) ID from app registration overview page.

CLIENT_SECRET Client secret saved from Certificates & Secrets page.

The values should not be in quotation marks. When you are done, the file should
be similar to the following:

JavaScript

CLIENT_ID=8791c036-c035-45eb-8b0b-265f43cc4824
CLIENT_SECRET=X7szTuPwKNts41:-/fa3p.p@l6zsyI/p
NODE_ENV=development
SERVER_SOURCE=<https://localhost:3000>

3. Open the add-in manifest file "manifest\manifest_local.xml" and then scroll to the
bottom of the file. Just above the </VersionOverrides> end tag, you'll find the
following markup.

XML

<WebApplicationInfo>
<Id>$app-id-guid$</Id>
<Resource>api://localhost:3000/$app-id-guid$</Resource>
<Scopes>
<Scope>Files.Read</Scope>
<Scope>profile</Scope>
<Scope>openid</Scope>
</Scopes>
</WebApplicationInfo>
4. Replace the placeholder "$app-id-guid$" in both places in the markup with the
Application ID that you copied when you created the Office-Add-in-NodeJS-SSO
app registration. The "$" symbols are not part of the ID, so don't include them. This
is the same ID you used for the CLIENT_ID in the .ENV file.

7 Note

The <Resource> value is the Application ID URI you set when you registered
the add-in. The <Scopes> section is used only to generate a consent dialog
box if the add-in is sold through AppSource.

5. Open the \public\javascripts\fallback-msal\authConfig.js file. Replace the


placeholder "$app-id-guid$" with the application ID that you saved from the
Office-Add-in-NodeJS-SSO app registration you created previously.

6. Save the changes to the file.

Code the client-side

Call our web server REST API


1. In your code editor, open the file public\javascripts\ssoAuthES6.js . It already has
code that ensures that Promises are supported, even in the Trident (Internet
Explorer 11) webview control, and an Office.onReady call to assign a handler to
the add-in's only button.

7 Note

As the name suggests, the ssoAuthES6.js uses JavaScript ES6 syntax because
using async and await best shows the essential simplicity of the SSO API.
When the localhost server is started, this file is transpiled to ES5 syntax so that
the sample will support Trident.

2. In the getFileNameList function, replace TODO 1 with the following code. About
this code, note:

The function getFileNameList is called when the user chooses the Get
OneDrive File Names button on the task pane.
It calls the callWebServerAPI function specifying which REST API to call. This
returns JSON containing a list of file names from the user's OneDrive.
The JSON is passed to the writeFileNamesToOfficeDocument function to list
the file names in the document.

JavaScript

try {
const jsonResponse = await callWebServerAPI('GET',
'/getuserfilenames');
if (jsonResponse === null) {
// Null is returned when a message was displayed to the user
// regarding an authentication error that cannot be resolved.
return;
}
await writeFileNamesToOfficeDocument(jsonResponse);
showMessage('Your OneDrive filenames are added to the document.');
} catch (error) {
console.log(error.message);
showMessage(error.message);
}

3. In the callWebServerAPI function, replace TODO 2 with the following code. About
this code, note:

The function calls getAccessToken which is our own function that


encapsulates using Office SSO or MSAL fallback as necessary to get the
token. If it returns a null token, a message was shown for an auth error
condition that cannot be resolved, so the function also returns null.
The function uses the fetch API to call the web server and if successful,
returns the JSON body.

JavaScript

const accessToken = await getAccessToken(authSSO);


if (accessToken === null) {
return null;
}
const response = await fetch(path, {
method: method,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + accessToken,
},
});

// Check for success condition: HTTP status code 2xx.


if (response.ok) {
return response.json();
}

4. In the callWebServerAPI function, replace TODO 3 with the following code. About
this code, note:

This code handles the scenario where the SSO token expired. If so we need to
call Office.auth.getAccessToken to get a refreshed token. The simplest way is
to make a recursive call which results in a new call to
Office.auth.getAccessToken . The retryRequest parameter ensures the

recursive call is only attempted once.


The TokenExpiredError string is set by our web server whenever it detects an
expired token.

JavaScript

// Check for fail condition: Is SSO token expired? If so, retry the
call which will get a refreshed token.
const jsonBody = await response.json();
if (
authSSO === true &&
jsonBody != null &&
jsonBody.type === 'TokenExpiredError'
) {
if (!retryRequest) {
return callWebServerAPI(method, path, true); // Try the call
again. The underlying call to Office JS getAccessToken will refresh the
token.
} else {
// Indicates a second call to retry and refresh the token
failed.
authSSO = false;
return callWebServerAPI(method, path, true); // Try the call
again, but now using MSAL fallback auth.
}
}

5. In the callWebServerAPI function, replace TODO 4 with the following code. About
this code, note:

The Microsoft Graph string is set by our web server whenever a Microsoft
Graph call fails.

JavaScript

// Check for fail condition: Did we get a Microsoft Graph API error,
which is returned as bad request (403)?
if (response.status === 403 && jsonBody.type === 'Microsoft Graph') {
throw new Error('Microsoft Graph error: ' + jsonBody.errorDetails);
}

6. In the callWebServerAPI function, replace TODO 5 with the following code.

JavaScript

// Handle other errors.


throw new Error(
'Unknown error from web server: ' + JSON.stringify(jsonBody)
);

7. In the getAccessToken function, replace TODO 6 with the following code. About this
code, note:

authSSO tracks if we are using SSO, or using MSAL fallback. If SSO is used, the
function calls Office.auth.getAccessToken and returns the token.
Errors are handled by the handleSSOErrors function which will return a token
if it switches to fallback MSAL authentication.
Fallback authentication uses the MSAL library to sign in the user. The add-in
itself is an SPA, and uses an SPA app registration to access the web server.

JavaScript

if (authSSO) {
try {
// Get the access token from Office host using SSO.
// Note that Office.auth.getAccessToken modifies the options
parameter. Create a copy of the object
// to avoid modifying the original object.
const options = JSON.parse(JSON.stringify(ssoOptions));
const token = await Office.auth.getAccessToken(options);
return token;
} catch (error) {
console.log(error.message);
return handleSSOErrors(error);
}
} else {
// Get access token through MSAL fallback.
try {
const accessToken = await getAccessTokenMSAL();
return accessToken;
} catch (error) {
console.log(error);
throw new Error(
'Cannot get access token. Both SSO and fallback auth
failed. ' +
error
);
}
}

8. In the handleSSOErrors function, replace TODO 7 with the following code. For more
information about these errors, see Troubleshoot SSO in Office Add-ins.

JavaScript

switch (error.code) {
case 13001:
// No one is signed into Office. If the add-in cannot be
effectively used when no one
// is logged into Office, then the first call of getAccessToken
should pass the
// `allowSignInPrompt: true` option. Since this sample does
that, you should not see
// this error.
showMessage(
'No one is signed into Office. But you can use many of the
add-ins functions anyway. If you want to log in, press the Get OneDrive
File Names button again.'
);
break;
case 13002:
// The user aborted the consent prompt. If the add-in cannot be
effectively used when consent
// has not been granted, then the first call of getAccessToken
should pass the `allowConsentPrompt: true` option.
showMessage(
'You can use many of the add-ins functions even though you
have not granted consent. If you want to grant consent, press the Get
OneDrive File Names button again.'
);
break;
case 13006:
// Only seen in Office on the web.
showMessage(
'Office on the web is experiencing a problem. Please sign
out of Office, close the browser, and then start again.'
);
break;
case 13008:
// Only seen in Office on the web.
showMessage(
'Office is still working on the last operation. When it
completes, try this operation again.'
);
break;
case 13010:
// Only seen in Office on the web.
showMessage(
"Follow the instructions to change your browser's zone
configuration."
);
break;

9. Replace TODO 8 with the following code. For any errors that can't be handled the
code switches to fallback authentication using MSAL.

JavaScript

default: //recursive call.


// For all other errors, including 13000, 13003, 13005, 13007,
13012, and 50001, fall back
// to MSAL sign-in.
showMessage('SSO failed. Trying fallback auth.');
authSSO = false;
return getAccessToken(false);
}
return null; // Return null for errors that show a message to the user.

Code the web server REST API


The web server provides REST APIs for the client to call. For example, the REST API
/getuserfilenames gets a list of filenames from the user's OneDrive folder. Each REST

API call requires an access token by the client to ensure the correct client is accessing
their data. The access token is exchanged for a Microsoft Graph token through the On-
Behalf-Of flow (OBO). The new Microsoft Graph token is cached by the MSAL library for
subsequent API calls. It's never sent outside of the web server. For more information, see
Middle-tier access token request

Create the route and implement On-Behalf-Of flow


1. Open the file routes\getFilesRoute.js and replace TODO 9 with the following
code. About this code, note:

It calls authHelper.validateJwt . This ensures the access token is valid and


hasn't been tampered with.
For more information, see Validating tokens.

JavaScript

router.get(
"/getuserfilenames",
authHelper.validateJwt,
async function (req, res) {
// TODO 10: Exchange the access token for a Microsoft Graph token
// by using the OBO flow.
}
);

2. Replace TODO 10 with the following code. About this code, note:

It only requests the minimum scopes it needs, such as files.read .


It uses the MSAL authHelper to perform the OBO flow in the call to
acquireTokenOnBehalfOf .

JavaScript

try {
const authHeader = req.headers.authorization;
let oboRequest = {
oboAssertion: authHeader.split(' ')[1],
scopes: ["files.read"],
};

// The Scope claim tells you what permissions the client application
has in the service.
// In this case we look for a scope value of access_as_user, or full
access to the service as the user.
const tokenScopes = jwt.decode(oboRequest.oboAssertion).scp.split('
');
const accessAsUserScope = tokenScopes.find(
(scope) => scope === 'access_as_user'
);
if (!accessAsUserScope) {
res.status(401).send({ type: "Missing access_as_user" });
return;
}
const cca = authHelper.getConfidentialClientApplication();
const response = await cca.acquireTokenOnBehalfOf(oboRequest);
// TODO 11: Call Microsoft Graph to get list of filenames.
} catch (err) {
// TODO 12: Handle any errors.
}

3. Replace TODO 11 with the following code. About this code, note:

It constructs the URL for the Microsoft Graph API call and then makes the call
via the getGraphData function.
It returns errors by sending an HTTP 500 response along with details.
On success it returns the JSON with the filename list to the client.

JavaScript
// Minimize the data that must come from MS Graph by specifying only
the property we need ("name")
// and only the top 10 folder or file names.
const rootUrl = '/me/drive/root/children';

// Note that the last parameter, for queryParamsSegment, is hardcoded.


If you reuse this code in
// a production add-in and any part of queryParamsSegment comes from
user input, be sure that it is
// sanitized so that it cannot be used in a Response header injection
attack.
const params = '?$select=name&$top=10';

const graphData = await getGraphData(


response.accessToken,
rootUrl,
params
);

// If Microsoft Graph returns an error, such as invalid or expired


token,
// there will be a code property in the returned object set to a HTTP
status (e.g. 401).
// Return it to the client. On client side it will get handled in the
fail callback of `makeWebServerApiCall`.
if (graphData.code) {
res
.status(403)
.send({
type: "Microsoft Graph",
errorDetails:
"An error occurred while calling the Microsoft Graph API.\n" +
graphData,
});
} else {
// MS Graph data includes OData metadata and eTags that we don't
need.
// Send only what is actually needed to the client: the item names.
const itemNames = [];
const oneDriveItems = graphData["value"];
for (let item of oneDriveItems) {
itemNames.push(item["name"]);
}

res.status(200).send(itemNames);
}
// TODO 12: Check for expired token.

4. Replace TODO 12 with the following code. This code specifically checks if the token
expired because the client can request a new token and call again.

JavaScript
} catch (err) {
// On rare occasions the SSO access token is unexpired when Office
validates it,
// but expires by the time it is used in the OBO flow. Microsoft
identity platform will respond
// with "The provided value for the 'assertion' is not valid. The
assertion has expired."
// Construct an error message to return to the client so it can
refresh the SSO token.
if (err.errorMessage.indexOf('AADSTS500133') !== -1) {
res.status(401).send({ type: "TokenExpiredError", errorDetails:
err });
} else {
res.status(403).send({ type: "Unknown", errorDetails: err });
}
}

The sample must handle both fallback authentication through MSAL and SSO
authentication through Office. The sample will try SSO first, and the authSSO boolean at
the top of the file tracks if the sample is using SSO or has switched to fallback auth.

Run the project


1. Ensure that you have some files in your OneDrive so that you can verify the results.

2. Open a command prompt in the root of the \Begin folder.

3. Run the command npm install to install all package dependencies.

4. Run the command npm start to start the middle-tier server.

5. You need to sideload the add-in into an Office application (Excel, Word, or
PowerPoint) to test it. The instructions depend on your platform. There are links to
instructions at Sideload an Office Add-in for Testing.

6. In the Office application, on the Home ribbon, select the Show Add-in button in
the SSO Node.js group to open the task pane add-in.

7. Click the Get OneDrive File Names button. If you're logged into Office with either
a Microsoft 365 Education or work account, or a Microsoft account, and SSO is
working as expected the first 10 file and folder names in your OneDrive for
Business are inserted into the document. (It may take as much as 15 seconds the
first time.) If you're not logged in, or you're in a scenario that doesn't support SSO,
or SSO isn't working for any reason, you'll be prompted to sign in. After you sign
in, the file and folder names appear.
7 Note

If you were previously signed into Office with a different ID, and some Office
applications that were open at the time are still open, Office may not reliably
change your ID even if it appears to have done so. If this happens, the call to
Microsoft Graph may fail or data from the previous ID may be returned. To prevent
this, be sure to close all other Office applications before you press Get OneDrive
File Names.

Security notes
The /getuserfilenames route in getFilesroute.js uses a literal string to compose
the call for Microsoft Graph. If you change the call so that any part of the string
comes from user input, sanitize the input so that it cannot be used in a Response
header injection attack.

In app.js the following content security policy is in place for scripts. You may want
to specify additional restrictions depending on your add-in security needs.

"Content-Security-Policy": "script-src https://appsforoffice.microsoft.com

https://ajax.aspnetcdn.com https://alcdn.msauth.net " +


process.env.SERVER_SOURCE,

Always follow security best practices in the Microsoft identity platform documentation.
Troubleshoot error messages for single
sign-on (SSO)
Article • 08/14/2023

This article provides some guidance about how to troubleshoot problems with single
sign-on (SSO) in Office Add-ins, and how to make your SSO-enabled add-in robustly
handle special conditions or errors.

7 Note

The Single Sign-on API is currently supported for Word, Excel, Outlook, and
PowerPoint. For more information about where the Single Sign-on API is currently
supported, see IdentityAPI requirement sets. If you're working with an Outlook
add-in, be sure to enable Modern Authentication for the Microsoft 365 tenancy. For
information about how to do this, see Enable or disable modern authentication
for Outlook in Exchange Online.

Debugging tools
We strongly recommend that you use a tool that can intercept and display the HTTP
Requests from, and Responses to, your add-in's web service when you are developing.
Two of the most popular are:

Fiddler : Free (Documentation )


Charles : Free for 30 days. (Documentation )

Causes and handling of errors from


getAccessToken
For examples of the error handling described in this section, see:

HomeES6.js in Office-Add-in-ASPNET-SSO
ssoAuthES6.js in Office-Add-in-NodeJS-SSO

13000
The getAccessToken API is not supported by the add-in or the Office version.
The version of Office does not support SSO. The required version is Microsoft 365
subscription, in any monthly channel.
The add-in manifest is missing the proper WebApplicationInfo section.

Your add-in should respond to this error by falling back to an alternate system of user
authentication. For more information, see Requirements and Best Practices.

13001
The user is not signed into Office. In most scenarios, you should prevent this error from
ever being seen by passing the option allowSignInPrompt: true in the AuthOptions
parameter.

But there may be exceptions. For example, you want the add-in to open with features
that require a logged in user; but only if the user is already logged into Office. If the user
is not, you want the add-in to open with an alternate set of features that do not require
that the user is signed in. In this case, logic which runs when the add-in launches calls
getAccessToken without allowSignInPrompt: true . Use the 13001 error as the flag to tell

the add-in to present the alternate set of features.

Another option is to respond to 13001 by falling back to an alternate system of user


authentication. This will sign the user into AAD, but not sign the user into Office.

This error is never seen in Office on the web. If the user's cookie expires, Office on the
web returns error 13006.

13002
The user aborted sign in or consent; for example, by choosing Cancel on the consent
dialog.

If your add-in provides functions that don't require the user to be signed in (or to
have granted consent), then your code should catch this error and allow the add-in
to stay running.
If the add-in requires a signed-in user who has granted consent, your code should
have a sign-in button appear.

13003
User Type not supported. The user isn't signed into Office with a valid Microsoft account
or Microsoft 365 Education or work account. This may happen if Office runs with an on-
premises domain account, for example. Your code should fall back to an alternate
system of user authentication. In Outlook, this error may also occur if modern
authentication is disabled for the user's tenant in Exchange Online. For more
information, see Requirements and Best Practices.

13004
Invalid Resource. (This error should only be seen in development.) The add-in manifest
hasn't been configured correctly. Update the manifest. For more information, see
Validate an Office Add-in's manifest. The most common problem is that the <Resource>
element (in the <WebApplicationInfo> element) has a domain that does not match the
domain of the add-in. Although the protocol part of the Resource value should be "api"
not "https"; all other parts of the domain name (including port, if any) should be the
same as for the add-in.

13005
Invalid Grant. This usually means that Office has not been pre-authorized to the add-in's
web service. For more information, see Create the service application and Register the
add-in with Azure AD v2.0 endpoint. This also may happen if the user has not granted
your service application permissions to their profile , or has revoked consent. Your code
should fall back to an alternate system of user authentication.

Another possible cause, during development, is that your add-in using Internet Explorer,
and you are using a self-signed certificate. (To determine which browser or webview is
being used by the add-in, see Browsers and webview controls used by Office Add-ins.)

13006
Client Error. This error is only seen in Office on the web. Your code should suggest that
the user sign out and then restart the Office browser session.

13007
The Office application was unable to get an access token to the add-in's web service.

If this error occurs during development, be sure that your add-in registration and
add-in manifest specify the profile permission (and the openid permission, if you
are using MSAL.NET). For more information, see Register the add-in with Azure AD
v2.0 endpoint.
In production, an account mismatch could cause this error. For example, if the user
attempts to sign in with a personal Microsoft account (MSA) when a Work or
school account was expected. For these cases, your code should fall back to an
alternate system of user authentication. For more information on account types,
see Identity and account types for single- and multi-tenant apps

13008
The user triggered an operation that calls getAccessToken before a previous call of
getAccessToken completed. This error is only seen on Office on the web. Your code

should ask the user to repeat the operation after the previous operation has completed.

13010
The user is running the add-in in Office on Microsoft Edge. The user's Microsoft 365
domain, and the login.microsoftonline.com domain, are in a different security zones in
the browser settings. This error is only seen on Office on the web. If this error is
returned, the user will have already seen an error explaining this and linking to a page
about how to change the zone configuration. If your add-in provides functions that
don't require the user to be signed in, then your code should catch this error and allow
the add-in to stay running.

13012
There are several possible causes.

The add-in is running on a platform that does not support the getAccessToken API.
For example, it is not supported on iPad. See also Identity API requirement sets.
The Office document was opened from the Files tab of a Teams channel using the
Edit in Teams option on the Open dropdown menu. The getAccessToken API isn't
supported in this scenario.
The forMSGraphAccess option was passed in the call to getAccessToken and the
user obtained the add-in from AppSource. In this scenario, the tenant admin has
not granted consent to the add-in for the Microsoft Graph scopes (permissions)
that it needs. Recalling getAccessToken with the allowConsentPrompt will not solve
the problem because Office is allowed to prompt the user for consent to only the
AAD profile scope.

Your code should fall back to an alternate system of user authentication.

In development, the add-in is sideloaded in Outlook and the forMSGraphAccess option


was passed in the call to getAccessToken .
13013
The getAccessToken was called too many times in a short amount of time, so Office
throttled the most recent call. This is usually caused by an infinite loop of calls to the
method. There are scenarios when recalling the method is advisable. However, your
code should use a counter or flag variable to ensure that the method is not recalled
repeatedly. If the same "retry" code path is running again, the code should fall back to
an alternate system of user authentication. For a code example, see how the
retryGetAccessToken variable is used in HomeES6.js or ssoAuthES6.js .

50001
This error (which is not specific to getAccessToken ) may indicate that the browser has
cached an old copy of the office.js files. When you are developing, clear the browser's
cache. Another possibility is that the version of Office is not recent enough to support
SSO. On Windows, the minimum version is 16.0.12215.20006. On Mac, it is
16.32.19102902.

In a production add-in, the add-in should respond to this error by falling back to an
alternate system of user authentication. For more information, see Requirements and
Best Practices.

Errors on the server-side from Azure Active


Directory
For samples of the error-handling described in this section, see:

Office-Add-in-ASPNET-SSO
Office-Add-in-NodeJS-SSO

Conditional access / Multifactor authentication errors


In certain configurations of identity in AAD and Microsoft 365, it is possible for some
resources that are accessible with Microsoft Graph to require multifactor authentication
(MFA), even when the user's Microsoft 365 tenancy does not. When AAD receives a
request for a token to the MFA-protected resource, via the on-behalf-of flow, it returns
to your add-in's web service a JSON message that contains a claims property. The
claims property has information about what further authentication factors are needed.
Your code should test for this claims property. Depending on your add-in's
architecture, you may test for it on the client-side, or you may test for it on the server-
side and relay it to the client. You need this information in the client because Office
handles authentication for SSO add-ins. If you relay it from the server-side, the message
to the client can be either an error (such as 500 Server Error or 401 Unauthorized ) or in
the body of a success response (such as 200 OK ). In either case, the (failure or success)
callback of your code's client-side AJAX call to your add-in's web API should test for this
response.

Regardless of your architecture, if the claims value has been sent from AAD, your code
should recall getAccessToken and pass the option authChallenge: CLAIMS-STRING-HERE in
the options parameter. When AAD sees this string, it prompts the user for the
additional factor(s) and then returns a new access token which will be accepted in the
on-behalf-of flow.

Consent missing errors


If AAD has no record that consent (to the Microsoft Graph resource) was granted to the
add-in by the user (or tenant administrator), AAD will send an error message to your
web service. Your code must tell the client (in the body of a 403 Forbidden response, for
example).

If the add-in needs Microsoft Graph scopes that can only be consented to by an admin,
your code should throw an error. If the only scopes that are needed can be consented to
by the user, then your code should fall back to an alternate system of user
authentication.

Invalid or missing scope (permission) errors


This kind of error should only be seen in development.

Your server-side code should send a 403 Forbidden response to the client which
should log the error to the console or record it in a log.
Be sure your add-in manifest Scopes section specifies all needed permissions. And
be sure your registration of the add-in's web service specifies the same
permissions. Check for spelling mistakes too. For more information, see Register
the add-in with Azure AD v2.0 endpoint.

Invalid audience error in the access token for Microsoft


Graph
Your server-side code should send a 403 Forbidden response to the client which should
present a friendly message to the user and possibly also log the error to the console or
record it in a log.
Authenticate and authorize with the
Office dialog API
Article • 05/20/2023

Always use the Office dialog API to authenticate and authorize users with your Office
Add-in. You must also use the Office dialog API if you're implementing fallback
authentication when single sign-on (SSO) cannot be used.

Office Add-ins run in an iframe when opened in Office on the web. Many identity
authorities, also called Secure Token Services (STS), prevent their sign-in page from
opening in an iframe. These include Google, Facebook, and services protected by the
Microsoft identity platform (formerly Azure AD V 2.0) such as a Microsoft account, a
Microsoft 365 Education or work account, or other common account. Also security
features implemented in the webview when Office Add-ins run in Office on Windows, or
Office on Mac can prevent sign-in pages from working correctly.

For authorization to work correctly, the sign-in page must open in a separate browser or
webview control instance. This is why Office provides the Office dialog API, specifically
the displayDialogAsync method.

7 Note

This article assumes that you're familiar with Use the Office dialog API in your
Office Add-ins.
For brevity hereafter, this article uses "browser instance" to mean "browser or
webview instance".

The dialog box that is opened with this API has the following characteristics.

It is nonmodal .
It is a completely separate browser instance from the task pane, meaning:
It has its own runtime environment and window object and global variables.
There is no shared execution environment with the task pane.
It does not share the same session storage (the Window.sessionStorage
property) as the task pane.
The first page opened in the dialog box must be hosted in the same domain as the
task pane, including protocol, subdomains, and port, if any.
The dialog box can send information back to the task pane by using the
messageParent method. We recommend that this method be called only from a
page that is hosted in the same domain as the task pane, including protocol,
subdomains, and port. Otherwise, there are complications in how you call the
method and process the message. For more information, see Cross-domain
messaging to the host runtime.

By default, the dialog box opens in a new web view control, not in an iframe. This
ensures that it can open the sign-in page of an identity provider. As you'll see later in
this article, the characteristics of the Office dialog box have implications for how you use
authentication or authorization libraries such as Microsoft Authentication Library (MSAL)
and Passport.

7 Note

To configure the dialog box to open in a floating iframe, pass the displayInIframe:
true option in the call to displayDialogAsync . Do not do this when you're using the
Office dialog API for sign in.

Authentication flow with the Office dialog box


The following is a typical authentication flow.
1. The first page that opens in the dialog box is a page (or other resource) that is
hosted in the add-in's domain; that is, the same domain as the task pane window.
This page can have a UI that only says "Please wait, we are redirecting you to the
page where you can sign in to NAME-OF-PROVIDER." The code in this page
constructs the URL of the identity provider's sign-in page with information that is
either passed to the dialog box as described in Pass information to the dialog box
or is hard-coded into a configuration file of the add-in, such as a web.config file.
2. The dialog box window then redirects to the sign-in page. The URL includes a
query parameter that tells the identity provider to redirect the dialog box window
to a specific page after the user signs in. We'll call this page redirectPage.html in
this article. The results of the sign-in attempt can be passed to the task pane with a
call of messageParent on this page. We recommend that this be a page in the same
domain as the host window.
3. The identity provider's service processes the incoming GET request from the dialog
box window. If the user is already signed in, it immediately redirects the window to
redirectPage.html and includes user data as a query parameter. If the user isn't
already signed in, the provider's sign-in page appears in the window, and the user
signs in. For most providers, if the user can't sign in successfully, the provider
shows an error page in the dialog box window and does not redirect to
redirectPage.html. The user must close the window by selecting the X in the
corner. If the user successfully signs in, the dialog box window is redirected to
redirectPage.html and user data is included as a query parameter.
4. When the redirectPage.html page opens, it calls messageParent to report the
success or failure to the task pane page and optionally also report user data or
error data. Other possible messages include passing an access token or telling the
task pane that the token is in storage.
5. The DialogMessageReceived event fires in the task pane page and its handler closes
the dialog box window and may further process of the message.

Support multiple identity providers


If your add-in gives the user a choice of providers, such as a Microsoft account, Google,
or Facebook, you need a local first page (see preceding section) that provides a UI for
the user to select a provider. Selection triggers the construction of the sign-in URL and
redirection to it.

Authorization of the add-in to an external resource


In the modern web, users and web applications are security principals. The application
has its own identity and permissions to an online resource such as Microsoft 365,
Google Plus, Facebook, or LinkedIn. The application is registered with the resource
provider before it is deployed. The registration includes:
A list of the permissions that the application needs.
A URL to which the resource service should return an access token when the
application accesses the service.

When a user invokes a function in the application that accesses the user's data in the
resource service, they are prompted to sign in to the service and then prompted to
grant the application the permissions it needs to the user's resources. The service then
redirects the sign-in window to the previously registered URL and passes the access
token. The application uses the access token to access the user's resources.

You can use the Office dialog API to manage this process by using a flow that is similar
to the one described for users to sign in. The only differences are:

If the user hasn't previously granted the application the permissions it needs, the
user is prompted to do so in the dialog box after signing in.
Your code in the dialog box window sends the access token to the host window
either by using messageParent to send the stringified access token or by storing
the access token where the host window can retrieve it (and using messageParent
to tell the host window that the token is available). The token has a time limit, but
while it lasts, the host window can use it to directly access the user's resources
without any further prompting.

Some authentication sample add-ins that use the Office dialog API for this purpose are
listed in Samples.

Use authentication libraries with the dialog box


Because the Office dialog box and the task pane run in different browser runtime
instances, you must use authentication/authorization libraries differently from how they
are used when authentication and authorization take place in the same window. The
following sections describe the ways that you can and can't use these libraries.

You usually cannot use the library's internal cache to


store tokens
Typically, auth-related libraries provide an in-memory cache to store the access token. If
subsequent calls to the resource provider (such as Google, Microsoft Graph, Facebook,
etc.) are made, the library will first check to see if the token in its cache is expired. If it is
unexpired, the library returns the cached token rather than making another round-trip
to the STS for a new token. But this pattern isn't usable in Office Add-ins. Since the sign-
in process occurs in the Office dialog box's browser instance, the token cache is in that
instance.

Closely related to this is the fact that a library will typically provide both interactive and
"silent" methods for getting a token. When you can do both the authentication and the
data calls to the resource in the same browser instance, your code calls the silent
method to obtain a token just before your code adds the token to the data call. The
silent method checks for an unexpired token in the cache and returns it, if there is one.
Otherwise, the silent method calls the interactive method which redirects to the STS's
sign-in. After sign-in completes, the interactive method returns the token, but also
caches it in memory. But when the Office dialog API is being used, the data calls to the
resource, which would call the silent method, are in the task pane's browser instance.
The library's token cache does not exist in that instance.

As an alternative, your add-in's dialog box browser instance can directly call the library's
interactive method. When that method returns a token, your code must explicitly store
the token someplace where the task pane's browser instance can retrieve it, such as
Local Storage* or a server-side database. Another option is to pass the token to the task
pane with the messageParent method. This alternative is only possible if the interactive
method stores the access token in a place where your code can read it. Sometimes a
library's interactive method is designed to store the token in a private property of an
object that is inaccessible to your code.

7 Note

* There is a bug that will effect your strategy for token handling. If the add-in is
running in Office on the web in either the Safari or Edge browser, the dialog box
and task pane do not share the same Local Storage, so it can't be used to
communicate between them.

You usually cannot use the library's "auth context" object


Often, an auth-related library has a method that both obtains a token interactively and
also creates an "auth-context" object which the method returns. The token is a property
of the object (possibly private and inaccessible directly from your code). That object has
the methods that get data from the resource. These methods include the token in the
HTTP Requests that they make to the resource provider (such as Google, Microsoft
Graph, Facebook, etc.).

These auth-context objects, and the methods that create them, are not usable in Office
Add-ins. Since the sign-in occurs in the Office dialog box's browser instance, the object
would have to be created there. But the data calls to the resource are in the task pane
browser instance and there is no way to get the object from one instance to another.
For example, you can't pass the object with messageParent because messageParent can
only pass string values. A JavaScript object with methods can't be reliably stringified.

How you can use libraries with the Office dialog API
In addition to, or instead of, monolithic "auth context" objects, most libraries provide
APIs at a lower level of abstraction that enable your code to create less monolithic
helper objects. For example, MSAL.NET v. 3.x.x has an API to construct a sign-in URL,
and another API that constructs an AuthResult object that contains an access token in a
property that is accessible to your code. For examples of MSAL.NET in an Office Add-in
see: Office Add-in Microsoft Graph ASP.NET and Outlook Add-in Microsoft Graph
ASP.NET . For an example of using msal.js in an add-in, see Office Add-in Microsoft
Graph React .

For more information about authentication and authorization libraries, see Microsoft
Graph: Recommended libraries and Other external services: Libraries.

Samples
Office Add-in Microsoft Graph ASP.NET : An ASP.NET based add-in (Excel, Word,
or PowerPoint) that uses the MSAL.NET library and the Authorization Code Flow to
sign in and get an access token for Microsoft Graph data.
Outlook Add-in Microsoft Graph ASP.NET : Just like the one above, but the Office
application is Outlook.
Office Add-in Microsoft Graph React : A NodeJS based add-in (Excel, Word, or
PowerPoint) that uses the msal.js library and the Implicit Flow to sign in and get an
access token for Microsoft Graph data.

See also
Authorize external services in your Office Add-in
Use the Office dialog API in your Office Add-ins
Authorization with non-Microsoft
identity providers
Article • 03/21/2023

There are many popular identity providing services, in addition to the Microsoft identity
platform, that you can use in your add-in. They give users, and applications such as your
Office Add-in, access to the users' accounts in other applications.

The industry standard framework for enabling web application access to an online
service is OAuth 2.0. In most situations, you don't need to know the details of how the
framework works to use it in your add-in. Many libraries are available that simplify the
details for you.

A fundamental idea of OAuth is that an application can be a security principal unto itself,
just like a user or a group, with its own identity and set of permissions. In the most
typical scenarios, when the user takes an action in the Office Add-in that requires the
online service, the add-in sends the service a request for a specific set of permissions to
the user's account. The service then prompts the user to grant the add-in those
permissions. After the permissions are granted, the service sends the add-in a small
encoded access token. The add-in can use the service by including the token in all its
requests to the service's APIs. But the add-in can act only within the permissions that
the user granted it. The token also expires after a specified time.

Several OAuth patterns, called flows or grant types, are designed for different scenarios.
The following two patterns are the most commonly implemented.

Implicit flow: Communication between the add-in and the online service is
implemented with client-side JavaScript. This flow is commonly used in single-
page applications (SPAs).
Authorization Code flow: Communication is server-to-server between your add-
in's web application and the online service. So, it is implemented with server-side
code.

The purpose of an OAuth flow is to secure the identity and authorization of the
application. In the Authorization Code flow, you're provided a client secret that needs to
be kept hidden. An application that has no server-side backend, such as an SPA, has no
way to protect the secret, so we recommend that you use the Implicit flow in SPAs.

You should be familiar with the pros and cons of the Implicit flow and the Authorization
Code flow. For more information about these two flows, see Authorization Code and
Implicit .
7 Note

You also have the option of using a middleman service to perform authorization
and pass the access token to your add-in. For details about this scenario, see the
Middleman services section later in this article.

Use the Implicit flow in Office Add-ins


The best way to find out if an online service supports the Implicit flow is to consult the
service's documentation.

For information about libraries that support the Implicit flow, see the Libraries section
later in this article.

Use the Authorization Code flow in Office Add-


ins
Many libraries are available for implementing the Authorization Code flow in various
languages and frameworks. For more information about some of these libraries, see the
Libraries section later in this article.

Libraries
Libraries are available for many languages and platforms, for both the Implicit flow and
the Authorization Code flow. Some libraries are general purpose, while others are for
specific online services.

Facebook: Search Facebook for Developers for "library" or "sdk".

General OAuth 2.0: A page of links to libraries for over a dozen languages is maintained
by the IETF OAuth Working Group at: OAuth Code . Note that some of these libraries
are for implementing an OAuth compliant service. The libraries of interest to you as a an
add-in developer are called client libraries on this page because your web server is a
client of the OAuth compliant service.

Middleman services
Your add-in can use a middleman service such as OAuth.io or Auth0 to perform
authorization. A middleman service may either provide access tokens for popular online
services or simplify the process of enabling social login for your add-in, or both. With
very little code, your add-in can use either client-side script or server-side code to
connect to the middleman service and it will send your add-in any required tokens for
the online service. All of the authorization implementation code is in the middleman
service.

We recommend that the UI for authentication/authorization in your add-in use our


Dialog APIs to open a login page. See Authenticate with the Office dialog API for more
information. When you open an Office dialog in this way, the dialog has a completely
new and separate instance of the browser and JavaScript engine from the instance in
the parent page (e.g., the add-in's task pane or FunctionFile). A token, and any other
information that can be converted to a string, is passed back to the parent using an API
called messageParent . The parent page can then use the token to make authorized calls
to the resource. Because of this architecture, you must be careful how you use the APIs
provided by a middleman service. Often the service will provide an API set in which your
code creates some kind of context object which both gets a token and uses that token
in making subsequent calls to the resource. Often the service has a single API method
that makes the initial call and creates the context object. An object like this cannot be
completely stringified, so it cannot be passed from the Office dialog to the parent page.
Typically, the middleman service provides a second API set, at a lower level of
abstraction, such as a REST API. This second set will have an API that gets a token from
the service, and other APIs that pass the token to service when using it to get authorized
access to the resource. You need to work with an API at this lower level of abstraction so
that you can get the token in the Office dialog and then use messageParent to pass it to
the parent page.

What is CORS?
CORS stands for Cross Origin Resource Sharing . For information about how to use
CORS inside add-ins, see Addressing same-origin policy limitations in Office Add-ins.

See also
Overview of authentication and authorization in Office Add-ins.
Authorize to Microsoft Graph from an
Office Add-in
Article • 06/23/2023

Your add-in can get authorization to Microsoft Graph data by obtaining an access token
to Microsoft Graph from the Microsoft identity platform. Use either the Authorization
Code flow or the Implicit flow just as you would in other web applications but with one
exception: The Microsoft identity platform doesn't allow its sign-in page to open in an
iframe. When an Office Add-in is running in Office on the web, the task pane is an iframe.
This means you'll need to open the sign-in page in a dialog box by using the Office
dialog API. This affects how you use authentication and authorization helper libraries.
For more information, see Authentication with the Office dialog API.

7 Note

If you're implementing SSO and plan to access Microsoft Graph, see Authorize to
Microsoft Graph with SSO.

For information about programming authentication using the Microsoft identity


platform, see Microsoft identity platform documentation. You'll find tutorials and guides
in that documentation set, as well as links to relevant samples. Once again, you may
need to adjust the code in the samples to run in the Office dialog box to account for the
Office dialog box that runs in a separate process from the task pane.

After your code obtains the access token to Microsoft Graph, either it passes the access
token from the dialog box to the task pane, or it stores the token in a database and
signals the task pane that the token is available. (See Authentication with the Office
dialog API for details.) Code in the task pane requests data from Microsoft Graph and
includes the token in those requests. For more information about calling Microsoft
Graph and the Microsoft Graph SDKs, see Microsoft Graph documentation.

Recommended libraries and samples


We recommend that you use the following libraries when accessing Microsoft Graph.

For add-ins using a server-side with a .NET-based framework such as .NET Core or
ASP.NET, use MSAL.NET .
For add-ins using a NodeJS-based server-side, use Passport Azure AD .
For add-ins using the Implicit flow, use msal.js .
For more information about recommended libraries for working with Microsoft Identity
Platform (formerly AAD v.2.0), see Microsoft identity platform authentication libraries.

The following samples get Microsoft Graph data from an Office Add-in.

Office Add-in Microsoft Graph ASP.NET


Outlook Add-in Microsoft Graph ASP.NET
Office Add-in Microsoft Graph React
Design the UI of Office Add-ins
Article • 04/04/2023

Office Add-ins extend the Office experience by providing contextual functionality that
users can access within Office clients. Add-ins empower users to get more done by
enabling them to access external functionality within Office, without costly context
switches.

Your add-in UI design must integrate seamlessly with Office to provide an efficient,
natural interaction for your users. Take advantage of add-in commands to provide
access to your add-in and apply the best practices that we recommend when you create
a custom HTML-based UI.

Office design principles


Office applications follow a general set of interaction guidelines. The applications share
content and have elements that look and behave similarly. This commonality is built on
a set of design principles. The principles help the Office team create interfaces that
support customers’ tasks. Understanding and following them will help you support your
customers’ goals inside of Office.

Follow the Office design principles to create positive add-in experiences.

Design explicitly for Office. The functionality, as well as the look and feel, of an
add-in must harmoniously complement the Office experience. Add-ins should feel
native. They should fit seamlessly into Word on an iPad or PowerPoint on the web.
A well-designed add-in will be an appropriate blend of your experience, the
platform, and the Office application. Apply document and UI theming where
appropriate. Consider using Fluent UI for the web as your design language and
tool set. The Fluent UI for the web has two flavors.
For non-React UIs: Use Fabric Core, an open-source collection of CSS classes
and Sass mixins that give you access to colors, animations, fonts, icons, and
grids. (It's called "Fabric Core" instead of "Fluent Core" for historical reasons.) To
get started, see Fabric Core in Office Add-ins.

7 Note

While Fabric Core is the recommended library to design non-React add-ins,


the team is working on Fluent UI Web Components to provide a newer
solution. Built on FAST , the Fluent UI Web Components library allows you to
use, customize, and build Web Components to create a more modern and
standards-based UI. We invite you to test this library by completing the quick
start and welcome feedback on your experience through GitHub .

For React UIs: use Fluent UI React, a React front-end framework designed to
build experiences that fit seamlessly into a broad range of Microsoft products. It
provides robust, up-to-date, accessible React-based components which are
highly customizable using CSS-in-JS. To get started, see Fluent UI React in Office
Add-ins.

Favor content over chrome. Allow customers’ page, slide, or spreadsheet to


remain the focus of the experience. An add-in is an auxiliary interface. No
accessory chrome should interfere with the add-in’s content and functionality.
Brand your experience wisely. We know it's important to provide users with a
unique, recognizable experience but avoid distraction. Strive to keep the focus on
content and task completion, not brand attention.

Make it enjoyable and keep users in control. People enjoy using products that are
both functional and visually appealing. Craft your experience carefully. Get the
details right by considering every interaction and visual detail. Allow users to
control their experience. The necessary steps to complete a task must be clear and
relevant. Important decisions should be easy to understand. Actions should be
easily reversible. An add-in is not a destination – it’s an enhancement to Office
functionality.

Design for all platforms and input methods. Add-ins are designed to work on all
the platforms that Office supports, and your add-in UX should be optimized to
work across platforms and form factors. Support mouse/keyboard and touch input
devices, and ensure that your custom HTML UI is responsive to adapt to different
form factors. For more information, see Touch.

See also
Add-in development best practices
Use Fluent UI React in Office Add-ins
Article • 07/17/2023

Fluent UI React is the official open-source JavaScript front-end framework designed to


build experiences that fit seamlessly into a broad range of Microsoft products, including
Microsoft 365 applications. It provides robust, up-to-date, accessible React-based
components which are highly customizable using CSS-in-JS.

7 Note

This article describes the use of Fluent UI React in the context of Office Add-ins.
However, it's also used in a wide range of Microsoft 365 apps and extensions. For
more information, see Fluent UI React and the Fluent UI Web open source
repository.

This article describes how to create an add-in that's built with React and that uses Fluent
UI React components.

Create an add-in project


You'll use the Yeoman generator for Office Add-ins to create an add-in project that uses
React.

Install the prerequisites


Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.
Create the project
Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project using React framework
Choose a script type: TypeScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Word

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.
Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your add-
in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

 Tip

If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server

To test your add-in in Word, run the following command in the root directory
of your project. This starts the local web server and opens Word with your
add-in loaded.

command line

npm start

To test your add-in in Word on a browser, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of a Word document on your
OneDrive or a SharePoint library to which you have permissions.
7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM
fF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document


https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp

npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

3. To open the add-in task pane, on the Home tab, select the Show Taskpane button.
Notice the default text and the Run button at the bottom of the task pane. In the
remainder of this walkthrough, you'll redefine this text and button by creating a
React component that uses Fluent UI React components.
Install Fluent UI React as a dependency
Before you can create React components, you must first install Fluent UI React as a
dependency in your add-in project. To do so, run the following code in the root
directory of your add-in project.

command line

npm install @fluentui/react-components

Set up the style renderer in your project


The styles of Fluent UI React components are defined by a style renderer, which assigns
values to CSS variables based on a specified theme. To set up a style renderer in your
project that uses Fluent UI React, you must add the FluentProvider component and
pass it a theme. To learn more about Fluent UI React themes and the FluentProvider
component, see Theming .

Complete the following steps to add the FluentProvider component to your add-in
project and configure it to apply the Web Light theme.

1. Open your add-in project in a code editor, navigate to ./src/taskpane, then open
the index.tsx file.
2. Replace the contents of the file with the following code.

TypeScript

import App from "./components/App";


import { FluentProvider, webLightTheme } from "@fluentui/react-
components";
import * as React from "react";
import * as ReactDOM from "react-dom";

/* global document, Office, module, require */

let isOfficeInitialized = false;

const title = "Contoso Task Pane Add-in";

const render = (Component) => {


ReactDOM.render(
<FluentProvider theme={webLightTheme}>
<Component title={title} isOfficeInitialized=
{isOfficeInitialized} />
</FluentProvider>,
document.getElementById("container")
);
};

/* Render application after Office initializes */


Office.onReady(() => {
isOfficeInitialized = true;
render(App);
});

if ((module as any).hot) {
(module as any).hot.accept("./components/App", () => {
const NextApp = require("./components/App").default;
render(NextApp);
});
}

3. Save your changes.

Create a React component that uses Fluent UI


React
In this section, you'll create a new React component ( ButtonExample ) within your add-in
project. The component uses the Button and Label components from Fluent UI
React.

The code does the following:


Uses import * as React from "react"; to reference the React library.
References the Fluent UI React components ( Button , ButtonProps , and Label ) that
are used to create the ButtonExample component.
Uses export class ButtonPrimaryExample extends React.Component to declare the
new ButtonExample component.
Declares the insertText function to handle the button's onClick event.
Defines the UI of the React component in the render function. The HTML markup
uses the Button and Label components from Fluent UI React and specifies that
when the onClick event occurs, the insertText function will run.

1. Navigate to .src/taskpane/components, then create a new file named Button.tsx.

2. In Button.tsx, add the following code to define the ButtonExample component.

TypeScript

import * as React from "react";


import { Button, ButtonProps, Label } from "@fluentui/react-
components";

/* global Word */

export class ButtonExample extends React.Component<ButtonProps, {}> {


public constructor(props) {
super(props);
}

insertText = async () => {


// Write text to the document when the button is selected.
await Word.run(async (context) => {
let body = context.document.body;
body.insertParagraph("Hello Fluent UI React!",
Word.InsertLocation.end);
await context.sync();
});
};

//Defines the Label and Button Fluent React UI components.


public render() {
let { disabled } = this.props;
return (
<div className="ms-BasicButtonExample">
<Label weight="semibold">Click the button to insert text.
</Label>
<br />
<Button appearance="primary" disabled={disabled} size="large"
onClick={this.insertText}>
Insert text
</Button>
</div>
);
}
}

3. Save your changes.

Add the React component to your add-in


To add the newly created ButtonExample component to your add-in, complete the
following steps.

1. Navigate to ./src/taskpane/components, then open App.tsx.

2. Add the following import statement to reference the ButtonExample component


from Button.tsx.

TypeScript

import { ButtonExample } from './Button';

3. Remove the following import statements, as they aren't used in this sample.

TypeScript

import { DefaultButton } from "@fluentui/react";


import Progress from './Progress';

4. Replace the default render() function with the following code that uses the
ButtonExample component.

TypeScript

render() {
return (
<div className="ms-welcome">
<Header logo="assets/logo-filled.png" title={this.props.title}
message="Welcome" />
<HeroList message="Discover what this add-in can do for you
today!" items={this.state.listItems} >
<ButtonExample />
</HeroList>
</div>
);
}
5. Save your changes.

See the result


In Word, the add-in task pane automatically updates when you save changes to App.tsx.
The default text and button at the bottom of the task pane now reflect the UI defined by
the ButtonExample component. Select the Insert text button to insert text into the
document.

Congratulations, you've successfully created a task pane add-in using React and Fluent
UI React!

Migrate to Fluent UI React v9


If you have an existing add-in that implements an older version of Fluent UI React, we
recommend migrating to Fluent UI v9. For guidance on the migration process, see
Getting started migrating to v9 .

See also
Fabric Core in Office Add-ins
UX design patterns for Office Add-ins
Fluent UI GitHub repository
Fabric Core in Office Add-ins
Article • 04/04/2023

Fabric Core is an open-source collection of CSS classes and Sass mixins that's intended
for use in non-React Office Add-ins. Fabric Core contains basic elements of the Fluent UI
design language such as icons, colors, typefaces, and grids. Fabric Core is framework
independent, so it can be used with any single-page application or any server-side web
UI framework. (It's called "Fabric Core" instead of "Fluent Core" for historical reasons.)

If your add-in's UI isn't React-based, you can also make use of a set of non-React
components. See Use Office UI Fabric JS components.

7 Note

This article describes the use of Fabric Core in the context of Office Add-ins, but it's
also used in a wide range of Microsoft 365 apps and extensions. For more
information, see Fabric Core and the open source repo Office UI Fabric Core .

7 Note

While Fabric Core is the recommended library to design non-React add-ins, the
team is working on Fluent UI Web Components to provide a newer solution. Built
on FAST , the Fluent UI Web Components library allows you to use, customize,
and build Web Components to create a more modern and standards-based UI. We
invite you to test this library by completing the quick start and welcome feedback
on your experience through GitHub .

Use Fabric Core: icons, fonts, colors


1. Add the content delivery network (CDN) reference to the HTML on your page.

HTML

<link rel="stylesheet"
href="https://static2.sharepointonline.com/files/fabric/office-ui-
fabric-core/9.6.1/css/fabric.min.css">

2. Use Fabric Core icons and fonts.


To use a Fabric Core icon, include the "i" element on your page, and then reference
the appropriate classes. You can control the size of the icon by changing the font
size. For example, the following code shows how to make an extra-large table icon
that uses the themePrimary (#0078d7) color.

HTML

<i class="ms-Icon ms-font-xl ms-Icon--Table ms-fontColor-themePrimary">


</i>

For more detailed instructions, see Fluent UI Icons. To find more icons that are
available in Fabric Core, use the search feature on that page. When you find an
icon to use in your add-in, be sure to prefix the icon name with ms-Icon-- .

For information about font sizes and colors that are available in Fabric Core, see
Typography and the Colors table of contents at Colors.

Examples are included in the Samples later in this article.

Use Office UI Fabric JS components


Add-ins with non-React UIs can also use any of the many components from Office UI
Fabric JS , including buttons, dialogs, pickers, and more. See the readme of the repo
for instructions.

Examples are included in the Samples later in this article.

Samples
The following sample add-ins use Fabric Core and/or Office UI Fabric JS components.
Some of these repos are archived, meaning that they are no longer being updated with
bug or security fixes, but you can still use them to learn how to use Fabric Core and
Fabric UI components.

Excel Add-in JavaScript SalesTracker


Excel Add-in SalesLeads
Excel Add-in WoodGrove Expense Trends
Excel Content Add-in Humongous Insurance
Office Add-in Fabric UI Sample
Office-Add-in-UX-Design-Patterns-Code
Outlook Add-in GifMe
PowerPoint Add-in Microsoft Graph ASPNET InsertChart
Word Add-in Angular2 StyleChecker
Word Add-in JS Redact
Word Add-in MarkdownConversion
Accessibility guidelines
Article • 05/18/2023

As you design and develop your Office Add-ins, you'll want to ensure that all potential
users and customers are able to use your add-in successfully. Apply the following
guidelines to ensure that your solution is accessible to all audiences.

Design for multiple input methods


Ensure that users can perform operations by using only the keyboard. Users should
be able to move to all actionable elements on the page by using a combination of
the Tab and arrow keys.
On a mobile device, when users operate a control by touch, the device should
provide useful audio feedback.
Provide helpful labels for all interactive controls.

Make your add-in easy to use


Don't rely on a single attribute, such as color, size, shape, location, orientation, or
sound, to convey meaning in your UI.
Avoid unexpected changes of context, such as moving the focus to a different UI
element without user action.
Provide a way to verify, confirm, or reverse all binding actions.
Provide a way to pause or stop media, such as audio and video.
Don't impose a time limit for user action.

Make your add-in easy to see


Avoid unexpected color changes.
Provide meaningful and timely information to describe UI elements, titles and
headings, inputs, and errors. Ensure that names of controls adequately describe
the intent of the control.
Follow standard guidelines for color contrast.

Account for assistive technologies


Avoid using features that interfere with assistive technologies, including visual,
audio, or other interactions.
Don't provide text in an image format. Screen readers can't read text within
images.
Provide a way for users to adjust or mute all audio sources.
Provide a way for users to turn on captions or audio description with audio
sources.
Provide alternatives to sound as a means to alert users, such as visual cues or
vibrations.

See also
Web Content Accessibility Guidelines (WCAG) 2.0
Guidance on Applying WCAG 2.0 to Non-Web Information and Communications
Technologies (WCAG2ICT)
European Standard on accessibility requirements for Information and
Communication Technologies (ICT)
Data visualization style guidelines for
Office Add-ins
Article • 06/29/2023

Good data visualizations help users find insights in their data. They can use those
insights to tell stories that inform and persuade. This article provides guidelines to help
you design effective data visualizations in your add-ins for Excel and other Office apps.

We recommend that you use Fluent UI to create the chrome for your data visualizations.
Fluent UI includes styles and components that integrate seamlessly with the Office look
and feel.

Data visualization elements


Data visualizations share a general framework and common visual and interactive
elements, including titles, labels, and data plots, as shown in the following figure.

Chart titles
Follow these guidelines for chart titles.

Make your chart titles easily readable. Position them to create a clear visual
hierarchy in relation to the rest of the chart.
In general, use sentence capitalization (capitalize the first word). To create contrast
or to reinforce hierarchies, you can use all caps, but all caps should be used
sparingly.

Incorporate the Fluent UI type ramp to make your charts consistent with the Office
UI, which uses Segoe. You can also use a different typeface to differentiate chart
content from the UI.
Fluent UI React typography styles
Fabric Core typography styles

Use sans-serif typefaces with large counters.

Axis labels
Make your axis labels dark enough to read clearly, with adequate contrast ratios
between the text and background colors. Make sure that they are not so dark that they
compete with data ink.

Light grays are most effective for axis labels. Explore the following Fluent UI neutral
color palettes.

Fluent UI React color schemes


Fabric Core color schemes

Data ink
The pixels that represent the actual data in a chart are referred to as data ink. This
should be the central focus of the visualization. Avoid the use of drop shadows, heavy
outlines, or unnecessary design elements that distort or compete with the data. Use
gradients only when data values are tied to color values. Avoid three-dimensional charts
unless a measurable, objective value is bound to a third dimension.

Color
Choose colors that follow operating system or application themes rather than
hardcoded colors. At the same time, make sure that the colors you apply don't distort
the data. Misuse of color in data visualizations can result in data distortion and incorrect
reading of information.

For best practices for use of color in data visualizations, see the following:

Why rainbow colors aren't the best option for data visualizations
Color Brewer 2.0: Color Advice for Cartography
I Want Hue

Gridlines
Gridlines are often necessary for accurately reading a chart, but should be presented as
a secondary visual element, enhancing the data ink, not competing with it. Make static
gridlines thin and light, unless they are designed specifically for high contrast. You can
also use interaction to create dynamic, just-in-time gridlines that appear in context
when a user interacts with a chart.

Light grays are most effective for gridlines. Explore the following Fluent UI neutral color
palettes.

Fluent UI React color schemes


Fabric Core color schemes

The following image shows a data visualization with gridlines.

Legends
Add legends if necessary to:

Distinguish between series.


Present scale or value changes.

Make sure that your legends enhance the data ink and don't compete with it. Place
legends:
Flush left above the plot area by default, if all legend items fit above the chart.
On the upper right side of the plot area, if all legend items don't fit above the
chart, and make it scrollable, if necessary.

To optimize for readability and accessibility, map legend markers to the relevant chart
shape. For example, use circle legend markers for scatter plot and bubble chart legends.
Use line segment legend markers for line charts.

Data labels and tooltips


Ensure that data labels and tooltips have adequate white space and type variation. Use
algorithms to minimize occlusion and collision. For example, a tooltip might surface to
the right of a data point by default, but surface to the left if right edges are detected.

Design principles
The Office Design team created the following set of design principles, which we use
when designing new data visualizations for the Office product suite.

Visual design principles


Visualizations should honor and enhance the data, making it easy to understand.
Highlight the data, adding supporting elements only as needed to provide context.
Avoid unnecessary embellishments, such as drop shadows and outlines, chart junk,
or data distortion.
Visualizations should encourage exploration by providing rich visual feedback. Use
well-established interaction patterns, interface controls, and clear system feedback.
Embody time-honored design principles. Use established typographic and visual
communication design principles to enhance form, readability, and meaning.

Interaction design principles


Design to allow for exploration.
Allow for direct interactions with objects that reveal new insights (sorting via drag,
for example).
Use simple, direct, familiar interaction models.

For more information about how to design user-friendly interactive data visualizations,
see UI Tenets and Traps .
Motion design principles
Motion follows stimulus. Visual elements should move in the same direction at the same
rate. This applies to:

Chart creation
Transition from one chart type to another chart type
Filtering
Sorting
Adding or subtracting data
Brushing or slicing data
Resizing a chart

Create a perception of causality. When staging animations:

Stage one thing at a time.


Stage changes to axes before changes to data ink.
Stage and animate objects as a group if they are moving at the same speed in the
same direction.
Stage data elements in groups of no more than 4-5 objects. Viewers have difficulty
tracking more than 4-5 objects independently.

Motion adds meaning.

Animations increase user comprehension of changes to the data, provide context,


and act as a non-verbal annotation layer.
Motion should occur in a meaningful coordinate space of the visualization.
Tailor the animation to the visual.
Avoid gratuitous animations.

Motion follows data.

Preserve data mappings. If an area is tied to a measure, maintain that area in


transition.
Maintain a consistent animation design language. Where possible, map data
visualization animation to existing Office motion design language. Use similar
animations for similar chart types.

Accessibility in data visualizations


Don't use color as the only way to communicate information. People who are color
blind will not be able to interpret the results. Use shape, size and texture in
addition to color when possible to communicate information.
Make all interactive elements, such as push buttons or pick lists, accessible from a
keyboard.
Send accessibility events to screen readers to announce focus changes, tooltips,
and so on.

See also
The Five Best Libraries for Building Data Visualizations
The Visual Display of Quantitative Information
Voice and tone guidelines
Article • 05/18/2023

As you design your Office Add-ins, consider the voice that you use in your UI text and
elements. Strive to match the voice and tone of the Office UI, which is conversational,
engaging, and accessible to users.

Use a natural style. Write the way that you speak. Avoid jargon and overly
technical words and phrases. Use terms that are familiar to your users.
Use simple, direct language. Use short words and sentences, and active voice in
your text.
Be consistent. Use the same words for the same concepts throughout.
Engage the user. Address the user as "you". Avoid using third person. Use
imperatives for user tasks.
Be helpful and empathetic. Make your text positive, polite, supportive, and
encouraging. Emphasize what users can accomplish ― not what they can't.
Know your customers. Be mindful of cultural considerations and globalization
when you use idioms or colloquialisms.

See also
Guidelines for writing for all abilities
Top 10 tips for Microsoft style and voice
Guidelines on word choice
Office Add-in validation policies
Office Add-in design language
Article • 04/04/2023

The Office design language is a clean and simple visual system that ensures consistency
across experiences. It contains a set of visual elements that define Office interfaces,
including:

A standard typeface
A common color palette
A set of typographic sizes and weights
Icon guidelines
Shared icon assets
Animation definitions
Common components

Fluent UI is the official front-end framework for building with the Office design
language. Using Fluent UI is optional, but it is the fastest way to ensure that your add-
ins feel like a natural extension of Office. Take advantage of Fluent UI to design and
build add-ins that complement Office.

Many Office Add-ins are associated with a preexisting brand. You can retain a strong
brand and its visual or component language in your add-in. Look for opportunities to
retain your own visual language while integrating with Office. Consider ways to swap
out Office colors, typography, icons, or other stylistic elements with elements of your
own brand. Consider ways to follow common add-in layouts or UX design patterns while
inserting controls and components that are familiar to your customers.

Inserting a heavily branded HTML-based UI inside of Office can create dissonance for
customers. Find a balance that fits seamlessly in Office but also clearly aligns with your
service or parent brand. When an add-in does not fit with Office, it's often because
stylistic elements conflict. For example, typography is too large and off grid, colors are
contrasting or particularly loud, or animations are superfluous and behave differently
than Office. The appearance and behavior of controls or components veer too far from
Office standards.
Color guidelines for Office Add-ins
Article • 04/04/2023

Color is often used to emphasize brand and reinforce visual hierarchy. It helps identify
an interface as well as guide customers through an experience. Inside Office, color is
used for the same goals but it is applied purposefully and minimally. At no point does it
overwhelm customer content. Even when each Office app is branded with its own
dominant color, it is used sparingly.

Fabric Core includes a set of default theme colors. When Fabric Core is applied to an
Office Add-in in components or in layouts, the same goals apply. Color should
communicate hierarchy, purposefully guiding customers to action without interfering
with content. Fabric Core theme colors can introduce a new accent color to the overall
interface. This new accent can conflict with Office app branding and interfere with
hierarchy. In other words, Fabric Core can introduce a new accent color to the overall
interface when used inside an add-in. This new accent color can distract and interfere
with the overall hierarchy. Consider ways to avoid conflicts and interference. Use neutral
accents or overwrite Fabric Core theme colors to match Office app branding or your
own brand colors.

Office applications allow customers to personalize their interfaces by applying an Office


UI theme. Customers can choose between four UI themes to vary styling of backgrounds
and buttons in Word, PowerPoint, Excel and other apps in the Office suite. To make your
add-ins feel like a natural part of Office and respond to personalization, use our
Theming APIs. For example, task pane background colors switch to a dark gray in some
themes. Our theming APIs allow you to follow suit and adjust foreground text to ensure
accessibility.
7 Note

For mail and task pane add-ins, use the Context.officeTheme property to
match the theme of the Office applications. This API is currently available in
Office 2016 or later.
For PowerPoint content add-ins, see Use Office themes in your PowerPoint
add-ins.

Apply the following general guidelines for color.

Use color sparingly to communicate hierarchy and reinforce brand.


Overuse of a single accent color applied to both interactive and non-interactive
elements can lead to confusion. For example, avoid using the same color for
selected and unselected items in a navigation menu.
Avoid unnecessary conflicts with Office branded app colors.
Use your own brand colors to build association with your service or company.
Ensure that all text is accessible. Be sure that there is a 4.5:1 contrast ratio between
foreground text and background.
Be aware of color blindness. Use more than just color to indicate interactivity and
hierarchy.
Refer to icon guidelines to learn more about designing add-in command icons
with the Office icon color pallette.
Icons
Article • 04/04/2023

Icons are the visual representation of a behavior or concept. They are often used to add
meaning to controls and commands. Visuals, either realistic or symbolic, enable the user
to navigate the UI the same way signs help users navigate their environment. They
should be simple, clear, and contain only the necessary details to enable customers to
quickly parse what action will occur when they choose a control.

Office app ribbon interfaces have a standard visual style. This ensures consistency and
familiarity across Office apps. The guidelines will help you design a set of PNG assets for
your solution that fit in as a natural part of Office.

Many HTML containers contain controls with iconography. Use Fabric Core’s custom
font to render Office styled icons in your add-in. The icon font provided by Fabric Core
contains many glyphs for common Office metaphors that you can scale, color, and style
to suit your needs. If you have an existing visual language with your own set of icons,
feel free to use it in your HTML canvases. Building continuity with your own brand with a
standard set of icons is an important part of any design language. Be careful to avoid
creating confusion for customers by conflicting with Office metaphors.

Design icons for add-in commands


Add-in commands add buttons, text, and icons to the Office UI. Your add-in command
buttons should provide meaningful icons and labels that clearly identify the action the
user is taking when they use a command. The following articles provide stylistic and
production guidelines to help you design icons that integrate seamlessly with Office.

For the Monoline style of Microsoft 365, see Monoline style icon guidelines for
Office Add-ins.
For the Fresh style of perpetual Office 2013+, see Fresh style icon guidelines for
Office Add-ins.

7 Note

You must choose one style or the other and your add-in will use the same icons
whether it is running in Microsoft 365 or perpetual Office.

See also
Add-in development best practices
Add-in commands for Excel, Word, and PowerPoint
Fresh style icon guidelines for Office
Add-ins
Article • 04/04/2023

The Office 2013+ (perpetual) versions of Office use Microsoft's Fresh style iconography.
If you would prefer that your icons match the Monoline style of Microsoft 365, see
Monoline style icon guidelines for Office Add-ins.

Office Fresh visual style


The Fresh icons include only essential communicative elements. Non-essential elements
including perspective, gradients, and light source are removed. The simplified icons
support faster parsing of commands and controls. Follow this style to best fit with Office
perpetual clients.

Best practices
Follow these guidelines when you create your icons.

Do Don't

Keep visuals simple and clear, Don't use artifacts that make your icon look messy.
focusing on the key elements
of the communication.

Use the Office icon language to Don’t repurpose Fabric Core glyphs for add-in commands in the
represent behaviors or Office app ribbon or contextual menus. Fabric Core icons are
concepts. stylistically different and will not match.

Reuse common Office visual Don't reuse visual metaphors for different commands. Using the
metaphors such as paintbrush same icon for different behaviors and concepts can cause
for format or magnifying glass confusion.
for find.

Redraw your icons to make Don't resize your icons by shrinking or enlarging in size. This
them small or larger. Take the can lead to poor visual quality and unclear actions. Complex
time to redraw cutouts, icons created at a larger size may lose clarity if resized to be
corners, and rounded edges to smaller without redraw.
maximize line clarity.
Do Don't

Use a white fill for accessibility. Avoid relying on your logo or brand to communicate what an
Most objects in your icons will add-in command does. Brand marks aren't always recognizable
require a white background to at smaller icon sizes and when modifiers are applied. Brand
be legible across Office UI marks often conflict with Office app ribbon icon styles, and can
themes and in high-contrast compete for user attention in a saturated environment.
modes.

Use the PNG format with a None.


transparent background.

Avoid localizable content in None.


your icons, including
typographic characters,
indications of paragraph rags,
and question marks.

Icon size recommendations and requirements


Office desktop icons are bitmap images. Different sizes will render depending on the
user's DPI setting and touch mode. Include all eight supported sizes to create the best
experience in all supported resolutions and contexts. The following are the supported
sizes - three are required.

16 px (Required)
20 px
24 px
32 px (Required)
40 px
48 px
64 px (Recommended, best for Mac)
80 px (Required)

) Important

For an image that is your add-in's representative icon, see Create effective listings
in AppSource and within Office for size and other requirements.

Make sure to redraw your icons for each size rather than shrink them to fit.
Icon anatomy and layout
Office icons are typically comprised of a base element with action and conceptual
modifiers overlaid. Action modifiers represent concepts such as add, open, new, or close.
Conceptual modifiers represent status, alteration, or a description of the icon.

To create commands that align with the Office UI, follow layout guidelines for the base
element and modifiers. This ensures that your commands look professional and that
your customers will trust your add-in. If you make exceptions to these guidelines, do so
intentionally.

The following image shows the layout of base elements and modifiers in an Office icon.

Center base elements in the pixel frame with empty padding all around.
Place action modifiers on the top left.
Place conceptual modifiers on the bottom right.
Limit the number of elements in your icons. At 32 px, limit the number of modifiers
to a maximum of two. At 16 px, limit the number of modifiers to one.
Base element padding
Place base elements consistently across sizes. If base elements can't be centered in the
frame, align them to the top left, leaving the extra pixels on the bottom right. For best
results, apply the padding guidelines listed in the table in the following section.

Modifiers
All modifiers should have a 1 px transparent cutout between each element, including
the background. Elements should not directly overlap. Create whitespace between rules
and edges. Modifiers can vary slightly in size, but use these dimensions as a starting
point.

Icon size Padding around base element Modifier size

16 px 0 9 px

20 px 1px 10 px

24 px 1px 12 px

32 px 2px 14 px

40 px 2px 20 px

48 px 3px 22 px

64 px 5px 29 px

80 px 5px 38 px

Icon colors

7 Note

These color guidelines are for ribbon icons used in Add-in commands. These icons
are not rendered with Fluent UI and the color palette is different from the palette
described at Microsoft UI Fabric | Colors | Shared .

Office icons have a limited color palette. Use the colors listed in the following table to
guarantee seamless integration with the Office UI. Apply the following guidelines to the
use of color.
Use color to communicate meaning rather than for embellishment. It should
highlight or emphasize an action, status, or an element that explicitly differentiates
the mark.
If possible, use only one additional color beyond gray. Limit additional colors to
two at the most.
Colors should have a consistent appearance in all icon sizes. Office icons have
slightly different color palettes for different icon sizes. 16 px and smaller icons are
slightly darker and more vibrant than 32 px and larger icons. Without these subtle
adjustments, colors appear to vary across sizes.

Color name RGB Hex Color Category

Text Gray (80) 80, 80, 80 #505050 Text

Text Gray (95) 95, 95, 95 #5F5F5F Text

Text Gray (105) 105, 105, 105 #696969 Text

Dark Gray 32 128, 128, 128 #808080 32 px and above

Medium Gray 32 158, 158, 158 #9E9E9E 32 px and above

Light Gray ALL 179, 179, 179 #B3B3B3 All sizes

Dark Gray 16 114, 114, 114 #727272 16 px and below

Medium Gray 16 144, 144, 144 #909090 16 and below

Blue 32 77, 130, 184 #4d82B8 32 px and above


Color name RGB Hex Color Category

Blue 16 74, 125, 177 #4A7DB1 16 px and below

Yellow ALL 234, 194, 130 #EAC282 All sizes

Orange 32 231, 142, 70 #E78E46 32 px and above

Orange 16 227, 142, 70 #E3751C 16 px and below

Pink ALL 230, 132, 151 #E68497 All sizes

Green 32 118, 167, 151 #76A797 32 px and above

Green 16 104, 164, 144 #68A490 16 px and below

Red 32 216, 99, 68 #D86344 32 px and above

Red 16 214, 85, 50 #D65532 16 px and below

Purple 32 152, 104, 185 #9868B9 32 px and above

Purple 16 137, 89, 171 #8959AB 16 px and below

Icons in high contrast modes


Office icons are designed to render well in high contrast modes. Foreground elements
are well differentiated from backgrounds to maximize legibility and enable recoloring. In
high contrast modes, Office will recolor any pixel of your icon with a red, green, or blue
value less than 190 to full black. All other pixels will be white. In other words, each RGB
channel is assessed where 0-189 values are black and 190-255 values are white. Other
high-contrast themes recolor using the same 190 value threshold but with different
rules. For example, the high-contrast white theme will recolor all pixels greater than 190
opaque but all other pixels as transparent. Apply the following guidelines to maximize
legibility in high-contrast settings.

Aim to differentiate foreground and background elements along the 190 value
threshold.
Follow Office icon visual styles.
Use colors from our icon palette.
Avoid the use of gradients.
Avoid large blocks of color with similar values.

See also
Icon manifest element
IconUrl manifest element
HighResolutionIconUrl manifest element
Create an icon for your add-in
Monoline style icon guidelines for
Office Add-ins
Article • 04/04/2023

Monoline style iconography are used in Office apps. If you would prefer that your icons
match the Fresh style of perpetual Office 2013+, see Fresh style icon guidelines for
Office Add-ins.

Office Monoline visual style


The goal of the Monoline style to have consistent, clear, and accessible iconography to
communicate action and features with simple visuals, ensure the icons are accessible to
all users, and have a style that is consistent with those used elsewhere in Windows.

The following guidelines are for 3rd party developers who want to create icons for
features that will be consistent with the icons already present Office products.

Design principles
Simple, clean, clear.
Contain only necessary elements.
Inspired by Windows icon style.
Accessible to all users.

Convey meaning

Use descriptive elements such as a page to represent a document or an envelope


to represent mail.
Use the same element to represent the same concept, i.e., mail is always
represented by an envelope, not a stamp.
Use a core metaphor during concept development.

Reduction of elements
Reduce the icon to its core meaning, using only elements that are essential to the
metaphor.
Limit the number of elements in an icon to two, regardless of icon size.
Consistency
Sizes, arrangement, and color of icons should be consistent.

Styling

Perspective

Monoline icons are forward-facing by default. Certain elements that require perspective
and/or rotation, such as a cube, are allowed, but exceptions should be kept to a
minimum.

Embellishment

Monoline is a clean minimal style. Everything uses flat color, which means there are no
gradients, textures, or light sources.

Designing

Sizes
We recommend that you produce each icon in all these sizes to support high DPI
devices. The absolutely required sizes are 16 px, 20 px, and 32 px, as those are the 100%
sizes.

16 px, 20 px, 24 px, 32 px, 40 px, 48 px, 64 px, 80 px, 96 px

) Important

For an image that is your add-in's representative icon, see Create effective listings
in AppSource and within Office for size and other requirements.

Layout
The following is an example of icon layout with a modifier.
Elements
Base: The main concept that the icon represents. This is usually the only visual
needed for the icon, but sometimes the main concept can be enhanced with a
secondary element, a modifier.

Modifier Any element that overlays the base; that is, a modifier that typically
represents an action or a status. It modifies the base element by acting as an
addition, alteration, or a descriptor.

Construction

Element placement

Base elements are placed in the center of the icon within the padding. If it can't be
placed perfectly centered, then the base should err to the top right. In the following
example, the icon is perfectly centered.
In the following example, the icon is erring to the left.

Modifiers are almost always placed in the bottom right corner of the icon canvas. In
some rare cases, modifiers are placed in a different corner. For example, if the base
element would be unrecognizable with the modifier in the bottom right corner, then
consider placing it in the upper left corner.

Padding

Each size icon has a specified amount of padding around the icon. The base element
stays within the padding, but the modifier should butt up to the edge of the canvas,
extending outside of the padding to the edge of the icon border. The following images
show the recommended padding to use for each of the icon sizes.

16px 20px 24px 32px 40px 48px 64px 80px 96px

Line weights
Monoline is a style dominated by line and outlined shapes. Depending on what size you
are producing the icon should use the following line weights.

Icon Size: 16px 20px 24px 32px 40px 48px 64px 80px 96px

Line 1px 1px 1px 1px 2px 2px 2px 2px 3px
Weight:
Icon Size: 16px 20px 24px 32px 40px 48px 64px 80px 96px

Example
icon:

Cutouts
When an icon element is placed on top of another element, a cutout (of the bottom
element) is used to provide space between the two elements, mainly for readability
purposes. This usually happens when a modifier is placed on top of a base element, but
there are also cases where neither of the elements is a modifier. These cutouts between
the two elements is sometimes referred to as a "gap".

The size of the gap should be the same width as the line weight used on that size. If
making a 16 px icon, the gap width would be 1px and if it is a 48 px icon then the gap
should be 2px. The following example shows a 32 px icon with a gap of 1px between the
modifier and the underlying base.

In some cases, the gap can be increase by a 1/2 px if the modifier has a diagonal or
curved edge and the standard gap doesn't provide enough separation. This will likely
only affect the icons with 1px line weight: 16 px, 20 px, 24 px, and 32 px.

Background fills
Most icons in the Monoline icon set require background fills. However, there are cases
where the object would not naturally have a fill, so no fill should be applied. The
following icons have a white fill.
The following icons have no fill. (The gear icon is included to show that the center hole
is not filled.)

Best practices for fills

Dos

Fill any element that has a defined boundary, and would naturally have a fill.
Use a separate shape to create the background fill.
Use Background Fill from the color palette.
Maintain the pixel separation between overlapping elements.
Fill between multiple objects.

Don'ts

Don't fill objects that would not naturally be filled; for example, a paperclip.
Don't fill brackets.
Don't fill behind numbers or alpha characters.

Color
The color palette has been designed for simplicity and accessibility. It contains 4 neutral
colors and two variations for blue, green, yellow, red, and purple. Orange is intentionally
not included in the Monoline icon color palette. Each color is intended to be used in
specific ways as outlined in this section.

Palette
How to use color
In the Monoline color palette, all colors have Standalone, Outline, and Fill variations.
Generally, elements are constructed with a fill and a border. The colors are applied in
one of the following patterns.

The Standalone color alone for objects that have no fill.


The border uses the Outline color and the fill uses the Fill color.
The border uses the Standalone color and the fill uses the Background Fill color.

The following are examples of using color.

The most common situation will be to have an element use Dark Gray Standalone with
Background Fill.

When using a colored Fill, it should always be with its corresponding Outline color. For
example, Blue Fill should only be used with Blue Outline. But there are two exceptions to
this general rule.

Background Fill can be used with any color Standalone.


Light Gray Fill can be used with two different Outline colors: Dark Gray or Medium
Gray.

When to use color


Color should be used to convey the meaning of the icon rather than for embellishment.
It should highlight the action to the user. When a modifier is added to a base element
that has color, the base element is typically turned into Dark Gray and Background Fill
so that the modifier can be the element of color, such as the case below with the "X"
modifier being added to the picture base in the leftmost icon of the following set.

You should limit your icons to one additional color, other than the Outline and Fill
mentioned above. However, more colors can be used if it is vital for its metaphor, with a
limit of two additional colors other than gray. In rare cases, there are exceptions when
more colors are needed. The following are good examples of icons that use just one
color.

But the following icons use too many colors.

Use Medium Gray for interior "content", such as grid lines in an icon of a spreadsheet.
Additional interior colors are used when the content needs to show the behavior of the
control.

Text lines
When text lines are in a "container" (for example, text on a document), use medium
gray. Text lines not in a container should be Dark Gray.

Text
Avoid using text characters in icons. Since Office products are used around the world,
we want to keep icons as language neutral as possible.
Production

Icon file format


The final icons should be saved as .png image files. Use PNG format with a transparent
background and have 32-bit depth.

See also
Icon manifest element
IconUrl manifest element
HighResolutionIconUrl manifest element
Create an icon for your add-in
Layout
Article • 04/04/2023

Each HTML container embedded in Office will have a layout. These layouts are the main
screens of your add-in. In them you will create experiences that enable customers to
initiate actions, modify settings, view, scroll, or navigate content. Design your add-in
with a consistent layouts across screens to guarantee continuity of experience. If you
have an existing website that your customers are familiar with using, consider reusing
layouts from your existing web pages. Adapt them to fit harmoniously within Office
HTML containers.

For guidelines on layout, see Task pane, Content. For more information about how to
assemble Fluent UI React, or Office UI Fabric JS, components into common layouts and
user experience flows, see UX design patterns templates.

Apply the following general guidelines for layouts.

Avoid narrow or wide margins on your HTML containers. 20 pixels is a great


default.
Align elements intentionally. Extra indents and new points of alignment should aid
visual hierarchy.
Office interfaces are on a 4px grid. Aim to keep your padding between elements at
multiples of 4.
Overcrowding your interface can lead to confusion and inhibit ease of use with
touch interactions.
Keep layouts consistent across screens. Unexpected layout changes look like visual
bugs that contribute to a lack of confidence and trust with your solution.
Follow common layout patterns. Conventions help users understand how to use an
interface.
Avoid redundant elements like branding or commands.
Consolidate controls and views to avoid requiring too much mouse movement.
Create responsive experiences that adapt to HTML container widths and heights.
Using motion in Office Add-ins
Article • 06/29/2023

When you design an Office Add-in, you can use motion to enhance the user experience.
UI elements, controls, and components often have interactive behaviors that require
transitions, motion, or animation. Common characteristics of motion across UI elements
define the animation aspects of a design language.

Because Office is focused on productivity, the animation language supports the goal of
helping customers get things done. It strikes a balance between performant response,
reliable choreography, and detailed delight. Office Add-ins sit within this existing
animation language. Given this context, it's important to consider the following
guidelines when applying motion.

Create motion with a purpose


Motion should have a purpose that communicates additional value to the user. Consider
the tone and purpose of your content when choosing animations. Handle critical
messages differently than exploratory navigation.

Standard elements used in an add-in can incorporate motion to help focus the user,
show how elements relate to each other, and validate user actions. Choreograph
elements to reinforce hierarchy and mental models.

Best practices

Do Don't

Identify key elements in the add-in that should Don't overwhelm the user by animating
have motion. Commonly animated elements in an every element. Avoid applying multiple
add-in are panels, overlays, modals, tool tips, motions that attempt to lead or focus the
menus, and teaching call outs. user on many elements at once.

Use simple, subtle motion that behaves in Don't create wait time for a motion. Motion
expected ways. Consider the origin of your in add-ins should not hinder task
triggering element. Use motion to create a link completion.
between the action and the resulting UI.
Use expected motions
We recommend using Fluent UI to create a visual connection with the Office platform.

Fluent UI React motion patterns


Fabric Core motion and animation patterns

Use it to fit seamlessly in your add-in. It will help you create experiences that are more
felt than observed. The animation CSS classes provide directionality, enter/exit, and
duration specifics that reinforce Office mental models and provide opportunities for
customers to learn how to interact with your add-in.

Best practices

Do Don't

Use motion that aligns with behaviors in Fluent UI. Don't create motions that interfere or
conflict with common motion patterns
in Office.

Ensure that there is a consistent application of motion Don't use different motions to
across like elements. animate the same component or
object.

Create consistency with use of direction in animation. Don't animate an element using
For example, a panel that opens from the right should multiple directions.
close to the right.
Avoid out of character motion for an element
Consider the size of the HTML canvas (task pane, dialog box, or content add-in) when
implementing motion. Avoid overloading in constrained spaces. Moving elements
should be in tune with Office. The character of add-in motion should be performant,
reliable, and fluid. Instead of impeding productivity, aim to inform and direct.

Best practices

Do Don't

Use Don't use exaggerated animations. Avoid creating experiences that embellish
recommended and distract your customers.
motion durations.

Follow Don't move elements in a jerky or disjointed manner. Avoid anticipations,


recommended bounces, rubber band, or other effects that emulate natural world physics.
easing curves.
See also
Fluent UI React motion patterns
Fabric Core animation guidelines
Motion for Universal Windows Platform apps
Typography
Article • 04/04/2023

Segoe is the standard typeface for Office. Use it in your add-in to align with Office task
panes, dialog boxes, and content objects. Fabric Core gives you access to Segoe. It
provides a full type ramp of Segoe with many variations - across font weight and size -
in convenient CSS classes. Not all Fabric Core sizes and weights will look great in an
Office Add-in. To fit harmoniously or avoid conflicts, consider using a subset of the
Fabric Core type ramp. The following table lists Fabric Core's base classes that we
recommend for use in Office Add-ins.

7 Note

Text color is not included in these base classes. Use Fabric Core's "neutral primary"
for most text on white backgrounds.

To learn more about available typography, see Web Typography.

Type Class Size Weight Recommended Usage

Hero .ms- 28 Segoe This class is larger than all other typographic
font- px Light elements in Office. Use it sparingly to avoid
xxl unseating visual hierarchy.
Avoid use on long strings in constrained spaces.
Provide ample whitespace around text using this
class.
Commonly used for first-run messages, hero
elements, or other calls to action.

Title .ms- 21 Segoe This class matches the task pane title of Office
font- px Light applications.
xl Use it sparingly to avoid a flat typographic
hierarchy.
Commonly used as the top-level element such as
dialog box, page, or content titles.

Subtitle .ms- 17 Segoe This class is the first stop below titles.
font- px Semilight Commonly used as a subtitle, navigation element,
l or group header.
Type Class Size Weight Recommended Usage

Body .ms- 14 Segoe Commonly used as body text within add-ins.


font- px Regular
m

Caption .ms- 11 Segoe Commonly used for secondary or tertiary text such
font- px Regular as timestamps, by lines, captions, or field labels.
xs

Annotation .ms- 10 Segoe The smallest step in the type ramp should be used
font- px Semibold rarely. It's available for circumstances where
mi legibility is not required.
UX design patterns for Office Add-ins
Article • 06/27/2023

Designing the user experience for Office Add-ins should provide a compelling
experience for Office users and extend the overall Office experience by fitting seamlessly
within the default Office UI.

Our UX patterns are composed of components. Components are controls that help your
customers interact with elements of your software or service. Buttons, navigation, and
menus are examples of common components that often have consistent styles and
behaviors.

Fluent UI React components look and behave like a part of Office, as do the framework-
neutral components of Office UI Fabric JS. Take advantage of either set of components
to integrate with Office. Alternatively, if your add-in has its own preexisting component
language, you don't need to discard it. Look for opportunities to retain it while
integrating with Office. Consider ways to swap out stylistic elements, remove conflicts,
or adopt styles and behaviors that remove user confusion.

The provided patterns are best practice solutions based on common customer scenarios
and user experience research. They are meant to provide both a quick entry point to
designing and developing add-ins as well as guidance to achieve balance between
Microsoft brand elements and your own. Providing a clean, modern user experience that
balances design elements from Microsoft's Fluent UI design language and the partner's
unique brand identity may help increase user retention and adoption of your add-in.

Use the UX pattern templates to:

Apply solutions to common customer scenarios.


Apply design best practices.
Incorporate Fluent UI components and styles.
Build add-ins that visually integrate with the default Office UI.
Ideate and visualize UX.

Getting started
The patterns are organized by key actions or experiences that are common in an add-in.
The main groups are:

First-run experience (FRE)


Authentication
Navigation
Branding Design

Browse each grouping to get an idea of how you can design your add-in using best
practices.

7 Note

The example screens shown throughout this documentation are designed and
displayed at a resolution of 1366x768.

See also
Design tool kits
Best practices for developing Office Add-ins
Fluent UI React in Office Add-ins
Authentication patterns
Article • 08/15/2023

Add-ins may require users to sign-in or sign-up in order to access features and
functionality. Input boxes for username and password or buttons that start third party
credential flows are common interface controls in authentication experiences. A simple
and efficient authentication experience is an important first step to getting users started
with your add-in.

Best practices
Do Don't

Prior to sign-in, describe the value of your add-in Expect users to sign-in without
or demonstrate functionality without requiring an understanding the value and benefits of your
account. add-in.

Guide users through authentication flows with a Draw attention to secondary and tertiary
primary, highly visible button on each screen. tasks with competing buttons and calls to
action.

Use clear button labels that describe specific Use vague button labels like "Submit" or "Get
tasks like "Sign in" or "Create account". started" to guide users through
authentication flows.

Use a dialog to focus users' attention on Overcrowd your task pane with a first-run
authentication forms. experience and authentication forms.

Find small efficiencies in the flow like auto- Add unnecessary steps to the interaction like
focusing on input boxes. requiring users to click into form fields.

Provide a way for users to sign out and Force users to uninstall to switch identities.
reauthenticate.

Authentication flow
1. First-Run Placemat - Place your sign-in button as a clear call-to action inside your
add-in's first-run experience.
2. Identity Provider Choices Dialog - Display a clear list of identity providers including
a username and password form if applicable. Your add-in UI may be blocked while
the authentication dialog is open.

3. Identity Provider Sign-in - The identity provider will have their own UI. Microsoft
Azure Active Directory allows customization of sign-in and access panel pages for
consistent look and feel with your service. Learn More.
4. Progress - Indicate progress while settings and UI load.

7 Note

When using Microsoft's Identity service you'll have the opportunity to use a
branded sign-in button that is customizable to light and dark themes. Learn more.

Single Sign-On authentication flow

7 Note
The single sign-on API is currently supported for Word, Excel, Outlook, and
PowerPoint. For more information about single sign-on support, see IdentityAPI
requirement sets. If you're working with an Outlook add-in, be sure to enable
Modern Authentication for the Microsoft 365 tenancy. For information about how
to do this, see Enable or disable modern authentication for Outlook in Exchange
Online.

Use single sign-on for a smoother end-user experience. The user's identity within Office
(either a Microsoft account or a Microsoft 365 identity) is used to sign in to your add-in.
As a result users only sign in once. This removes friction in the experience making it
easier for your customers to get started.

1. As an add-in is being installed, a user will see a consent window similar to the one
following:

7 Note

The add-in publisher will have control over the logo, strings and permission
scopes included in the consent window. The UI is pre-configured by
Microsoft.

2. The add-in will load after the user consents. It can extract and display any
necessary user customized information.
See also
Learn more about developing SSO Add-ins
Branding patterns
Article • 05/18/2023

These patterns provide brand visibility and context to your add-in users.

Best practices
Do Don't

Use familiar UI components with Don't invent new UI components that contradict
applied branding accents like established Office UI.
typography and color.

Place your add-in branding in a brand Don't repeat your task pane name in an immediately
bar footer at the bottom of your UI. adjacent brand bar at the top of your UI.

Use brand elements sparingly. Fit your Don't insert excessively branded elements into Office UI
solution into Office such that is that distract and confuse customers.
complementary.

Make your solution recognizable and Don't hide your solution with unrecognizable and
connect your screens together with inconsistently applied visual elements.
consistent visual elements.

Build connection with a parent service Don't make customers learn a new brand concept if
or business to ensure that customers there's a useful and understandable relationship that
know and trust your solution. can be leveraged to build trust and value.

Apply the following patterns and components as applicable to allow users to embrace
the full utility of your add-in.

Brand Bar
The brand bar is a space in the footer to include your brand name and logo. It also
serves as a link to your brand's website and an optional access location.
Splash Screen
Use this screen to display your branding while the add-in is loading or transitioning
between UI states.
First-run experience patterns
Article • 05/18/2023

A first-run experience (FRE) is a user's introduction to your add-in. An FRE is presented


when a user opens an add-in for the first time and provides them with insight into the
functions, features, and/or benefits of the add-in. This experience helps shape the user's
impression of an add-in and can strongly influence their likelihood to come back to and
continue using your add-in.

Best practices
Follow these best practices when crafting your first-run experience.

Do Don't

Provide a simple and brief introduction to Don't include information and call-outs that aren't
the main actions in the add-in. relevant to getting started.

Give users the opportunity to complete an Don't expect users to learn everything at once.
action that will positively impact their use Focus on the action that provides the most value.
of the add-in.

Create an engaging experience that users Don't force the users to click through the first-run
will want to complete. experience. Give users an option to bypass the first-
run experience.

Consider whether showing users the first-run experience once or periodically is


important to your scenario. For example, if your add-in is only utilized periodically, users
may become less familiar with your add-in and may benefit from another interaction
with the first-run experience.

Apply the following patterns as applicable to create or enhance the first-run experience
for your add-in.

Carousel
The carousel takes users through a series of features or informational pages before they
start using the add-in.

Figure 1. Allow users to advance or skip the beginning pages of the carousel flow
Figure 2. Minimize the number of carousel screens to only what is needed to effectively
communicate your message

Figure 3. Provide a clear call to action to exit the first-run experience


Value Placemat
The value placement communicates your add-in's value proposition through logo
placement, a clearly stated value proposition, feature highlights or summary, and a call-
to-action.

Figure 4. A value placemat with logo, clear value proposition, feature summary, and call-
to-action

Video Placemat
The video placemat shows users a video before they start using your add-in.

Figure 5. First-run video placemat - The screen contains a still image from the video with a
play button and clear call-to-action button

Figure 6. Video player - Users presented with a video within a dialog window
Navigation patterns
Article • 05/18/2023

The main features of an add-in are accessed through specific command types and
limited screen area. It's important that navigation is intuitive, provides context, and
allows the user to move easily throughout the add-in.

Best practices
Do Don't

Ensure the user has a clearly visible navigation Don't complicate the navigation process by
option. using non-standard UI.

Utilize the following components as applicable Don't make it difficult for the user to
to allow users to navigate through your add-in. understand their current place or context within
the add-in

Command Bar
The CommandBar is a surface within the task pane that houses commands that operate
on the content of the window, panel, or parent region it resides above. Optional features
include a hamburger menu access point, search, and side commands.

Tab Bar
The tab bar shows navigation using buttons with vertically stacked text and icons. Use
the tab bar to provide navigation using tabs with short and descriptive titles.

Back Button
The back button allows users to recover from a drill-down navigational action. This
pattern helps ensure users follow an ordered series of steps.
Design toolkits
Article • 06/23/2023

To help you get started, we've created toolkits for use with either the Sketch
application for Mac or the Adobe XD application for Windows or Mac. The following
downloads include all of our available patterns, along with brief descriptions and layout
recommendations.

Downloads
Fluent UI Design Sketch Toolkit
Fluent UI Design Adobe XD Toolkit
Add-in Sketch Toolkit
Add-in Adobe XD Toolkit
Segoe UI and Fabric MDL2 icon font
Office UI elements for Office Add-ins
Article • 04/04/2023

You can use several types of UI elements to extend the Office UI, including add-in
commands and HTML containers. These UI elements look like a natural extension of
Office and work across platforms. You can insert your custom web-based code into any
of these elements.

The following image shows the types of Office UI elements that you can create.

Add-in commands
Use add-in commands to add entry points to your add-in to the Office app ribbon.
Commands start actions in your add-in either by running JavaScript code, or by
launching an HTML container. You can create two types of add-in commands.
Command type Description

Ribbon buttons, Use to add custom buttons, menus (dropdowns), or tabs to the default
menus, and tabs ribbon in Office. Use Buttons and menus to trigger an action in Office. Use
tabs to group and organize buttons and menus.

Context menus Use to extend the default context menu. Context menus are displayed when
users right-click text in an Office document or a table in Excel.

HTML containers
Use HTML containers to embed HTML-based UI code within Office clients. These web
pages can then reference the Office JavaScript API to interact with content in the
document. You can create three types of HTML containers.

HTML Description
container

Task panes Display custom UI in the right pane of the Office document. Use task panes to
allow users to interact with your add-in side-by-side with the Office document.

Content Display custom UI embedded within Office documents. Use content add-ins to
add-ins allow users to interact with your add-in directly within the Office document. For
example, you might want to show external content such as videos or data
visualizations from other sources.

Dialog Display custom UI in a dialog box that overlays the Office document. Use a dialog
boxes box for interactions that require focus and more real estate, and do not require a
side-by-side interaction with the document.

See also
Add-in commands for Excel, Word, and PowerPoint
Task panes
Content add-ins
Dialog boxes
Add-in commands
Article • 04/11/2023

Add-in commands are UI elements that extend the Office UI and start actions in your
add-in. You can use add-in commands to add a button on the ribbon or an item to a
context menu. When users select an add-in command, they initiate actions such as
running JavaScript code, or showing a page of the add-in in a task pane. Add-in
commands help users find and use your add-in, which can help increase your add-in's
adoption and reuse, and improve customer retention.

7 Note

SharePoint catalogs do not support add-in commands. You can deploy add-in
commands via Integrated Apps or AppSource, or use sideloading to deploy
your add-in command for testing.
Content add-ins do not currently support add-in commands.

Types of add-in commands


There are two types of add-in commands, based on the kind of action that the
command triggers.

Task pane commands: The button or menu item opens the add-in's task pane. You
add this kind of add-in command with markup in the manifest. The "code behind"
the command is provided by Office.
Function commands: The button or menu item runs any arbitrary JavaScript. The
code almost always calls APIs in the Office JavaScript Library, but it doesn't have to.
This type of add-in typically displays no UI other than the button or menu item
itself. Note the following about function commands:
The function that is triggered can call the displayDialogAsync method to show a
dialog, which is a good way to display an error, show progress, or prompt for
input from the user. If the add-in is configured to use a shared runtime, the
function can also call the showAsTaskpane method.
The runtime in which the function command runs is a full browser-based
runtime. It can render HTML and call out to the Internet to send or get data.

How do add-in commands appear?


An add-in command appears on the ribbon as a button or an item in a drop-down
menu. When a user installs an add-in, its commands appear in the UI as a group of
buttons. This can either be on the ribbon's default tab or on a custom tab. If you're
using the simplified ribbon layout, the add-in name is removed from the app bar. Only
the add-in command button on the ribbon remains.

As the ribbon gets more crowded, add-in commands will be displayed in the overflow
menu. The add-in commands for an add-in are usually grouped together.

Excel, PowerPoint, and Word


The default tab depends on the application and context. For Excel, PowerPoint, and
Word, the default tab is Home.
Outlook
For Outlook, the default add-in command location is based on the current Outlook
mode.

Scenario Tab location

Reading a message Home tab

Composing a message Message tab

Creating or viewing an appointment or Meeting, Meeting Occurrence, Meeting Series,


meeting as the organizer or Appointment tabs.1

Viewing a meeting as an attendee Meeting, Meeting Occurrence, or Meeting Series


tabs.1

Using a module extension The extension's custom tab.

1
If a user selects an item in the calendar but doesn't open the pop-out, the add-in's
ribbon group won't be visible on the ribbon.

Modern Outlook on the web


In Outlook on the web, the add-in name is displayed in an overflow menu. If the add-in
has multiple add-in commands, you can expand the add-in menu to see the group of
buttons labeled with the add-in name.
Drop-down menu
A drop-down menu add-in command defines a static list of items. The menu can be any
mix of items that execute a function or that open a task pane. Submenus are not
supported.
Command capabilities
The following command capabilities are currently supported.

Extension points
Ribbon tabs - Extend built-in tabs or create a new custom tab. An add-in can have
just one custom tab.
Context menus - Extend selected context menus.

Control types
Simple buttons - trigger specific actions.
Menus - simple menu dropdown with buttons that trigger actions.

Default Enabled or Disabled Status


You can specify whether the command is enabled or disabled when your add-in
launches, and programmatically change the setting.

7 Note

This feature is not supported in all Office applications or scenarios. For more
information, see Enable and Disable Add-in Commands.

Position on the ribbon (preview)


You can specify where a custom tab appears on the Office application's ribbon, such as
"just to the right of the Home tab".
7 Note

This feature is not supported in all Office applications or scenarios. For more
information, see Position a custom tab on the ribbon.

Integration of built-in Office buttons


You can insert the built-in Office ribbon buttons into your custom command groups and
your custom ribbon tab.

7 Note

This feature is not supported in all Office applications or scenarios. For more
information, see Integrate built-in Office buttons into custom tabs.

Contextual tabs
You can specify that a tab is only visible on the ribbon in certain contexts, such as when
a chart is selected in Excel.

7 Note

This feature is not supported in all Office applications or scenarios. For more
information, see Create custom contextual tabs in Office Add-ins.

Supported platforms
Add-in commands are currently supported on the following platforms, except for
limitations specified in the subsections of Command capabilities earlier.

Office on Windows (build 16.0.6769+, connected to a Microsoft 365 subscription)


Office on Mac (build 15.33+, connected to a Microsoft 365 subscription)
Office on the web
Office 2019 or later on Windows or Mac

7 Note

For information about support in Outlook, see Outlook support notes.


Debug
To debug an add-in command, you must run it in Office on the web. For details, see
Debug add-ins in Office on the web.

Best practices
Apply the following best practices when you develop add-in commands.

Use commands to represent a specific action with a clear and specific outcome for
users. Do not combine multiple actions in a single button.

Provide granular actions that make common tasks within your add-in more
efficient to perform. Minimize the number of steps an action takes to complete.

For the placement of your commands in the Office app ribbon:


Place commands on an existing tab (Insert, Review, and so on) if the
functionality provided fits there. For example, if your add-in enables users to
insert media, add a group to the Insert tab. Note that not all tabs are available
across all Office versions. For more information, see Office Add-ins manifest.
Place commands on the Home tab if the functionality doesn't fit on another tab,
and you have fewer than six top-level commands. You can also add commands
to the Home tab if your add-in needs to work across Office versions (such as
Office on the web or desktop) and a tab is not available in all versions (for
example, the Design tab doesn't exist in Office on the web).
Place commands on a custom tab if you have more than six top-level
commands.
Name your group to match the name of your add-in. If you have multiple
groups, name each group based on the functionality that the commands in that
group provide.
Do not add superfluous buttons to increase the real estate of your add-in.
Do not position a custom tab to the left of the Home tab, or give it focus by
default when the document opens, unless your add-in is the primary way users
will interact with the document. Giving excessive prominence to your add-in
inconveniences and annoys users and administrators.
If your add-in is the primary way users interact with the document and you have
a custom ribbon tab, consider integrating into the tab the buttons for the Office
functions that users will frequently need.
If the functionality that is provided with a custom tab should only be available in
certain contexts, use custom contextual tabs. If you use custom contextual tabs,
make sure to implement a fallback experience for when your add-in runs on
platforms that don't support custom contextual tabs.

7 Note

Add-ins that take up too much space might not pass AppSource validation.

For all icons, follow the icon design guidelines.

Provide a version of your add-in that works on Office applications or platforms


(such as iPad) that don't support commands. A single add-in manifest can be used
for these versions.

Next steps
The best way to get started using add-in commands is to take a look at the Office Add-
in commands samples on GitHub.

For more information about specifying add-in commands in an XML manifest, see
Create add-in commands with the XML manifest and the VersionOverrides reference
content.

For more information about specifying add-in commands in the unified manifest for
Microsoft 365, see Create add-in commands with the unified manifest for Microsoft 365.
Create add-in commands with the XML
manifest
Article • 04/11/2023

Add-in commands provide an easy way to customize the default Office user interface
(UI) with specified UI elements that perform actions. For an introduction to add-in
commands, see Add-in commands.

This article describes how to edit your XML manifest to define add-in commands and
how to create the code for function commands.

 Tip

For instructions on how to create add-in commands with the unified manifest for
Microsoft 365, see Create add-in commands with the unified manifest for
Microsoft 365.

The following diagram shows the hierarchy of elements used to define add-in
commands. These elements are described in more detail in this article.
Sample commands
All the task pane add-ins created by yo office have add-in commands. They contain an
add-in command (button) to show the task pane. Generate these projects by following
one of the quick starts, such as Build an Excel task pane add-in. Ensure that you have
read Add-in commands to understand command capabilities.
Important parts of an add-in command
The following steps explain how to add add-in commands to an existing add-in.

Step 1: Add VersionOverrides element


The <VersionOverrides> element is the root element that contains the definition of
your add-in command. Details on the valid attributes and implications are found in
Version overrides in the manifest.

The following example shows the <VersionOverrides> element and its child elements.

XML

<OfficeApp>
...
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides"
xsi:type="VersionOverridesV1_0">
<Requirements>
<!-- add information about requirement sets -->
</Requirements>
<Hosts>
<Host xsi:type="Workbook">
<!-- add information about form factors -->
</Host>
</Hosts>
<Resources>
<!-- add information about resources -->
</Resources>
</VersionOverrides>
...
</OfficeApp>

Step 2: Add Hosts, Host, and DesktopFormFactor


elements
The <Hosts> element contains one or more <Host> elements. A <Host> element
specifies a particular Office application. The <Host> element contains child elements
that specify the add-in commands to display after your add-in is installed in that Office
application. To show the same add-in commands in two or more different Office
applications, you must duplicate the child elements in each <Host>.

The <DesktopFormFactor> element specifies the settings for an add-in that runs in
Office on the web, Windows, and Mac.
The following example shows the <Hosts>, <Host>, and <DesktopFormFactor>
elements.

XML

<OfficeApp>
...
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides"
xsi:type="VersionOverridesV1_0">
...
<Hosts>
<Host xsi:type="Workbook">
<DesktopFormFactor>

<!-- information about FunctionFile and ExtensionPoint -->

</DesktopFormFactor>
</Host>
</Hosts>
...
</VersionOverrides>
...
</OfficeApp>

Step 3: Add the FunctionFile element


The <FunctionFile> element specifies a file that contains JavaScript code to run when
an add-in command uses the ExecuteFunction action. The <FunctionFile> element's
resid attribute is set to a HTML file that includes all the JavaScript files your add-in
commands require. You can't link directly to a JavaScript file. You can only link to an
HTML file. The file name is specified as a <Url> element in the <Resources> element.

7 Note

The yo office projects use webpack to avoid manually adding the JavaScript to
the HTML.

The following is an example of the <FunctionFile> element.

XML

<DesktopFormFactor>
<FunctionFile resid="Commands.Url" />
<ExtensionPoint xsi:type="PrimaryCommandSurface">
<!-- information about this extension point -->
</ExtensionPoint>
<!-- You can define more than one ExtensionPoint element as needed -->
</DesktopFormFactor>

) Important

Office.js must be initialized before the add-in command logic runs. For more
information, see Initialize your Office Add-in.

Outlook notifications
When an add-in needs to provide status updates, such as progress indicators or error
messages, it must do so through the notification APIs. The processing for the
notifications must also be defined in a separate HTML file that is specified in the
FunctionFile node of the manifest.

Step 4: Add ExtensionPoint elements


The <ExtensionPoint> element defines where add-in commands should appear in the
Office UI.

The following examples show how to use the <ExtensionPoint> element with
PrimaryCommandSurface and ContextMenu attribute values, and the child elements
that should be used with each.

) Important

For elements that contain an ID attribute, make sure you provide a unique ID. We
recommend that you use your company's name along with your ID. For example,
use the following format: <CustomTab id="mycompanyname.mygroupname"> .

XML

<ExtensionPoint xsi:type="PrimaryCommandSurface">
<CustomTab id="Contoso Tab">
<!-- If you want to use a default tab that comes with Office, remove the
above CustomTab element, and then uncomment the following OfficeTab element
-->
<!-- <OfficeTab id="TabData"> -->
<Label resid="residLabel4" />
<Group id="Group1Id12">
<Label resid="residLabel4" />
<Icon>
<bt:Image size="16" resid="icon1_32x32" />
<bt:Image size="32" resid="icon1_32x32" />
<bt:Image size="80" resid="icon1_32x32" />
</Icon>
<Tooltip resid="residToolTip" />
<Control xsi:type="Button" id="Button1Id1">

<!-- information about the control -->


</Control>
<!-- other controls, as needed -->
</Group>
</CustomTab>
</ExtensionPoint>
<ExtensionPoint xsi:type="ContextMenu">
<OfficeMenu id="ContextMenuCell">
<Control xsi:type="Menu" id="ContextMenu2">
<!-- information about the control -->
</Control>
<!-- other controls, as needed -->
</OfficeMenu>
</ExtensionPoint>

Step 5: Add Control elements


The <Control> element defines the usable surface of command (button, menu, etc) and
the action associated with it.

Button controls
A button control performs a single action when the user selects it. It can either execute a
JavaScript function or show a task pane. The following example shows how to define
two buttons. The first button runs a JavaScript function without showing a UI, and the
second button shows a task pane. In the <Control> element:

The type attribute is required, and must be set to Button.


The id attribute of the <Control> element is a string with a maximum of 125
characters.

XML

<!-- Define a control that calls a JavaScript function. -->


<Control xsi:type="Button" id="Button1Id1">
<Label resid="residLabel" />
<Tooltip resid="residToolTip" />
<Supertip>
<Title resid="residLabel" />
<Description resid="residToolTip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="icon1_32x32" />
<bt:Image size="32" resid="icon1_32x32" />
<bt:Image size="80" resid="icon1_32x32" />
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>highlightSelection</FunctionName>
</Action>
</Control>

<!-- Define a control that shows a task pane. -->


<Control xsi:type="Button" id="Button2Id1">
<Label resid="residLabel2" />
<Tooltip resid="residToolTip" />
<Supertip>
<Title resid="residLabel" />
<Description resid="residToolTip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="icon2_32x32" />
<bt:Image size="32" resid="icon2_32x32" />
<bt:Image size="80" resid="icon2_32x32" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="residUnitConverterUrl" />
</Action>
</Control>

The following code shows an example function used by <FunctionName>. Note the call
to event.completed. This signals that you've successfully handled the event. When a
function is called multiple times, such as multiple clicks on the same add-in command,
all events are automatically queued. The first event runs automatically, while the other
events remain on the queue. When your function calls event.completed , the next
queued call to that function runs. You must implement event.completed , otherwise your
function won't run.

JavaScript

// Initialize the Office Add-in.


Office.onReady(() => {
// If needed, Office.js is ready to be called
});

// The command function.


async function highlightSelection(event) {

// Implement your custom code here. The following code is a simple Excel
example.
try {
await Excel.run(async (context) => {
const range = context.workbook.getSelectedRange();
range.format.fill.color = "yellow";
await context.sync();
});
} catch (error) {
// Note: In a production add-in, notify the user through your add-
in's UI.
console.error(error);
}

// Calling event.completed is required. event.completed lets the


platform know that processing has completed.
event.completed();
}

// You must register the function with the following line.


Office.actions.associate("highlightSelection", highlightSelection);

Menu controls
A menu control can be used with either PrimaryCommandSurface or ContextMenu,
and defines:

A root-level menu item.


A list of submenu items.

When used with PrimaryCommandSurface, the root menu item displays as a button on
the ribbon. When the button is selected, the submenu displays as a drop-down list.
When used with ContextMenu, a menu item with a submenu is inserted on the context
menu. In both cases, individual submenu items can either execute a JavaScript function
or show a task pane. Only one level of submenus is supported at this time.

The following example shows how to define a menu item with two submenu items. The
first submenu item shows a task pane, and the second submenu item runs a JavaScript
function. In the <Control> element:

The xsi:type attribute is required, and must be set to Menu.


The id attribute is a string with a maximum of 125 characters.

XML

<Control xsi:type="Menu" id="TestMenu2">


<Label resid="residLabel3" />
<Tooltip resid="residToolTip" />
<Supertip>
<Title resid="residLabel" />
<Description resid="residToolTip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="icon1_32x32" />
<bt:Image size="32" resid="icon1_32x32" />
<bt:Image size="80" resid="icon1_32x32" />
</Icon>
<Items>
<Item id="showGallery2">
<Label resid="residLabel3"/>
<Supertip>
<Title resid="residLabel" />
<Description resid="residToolTip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="icon1_32x32" />
<bt:Image size="32" resid="icon1_32x32" />
<bt:Image size="80" resid="icon1_32x32" />
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>MyTaskPaneID1</TaskpaneId>
<SourceLocation resid="residUnitConverterUrl" />
</Action>
</Item>
<Item id="showGallery3">
<Label resid="residLabel5"/>
<Supertip>
<Title resid="residLabel" />
<Description resid="residToolTip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="icon4_32x32" />
<bt:Image size="32" resid="icon4_32x32" />
<bt:Image size="80" resid="icon4_32x32" />
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>getButton</FunctionName>
</Action>
</Item>
</Items>
</Control>

Step 6: Add the Resources element


The <Resources> element contains resources used by the different child elements of
the <VersionOverrides> element. Resources include icons, strings, and URLs. An
element in the manifest can use a resource by referencing the id of the resource. Using
the id helps organize the manifest, especially when there are different versions of the
resource for different locales. An id has a maximum of 32 characters.

The following shows an example of how to use the <Resources> element. Each resource
can have one or more <Override> child elements to define a different resource for a
specific locale.

XML

<Resources>
<bt:Images>
<bt:Image id="icon1_16x16"
DefaultValue="https://www.contoso.com/Images/icon_default.png">
<bt:Override Locale="ja-jp" Value="https://www.contoso.com/Images/ja-
jp16-icon_default.png" />
</bt:Image>
<bt:Image id="icon1_32x32"
DefaultValue="https://www.contoso.com/Images/icon_default.png">
<bt:Override Locale="ja-jp" Value="https://www.contoso.com/Images/ja-
jp32-icon_default.png" />
</bt:Image>
<bt:Image id="icon1_80x80"
DefaultValue="https://www.contoso.com/Images/icon_default.png">
<bt:Override Locale="ja-jp" Value="https://www.contoso.com/Images/ja-
jp80-icon_default.png" />
</bt:Image>
</bt:Images>
<bt:Urls>
<bt:Url id="residDesktopFuncUrl"
DefaultValue="https://www.contoso.com/Pages/Home.aspx">
<bt:Override Locale="ja-jp"
Value="https://www.contoso.com/Pages/Home.aspx" />
</bt:Url>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="residLabel" DefaultValue="GetData">
<bt:Override Locale="ja-jp" Value="JA-JP-GetData" />
</bt:String>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="residToolTip" DefaultValue="Get data for your document.">
<bt:Override Locale="ja-jp" Value="JA-JP - Get data for your
document." />
</bt:String>
</bt:LongStrings>
</Resources>

7 Note

You must use Secure Sockets Layer (SSL) for all URLs in the <Image> and <Url>
elements.

Outlook support notes


Add-in commands are available in the following Outlook versions.

Outlook 2013 or later on Windows


Outlook 2016 or later on Mac
Outlook on iOS
Outlook on Android
Outlook on the web for Exchange 2016 or later
Outlook on the web for Microsoft 365 and Outlook.com.

Support for add-in commands in Exchange 2016 requires Cumulative Update 5 .

If your add-in uses an XML manifest, then add-in commands are only available for add-
ins that do not use ItemHasAttachment, ItemHasKnownEntity, or
ItemHasRegularExpressionMatch rules to limit the types of items they activate on.
However, contextual add-ins can present different commands depending on whether
the currently selected item is a message or appointment, and can choose to appear in
read or compose scenarios. Using add-in commands if possible is a best practice.

See also
Add-in commands
Sample: Create an Excel add-in with command buttons
Sample: Create an Word add-in with command buttons
Sample: Create an PowerPoint add-in with command buttons
Create add-in commands with the
unified manifest for Microsoft 365
Article • 07/27/2023

Add-in commands provide an easy way to customize the default Office user interface
(UI) with specified UI elements that perform actions. For an introduction to add-in
commands, see Add-in commands.

This article describes how to configure the Unified manifest for Microsoft 365 (preview)
to define add-in commands and how to create the code for function commands.

 Tip

Instructions for creating add-in commands with the XML manifest are in Create
add-in commands with the XML manifest.

7 Note

The unified manifest is currently supported only for Outlook Add-ins on Windows.
It's in preview and not yet supported for production add-ins.

Starting point and major steps


Both of the tools that create add-in projects with a unified manifest — the Office
Yeoman generator and Teams Toolkit — create projects with one or more add-in
commands. The only time you won't already have an add-in command is if you are
updating an add-in which previously didn't have one.

Two decisions
Decide which of two types of add-in commands you need: Task pane or function.
Decide which kind of UI element you need: button or menu item. Then carry out
the steps in the sections and subsections below that correspond to your decisions.

Add a task pane command


The following subsections explain how to include a task pane command in an add-in.
Configure the runtime for the task pane command
1. Open the unified manifest and find the "extensions.runtimes" array.

2. Ensure that there is a runtime object that has an "actions.type" property with the
value "openPage". This type of runtime opens a task pane.

3. Ensure that the "requirements.capabilities" array contains an object that specifies a


Requirement Set that supports add-in commands. For Outlook the minimum
requirement set for add-in commands is Mailbox 1.3.

7 Note

When support for the unified manifest is extended to other Office host
applications, the minimum requirement set for add-in commands in those
other hosts will be AddinCommands 1.1.

4. Ensure that the "id" of the runtime object has a descriptive name such as
"TaskPaneRuntime".

5. Ensure that the "code.page" property of the runtime object is set to the URL of the
page that should open in the task pane, such as
"https://localhost:3000/taskpane.html".

6. Ensure that the "actions.view" of the runtime object has a name that describes the
content of the page that you set in the preceding step, such as "homepage" or
"dashboard".

7. Ensure that the "actions.id" of the runtime object has a descriptive name such as
"ShowTaskPane" that indicates what happens when the user selects the add-in
command button or menu item.

8. Set the other properties and subproperties of the runtime object as shown in the
following completed example of a runtime object. The "type" and "lifetime"
properties are required and in Outlook Add-ins (which is the only host that
currently supports the unified manifest) they always have the values shown in this
example.

JSON

"runtimes": [
{
"requirements": {
"capabilities": [
{
"name": "Mailbox",
"minVersion": "1.3"
}
]
},
"id": "TaskPaneRuntime",
"type": "general",
"code": {
"page": "https://localhost:3000/taskpane.html"
},
"lifetime": "short",
"actions": [
{
"id": "ShowTaskPane",
"type": "openPage",
"view": "homepage"
}
]
}
]

Configure the UI for the task pane command


1. Ensure that the extension object for which you configured a runtime has a
"ribbons" array property as a peer to the "runtimes" array. There is typically only
one extension object in the "extensions" array.

2. Ensure that the array has an object with array properties named "contexts" and
"tabs", as shown in the following example.

JSON

"ribbons": [
{
"contexts": [
// child objects omitted
],
"tabs": [
// child objects omitted
]
}
]

3. Ensure that the "contexts" array has strings that specify the windows or panes in
which the UI for the task pane command should appear. For example, "mailRead"
means that it will appear in the reading pane or message window when an email
message is open, but "mailCompose" means it will appear when a new message or
a reply is being composed. The following are the allowable values:

"mailRead"
"mailCompose"
"meetingDetailsOrganizer"
"meetingDetailsAttendee"

The following is an example.

JSON

"contexts": [
"mailRead"
],

4. Ensure that the "tabs" array has an object with a "builtInTabId" string property that
is set to the ID of ribbon tab in which you want your task pane command to
appear. Also, ensure that there is a "groups" array with at least one object in it. The
following is an example.

JSON

"tabs": [
{
"builtInTabID": "TabDefault",
"groups": [
{
// properties omitted
}
]
}
]

7 Note

The only allowed value for the "builtInTabID" property is "TabDefault", which
in Outlook is either the Home, Message, or Meeting tab. When support for
the unified manifest is added to other Office host applications, there will be
other possible values.

5. Ensure that the "groups" array has an object to define the custom control group
that will hold your add-in command UI controls. The following is an example. Note
the following about this JSON:
The "id" must be unique across all groups in all ribbon objects in the
manifest. Maximum length is 64 characters.
The "label" appears on the group on the ribbon. Maximum length is 64
characters.
One of the "icons" appears on the group only if the Office application
window, and hence the ribbon, has been sized by the user too small for any
of the controls in the group to appear. Office decides when to use one of
these icons and which one to use based on the size of the window and the
resolution of the device. You cannot control this. You must provide image
files for 16, 32, and 80 pixels, while five other sizes are also supported (20, 24,
40, 48, and 64 pixels). You must use Secure Sockets Layer (SSL) for all URLs.

7 Note

The name of the "icons.file" property may change during the preview of the
unified manifest for Office Add-ins. If you get intellisense or manifest
validation errors, try replacing "file" with "url".

JSON

"groups": [
{
"id": "msgReadGroup",
"label": "Contoso Add-in",
"icons": [
{
"size": 16,
"file": "https://localhost:3000/assets/icon-16.png"
},
{
"size": 32,
"file": "https://localhost:3000/assets/icon-32.png"
},
{
"size": 80,
"file": "https://localhost:3000/assets/icon-80.png"
}
],
"controls": [
{
// properties omitted
}
]
}
]
6. Ensure that there is a control object in the "controls" array for each button or
custom menu you want. The following is an example. Note the following about this
JSON:

The "id", "label", and "icons" properties have the same purpose and the same
restrictions as the corresponding properties of a group object, except that
they apply to a specific button or menu within the group.
The "type" property is set to "button" which means that the control will be a
ribbon button. You can also configure a task pane command to be run from a
menu item. See Menu and menu items.
The "supertip.title" (maximum length: 64 characters) and
"supertip.description" (maximum length: 128 characters) appear when the
cursor is hovering over the button or menu.
The "actionId" must be an exact match for the "runtimes.actions.id" that you
set in Configure the runtime for the task pane command.

JSON

{
"id": "msgReadOpenPaneButton",
"type": "button",
"label": "Show Task Pane",
"icons": [
{
"size": 16,
"file": "https://localhost:3000/assets/icon-16.png"
},
{
"size": 32,
"file": "https://localhost:3000/assets/icon-32.png"
},
{
"size": 80,
"file": "https://localhost:3000/assets/icon-80.png"
}
],
"supertip": {
"title": "Show Contoso Task Pane",
"description": "Opens the Contoso task pane."
},
"actionId": "ShowTaskPane"
}

You've now completed adding a task pane command to your add-in. Sideload and test
it.
Add a function command
The following subsections explain how to include a function command in an add-in.

Create the code for the function command


1. Ensure that your source code includes a JavaScript or Typescript file with the
function that you want to run with your function command. The following is an
example. Since this article is about creating add-in commands, and not about
teaching the Office JavaScript Library, we provide it with minimal comments, but
do note the following:

For purposes of this article, the file is named commands.js.


The function will cause a small notification to appear on an open email
message with the text "Action performed".
Like all code that call APIs in the Office JavaScript Library, it must begin by
initializing the library. It does this by calling Office.onReady .
The last thing the code calls is Office.actions.associate to tell Office which
function in the file should be run when the UI for your function command is
invoked. The function maps the function name to an action ID that you
configure in the manifest in a later step. If you define multiple function
commands in the same file, your code must call associate for each one.
The function must take a parameter of type Office.AddinCommands.Event.
The last line of the function must call event.completed.

JavaScript

Office.onReady(function() {
// Add any initialization code here.
});

function setNotification(event) {
const message = {
type:
Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage,
message: "Performed action.",
icon: "Icon.80x80",
persistent: true,
};

// Show a notification message.


Office.context.mailbox.item.notificationMessages.replaceAsync("ActionPe
rformanceNotification", message);

// Be sure to indicate when the add-in command function is complete.


event.completed();
}

// Map the function to the action ID in the manifest.


Office.actions.associate("SetNotification", setNotification);

2. Ensure that your source code includes an HTML file that is configured to load the
function file you created. The following is an example. Note the following about
this JSON:

For purposes of this article, the file is named commands.html.

The <body> element is empty because the file has no UI. Its only purpose is to
load JavaScript files.

The Office JavaScript Library and the commands.js file that you created in the
preceding step is explicitly loaded.

7 Note

It's common in Office Add-in development to use tools like webpack


and its plugins to automatically inject <script> tags into HTML files at
build time. If you use such a tool, you shouldn't include any <script>
tags in your source file that are going to be inserted by the tool.

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

<!-- Office JavaScript Library -->


<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js">
</script>
<!-- Function command file -->
<script src="commands.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>

Configure the runtime for the function command


1. Open the unified manifest and find the "extensions.runtimes" array.

2. Ensure that there is a runtime object that has a "actions.type" property with the
value "executeFunction".

3. Ensure that the "requirements.capabilities" array contains objects that specify any
Requirement Sets that are needed to support the APIs add-in commands. For
Outlook, the minimum requirement set for add-in commands is Mailbox 1.3. But if
your function command calls that API that is part of later Mailbox requirement set,
such as Mailbox 1.5, then you need to specify the later version (e.g., "1.5") as the
"minVersion" value.

7 Note

When support for the unified manifest is extended to other Office host
applications, the minimum requirement set for add-in commands in those
other hosts will be AddinCommands 1.1.

4. Ensure that the "id" of the runtime object has a descriptive name such as
"CommandsRuntime".

5. Ensure that the "code.page" property of the runtime object is set to the URL of the
UI-less HTML page that loads your function file, such as
"https://localhost:3000/commands.html".

6. Ensure that the "actions.id" of the runtime object has a descriptive name such as
"SetNotification" that indicates what happens when the user selects the add-in
command button or menu item.

) Important

The value of "actions.id" must exactly match the first parameter of the call to
Office.actions.associate in the function file.

7. Set the other properties and subproperties of the runtime object as shown in the
following completed example of a runtime object. The "type" and "lifetime"
properties are required and they always have the values shown in Outlook add-ins,
which is the only host that currently supports the unified manifest.

JSON
"runtimes": [
{
"id": "CommandsRuntime",
"type": "general",
"code": {
"page": "https://localhost:3000/commands.html"
},
"lifetime": "short",
"actions": [
{
"id": "SetNotification",
"type": "executeFunction",
}
]
}
]

Configure the UI for the function command


1. Ensure that the extension object for which you configured a runtime has a
"ribbons" array property as a peer to the "runtimes" array. There is typically only
one extension object in the "extensions" array.

2. Ensure that the array has an object with array properties named "contexts" and
"tabs", as shown in the following example.

JSON

"ribbons": [
{
"contexts": [
// child objects omitted
],
"tabs": [
// child objects omitted
]
}
]

3. Ensure that the "contexts" array has strings that specify the windows or panes in
which the UI for the function command should appear. For example, "mailRead"
means that it will appear in the reading pane or message window when an email
message is open, but "mailCompose" means it will appear when a new message or
a reply is being composed. The following are the allowable values:

mailRead
mailCompose
meetingDetailsOrganizer
meetingDetailsAttendee

The following is an example.

JSON

"contexts": [
"mailRead"
],

4. Ensure that the "tabs" array has an object with a "builtInTabId" string property that
is set to the ID of ribbon tab in which you want your function command to appear
and a "groups" array with at least one object in it. The following is an example.

JSON

"tabs": [
{
"builtInTabID": "TabDefault",
"groups": [
{
// properties omitted
}
]
}
]

7 Note

The only allowed value for the "builtInTabID" property is "TabDefault", which
in Outlook is either the Home, Message, or Meeting tab. When support for
the unified manifest is added to other Office host applications, there will be
other possible values.

5. Ensure that the "groups" array has an object to define the custom control group
that will hold your add-in command UI controls. The following is an example. Note
the following about this JSON:

The "id" must be unique across all groups in all ribbon objects in the
manifest. Maximum length is 64 characters.
The "label" appears on the group on the ribbon. Maximum length is 64
characters.
One of the "icons" appears on the group only if the Office application
window, and hence the ribbon, has been sized by the user too small for any
of the controls in the group to appear. Office decides when to use one of
these icons and which one to use based on the size of the window and the
resolution of the device. You cannot control this. You must provide image
files for 16, 32, and 80 pixels, while five other sizes are also supported (20, 24,
40, 48, and 64 pixels). You must use Secure Sockets Layer (SSL) for all URLs.

7 Note

The name of the "icons.file" property may change during the preview of the
unified manifest for Office Add-ins. If you get intellisense or manifest
validation errors, try replacing "file" with "url".

JSON

"groups": [
{
"id": "msgReadGroup",
"label": "Contoso Add-in",
"icons": [
{
"size": 16,
"file": "https://localhost:3000/assets/icon-16.png"
},
{
"size": 32,
"file": "https://localhost:3000/assets/icon-32.png"
},
{
"size": 80,
"file": "https://localhost:3000/assets/icon-80.png"
}
],
"controls": [
{
// properties omitted
}
]
}
]

6. Ensure that there is a control object in the "controls" array for each button or
custom menu you want. The following is an example. Note the following about this
JSON:
The "id", "label", and "icons" properties have the same purpose and the same
restrictions as the corresponding properties of a group object, except that
they apply to a specific button or menu within the group.
The "type" property is set to "button" which means that the control will be a
ribbon button. You can also configure a function command to be run from a
menu item. See Menu and menu items.
The "supertip.title" (maximum length: 64 characters) and
"supertip.description" (maximum length: 128 characters) appear when the
cursor is hovering over the button or menu.
The "actionId" must be an exact match for the "runtime.actions.id" that you
set in Configure the runtime for the function command.

JSON

{
"id": "msgReadSetNotificationButton",
"type": "button",
"label": "Set Notification",
"icons": [
{
"size": 16,
"file": "https://localhost:3000/assets/icon-16.png"
},
{
"size": 32,
"file": "https://localhost:3000/assets/icon-32.png"
},
{
"size": 80,
"file": "https://localhost:3000/assets/icon-80.png"
}
],
"supertip": {
"title": "Set Notification",
"description": "Displays a notification message on the current
message."
},
"actionId": "SetNotification"
}

You've now completed adding a function command to your add-in. Sideload and test it.

Menu and menu items


In addition to custom buttons, you can also add custom drop down menus to the Office
ribbon. This section explains how by using an example with two menu items. One
invokes a task pane command. The other invokes a function command.
Configure the runtimes and code
Carry out the steps of the following sections:

Configure the runtime for the task pane command


Create the code for the function command
Configure the runtime for the function command

Configure the UI for the menu


1. Ensure that the extension object for which you configured a runtime has a
"ribbons" array property as a peer to the "runtimes" array. There is typically only
one extension object in the "extensions" array.

2. Ensure that the array has an object with array properties named "contexts" and
"tabs", as shown in the following example.

JSON

"ribbons": [
{
"contexts": [
// child objects omitted
],
"tabs": [
// child objects omitted
]
}
]

3. Ensure that the "contexts" array has strings that specify the windows or panes in
which the menu should appear on the ribbon. For example, "mailRead" means that
it will appear in the reading pane or message window when an email message is
open, but "mailCompose" means it will appear when a new message or a reply is
being composed. The following are the allowable values:

mailRead
mailCompose
meetingDetailsOrganizer
meetingDetailsAttendee

The following is an example.

JSON
"contexts": [
"mailRead"
],

4. Ensure that the "tabs" array has an object with a "builtInTabId" string property that
is set to the ID of ribbon tab in which you want your task pane command to
appear and a "groups" array with at least one object in it. The following is an
example.

JSON

"tabs": [
{
"builtInTabID": "TabDefault",
"groups": [
{
// properties omitted
}
]
}
]

7 Note

The only allowed value for the "builtInTabID" property is "TabDefault", which
in Outlook is either the Home, Message, or Meeting tab. When support for
the unified manifest is added to other Office host applications, there will be
other possible values.

5. Ensure that the "groups" array has an object to define the custom control group
that will hold your drop down menu control. The following is an example. Note the
following about this JSON:

The "id" must be unique across all groups in all ribbon objects in the
manifest. Maximum length is 64 characters.
The "label" appears on the group on the ribbon. Maximum length is 64
characters.
One of the "icons" appears on the group only if the Office application
window, and hence the ribbon, has been sized by the user too small for any
of the controls in the group to appear. Office decides when to use one of
these icons and which one to use based on the size of the window and the
resolution of the device. You cannot control this. You must provide image
files for 16, 32, and 80 pixels, while five other sizes are also supported (20, 24,
40, 48, and 64 pixels). You must use Secure Sockets Layer (SSL) for all URLs.

7 Note

The name of the "icons.file" property may change during the preview of the
unified manifest for Office Add-ins. If you get intellisense or manifest
validation errors, try replacing "file" with "url".

JSON

"groups": [
{
"id": "msgReadGroup",
"label": "Contoso Add-in",
"icons": [
{
"size": 16,
"file": "https://localhost:3000/assets/icon-16.png"
},
{
"size": 32,
"file": "https://localhost:3000/assets/icon-32.png"
},
{
"size": 80,
"file": "https://localhost:3000/assets/icon-80.png"
}
],
"controls": [
{
// properties omitted
}
]
}
]

6. Ensure that there is a control object in the "controls" array. The following is an
example. Note the following about this JSON:

The "id", "label", and "icons" properties have the same purpose and the same
restrictions as the corresponding properties of a group object, except that
they apply to the drop down menu within the group.
The "type" property is set to "menu" which means that the control will be a
drop down menu.
The "supertip.title" (maximum length: 64 characters) and
"supertip.description" (maximum length: 128 characters) appear when the
cursor is hovering over the menu.
The "items" property contains the JSON for the two menu options. The values
are added in later steps.

JSON

{
"id": "msgReadMenu",
"type": "menu",
"label": "Contoso Menu",
"icons": [
{
"size": 16,
"file": "https://localhost:3000/assets/icon-16.png"
},
{
"size": 32,
"file": "https://localhost:3000/assets/icon-32.png"
},
{
"size": 80,
"file": "https://localhost:3000/assets/icon-80.png"
}
],
"supertip": {
"title": "Show Contoso Actions",
"description": "Opens the Contoso menu."
},
"items": [
{
"id": "",
"type": "",
"label": "",
"supertip": {},
"actionId": ""
},
{
"id": "",
"type": "",
"label": "",
"supertip": {},
"actionId": ""
}
]
}

7. The first item shows a task pane. The following is an example. Note the following
about this code:

The "id", "label", and "supertip" properties have the same purpose and the
same restrictions as the corresponding properties of the parent menu object,
except that they apply to just this menu option.
The "icons" property is optional for menu items and there isn't one in this
example. If you include one, it has the same purposes and restrictions as the
"icons" property of the parent menu, except that the icon appears on the
menu item beside the label.
The "type" property is set to "menuItem".
The "actionId" must be an exact match for the "runtimes.actions.id" that you
set in Configure the runtime for the task pane command.

JSON

{
"id": "msgReadOpenPaneMenuItem",
"type": "menuItem",
"label": "Show Task Pane",
"supertip": {
"title": "Show Contoso Task Pane",
"description": "Opens the Contoso task pane."
},
"actionId": "ShowTaskPane"
},

8. The second item runs a function command. The following is an example. Note the
following about this code:

The "actionId" must be an exact match for the "runtimes.actions.id" that you
set in Configure the runtime for the function command.

JSON

{
"id": "msgReadSetNotificationMenuItem",
"type": "menuItem",
"label": "Set Notification",
"supertip": {
"title": "Set Notification",
"description": "Displays a notification message on the current
message."
},
"actionId": "SetNotification"
}

You've now completed adding a menu to your add-in. Sideload and test it.

See also
Add-in commands
Unified manifest for Microsoft 365 (preview).
Create custom contextual tabs in Office
Add-ins
Article • 03/14/2023

A contextual tab is a hidden tab control in the Office ribbon that is displayed in the tab
row when a specified event occurs in the Office document. For example, the Table
Design tab that appears on the Excel ribbon when a table is selected. You include
custom contextual tabs in your Office Add-in and specify when they are visible or
hidden, by creating event handlers that change the visibility. (However, custom
contextual tabs do not respond to focus changes.)

7 Note

This article assumes that you are familiar with the following documentation. Please
review it if you haven't worked with Add-in Commands (custom menu items and
ribbon buttons) recently.

Basic concepts for Add-in Commands

) Important

Custom contextual tabs are currently only supported on Excel and only on these
platforms and builds.

Excel on Windows: Version 2102 (Build 13801.20294) or later.


Excel on Mac: Version 16.53.806.0 or later.
Excel on the web

7 Note

Custom contextual tabs work only on platforms that support the following
requirement sets. For more about requirement sets and how to work with them, see
Specify Office applications and API requirements.

RibbonApi 1.2
SharedRuntime 1.1
You can use the runtime checks in your code to test whether the user's host and
platform combination supports these requirement sets as described in Runtime
checks for method and requirement set support. (The technique of specifying the
requirement sets in the manifest, which is also described in that article, does not
currently work for RibbonApi 1.2.) Alternatively, you can implement an alternate UI
experience when custom contextual tabs are not supported.

Behavior of custom contextual tabs


The user experience for custom contextual tabs follows the pattern of built-in Office
contextual tabs. The following are the basic principles for the placement custom
contextual tabs.

When a custom contextual tab is visible, it appears on the right end of the ribbon.
If one or more built-in contextual tabs and one or more custom contextual tabs
from add-ins are visible at the same time, the custom contextual tabs are always to
the right of all of the built-in contextual tabs.
If your add-in has more than one contextual tab and there are contexts in which
more than one is visible, they appear in the order in which they are defined in your
add-in. (The direction is the same direction as the Office language; that is, is left-
to-right in left-to-right languages, but right-to-left in right-to-left languages.) See
Define the groups and controls that appear on the tab for details about how you
define them.
If more than one add-in has a contextual tab that is visible in a specific context,
then they appear in the order in which the add-ins were launched.
Custom contextual tabs, unlike custom core tabs, are not added permanently to
the Office application's ribbon. They are present only in Office documents on
which your add-in is running.

Major steps for including a contextual tab in an


add-in
The following are the major steps for including a custom contextual tab in an add-in.

1. Configure the add-in to use a shared runtime.


2. Define the tab and the groups and controls that appear on it.
3. Register the contextual tab with Office.
4. Specify the circumstances when the tab will be visible.
Configure the add-in to use a shared runtime
Adding custom contextual tabs requires your add-in to use the shared runtime. For
more information, see Configure an add-in to use a shared runtime.

Define the groups and controls that appear on


the tab
Unlike custom core tabs, which are defined with XML in the manifest, custom contextual
tabs are defined at runtime with a JSON blob. Your code parses the blob into a
JavaScript object, and then passes the object to the Office.ribbon.requestCreateControls
method. Custom contextual tabs are only present in documents on which your add-in is
currently running. This is different from custom core tabs which are added to the Office
application ribbon when the add-in is installed and remain present when another
document is opened. Also, the requestCreateControls method may be run only once in
a session of your add-in. If it is called again, an error is thrown.

7 Note

The structure of the JSON blob's properties and subproperties (and the key names)
is roughly parallel to the structure of the CustomTab element and its descendant
elements in the manifest XML.

We'll construct an example of a contextual tabs JSON blob step-by-step. The full schema
for the contextual tab JSON is at dynamic-ribbon.schema.json. If you are working in
Visual Studio Code, you can use this file to get IntelliSense and to validate your JSON.
For more information, see Editing JSON with Visual Studio Code - JSON schemas and
settings .

1. Begin by creating a JSON string with two array properties named actions and
tabs . The actions array is a specification of all the functions that can be executed

by controls on the contextual tab. The tabs array defines one or more contextual
tabs, up to a maximum of 20.

JSON

'{
"actions": [

],
"tabs": [
]
}'

2. This simple example of a contextual tab will have only a single button and, thus,
only a single action. Add the following as the only member of the actions array.
About this markup, note:

The id and type properties are mandatory.


The value of type can be either "ExecuteFunction" or "ShowTaskpane".
The functionName property is only used when the value of type is
ExecuteFunction . It is the name of a function defined in the FunctionFile. For
more information about the FunctionFile, see Basic concepts for Add-in
Commands.
In a later step, you will map this action to a button on the contextual tab.

JSON

{
"id": "executeWriteData",
"type": "ExecuteFunction",
"functionName": "writeData"
}

3. Add the following as the only member of the tabs array. About this markup, note:

The id property is required. Use a brief, descriptive ID that is unique among


all contextual tabs in your add-in.
The label property is required. It is a user-friendly string to serve as the label
of the contextual tab.
The groups property is required. It defines the groups of controls that will
appear on the tab. It must have at least one member and no more than 20.
(There are also limits on the number of controls that you can have on a
custom contextual tab and that will also constrain how many groups that you
have. See the next step for more information.)

7 Note

The tab object can also have an optional visible property that specifies
whether the tab is visible immediately when the add-in starts up. Since
contextual tabs are normally hidden until a user event triggers their visibility
(such as the user selecting an entity of some type in the document), the
visible property defaults to false when not present. In a later section, we

show how to set the property to true in response to an event.

JSON

{
"id": "CtxTab1",
"label": "Contoso Data",
"groups": [

]
}

4. In the simple ongoing example, the contextual tab has only a single group. Add
the following as the only member of the groups array. About this markup, note:

All the properties are required.


The id property must be unique among all the groups in the manifest. Use a
brief, descriptive ID, of up to 125 characters.
The label is a user-friendly string to serve as the label of the group.
The icon property's value is an array of objects that specify the icons that the
group will have on the ribbon depending on the size of the ribbon and the
Office application window.
The controls property's value is an array of objects that specify the buttons
and menus in the group. There must be at least one.

) Important

The total number of controls on the whole tab can be no more than 20. For
example, you could have 3 groups with 6 controls each, and a fourth group
with 2 controls, but you cannot have 4 groups with 6 controls each.

JSON

{
"id": "CustomGroup111",
"label": "Insertion",
"icon": [

],
"controls": [

]
}
5. Every group must have an icon of at least two sizes, 32x32 px and 80x80 px.
Optionally, you can also have icons of sizes 16x16 px, 20x20 px, 24x24 px, 40x40
px, 48x48 px, and 64x64 px. Office decides which icon to use based on the size of
the ribbon and Office application window. Add the following objects to the icon
array. (If the window and ribbon sizes are large enough for at least one of the
controls on the group to appear, then no group icon at all appears. For an example,
watch the Styles group on the Word ribbon as you shrink and expand the Word
window.) About this markup, note:

Both the properties are required.


The size property unit of measure is pixels. Icons are always square, so the
number is both the height and the width.
The sourceLocation property specifies the full URL to the icon.

) Important

Just as you typically must change the URLs in the add-in's manifest when you
move from development to production (such as changing the domain from
localhost to contoso.com), you must also change the URLs in your contextual
tabs JSON.

JSON

{
"size": 32,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/Group32x32.png"
},
{
"size": 80,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/Group80x80.png"
}

6. In our simple ongoing example, the group has only a single button. Add the
following object as the only member of the controls array. About this markup,
note:

All the properties, except enabled , are required.


type specifies the type of control. The values can be "Button", "Menu", or

"MobileButton".
id can be up to 125 characters.
actionId must be the ID of an action defined in the actions array. (See step

1 of this section.)
label is a user-friendly string to serve as the label of the button.

superTip represents a rich form of tool tip. Both the title and description
properties are required.
icon specifies the icons for the button. The previous remarks about the

group icon apply here too.


enabled (optional) specifies whether the button is enabled when the

contextual tab appears starts up. The default if not present is true .

JSON

{
"type": "Button",
"id": "CtxBt112",
"actionId": "executeWriteData",
"enabled": false,
"label": "Write Data",
"superTip": {
"title": "Data Insertion",
"description": "Use this button to insert data into the
document."
},
"icon": [
{
"size": 32,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/WriteDataButton32x
32.png"
},
{
"size": 80,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/WriteDataButton80x
80.png"
}
]
}

The following is the complete example of the JSON blob.

JSON

`{
"actions": [
{
"id": "executeWriteData",
"type": "ExecuteFunction",
"functionName": "writeData"
}
],
"tabs": [
{
"id": "CtxTab1",
"label": "Contoso Data",
"groups": [
{
"id": "CustomGroup111",
"label": "Insertion",
"icon": [
{
"size": 32,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/Group32x32.png"
},
{
"size": 80,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/Group80x80.png"
}
],
"controls": [
{
"type": "Button",
"id": "CtxBt112",
"actionId": "executeWriteData",
"enabled": false,
"label": "Write Data",
"superTip": {
"title": "Data Insertion",
"description": "Use this button to insert data into the
document."
},
"icon": [
{
"size": 32,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/WriteDataButton32x32.pn
g"
},
{
"size": 80,
"sourceLocation":
"https://cdn.contoso.com/addins/datainsertion/Images/WriteDataButton80x80.pn
g"
}
]
}
]
}
]
}
]
}`
Register the contextual tab with Office with
requestCreateControls
The contextual tab is registered with Office by calling the
Office.ribbon.requestCreateControls method. This is typically done in either the function
that is assigned to Office.initialize or with the Office.onReady function. For more
about these functions and initializing the add-in, see Initialize your Office Add-in. You
can, however, call the method anytime after initialization.

) Important

The requestCreateControls method may be called only once in a given session of


an add-in. An error is thrown if it is called again.

The following is an example. Note that the JSON string must be converted to a
JavaScript object with the JSON.parse method before it can be passed to a JavaScript
function.

JavaScript

Office.onReady(async () => {
const contextualTabJSON = ` ... `; // Assign the JSON string such as the
one at the end of the preceding section.
const contextualTab = JSON.parse(contextualTabJSON);
await Office.ribbon.requestCreateControls(contextualTab);
});

Specify the contexts when the tab will be


visible with requestUpdate
Typically, a custom contextual tab should appear when a user-initiated event changes
the add-in context. Consider a scenario in which the tab should be visible when, and
only when, a chart (on the default worksheet of an Excel workbook) is activated.

Begin by assigning handlers. This is commonly done in the Office.onReady function as


in the following example which assigns handlers (created in a later step) to the
onActivated and onDeactivated events of all the charts in the worksheet.

JavaScript
Office.onReady(async () => {
const contextualTabJSON = ` ... `; // Assign the JSON string.
const contextualTab = JSON.parse(contextualTabJSON);
await Office.ribbon.requestCreateControls(contextualTab);

await Excel.run(context => {


const charts = context.workbook.worksheets
.getActiveWorksheet()
.charts;
charts.onActivated.add(showDataTab);
charts.onDeactivated.add(hideDataTab);
return context.sync();
});
});

Next, define the handlers. The following is a simple example of a showDataTab , but see
Handling the HostRestartNeeded error later in this article for a more robust version of
the function. About this code, note:

Office controls when it updates the state of the ribbon. The


Office.ribbon.requestUpdate method queues a request to update. The method will
resolve the Promise object as soon as it has queued the request, not when the
ribbon actually updates.
The parameter for the requestUpdate method is a RibbonUpdaterData object that
(1) specifies the tab by its ID exactly as specified in the JSON and (2) specifies
visibility of the tab.
If you have more than one custom contextual tab that should be visible in the
same context, you simply add additional tab objects to the tabs array.

JavaScript

async function showDataTab() {


await Office.ribbon.requestUpdate({
tabs: [
{
id: "CtxTab1",
visible: true
}
]});
}

The handler to hide the tab is nearly identical, except that it sets the visible property
back to false .

The Office JavaScript library also provides several interfaces (types) to make it easier to
construct the RibbonUpdateData object. The following is the showDataTab function in
TypeScript and it makes use of these types.

TypeScript

const showDataTab = async () => {


const myContextualTab: Office.Tab = {id: "CtxTab1", visible: true};
const ribbonUpdater: Office.RibbonUpdaterData = { tabs: [
myContextualTab ]};
await Office.ribbon.requestUpdate(ribbonUpdater);
}

Toggle tab visibility and the enabled status of a button at


the same time
The requestUpdate method is also used to toggle the enabled or disabled status of a
custom button on either a custom contextual tab or a custom core tab. For details about
this, see Enable and Disable Add-in Commands. There may be scenarios in which you
want to change both the visibility of a tab and the enabled status of a button at the
same time. You do this with a single call of requestUpdate . The following is an example
in which a button on a core tab is enabled at the same time as a contextual tab is made
visible.

JavaScript

function myContextChanges() {
Office.ribbon.requestUpdate({
tabs: [
{
id: "CtxTab1",
visible: true
},
{
id: "OfficeAppTab1",
groups: [
{
id: "CustomGroup111",
controls: [
{
id: "MyButton",
enabled: true
}
]
}
]
]}
]
});
}
In the following example, the button that is enabled is on the very same contextual tab
that is being made visible.

JavaScript

function myContextChanges() {
Office.ribbon.requestUpdate({
tabs: [
{
id: "CtxTab1",
visible: true,
groups: [
{
id: "CustomGroup111",
controls: [
{
id: "MyButton",
enabled: true
}
]
}
]
}
]
});
}

Open a task pane from contextual tabs


To open your task pane from a button on a custom contextual tab, create an action in
the JSON with a type of ShowTaskpane . Then define a button with the actionId property
set to the id of the action. This opens the default task pane specified by the
<Runtime> element in your manifest.

JSON

`{
"actions": [
{
"id": "openChartsTaskpane",
"type": "ShowTaskpane",
"title": "Work with Charts",
"supportPinning": false
}
],
"tabs": [
{
// some tab properties omitted
"groups": [
{
// some group properties omitted
"controls": [
{
"type": "Button",
"id": "CtxBt112",
"actionId": "openChartsTaskpane",
"enabled": false,
"label": "Open Charts Taskpane",
// some control properties omitted
}
]
}
]
}
]
}`

To open any task pane that is not the default task pane, specify a sourceLocation
property in the definition of the action. In the following example, a second task pane is
opened from a different button.

) Important

When a sourceLocation is specified for the action, then the task pane does
not use the shared runtime. It runs in a new separate runtime.
No more than one task pane can use the shared runtime, so no more than
one action of type ShowTaskpane can omit the sourceLocation property.

JSON

`{
"actions": [
{
"id": "openChartsTaskpane",
"type": "ShowTaskpane",
"title": "Work with Charts",
"supportPinning": false
},
{
"id": "openTablesTaskpane",
"type": "ShowTaskpane",
"title": "Work with Tables",
"supportPinning": false
"sourceLocation": "https://MyDomain.com/myPage.html"
}
],
"tabs": [
{
// some tab properties omitted
"groups": [
{
// some group properties omitted
"controls": [
{
"type": "Button",
"id": "CtxBt112",
"actionId": "openChartsTaskpane",
"enabled": false,
"label": "Open Charts Taskpane",
// some control properties omitted
},
{
"type": "Button",
"id": "CtxBt113",
"actionId": "openTablesTaskpane",
"enabled": false,
"label": "Open Tables Taskpane",
// some control properties omitted
}
]
}
]
}
]
}`

Localize the JSON text


The JSON blob that is passed to requestCreateControls is not localized the same way
that the manifest markup for custom core tabs is localized (which is described at Control
localization from the manifest). Instead, the localization must occur at runtime using
distinct JSON blobs for each locale. We suggest that you use a switch statement that
tests the Office.context.displayLanguage property. The following is an example.

JavaScript

function GetContextualTabsJsonSupportedLocale () {
const displayLanguage = Office.context.displayLanguage;

switch (displayLanguage) {
case 'en-US':
return `{
"actions": [
// actions omitted
],
"tabs": [
{
"id": "CtxTab1",
"label": "Contoso Data",
"groups": [
// groups omitted
]
}
]
}`;

case 'fr-FR':
return `{
"actions": [
// actions omitted
],
"tabs": [
{
"id": "CtxTab1",
"label": "Contoso Données",
"groups": [
// groups omitted
]
}
]
}`;

// Other cases omitted


}
}

Then your code calls the function to get the localized blob that is passed to
requestCreateControls , as in the following example.

JavaScript

const contextualTabJSON = GetContextualTabsJsonSupportedLocale();

Best practices for custom contextual tabs

Implement an alternate UI experience when custom


contextual tabs are not supported
Some combinations of platform, Office application, and Office build don't support
requestCreateControls . Your add-in should be designed to provide an alternate
experience to users who are running the add-in on one of those combinations. The
following sections describe two ways of providing a fallback experience.
Use noncontextual tabs or controls
There is a manifest element, OverriddenByRibbonApi, that is designed to create a
fallback experience in an add-in that implements custom contextual tabs when the add-
in is running on an application or platform that doesn't support custom contextual tabs.

The simplest strategy for using this element is to define a custom core tab (that is,
noncontextual custom tab) in the manifest that duplicates the ribbon customizations of
the custom contextual tabs in your add-in. But you add
<OverriddenByRibbonApi>true</OverriddenByRibbonApi> as the first child element of the

duplicate Group, Control, and menu <Item> elements on the custom core tabs. The
effect of doing so is the following:

If the add-in runs on an application and platform that support custom contextual
tabs, then the custom core groups and controls won't appear on the ribbon.
Instead, the custom contextual tab will be created when the add-in calls the
requestCreateControls method.
If the add-in runs on an application or platform that doesn't support
requestCreateControls , then the elements do appear on the custom core tab.

The following is an example. Note that "MyButton" will appear on the custom core tab
only when custom contextual tabs are not supported. But the parent group and custom
core tab will appear regardless of whether custom contextual tabs are supported.

XML

<OfficeApp ...>
...
<VersionOverrides ...>
...
<Hosts>
<Host ...>
...
<DesktopFormFactor>
<ExtensionPoint ...>
<CustomTab ...>
...
<Group ...>
...
<Control ... id="Contoso.MyButton1">
<OverriddenByRibbonApi>true</OverriddenByRibbonApi>
...
<Action ...>
...
</OfficeApp>

For more examples, see OverriddenByRibbonApi.


When a parent group, or menu is marked with
<OverriddenByRibbonApi>true</OverriddenByRibbonApi> , then it isn't visible, and all of its
child markup is ignored when custom contextual tabs aren't supported. So, it doesn't
matter if any of those child elements have the <OverriddenByRibbonApi> element or
what its value is. The implication of this is that if a menu item or control must be visible
in all contexts, then not only should it not be marked with
<OverriddenByRibbonApi>true</OverriddenByRibbonApi> , but its ancestor menu and group
must also not be marked this way.

) Important

Don't mark all of the child elements of a group or menu with


<OverriddenByRibbonApi>true</OverriddenByRibbonApi> . This is pointless if the

parent element is marked with


<OverriddenByRibbonApi>true</OverriddenByRibbonApi> for reasons given in the

preceding paragraph. Moreover, if you leave out the <OverriddenByRibbonApi>


on the parent (or set it to false ), then the parent will appear regardless of whether
custom contextual tabs are supported, but it will be empty when they are
supported. So, if all the child elements shouldn't appear when custom contextual
tabs are supported, mark the parent with
<OverriddenByRibbonApi>true</OverriddenByRibbonApi> .

Use APIs that show or hide a task pane in specified contexts

As an alternative to <OverriddenByRibbonApi>, your add-in can define a task pane


with UI controls that duplicate the functionality of the controls on a custom contextual
tab. Then use the Office.addin.showAsTaskpane and Office.addin.hide methods to show
the task pane when the contextual tab would have been shown if it was supported. For
details on how to use these methods, see Show or hide the task pane of your Office
Add-in.

Handle the HostRestartNeeded error


In some scenarios, Office is unable to update the ribbon and will return an error. For
example, if the add-in is upgraded and the upgraded add-in has a different set of
custom add-in commands, then the Office application must be closed and reopened.
Until it is, the requestUpdate method will return the error HostRestartNeeded . Your code
should handle this error. The following is an example of how. In this case, the
reportError method displays the error to the user.
JavaScript

function showDataTab() {
try {
Office.ribbon.requestUpdate({
tabs: [
{
id: "CtxTab1",
visible: true
}
]});
}
catch(error) {
if (error.code == "HostRestartNeeded"){
reportError("Contoso Awesome Add-in has been upgraded. Please
save your work, then close and reopen the Office application.");
}
}
}

Resources
Code sample: Create custom contextual tabs on the ribbon
Community demo of contextual tabs sample

https://www.youtube-nocookie.com/embed/9tLfm4boQIo
Enable and Disable Add-in Commands
Article • 04/04/2023

When some functionality in your add-in should only be available in certain contexts, you
can programmatically enable or disable your custom Add-in Commands. For example, a
function that changes the header of a table should only be enabled when the cursor is
in a table.

You can also specify whether the command is enabled or disabled when the Office client
application opens.

7 Note

This article assumes that you are familiar with the following documentation. Please
review it if you haven't worked with Add-in Commands (custom menu items and
ribbon buttons) recently.

Basic concepts for Add-in Commands

Office application and platform support only


The APIs described in this article are only available in Excel, PowerPoint, and Word.

Test for platform support with requirement sets


Requirement sets are named groups of API members. Office Add-ins use requirement
sets specified in the manifest or use a runtime check to determine whether an Office
application and platform combination supports APIs that an add-in needs. For more
information, see Office versions and requirement sets.

The enable/disable APIs belong to the RibbonApi 1.1 requirement set.

7 Note

The RibbonApi 1.1 requirement set is not yet supported in the manifest, so you
cannot specify it in the manifest's <Requirements> section. To test for support,
your code should call Office.context.requirements.isSetSupported('RibbonApi',
'1.1') . If, and only if, that call returns true , your code can call the enable/disable
APIs. If the call of isSetSupported returns false , then all custom add-in commands
are enabled all of the time. You must design your production add-in, and any in-
app instructions, to take account of how it will work when the RibbonApi 1.1
requirement set isn't supported. For more information and examples of using
isSetSupported , see Specify Office applications and API requirements, especially
Runtime checks for method and requirement set support. (The section Specify
which Office versions and platforms can host your add-in of that article doesn't
apply to Ribbon 1.1.)

Shared runtime required


The APIs and manifest markup described in this article require that the add-in's manifest
specify that it should use a shared runtime. To do this, take the following steps.

1. In the Runtimes element in the manifest, add the following child element: <Runtime
resid="Contoso.SharedRuntime.Url" lifetime="long" /> . (If there isn't already a

<Runtimes> element in the manifest, create it as the first child under the <Host>
element in the <VersionOverrides> section.)

2. In the Resources.Urls section of the manifest, add the following child element:
<bt:Url id="Contoso.SharedRuntime.Url" DefaultValue="https://{MyDomain}/{path-
to-start-page}" /> , where {MyDomain} is the domain of the add-in and {path-to-

start-page} is the path for the start page of the add-in; for example: <bt:Url

id="Contoso.SharedRuntime.Url"
DefaultValue="https://localhost:3000/index.html" /> .

3. Depending on whether your add-in contains a task pane, a function file, or an Excel
custom function, you must do one or more of the following three steps.

If the add-in contains a task pane, set the resid attribute of the
Action.SourceLocation element to exactly the same string as you used for the
resid of the <Runtime> element in step 1; for example,

Contoso.SharedRuntime.Url . The element should look like this:


<SourceLocation resid="Contoso.SharedRuntime.Url"/> .

If the add-in contains an Excel custom function, set the resid attribute of the
Page.SourceLocation element exactly the same string as you used for the
resid of the <Runtime> element in step 1; for example,

Contoso.SharedRuntime.Url . The element should look like this:


<SourceLocation resid="Contoso.SharedRuntime.Url"/> .

If the add-in contains a function file, set the resid attribute of the
FunctionFile element to exactly the same string as you used for the resid of
the <Runtime> element in step 1; for example, Contoso.SharedRuntime.Url .
The element should look like this: <FunctionFile
resid="Contoso.SharedRuntime.Url"/> .

Set the default state to disabled


By default, any Add-in Command is enabled when the Office application launches. If you
want a custom button or menu item to be disabled when the Office application
launches, you specify this in the manifest. Just add an Enabled element (with the value
false ) immediately below (not inside) the Action element in the declaration of the

control. The following shows the basic structure.

XML

<OfficeApp ...>
...
<VersionOverrides ...>
...
<Hosts>
<Host ...>
...
<DesktopFormFactor>
<ExtensionPoint ...>
<CustomTab ...>
...
<Group ...>
...
<Control ... id="Contoso.MyButton3">
...
<Action ...>
<Enabled>false</Enabled>
...
</OfficeApp>

Change the state programmatically


The essential steps to changing the enabled status of an Add-in Command are:

1. Create a RibbonUpdaterData object that (1) specifies the command, and its parent
group and tab, by their IDs as declared in the manifest; and (2) specifies the
enabled or disabled state of the command.
2. Pass the RibbonUpdaterData object to the Office.ribbon.requestUpdate() method.

The following is a simple example. Note that "MyButton", "OfficeAddinTab1", and


"CustomGroup111" are copied from the manifest.
JavaScript

function enableButton() {
Office.ribbon.requestUpdate({
tabs: [
{
id: "OfficeAppTab1",
groups: [
{
id: "CustomGroup111",
controls: [
{
id: "MyButton",
enabled: true
}
]
}
]
}
]
});
}

We also provide several interfaces (types) to make it easier to construct the


RibbonUpdateData object. The following is the equivalent example in TypeScript and it
makes use of these types.

TypeScript

const enableButton = async () => {


const button: Control = {id: "MyButton", enabled: true};
const parentGroup: Group = {id: "CustomGroup111", controls: [button]};
const parentTab: Tab = {id: "OfficeAddinTab1", groups: [parentGroup]};
const ribbonUpdater: RibbonUpdaterData = { tabs: [parentTab]};
Office.ribbon.requestUpdate(ribbonUpdater);
}

You can await the call of requestUpdate() if the parent function is asynchronous, but
note that the Office application controls when it updates the state of the ribbon. The
requestUpdate() method queues a request to update. The method will resolve the
promise object as soon as it has queued the request, not when the ribbon actually
updates.

Change the state in response to an event


A common scenario in which the ribbon state should change is when a user-initiated
event changes the add-in context.
Consider a scenario in which a button should be enabled when, and only when, a chart
is activated. The first step is to set the Enabled element for the button in the manifest to
false . See above for an example.

Second, assign handlers. This is commonly done in the Office.onReady function as in


the following example which assigns handlers (created in a later step) to the
onActivated and onDeactivated events of all the charts in the worksheet.

JavaScript

Office.onReady(async () => {
await Excel.run(context => {
const charts = context.workbook.worksheets
.getActiveWorksheet()
.charts;
charts.onActivated.add(enableChartFormat);
charts.onDeactivated.add(disableChartFormat);
return context.sync();
});
});

Third, define the enableChartFormat handler. The following is a simple example, but see
Best practice: Test for control status errors below for a more robust way of changing a
control's status.

JavaScript

function enableChartFormat() {
const button = {
id: "ChartFormatButton",
enabled: true
};
const parentGroup = {
id: "MyGroup",
controls: [button]
};
const parentTab = {
id: "CustomChartTab",
groups: [parentGroup]
};
const ribbonUpdater = {tabs: [parentTab]};
Office.ribbon.requestUpdate(ribbonUpdater);
}

Fourth, define the disableChartFormat handler. It would be identical to


enableChartFormat except that the enabled property of the button object would be set
to false .
Toggle tab visibility and the enabled status of a button at
the same time
The requestUpdate method is also used to toggle the visibility of a custom contextual
tab. For details about this and example code, see Create custom contextual tabs in
Office Add-ins.

Best practice: Test for control status errors


In some circumstances, the ribbon does not repaint after requestUpdate is called, so the
control's clickable status does not change. For this reason it is a best practice for the
add-in to keep track of the status of its controls. The add-in should conform to the
following rules.

1. Whenever requestUpdate is called, the code should record the intended state of
the custom buttons and menu items.
2. When a custom control is clicked, the first code in the handler, should check to see
if the button should have been clickable. If shouldn't have been, the code should
report or log an error and try again to set the buttons to the intended state.

The following example shows a function that disables a button and records the button's
status. Note that chartFormatButtonEnabled is a global boolean variable that is initialized
to the same value as the Enabled element for the button in the manifest.

JavaScript

function disableChartFormat() {
const button = {
id: "ChartFormatButton",
enabled: false
};
const parentGroup = {
id: "MyGroup",
controls: [button]
};
const parentTab = {
id: "CustomChartTab",
groups: [parentGroup]
};
const ribbonUpdater = {tabs: [parentTab]};
Office.ribbon.requestUpdate(ribbonUpdater);

chartFormatButtonEnabled = false;
}
The following example shows how the button's handler tests for an incorrect state of the
button. Note that reportError is a function that shows or logs an error.

JavaScript

function chartFormatButtonHandler() {
if (chartFormatButtonEnabled) {

// Do work here

} else {
// Report the error and try again to disable.
reportError("That action is not possible at this time.");
disableChartFormat();
}
}

Error handling
In some scenarios, Office is unable to update the ribbon and will return an error. For
example, if the add-in is upgraded and the upgraded add-in has a different set of
custom add-in commands, then the Office application must be closed and reopened.
Until it is, the requestUpdate method will return the error HostRestartNeeded . The
following is an example of how to handle this error. In this case, the reportError
method displays the error to the user.

JavaScript

function disableChartFormat() {
try {
const button = {
id: "ChartFormatButton",
enabled: false
};
const parentGroup = {
id: "MyGroup",
controls: [button]
};
const parentTab = {
id: "CustomChartTab",
groups: [parentGroup]
};
const ribbonUpdater = {tabs: [parentTab]};
Office.ribbon.requestUpdate(ribbonUpdater);

chartFormatButtonEnabled = false;
}
catch(error) {
if (error.code == "HostRestartNeeded"){
reportError("Contoso Awesome Add-in has been upgraded. Please
save your work, close the Office application, and restart it.");
}
}
}
Integrate built-in Office buttons into
custom control groups and tabs
Article • 04/04/2023

You can insert built-in Office buttons into your custom control groups on the Office
ribbon by using markup in the add-in's manifest. (You can't insert your custom add-in
commands into a built-in Office group.) You can also insert entire built-in Office control
groups into your custom ribbon tabs.

7 Note

This article assumes that you are familiar with the article Basic concepts for add-in
commands. Please review it if you haven't done so recently.

) Important

The add-in feature and markup described in this article is only available in
PowerPoint on the web.
The markup described in this article only works on platforms that support
requirement set AddinCommands 1.3. See the later section Behavior on
unsupported platforms.

Insert a built-in control group into a custom


tab
To insert a built-in Office control group into a tab, add an OfficeGroup element as a
child element in the parent <CustomTab> element. The id attribute of the of the
<OfficeGroup> element is set to the ID of the built-in group. See Find the IDs of
controls and control groups.

The following markup example adds the Office Paragraph control group to a custom tab
and positions it to appear just after a custom group.

XML

<ExtensionPoint xsi:type="ContosoRibbonTab">
<CustomTab id="Contoso.TabCustom1">
<Group id="Contoso.myCustomTab.group1">
<!-- additional markup omitted -->
</Group>
<OfficeGroup id="Paragraph" />
<Label resid="customTabLabel1" />
</CustomTab>
</ExtensionPoint>

Insert a built-in control into a custom group


To insert a built-in Office control into a custom group, add an OfficeControl element as
a child element in the parent <Group> element. The id attribute of the
<OfficeControl> element is set to the ID of the built-in control. See Find the IDs of
controls and control groups.

The following markup example adds the Office Superscript control to a custom group
and positions it to appear just after a custom button.

XML

<ExtensionPoint xsi:type="ContosoRibbonTab">
<CustomTab id="Contoso.TabCustom2">
<Group id="Contoso.TabCustom2.group1">
<Label resid="residCustomTabGroupLabel"/>
<Icon>
<bt:Image size="16" resid="blue-icon-16" />
<bt:Image size="32" resid="blue-icon-32" />
<bt:Image size="80" resid="blue-icon-80" />
</Icon>
<Control xsi:type="Button" id="Contoso.Button1">
<!-- information on the control omitted -->
</Control>
<OfficeControl id="Superscript" />
<!-- other controls, as needed -->
</Group>
<Label resid="customTabLabel1" />
</CustomTab>
</ExtensionPoint>

7 Note

Users can customize the ribbon in the Office application. Any user customizations
will override your manifest settings. For example, a user can remove a button from
any group and remove any group from a tab.
Find the IDs of controls and control groups
The IDs for supported controls and control groups are in files in the repo Office Control
IDs . Follow the instructions in the ReadMe file of that repo.

Behavior on unsupported platforms


If your add-in is installed on a platform that doesn't support requirement set
AddinCommands 1.3, then the markup described in this article is ignored and the built-
in Office controls/groups will not appear in your custom groups/tabs. To prevent your
add-in from being installed on platforms that don't support the markup, add a reference
to the requirement set in the <Requirements> section of the manifest. For instructions,
see Specify which Office versions and platforms can host your add-in. Alternatively,
design your add-in to have an experience when AddinCommands 1.3 isn't supported, as
described in Design for alternate experiences. For example, if your add-in contains
instructions that assume the built-in buttons are in your custom groups, you could
design a version that assumes that the built-in buttons are only in their usual places.
Position a custom tab on the ribbon
Article • 04/04/2023

You can specify where you want your add-in's custom tab to appear on the Office
application's ribbon by using markup in the add-in's manifest.

7 Note

This article assumes that you are familiar with the article Basic concepts for add-in
commands. Please review it if you have not done so recently.

) Important

The add-in feature and markup described in this article is only available in
PowerPoint on the web.
The markup described in this article only works on platforms that support
requirement set AddinCommands 1.3. See Behavior on unsupported
platforms below.

Specify where you want a custom tab to appear by identifying which built-in Office tab
you want it to be next to and specifying whether it should be on the left or right side of
the built-in tab. Make these specifications by including either an InsertBefore (left) or an
InsertAfter (right) element in the CustomTab element of your add-in's manifest. (You
cannot have both elements.)

In the following example, the custom tab is configured to appear just after the Review
tab. Note that the value of the <InsertAfter> element is the ID of the built-in Office tab.

XML

<ExtensionPoint xsi:type="ContosoRibbonTab">
<CustomTab id="Contoso.TabCustom2">
<Group id="Contoso.TabCustom2.group2">
<!-- additional markup omitted -->
</Group>
<Label resid="customTabLabel1" />
<InsertAfter>TabReview</InsertAfter>
</CustomTab>
</ExtensionPoint>

Keep the following points in mind.


The <InsertBefore> and <InsertAfter> elements are optional. If you use neither,
then your custom tab will appear as the rightmost tab on the ribbon.
The <InsertBefore> and <InsertAfter> elements are mutually exclusive. You
cannot use both.
If the user installs more than one add-in whose custom tab is configured for the
same place, say after the Review tab, then the tab for the most recently installed
add-in will be located in that place. The tabs of the previously installed add-ins will
be moved over one place. For example, the user installs add-ins A, B, and C in that
order and all are configured to insert a tab after the Review tab, then the tabs will
appear in this order: Review, AddinCTab, AddinBTab, AddinATab.
Users can customize the ribbon in the Office application. For example, a user can
move or hide your add-in's tab. You cannot prevent this or detect that it has
happened.
If a user moves one of the built-in tabs, then Office interprets the <InsertBefore>
and <InsertAfter> elements in terms of the default location of the built-in tab. For
example, if the user moves the Review tab to the right end of the ribbon, Office
will interpret the markup in the previous example as meaning "put the custom tab
just to the right of where the Review tab would be by default."

Specify which tab has focus when the


document opens
Office always gives default focus to the tab that is immediately to the right of the File
tab. By default this is the Home tab. If you configure your custom tab to be before the
Home tab, with <InsertBefore>TabHome</InsertBefore> , then your custom tab will have
focus when the document opens.

) Important

Giving excessive prominence to your add-in inconveniences and annoys users and
administrators. Do not position a custom tab before the Home tab unless your
add-in is the primary way users will interact with the document.

Behavior on unsupported platforms


If your add-in is installed on a platform that doesn't support requirement set
AddinCommands 1.3, then the markup described in this article is ignored and your
custom tab will appear as the rightmost tab on the ribbon. To prevent your add-in from
being installed on platforms that don't support the markup, add a reference to the
requirement set in the <Requirements> section of the manifest. For instructions, see
Specify which Office versions and platforms can host your add-in. Alternatively, design
your add-in to have an alternate experience when AddinCommands 1.3 is not
supported, as described in Design for alternate experiences. For example, if your add-in
contains instructions that assume the custom tab is where you want it, you could have
an alternate version that assumes the tab is the rightmost.
Content Office Add-ins
Article • 04/11/2023

Content add-ins are surfaces that can be embedded directly into Excel or PowerPoint
documents. Content add-ins give users access to interface controls that run code to
modify documents or display data from a data source. Use content add-ins when you
want to embed functionality directly into the document.

Figure 1. Typical layout for content add-ins

Best practices
Include some navigational or commanding element such as the CommandBar or
Pivot at the top of your add-in.
Include a branding element such as the BrandBar at the bottom of your add-in
(applies to Excel and PowerPoint add-ins only).

Variants
Content add-in sizes for Excel and PowerPoint in Office desktop and in a web browser
are user specified.

Personality menu
Personality menus can obstruct navigational and commanding elements located near
the top right of the add-in. The following are the current dimensions of the personality
menu on Windows and Mac.

For Windows, the personality menu measures 12x32 pixels, as shown.

Figure 2. Personality menu on Windows

For Mac, the personality menu measures 26x26 pixels, but floats 8 pixels in from the
right and 6 pixels from the top, which increases the occupied space to 34x32 pixels, as
shown.

Figure 3. Personality menu on Mac


Implementation
There are minor differences in the manifests between content add-ins and add-ins that
use task panes.

For the <OfficeApp> element, set the xsi:type attribute to "ContentApp" .


In the <DefaultSettings> element, add the <RequestedHeight> and
<RequestedWidth> elements.

For a sample that implements a content add-in, see Excel Content Add-in Humongous
Insurance in GitHub.

Support considerations
Check to see if your Office Add-in will work on a specific Office application or
platform.
Some content add-ins may require the user to "trust" the add-in to read and write
to Excel or PowerPoint. You can declare what level of permissions you want your
user to have in the add-in's manifest.

See also
Office client application and platform availability for Office Add-ins
Fabric Core in Office Add-ins
UX design patterns for Office Add-ins
Requesting permissions for API use in add-ins
Use the Office dialog API in Office Add-
ins
Article • 03/21/2023

Use the Office dialog API to open dialog boxes in your Office Add-in. This article
provides guidance for using the dialog API in your Office Add-in. Consider opening a
dialog box from a task pane, content add-in, or add-in command to do the following:

Sign in a user with a resource such as Google, Facebook, or Microsoft identity. For
more information, see Authenticate with the Office dialog API.
Provide more screen space, or even a full screen, for some tasks in your add-in.
Host a video that would be too small if confined to a task pane.

7 Note

Because overlapping UI elements are discouraged, avoid opening a dialog box


from a task pane unless your scenario requires it. When you consider how to use
the surface area of a task pane, note that task panes can be tabbed. For an example
of a tabbed task pane, see the Excel Add-in JavaScript SalesTracker sample.

The following image shows an example of a dialog box.


The dialog box always opens in the center of the screen. The user can move and resize
it. The window is nonmodal--a user can continue to interact with both the document in
the Office application and with the page in the task pane, if there is one.

Open a dialog box from a host page


The Office JavaScript APIs include a Dialog object and two functions in the
Office.context.ui namespace.

To open a dialog box, your code, typically a page in a task pane, calls the
displayDialogAsync method and passes to it the URL of the resource that you want to
open. The page on which this method is called is known as the "host page". For
example, if you call this method in script on index.html in a task pane, then index.html is
the host page of the dialog box that the method opens.

The resource that is opened in the dialog box is usually a page, but it can be a controller
method in an MVC application, a route, a web service method, or any other resource. In
this article, 'page' or 'website' refers to the resource in the dialog box. The following
code is a simple example.

JavaScript

Office.context.ui.displayDialogAsync('https://www.contoso.com/myDialog.html'
);

The URL uses the HTTPS protocol. This is mandatory for all pages loaded in a
dialog box, not just the first page loaded.
The dialog box's domain is the same as the domain of the host page, which can be
the page in a task pane or the function file of an add-in command. It is required
that the page, controller method, or other resource that is passed to the
displayDialogAsync method must be in the same domain as the host page.

) Important

The host page and the resource that opens in the dialog box must have the same
full domain. If you attempt to pass displayDialogAsync a subdomain of the add-in's
domain, it will not work. The full domain, including any subdomain, must match.

After the first page (or other resource) is loaded, a user can use links or other UI to
navigate to any website (or other resource) that uses HTTPS. You can also design the
first page to immediately redirect to another site.
By default, the dialog box will occupy 80% of the height and width of the device screen,
but you can set different percentages by passing a configuration object to the method,
as shown in the following example.

JavaScript

Office.context.ui.displayDialogAsync('https://www.contoso.com/myDialog.html'
, {height: 30, width: 20});

For a sample add-in that does this, see Build Office Add-ins for Excel . For more
samples that use displayDialogAsync , see Code samples.

Set both values to 100% to get what is effectively a full screen experience. The effective
maximum is 99.5%, and the window is still moveable and resizable.

You can open only one dialog box from a host window. An attempt to open another
dialog box generates an error. For example, if a user opens a dialog box from a task
pane, they can't open a second dialog box from a different page in the task pane.
However, when a dialog box is opened from an add-in command, the command opens
a new (but unseen) HTML file each time it is selected. This creates a new (unseen) host
window, so each such window can launch its own dialog box. For more information, see
Errors from displayDialogAsync.

Take advantage of a performance option in Office on the


web
The displayInIframe property is an additional property in the configuration object that
you can pass to displayDialogAsync . When this property is set to true , and the add-in
is running in a document opened in Office on the web, the dialog box will open as a
floating iframe rather than an independent window, which makes it open faster. The
following is an example.

JavaScript

Office.context.ui.displayDialogAsync('https://www.contoso.com/myDialog.html'
, {height: 30, width: 20, displayInIframe: true});

The default value is false , which is the same as omitting the property entirely. If the
add-in isn't running in Office on the web, the displayInIframe is ignored.

7 Note
You should not use displayInIframe: true if the dialog box will at any point
redirect to a page that can't be opened in an iframe. For example, the sign in pages
of many popular web services, such as Google and Microsoft account, can't be
opened in an iframe.

Send information from the dialog box to the


host page
Code in the dialog box uses the messageParent function to send a string message to the
host page. The string can be a word, sentence, XML blob, stringified JSON, or anything
else that can be serialized to a string or cast to a string. To use the messageParent
method, the dialog box must first initialize the Office JavaScript API.

7 Note

For clarity, in this section we call the message target the host page, but strictly
speaking the messages are going to the Runtime in the task pane (or the runtime
that is hosting a function file). The distinction is only significant in the case of
cross-domain messaging. For more information, see Cross-domain messaging to
the host runtime.

The following example shows how to initialize Office JS and send a message to the host
page.

JavaScript

Office.onReady(function() {
// Add any initialization code for your dialog here.
});

// Called when dialog signs in the user.


function userSignedIn() {
Office.context.ui.messageParent(true.toString());
}

The next example shows how to return a JSON string containing profile information.

JavaScript

function userProfileSignedIn(profile) {
const profileMessage = {
"name": profile.name,
"email": profile.email,
};
Office.context.ui.messageParent(JSON.stringify(profileMessage));
}

The messageParent function is one of only two Office JS APIs that can be called in the
dialog box. The other JS API that can be called in the dialog box is
Office.context.requirements.isSetSupported . For information about it, see Specify

Office applications and API requirements. However, in the dialog box, this API isn't
supported in volume-licensed perpetual Outlook 2016 (that is, the MSI version).

The host page must be configured to receive the message. You do this by adding a
callback parameter to the original call of displayDialogAsync . The callback assigns a
handler to the DialogMessageReceived event. The following is an example.

JavaScript

let dialog; // Declare dialog as global for use in later functions.


Office.context.ui.displayDialogAsync('https://www.contoso.com/myDialog.html'
, {height: 30, width: 20},
function (asyncResult) {
dialog = asyncResult.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived,
processMessage);
}
);

Office passes an AsyncResult object to the callback. It represents the result of the
attempt to open the dialog box. It does not represent the outcome of any events in the
dialog box. For more on this distinction, see Handle errors and events.

The value property of the asyncResult is set to a Dialog object, which exists in the
host page, not in the dialog box's execution context.
The processMessage is the function that handles the event. You can give it any
name you want.
The dialog variable is declared at a wider scope than the callback because it is
also referenced in processMessage .

The following is a simple example of a handler for the DialogMessageReceived event.

JavaScript

function processMessage(arg) {
const messageFromDialog = JSON.parse(arg.message);
showUserName(messageFromDialog.name);
}
Office passes the arg object to the handler. Its message property is the string sent by
the call of messageParent in the dialog box. In this example, it is a stringified
representation of a user's profile from a service, such as Microsoft account or Google, so
it's deserialized back to an object with JSON.parse . The showUserName implementation
isn't shown. It might display a personalized welcome message on the task pane.

When the user interaction with the dialog box is completed, your message handler
should close the dialog box, as shown in this example.

JavaScript

function processMessage(arg) {
dialog.close();
// message processing code goes here;
}

The dialog object must be the same one that is returned by the call of
displayDialogAsync . You need to declare the dialog object as a global variable. Or you

can scope the dialog object to the displayDialogAsync call with an anonymous callback
function as shown in the following example. In the example, processMessage does not
need to close the dialog since the close method is called in the anonymous callback
function.

JavaScript

Office.context.ui.displayDialogAsync('https://www.contoso.com/myDialog.html'
, {height: 30, width: 20},
function (asyncResult) {
const dialog = asyncResult.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived, (arg)
=> {
dialog.close();
processMessage(arg);
});
}
);

If the add-in needs to open a different page of the task pane after receiving the
message, you can use the window.location.replace method (or window.location.href )
as the last line of the handler. The following is an example.

JavaScript
function processMessage(arg) {
// message processing code goes here;
window.location.replace("/newPage.html");
// Alternatively ...
// window.location.href = "/newPage.html";
}

For an example of an add-in that does this, see the Insert Excel charts using Microsoft
Graph in a PowerPoint add-in sample.

Conditional messaging
Because you can send multiple messageParent calls from the dialog box, but you have
only one handler in the host page for the DialogMessageReceived event, the handler
must use conditional logic to distinguish different messages. For example, if the dialog
box prompts a user to sign in to an identity provider such as Microsoft account or
Google, it sends the user's profile as a message. If authentication fails, the dialog box
sends error information to the host page, as in the following example.

JavaScript

if (loginSuccess) {
const userProfile = getProfile();
const messageObject = {messageType: "signinSuccess", profile:
userProfile};
const jsonMessage = JSON.stringify(messageObject);
Office.context.ui.messageParent(jsonMessage);
} else {
const errorDetails = getError();
const messageObject = {messageType: "signinFailure", error:
errorDetails};
const jsonMessage = JSON.stringify(messageObject);
Office.context.ui.messageParent(jsonMessage);
}

About the previous example, note:

The loginSuccess variable would be initialized by reading the HTTP response from
the identity provider.
The implementation of the getProfile and getError functions is not shown. They
each get data from a query parameter or from the body of the HTTP response.
Anonymous objects of different types are sent depending on whether the sign in
was successful. Both have a messageType property, but one has a profile property
and the other has an error property.
The handler code in the host page uses the value of the messageType property to branch
as shown in the following example. Note that the showUserName function is the same as
in the previous example and showNotification function displays the error in the host
page's UI.

JavaScript

function processMessage(arg) {
const messageFromDialog = JSON.parse(arg.message);
if (messageFromDialog.messageType === "signinSuccess") {
dialog.close();
showUserName(messageFromDialog.profile.name);
window.location.replace("/newPage.html");
} else {
dialog.close();
showNotification("Unable to authenticate user: " +
messageFromDialog.error);
}
}

The showNotification implementation isn't shown. It might display status in a


notification bar on the task pane.

Cross-domain messaging to the host runtime


After the dialog opens, either the dialog or the parent runtime may navigate away from
the add-in's domain. If either of these things happens, then a call of messageParent will
fail unless your code specifies the domain of the parent runtime. You do this by adding a
DialogMessageOptions parameter to the call of messageParent . This object has a
targetOrigin property that specifies the domain to which the message should be sent.
If the parameter isn't used, Office assumes that the target is the same domain that the
dialog is currently hosting.

7 Note

Using messageParent to send a cross-domain message requires the Dialog Origin


1.1 requirement set. The DialogMessageOptions parameter is ignored on older
versions of Office that do not support the requirement set, so the behavior of the
method is unaffected if you pass it.

The following is an example of using messageParent to send a cross-domain message.

JavaScript
Office.context.ui.messageParent("Some message", { targetOrigin:
"https://resource.contoso.com" });

If the message doesn't include sensitive data, you can set the targetOrigin to "*" which
allows it to be sent to any domain. The following is an example.

JavaScript

Office.context.ui.messageParent("Some message", { targetOrigin: "*" });

 Tip

The DialogMessageOptions parameter was added to the messageParent method as a


required parameter in mid-2021. Older add-ins that send a cross-domain message
with the method no longer work until they are updated to use the new parameter.
Until the add-in is updated, in Office on Windows only, users and system
administrators can enable those add-ins to continue working by specifying the
trusted domains with a registry setting:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\AllowedDialogCo
mmunicationDomains. To do this, create a file with a .reg extension, save it to the
Windows computer, and then double-click it to run it. The following is an example
of the contents of such a file.

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\AllowedDialogComm
unicationDomains]
"My trusted domain"="https://www.contoso.com"
"Another trusted domain"="https://fabrikam.com"

Pass information to the dialog box


Your add-in can send messages from the host page to a dialog box using
Dialog.messageChild.

Use messageChild() from the host page


When you call the Office dialog API to open a dialog box, a Dialog object is returned. It
should be assigned to a variable that has global scope so that you can reference it from
other functions. The following is an example.

JavaScript

let dialog; // Declare as global variable.


Office.context.ui.displayDialogAsync('https://www.contoso.com/myDialog.html'
,
function (asyncResult) {
dialog = asyncResult.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived,
processMessage);
}
);

function processMessage(arg) {
dialog.close();

// message processing code goes here;

This Dialog object has a messageChild method that sends any string, including
stringified data, to the dialog box. This raises a DialogParentMessageReceived event in
the dialog box. Your code should handle this event, as shown in the next section.

Consider a scenario in which the UI of the dialog is related to the currently active
worksheet and that worksheet's position relative to the other worksheets. In the
following example, sheetPropertiesChanged sends Excel worksheet properties to the
dialog box. In this case, the current worksheet is named "My Sheet" and it's the second
sheet in the workbook. The data is encapsulated in an object and stringified so that it
can be passed to messageChild .

JavaScript

function sheetPropertiesChanged() {
const messageToDialog = JSON.stringify({
name: "My Sheet",
position: 2
});

dialog.messageChild(messageToDialog);
}

Handle DialogParentMessageReceived in the dialog box


In the dialog box's JavaScript, register a handler for the DialogParentMessageReceived
event with the UI.addHandlerAsync method. This is typically done in the Office.onReady
or Office.initialize function, as shown in the following. (A more robust example is
included later in this article.)

JavaScript

Office.onReady(function () {

Office.context.ui.addHandlerAsync(Office.EventType.DialogParentMessageReceiv
ed,
onMessageFromParent);
});

Then, define the onMessageFromParent handler. The following code continues the
example from the preceding section. Note that Office passes an argument to the
handler and that the message property of the argument object contains the string from
the host page. In this example, the message is reconverted to an object and jQuery is
used to set the top heading of the dialog to match the new worksheet name.

JavaScript

function onMessageFromParent(arg) {
const messageFromParent = JSON.parse(arg.message);
document.querySelector('h1').textContent = messageFromParent.name;
}

It's best practice to verify that your handler is properly registered. You can do this by
passing a callback to the addHandlerAsync method. This runs when the attempt to
register the handler completes. Use the handler to log or show an error if the handler
was not successfully registered. The following is an example. Note that reportError is a
function, not defined here, that logs or displays the error.

JavaScript

Office.onReady(function () {
Office.context.ui.addHandlerAsync(
Office.EventType.DialogParentMessageReceived,
onMessageFromParent,
onRegisterMessageComplete);
});

function onRegisterMessageComplete(asyncResult) {
if (asyncResult.status !== Office.AsyncResultStatus.Succeeded) {
reportError(asyncResult.error.message);
}
}
Conditional messaging from parent page to dialog box
Because you can make multiple messageChild calls from the host page, but you have
only one handler in the dialog box for the DialogParentMessageReceived event, the
handler must use conditional logic to distinguish different messages. You can do this in
a way that is precisely parallel to how you would structure conditional messaging when
the dialog box is sending a message to the host page as described in Conditional
messaging.

7 Note

In some situations, the messageChild API, which is a part of the DialogApi 1.2
requirement set, may not be supported. Some alternative ways for parent-to-
dialog-box messaging are described in Alternative ways of passing messages to a
dialog box from its host page.

) Important

The DialogApi 1.2 requirement set can't be specified in the <Requirements>


section of an add-in manifest. You will have to check for support for DialogApi 1.2
at runtime using the isSetSupported method as described in Runtime checks for
method and requirement set support. Support for manifest requirements is under
development.

Cross-domain messaging to the dialog runtime


After the dialog opens, either the dialog or the parent runtime may navigate away from
the add-in's domain. If either of these things happens, then calls to messageChild will fail
unless your code specifies the domain of the dialog runtime. You do this by adding a
DialogMessageOptions parameter to the call of messageChild . This object has a
targetOrigin property that specifies the domain to which the message should be sent.

If the parameter isn't used, Office assumes that the target is the same domain that the
parent runtime is currently hosting.

7 Note
Using messageChild to send a cross-domain message requires the Dialog Origin 1.1
requirement set. The DialogMessageOptions parameter is ignored on older versions
of Office that do not support the requirement set, so the behavior of the method is
unaffected if you pass it.

The following is an example of using messageChild to send a cross-domain message.

JavaScript

dialog.messageChild(messageToDialog, { targetOrigin:
"https://resource.contoso.com" });

If the message doesn't include sensitive data, you can set the targetOrigin to "*" which
allows it to be sent to any domain. The following is an example.

JavaScript

dialog.messageChild(messageToDialog, { targetOrigin: "*" });

Because the runtime that is hosting the dialog can't access the <AppDomains> section
of the manifest and thereby determine whether the domain from which the message
comes is trusted, you must use the DialogParentMessageReceived handler to determine
this. The object that is passed to the handler contains the domain that is currently
hosted in the parent as its origin property. The following is an example of how to use
the property.

JavaScript

function onMessageFromParent(arg) {
if (arg.origin === "https://addin.fabrikam.com") {
// process message
} else {
dialog.close();
showNotification("Messages from " + arg.origin + " are not
accepted.");
}
}

For example, your code could use the Office.onReady or Office.initialize function to store
an array of trusted domains in a global variable. The arg.origin property could then be
checked against that list in the handler.

 Tip
The DialogMessageOptions parameter was added to the messageChild method as a
required parameter in mid-2021. Older add-ins that send a cross-domain message
with the method no longer work until they are updated to use the new parameter.
Until the add-in is updated, in Office on Windows only, users and system
administrators can enable those add-ins to continue working by specifying the
trusted domains with a registry setting:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\AllowedDialogCo
mmunicationDomains. To do this, create a file with a .reg extension, save it to the
Windows computer, and then double-click it to run it. The following is an example
of the contents of such a file.

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\AllowedDialogComm
unicationDomains]
"My trusted domain"="https://www.contoso.com"
"Another trusted domain"="https://fabrikam.com"

Close the dialog box


You can implement a button in the dialog box that will close it. To do this, the click event
handler for the button should use messageParent to tell the host page that the button
has been clicked. The following is an example.

JavaScript

function closeButtonClick() {
const messageObject = {messageType: "dialogClosed"};
const jsonMessage = JSON.stringify(messageObject);
Office.context.ui.messageParent(jsonMessage);
}

The host page handler for DialogMessageReceived would call dialog.close , as in this
example. (See previous examples that show how the dialog object is initialized.)

JavaScript

function processMessage(arg) {
const messageFromDialog = JSON.parse(arg.message);
if (messageFromDialog.messageType === "dialogClosed") {
dialog.close();
}
}

Even when you don't have your own close-dialog UI, an end user can close the dialog
box by choosing the X in the upper-right corner. This action triggers the
DialogEventReceived event. If your host pane needs to know when this happens, it

should declare a handler for this event. See the section Errors and events in the dialog
box for details.

Code samples
All of the following samples use displayDialogAsync . Some have NodeJS-based servers
and others have ASP.NET/IIS-based servers, but the logic of using the method is the
same regardless of how the server-side of the add-in is implemented.

Training Content / Building Add-ins


Office Add-in Microsoft Graph ASPNET
Office Add-in Microsoft Graph React
Office Add-in NodeJS SSO
Office Add-in ASPNET SSO
Office Add-in SAAS Monetization Sample
Outlook Add-in Microsoft Graph ASPNET
Outlook Add-in SSO
Outlook Add-in Token Viewer
Outlook Add-in Actionable Message
Outlook Add-in Sharing to OneDrive
PowerPoint Add-in Microsoft Graph ASPNET InsertChart
Excel Shared Runtime Scenario

See also
Dialog API requirement sets
Best practices and rules for the Office dialog API
Authenticate with the Office dialog API
Use the Office dialog box to show a video
Handling errors and events in the Office dialog box
Runtimes in Office Add-ins
Best practices and rules for the Office
dialog API
Article • 04/04/2023

This article provides rules, gotchas, and best practices for the Office dialog API, including
best practices for designing the UI of a dialog and using the API with in a single-page
application (SPA)

7 Note

This article presupposes that you are familiar with the basics of using the Office
dialog API as described in Use the Office dialog API in your Office Add-ins.

See also Handling errors and events with the Office dialog box.

Rules and gotchas


The dialog box can only navigate to HTTPS URLs, not HTTP.
The URL passed to the displayDialogAsync method must be in the exact same
domain as the add-in itself. It cannot be a subdomain. But the page that is passed
to it can redirect to a page in another domain.
A host page can have only one dialog box open at a time. The host page could be
either a task pane or the function file of a function command.
Only two Office APIs can be called in the dialog box:
The messageParent function.
Office.context.requirements.isSetSupported (For more information, see Specify

Office applications and API requirements.)


The messageParent function should usually be called from a page in the exact
same domain as the add-in itself, but this is not mandatory. For more information,
see Cross-domain messaging to the host runtime.

Best practices

Avoid overusing dialog boxes


Because overlapping UI elements are discouraged, avoid opening a dialog box from a
task pane unless your scenario requires it. When you consider how to use the surface
area of a task pane, note that task panes can be tabbed. For an example of a tabbed
task pane, see the Excel Add-in JavaScript SalesTracker sample.

Design a dialog box UI


For best practices in dialog box design, see Dialog boxes in Office Add-ins.

Handle pop-up blockers with Office on the web


Attempting to display a dialog box while using Office on the web may cause the
browser's pop-up blocker to block the dialog box. If this happens, then Office on the
web will open a prompt similar to the following:

If the user chooses Allow, the Office dialog box opens. If the user chooses Ignore, the
prompt closes and the Office dialog box does not open. Instead, the
displayDialogAsync method returns error 12009. Your code should catch this error and

either provide an alternate experience that does not require a dialog, or display a
message to the user advising that the add-in requires them to allow the dialog. (For
more about 12009, see Errors from displayDialogAsync.)

If, for any reason, you want to turn off this feature, then your code must opt out. It
makes this request with the DialogOptions object that is passed to the
displayDialogAsync method. Specifically, the object should include promptBeforeOpen:
false . When this option is set to false, Office on the web will not prompt the user to

allow the add-in open a dialog, and the Office dialog will not open.

Do not use the _host_info value


Office automatically adds a query parameter called _host_info to the URL that is passed
to displayDialogAsync . It is appended after your custom query parameters, if any. It isn't
appended to any subsequent URLs that the dialog box navigates to. Microsoft may
change the content of this value, or remove it entirely, so your code should not read it.
The same value is added to the dialog box's session storage (that is, the
Window.sessionStorage property). Again, your code should neither read nor write to
this value.

Open another dialog immediately after closing one


You can't have more than one dialog open from a given host page, so your code should
call Dialog.close on an open dialog before it calls displayDialogAsync to open another
dialog. The close method is asynchronous. For this reason, if you call
displayDialogAsync immediately after a call of close , the first dialog may not have

completely closed when Office attempts to open the second. If that happens, Office will
return a 12007 error: "The operation failed because this add-in already has an active
dialog."

The close method doesn't accept a callback parameter, and it doesn't return a Promise
object so it cannot be awaited with either the await keyword or with a then method.
For this reason, we suggest the following technique when you need to open a new
dialog immediately after closing a dialog: encapsulate the code to open the new dialog
in a function and design the function to recursively call itself if the call of
displayDialogAsync returns 12007 . The following is an example.

JavaScript

function openFirstDialog() {
Office.context.ui.displayDialogAsync("https://MyDomain/firstDialog.html",
{ width: 50, height: 50},
(result) => {
if(result.status === Office.AsyncResultStatus.Succeeded) {
const dialog = result.value;
dialog.close();
openSecondDialog();
}
else {
// Handle errors
}
}
);
}

function openSecondDialog() {
Office.context.ui.displayDialogAsync("https://MyDomain/secondDialog.html",
{ width: 50, height: 50},
(result) => {
if(result.status === Office.AsyncResultStatus.Failed) {
if (result.error.code === 12007) {
openSecondDialog(); // Recursive call
}
else {
// Handle other errors
}
}
}
);
}

Alternatively, you could force the code to pause before it tries to open the second
dialog by using the setTimeout method. The following is an example.

JavaScript

function openFirstDialog() {
Office.context.ui.displayDialogAsync("https://MyDomain/firstDialog.html",
{ width: 50, height: 50},
(result) => {
if(result.status === Office.AsyncResultStatus.Succeeded) {
const dialog = result.value;
dialog.close();
setTimeout(() => {

Office.context.ui.displayDialogAsync("https://MyDomain/secondDialog.html", {
width: 50, height: 50},
(result) => { /* callback body */ }
);
}, 1000);
}
else {
// Handle errors
}
}
);
}

Best practices for using the Office dialog API in an SPA


If your add-in uses client-side routing, as single-page applications (SPAs) typically do,
you have the option to pass the URL of a route to the displayDialogAsync method
instead of the URL of a separate HTML page. We recommend against doing so for the
reasons given below.

7 Note

This article isn't relevant to server-side routing, such as in an Express-based web


application.
Problems with SPAs and the Office dialog API
The Office dialog box is in a new window with its own instance of the JavaScript engine,
and hence it's own complete execution context. If you pass a route, your base page and
all its initialization and bootstrapping code run again in this new context, and any
variables are set to their initial values in the dialog box. So this technique downloads
and launches a second instance of your application in the box window, which partially
defeats the purpose of an SPA. In addition, code that changes variables in the dialog box
window does not change the task pane version of the same variables. Similarly, the
dialog box window has its own session storage (the Window.sessionStorage property),
which isn't accessible from code in the task pane. The dialog box and the host page on
which displayDialogAsync was called look like two different clients to your server. (For a
reminder of what a host page is, see Open a dialog box from a host page.)

So, if you passed a route to the displayDialogAsync method, you wouldn't really have
an SPA; you'd have two instances of the same SPA. Moreover, much of the code in the
task pane instance would never be used in that instance and much of the code in the
dialog box instance would never be used in that instance. It would be like having two
SPAs in the same bundle.

Microsoft recommendations
Instead of passing a client-side route to the displayDialogAsync method, we
recommend that you do one of the following:

If the code that you want to run in the dialog box is sufficiently complex, create
two different SPAs explicitly; that is, have two SPAs in different folders of the same
domain. One SPA runs in the dialog box and the other in the dialog box's host
page where displayDialogAsync was called.
In most scenarios, only simple logic is needed in the dialog box. In such cases, your
project will be greatly simplified by hosting a single HTML page, with embedded or
referenced JavaScript, in the domain of your SPA. Pass the URL of the page to the
displayDialogAsync method. While this means that you are deviating from the
literal idea of a single-page app; you don't really have a single instance of an SPA
when you are using the Office dialog API.
Alternative ways of passing messages to
a dialog box from its host page
Article • 04/04/2023

The recommended way to pass data and messages from a parent page to a child dialog
box is with the messageChild method as described in Use the Office dialog API in your
Office Add-ins. If your add-in is running on a platform or host that does not support the
DialogApi 1.2 requirement set, there are two other ways that you can pass information
to the dialog box.

Add query parameters to the URL that is passed to displayDialogAsync .


Store the information somewhere that is accessible to both the host window and
dialog box. The two windows do not share a common session storage (the
Window.sessionStorage property), but if they have the same domain (including
port number, if any), they share a common Local Storage .*

7 Note

* There is a bug that will effect your strategy for token handling. If the add-in is
running in Office on the web in either the Safari or Edge browser, the dialog box
and task pane do not share the same Local Storage, so it cannot be used to
communicate between them.

Use local storage


To use local storage, call the setItem method of the window.localStorage object in the
host page before the displayDialogAsync call, as in the following example.

JavaScript

localStorage.setItem("clientID", "15963ac5-314f-4d9b-b5a1-ccb2f1aea248");

Code in the dialog box reads the item when it's needed, as in the following example.

JavaScript

const clientID = localStorage.getItem("clientID");


// You can also use property syntax:
// const clientID = localStorage.clientID;
Use query parameters
The following example shows how to pass data with a query parameter.

JavaScript

Office.context.ui.displayDialogAsync('https://myAddinDomain/myDialog.html?
clientID=15963ac5-314f-4d9b-b5a1-ccb2f1aea248');

For a sample that uses this technique, see Insert Excel charts using Microsoft Graph in a
PowerPoint add-in .

Code in your dialog box can parse the URL and read the parameter value.

) Important

Office automatically adds a query parameter called _host_info to the URL that is
passed to displayDialogAsync . (It is appended after your custom query parameters,
if any. It is not appended to any subsequent URLs that the dialog box navigates to.)
Microsoft may change the content of this value, or remove it entirely, in the future,
so your code should not read it. The same value is added to the dialog box's
session storage (the Window.sessionStorage property). Again, your code should
neither read nor write to this value.
Handle errors and events in the Office
dialog box
Article • 04/04/2023

This article describes how to trap and handle errors when opening the dialog box and
errors that happen inside the dialog box.

7 Note

This article presupposes that you are familiar with the basics of using the Office
dialog API as described in Use the Office dialog API in your Office Add-ins.

See also Best practices and rules for the Office dialog API.

Your code should handle two categories of events.

Errors returned by the call of displayDialogAsync because the dialog box cannot
be created.
Errors, and other events, in the dialog box.

Errors from displayDialogAsync


In addition to general platform and system errors, four errors are specific to calling
displayDialogAsync .

Code Meaning
number

12004 The domain of the URL passed to displayDialogAsync is not trusted. The domain must
be the same domain as the host page (including protocol and port number).

12005 The URL passed to displayDialogAsync uses the HTTP protocol. HTTPS is required. (In
some versions of Office, the error message text returned with 12005 is the same one
returned for 12004.)

12007 A dialog box is already opened from this host window. A host window, such as a task
pane, can only have one dialog box open at a time.

12009 The user chose to ignore the dialog box. This error can occur in Office on the web,
where users may choose not to allow an add-in to present a dialog box. For more
information, see Handling pop-up blockers with Office on the web.
Code Meaning
number

12011 The add-in is running in Office on the web and the user's browser configuration is
blocking popups. This most commonly happens when the browser is Edge Legacy and
the domain of the add-in is in different security zone from the domain that the dialog
is trying to open. Another scenario which triggers this error is that the browser is Safari
and it's configured to block all popups. Consider responding to this error with a
prompt to the user to change their browser configuration or use a different browser.

When displayDialogAsync is called, it passes an AsyncResult object to its callback


function. When the call is successful, the dialog box is opened, and the value property
of the AsyncResult object is a Dialog object. For an example of this, see Send
information from the dialog box to the host page. When the call to displayDialogAsync
fails, the dialog box is not created, the status property of the AsyncResult object is set
to Office.AsyncResultStatus.Failed , and the error property of the object is populated.
You should always provide a callback that tests the status and responds when it's an
error. For an example that reports the error message regardless of its code number, see
the following code. (The showNotification function, not defined in this article, either
displays or logs the error. For an example of how you can implement this function within
your add-in, see Office Add-in Dialog API Example .)

JavaScript

let dialog;
Office.context.ui.displayDialogAsync('https://myDomain/myDialog.html',
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
showNotification(asyncResult.error.code = ": " +
asyncResult.error.message);
} else {
dialog = asyncResult.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived,
processMessage);
}
});

Errors and events in the dialog box


Three errors and events in the dialog box will raise a DialogEventReceived event in the
host page. For a reminder of what a host page is, see Open a dialog box from a host
page.
Code Meaning
number

12002 One of the following:


- No page exists at the URL that was passed to displayDialogAsync .
- The page that was passed to displayDialogAsync loaded, but the dialog box was then
redirected to a page that it cannot find or load, or it has been directed to a URL with
invalid syntax.

12003 The dialog box was directed to a URL with the HTTP protocol. HTTPS is required.

12006 The dialog box was closed, usually because the user chose the Close button X.

Your code can assign a handler for the DialogEventReceived event in the call to
displayDialogAsync . The following is a simple example.

JavaScript

let dialog;
Office.context.ui.displayDialogAsync('https://myDomain/myDialog.html',
function (result) {
dialog = result.value;
dialog.addEventHandler(Office.EventType.DialogEventReceived,
processDialogEvent);
}
);

For an example of a handler for the DialogEventReceived event that creates custom
error messages for each error code, see the following example.

JavaScript

function processDialogEvent(arg) {
switch (arg.error) {
case 12002:
showNotification("The dialog box has been directed to a page
that it cannot find or load, or the URL syntax is invalid.");
break;
case 12003:
showNotification("The dialog box has been directed to a URL with
the HTTP protocol. HTTPS is required."); break;
case 12006:
showNotification("Dialog closed.");
break;
default:
showNotification("Unknown error in dialog box.");
break;
}
}
See also
For a sample add-in that handles errors in this way, see Office Add-in Dialog API
Example .
Use the Office dialog box to show a
video
Article • 04/04/2023

This article explains how to play a video in an Office Add-in dialog box.

7 Note

This article presumes you're familiar with the basics of using the Office dialog box
as described in Use the Office dialog API in your Office Add-ins.

To play a video in a dialog box with the Office dialog API, follow these steps.

1. Create a page containing an iframe and no other content. The page must be in the
same domain as the host page. For a reminder of what a host page is, see Open a
dialog box from a host page. In the src attribute of the iframe, point to the URL of
an online video. The protocol of the video's URL must be HTTPS. In this article,
we'll call this page "video.dialogbox.html". The following is an example of the
markup.

HTML

<iframe class="ms-firstrun-video__player" width="640" height="360"


src="https://www.youtube.com/embed/XVfOe5mFbAE?rel=0&autoplay=1"
frameborder="0" allowfullscreen>
</iframe>

2. Use a call of displayDialogAsync in the host page to open video.dialogbox.html.

3. If your add-in needs to know when the user closes the dialog box, register a
handler for the DialogEventReceived event and handle the 12006 event. For details,
see Errors and events in the Office dialog box.

For a sample of a video playing in a dialog box, see the video placemat design pattern.
Task panes in Office Add-ins
Article • 04/04/2023

Task panes are interface surfaces that typically appear on the right side of the window
within Word, PowerPoint, Excel, and Outlook. Task panes give users access to interface
controls that run code to modify documents or emails, or display data from a data
source. Use task panes when you don't need to embed functionality directly into the
document.

Figure 1. Typical task pane layout

Best practices
Do Don't

Include the name of your add-in in the title. Don't append your company name to
the title.
Do Don't

Use short descriptive names in the title. Don't append strings such as "add-in,"
"for Word," or "for Office" to the title of
your add-in.

Include some navigational or commanding element None.


such as the CommandBar or Pivot at the top of your
add-in.

Include a branding element such as the BrandBar at the None.


bottom of your add-in unless your add-in is to be used
within Outlook.

Variants
The following images show the various task pane sizes with the Office app ribbon at a
1366x768 resolution. For Excel, additional vertical space is required to accommodate the
formula bar.

Figure 2. Office 2016 desktop task pane sizes

Excel - 320x455 pixels


PowerPoint - 320x531 pixels
Word - 320x531 pixels
Outlook - 348x535 pixels

Figure 3. Office task pane sizes


Excel - 350x378 pixels
PowerPoint - 348x391 pixels
Word - 329x445 pixels
Outlook (on the web) - 320x570 pixels

Personality menu
Personality menus can obstruct navigational and commanding elements located near
the top right of the add-in. The following are the current dimensions of the personality
menu on Windows and Mac. (The personality menu isn't supported in Outlook.)

For Windows, the personality menu measures 12x32 pixels, as shown.

Figure 4. Personality menu on Windows

For Mac, the personality menu measures 26x26 pixels, but floats 8 pixels in from the
right and 6 pixels from the top, which increases the space to 34x32 pixels, as shown.

Figure 5. Personality menu on Mac


Implementation
For a sample that implements a task pane, see Excel Add-in JS WoodGrove Expense
Trends on GitHub.

See also
Fabric Core in Office Add-ins
UX design patterns for Office Add-ins
Add custom keyboard shortcuts to your
Office Add-ins
Article • 03/14/2023

Keyboard shortcuts, also known as key combinations, enable your add-in's users to work
more efficiently. Keyboard shortcuts also improve the add-in's accessibility for users with
disabilities by providing an alternative to the mouse.

) Important

Keyboard shortcuts are currently only supported on Excel and only on these
platforms and builds:

Excel on Windows: Version 2102 (Build 13801.20632) and later


Excel on Mac: 16.48 and later
Excel on the web

7 Note

Keyboard shortcuts work only on platforms that support the following requirement
sets. For more about requirement sets and how to work with them, see Specify
Office applications and API requirements.

SharedRuntime 1.1
Also needed if the add-in enables the user to customize keyboard shortcuts:
KeyboardShortcuts 1.1

7 Note

To start with a working version of an add-in with keyboard shortcuts already


enabled, clone and run the sample Excel Keyboard Shortcuts . When you are
ready to add keyboard shortcuts to your own add-in, continue with this article.

There are three steps to add keyboard shortcuts to an add-in.

1. Configure the add-in's manifest.


2. Create or edit the shortcuts JSON file to define actions and their keyboard
shortcuts.
3. Add one or more runtime calls of the Office.actions.associate API to map a
function to each action.

Configure the manifest


There are two small changes to the manifest to make. One is to enable the add-in to use
a shared runtime and the other is to point to a JSON-formatted file where you defined
the keyboard shortcuts.

Configure the add-in to use a shared runtime


Adding custom keyboard shortcuts requires your add-in to use the shared runtime. For
more information, Configure an add-in to use a shared runtime.

Link the mapping file to the manifest


Immediately below (not inside) the <VersionOverrides> element in the manifest, add an
ExtendedOverrides element. Set the Url attribute to the full URL of a JSON file in your
project that you will create in a later step.

XML

...
</VersionOverrides>
<ExtendedOverrides Url="https://contoso.com/addin/shortcuts.json">
</ExtendedOverrides>
</OfficeApp>

Create or edit the shortcuts JSON file


Create a JSON file in your project. Be sure the path of the file matches the location you
specified for the Url attribute of the ExtendedOverrides element. This file will describe
your keyboard shortcuts, and the actions that they will invoke.

1. Inside the JSON file, there are two arrays. The actions array will contain objects that
define the actions to be invoked and the shortcuts array will contain objects that
map key combinations onto actions. Here is an example.

JSON

{
"actions": [
{
"id": "SHOWTASKPANE",
"type": "ExecuteFunction",
"name": "Show task pane for add-in"
},
{
"id": "HIDETASKPANE",
"type": "ExecuteFunction",
"name": "Hide task pane for add-in"
}
],
"shortcuts": [
{
"action": "SHOWTASKPANE",
"key": {
"default": "Ctrl+Alt+Up"
}
},
{
"action": "HIDETASKPANE",
"key": {
"default": "Ctrl+Alt+Down"
}
}
]
}

For more information about the JSON objects, see Construct the action objects
and Construct the shortcut objects. The complete schema for the shortcuts JSON is
at extended-manifest.schema.json.

7 Note

You can use "CONTROL" in place of "Ctrl" throughout this article.

In a later step, the actions will themselves be mapped to functions that you write.
In this example, you will later map SHOWTASKPANE to a function that calls the
Office.addin.showAsTaskpane method and HIDETASKPANE to a function that calls
the Office.addin.hide method.

Create a mapping of actions to their functions


1. In your project, open the JavaScript file loaded by your HTML page in the
<FunctionFile> element.

2. In the JavaScript file, use the Office.actions.associate API to map each action that
you specified in the JSON file to a JavaScript function. Add the following JavaScript
to the file. Note the following about the code.

The first parameter is one of the actions from the JSON file.
The second parameter is the function that runs when a user presses the key
combination that is mapped to the action in the JSON file.

JavaScript

Office.actions.associate('-- action ID goes here--', function () {

});

3. To continue the example, use 'SHOWTASKPANE' as the first parameter.

4. For the body of the function, use the Office.addin.showAsTaskpane method to


open the add-in's task pane. When you are done, the code should look like the
following:

JavaScript

Office.actions.associate('SHOWTASKPANE', function () {
return Office.addin.showAsTaskpane()
.then(function () {
return;
})
.catch(function (error) {
return error.code;
});
});

5. Add a second call of Office.actions.associate function to map the HIDETASKPANE


action to a function that calls Office.addin.hide. The following is an example.

JavaScript

Office.actions.associate('HIDETASKPANE', function () {
return Office.addin.hide()
.then(function () {
return;
})
.catch(function (error) {
return error.code;
});
});

Following the previous steps lets your add-in toggle the visibility of the task pane by
pressing Ctrl+Alt+Up and Ctrl+Alt+Down. The same behavior is shown in the Excel
keyboard shortcuts sample in the Office Add-ins PnP repo in GitHub.

Details and restrictions

Construct the action objects


Use the following guidelines when specifying the objects in the actions array of the
shortcuts.json.

The property names id and name are mandatory.


The id property is used to uniquely identify the action to invoke using a keyboard
shortcut.
The name property must be a user friendly string describing the action. It must be a
combination of the characters A - Z, a - z, 0 - 9, and the punctuation marks "-", "_",
and "+".
The type property is optional. Currently only ExecuteFunction type is supported.

The following is an example.

JSON

"actions": [
{
"id": "SHOWTASKPANE",
"type": "ExecuteFunction",
"name": "Show task pane for add-in"
},
{
"id": "HIDETASKPANE",
"type": "ExecuteFunction",
"name": "Hide task pane for add-in"
}
]

The complete schema for the shortcuts JSON is at extended-manifest.schema.json.

Construct the shortcut objects


Use the following guidelines when specifying the objects in the shortcuts array of the
shortcuts.json.

The property names action , key , and default are required.


The value of the action property is a string and must match one of the id
properties in the action object.
The default property can be any combination of the characters A - Z, a -z, 0 - 9,
and the punctuation marks "-", "_", and "+". (By convention, lower case letters are
not used in these properties.)
The default property must contain the name of at least one modifier key (Alt, Ctrl,
Shift) and only one other key.
Shift cannot be used as the only modifier key. Combine Shift with either Alt or Ctrl.
For Macs, we also support the Command modifier key.
For Macs, Alt is mapped to the Option key. For Windows, Command is mapped to
the Ctrl key.
When two characters are linked to the same physical key in a standard keyboard,
then they are synonyms in the default property; for example, Alt+a and Alt+A are
the same shortcut, so are Ctrl+- and Ctrl+_ because "-" and "_" are the same
physical key.
The "+" character indicates that the keys on either side of it are pressed
simultaneously.

The following is an example.

JSON

"shortcuts": [
{
"action": "SHOWTASKPANE",
"key": {
"default": "Ctrl+Alt+Up"
}
},
{
"action": "HIDETASKPANE",
"key": {
"default": "Ctrl+Alt+Down"
}
}
]

The complete schema for the shortcuts JSON is at extended-manifest.schema.json.

7 Note

KeyTips, also known as sequential key shortcuts, such as the Excel shortcut to
choose a fill color Alt+H, H, are not supported in Office Add-ins.
Avoid key combinations in use by other add-ins
There are many keyboard shortcuts that are already in use by Office. Avoid registering
keyboard shortcuts for your add-in that are already in use, however there may be some
instances where it's necessary to override existing keyboard shortcuts or handle conflicts
between multiple add-ins that have registered the same keyboard shortcut.

In the case of a conflict, the user will see a dialog box the first time they attempt to use
a conflicting keyboard shortcut. Note that the text for the add-in option that is
displayed in this dialog comes from the name property in the action object in
shortcuts.json file.

The user can select which action the keyboard shortcut will take. After making the
selection, the preference is saved for future uses of the same shortcut. The shortcut
preferences are saved per user, per platform. If the user wishes to change their
preferences, they can invoke the Reset Office Add-ins shortcut preferences command
from the Tell me search box. Invoking the command clears all of the user's add-in
shortcut preferences and the user will again be prompted with the conflict dialog box
the next time they attempt to use a conflicting shortcut.
For the best user experience, we recommend that you minimize conflicts with Excel with
these good practices.

Use only keyboard shortcuts with the following pattern: Ctrl+Shift+Alt+x, where x
is some other key.
If you need more keyboard shortcuts, check the list of Excel keyboard shortcuts ,
and avoid using any of them in your add-in.
When the keyboard focus is inside the add-in UI, Ctrl+Spacebar and
Ctrl+Shift+F10 will not work as these are essential accessibility shortcuts.
On a Windows or Mac computer, if the "Reset Office Add-ins shortcut preferences"
command is not available on the search menu, the user can manually add the
command to the ribbon by customizing the ribbon through the context menu.

Customize the keyboard shortcuts per platform


It's possible to customize shortcuts to be platform-specific. The following is an example
of the shortcuts object that customizes the shortcuts for each of the following
platforms: windows , mac , web . Note that you must still have a default shortcut key for
each shortcut.

In the following example, the default key is the fallback key for any platform that is not
specified. The only platform not specified is Windows, so the default key will only apply
to Windows.

JSON

"shortcuts": [
{
"action": "SHOWTASKPANE",
"key": {
"default": "Ctrl+Alt+Up",
"mac": "Command+Shift+Up",
"web": "Ctrl+Alt+1",
}
},
{
"action": "HIDETASKPANE",
"key": {
"default": "Ctrl+Alt+Down",
"mac": "Command+Shift+Down",
"web": "Ctrl+Alt+2"
}
}
]

Localize the keyboard shortcuts JSON


If your add-in supports multiple locales, you'll need to localize the name property of the
action objects. Also, if any of the locales that the add-in supports have different
alphabets or writing systems, and hence different keyboards, you may need to localize
the shortcuts also. For information about how to localize the keyboard shortcuts JSON,
see Localize extended overrides.

Browser shortcuts that cannot be overridden


When using custom keyboard shortcuts on the web, some keyboard shortcuts that are
used by the browser cannot be overridden by add-ins. This list is a work in progress. If
you discover other combinations that cannot be overridden, please let us know by using
the feedback tool at the bottom of this page.

Ctrl+N
Ctrl+Shift+N
Ctrl+T
Ctrl+Shift+T
Ctrl+W
Ctrl+PgUp/PgDn

Enable custom keyboard shortcuts for specific


users
Your add-in can enable users to reassign the actions of the add-in to alternate keyboard
combinations.

7 Note

The APIs described in this section require the KeyboardShortcuts 1.1 requirement
set.

Use the Office.actions.replaceShortcuts method to assign a user's custom keyboard


combinations to your add-ins actions. The method takes a parameter of type
{[actionId:string]: string|null} , where the actionId s are a subset of the action IDs

that must be defined in the add-in's extended manifest JSON. The values are the user's
preferred key combinations. The value can also be null , which will remove any
customization for that actionId and revert back to the default keyboard combination
that is defined in the add-in's extended manifest JSON.

If the user is logged into Office, the custom combinations are saved in the user's
roaming settings per platform. Customizing shortcuts are currently not supported for
anonymous users.

JavaScript

const userCustomShortcuts = {
SHOWTASKPANE:"CTRL+SHIFT+1",
HIDETASKPANE:"CTRL+SHIFT+2"
};
Office.actions.replaceShortcuts(userCustomShortcuts)
.then(function () {
console.log("Successfully registered.");
})
.catch(function (ex) {
if (ex.code == "InvalidOperation") {
console.log("ActionId does not exist or shortcut combination is
invalid.");
}
});

To find out what shortcuts are already in use for the user, call the
Office.actions.getShortcuts method. This method returns an object of type
[actionId:string]:string|null} , where the values represent the current keyboard
combination the user must use to invoke the specified action. The values can come from
three different sources:
If there was a conflict with the shortcut and the user has chosen to use a different
action (either native or another add-in) for that keyboard combination, the value
returned will be null since the shortcut has been overridden and there is no
keyboard combination the user can currently use to invoke that add-in action.
If the shortcut has been customized using the Office.actions.replaceShortcuts
method, the value returned will be the customized keyboard combination.
If the shortcut has not been overridden or customized, it will return the value from
the add-in's extended manifest JSON.

The following is an example.

JavaScript

Office.actions.getShortcuts()
.then(function (userShortcuts) {
for (const action in userShortcuts) {
let shortcut = userShortcuts[action];
console.log(action + ": " + shortcut);
}
});

As described in Avoid key combinations in use by other add-ins, it is a good practice to


avoid conflicts in shortcuts. To discover if one or more key combinations are already in
use pass them as an array of strings to the Office.actions.areShortcutsInUse method. The
method returns a report containing key combinations that are already in use in the form
of an array of objects of type {shortcut: string, inUse: boolean} . The shortcut
property is a key combination, such as "CTRL+SHIFT+1". If the combination is already
registered to another action, the inUse property is set to true . For example,
[{shortcut: "CTRL+SHIFT+1", inUse: true}, {shortcut: "CTRL+SHIFT+2", inUse:

false}] . The following code snippet is an example:

JavaScript

const shortcuts = ["CTRL+SHIFT+1", "CTRL+SHIFT+2"];


Office.actions.areShortcutsInUse(shortcuts)
.then(function (inUseArray) {
const availableShortcuts = inUseArray.filter(function (shortcut) {
return !shortcut.inUse; });
console.log(availableShortcuts);
const usedShortcuts = inUseArray.filter(function (shortcut) { return
shortcut.inUse; });
console.log(usedShortcuts);
});
Next Steps
See the Excel keyboard shortcuts sample add-in.
Get an overview of working with extended overrides in Work with extended
overrides of the manifest.
Show or hide the task pane of your
Office Add-in
Article • 04/04/2023

) Important

The shared runtime is only supported in some Office applications. For more
information, see Shared runtime requirement sets.

You can show the task pane of your Office Add-in by calling the
Office.addin.showAsTaskpane() method.

JavaScript

function onCurrentQuarter() {
Office.addin.showAsTaskpane()
.then(function() {
// Code that enables task pane UI elements for
// working with the current quarter.
});
}

The previous code assumes a scenario where there is an Excel worksheet named
CurrentQuarterSales. The add-in will make the task pane visible whenever this
worksheet is activated. The method onCurrentQuarter is a handler for the
Office.Worksheet.onActivated event which has been registered for the worksheet.

You can also hide the task pane by calling the Office.addin.hide() method.

JavaScript

function onCurrentQuarterDeactivated() {
Office.addin.hide();
}

The previous code is a handler that is registered for the Office.Worksheet.onDeactivated


event.

Additional details on showing the task pane


When you call Office.addin.showAsTaskpane() , Office will display in a task pane the file
that you assigned as the resource ID ( resid ) value of the task pane. This resid value
can be assigned or changed by opening your manifest.xml file and locating
<SourceLocation> inside the <Action xsi:type="ShowTaskpane"> element. (See
Configure your Office Add-in to use a shared runtime for additional details.)

Since Office.addin.showAsTaskpane() is an asynchronous method, your code will


continue running until the method is complete. Wait for this completion with either the
await keyword or a then() method, depending on which JavaScript syntax you are

using.

Configure your add-in to use the shared


runtime
To use the showAsTaskpane() and hide() methods, your add-in must use the shared
runtime. For more information, see Configure your Office Add-in to use a shared
runtime.

Preservation of state and event listeners


The hide() and showAsTaskpane() methods only change the visibility of the task pane.
They do not unload or reload it (or reinitialize its state).

Consider the following scenario: A task pane is designed with tabs. The Home tab is
open when the add-in is first launched. Suppose a user opens the Settings tab and,
later, code in the task pane calls hide() in response to some event. Still later code calls
showAsTaskpane() in response to another event. The task pane will reappear, and the
Settings tab is still selected.
In addition, any event listeners that are registered in the task pane continue to run even
when the task pane is hidden.

Consider the following scenario: The task pane has a registered handler for the Excel
Worksheet.onActivated and Worksheet.onDeactivated events for a sheet named Sheet1.

The activated handler causes a green dot to appear in the task pane. The deactivated
handler turns the dot red (which is its default state). Suppose then that code calls
hide() when Sheet1 is not activated and the dot is red. While the task pane is hidden,

Sheet1 is activated. Later code calls showAsTaskpane() in response to some event. When
the task pane opens, the dot is green because the event listeners and handlers ran even
though the task pane was hidden.

Handle the visibility changed event


When your code changes the visibility of the task pane with showAsTaskpane() or
hide() , Office triggers the VisibilityModeChanged event. It can be useful to handle this
event. For example, suppose the task pane displays a list of all the sheets in a workbook.
If a new worksheet is added while the task pane is hidden, making the task pane visible
would not, in itself, add the new worksheet name to the list. But your code can respond
to the VisibilityModeChanged event to reload the Worksheet.name property of all the
worksheets in the Workbook.worksheets collection as shown in the example code below.
To register a handler for the event, you do not use an "add handler" method as you
would in most Office JavaScript contexts. Instead, there is a special function to which
you pass your handler: Office.addin.onVisibilityModeChanged. The following is an
example. Note that the args.visibilityMode property is type VisibilityMode.

JavaScript

Office.addin.onVisibilityModeChanged(function(args) {
if (args.visibilityMode == "Taskpane") {
// Code that runs whenever the task pane is made visible.
// For example, an Excel.run() that loads the names of
// all worksheets and passes them to the task pane UI.
}
});

The function returns another function that deregisters the handler. Here is a simple, but
not robust, example.

JavaScript

const removeVisibilityModeHandler =
Office.addin.onVisibilityModeChanged(function(args) {
if (args.visibilityMode == "Taskpane") {
// Code that runs whenever the task pane is made visible.
}
});

// In some later code path, deregister with:


removeVisibilityModeHandler();

The onVisibilityModeChanged method is asynchronous and returns a promise, which


means that your code needs to await the fulfillment of the promise before it can call the
deregister handler.

JavaScript

// await the promise from onVisibilityModeChanged and assign


// the returned deregister handler to removeVisibilityModeHandler.
const removeVisibilityModeHandler =
await Office.addin.onVisibilityModeChanged(function(args) {
if (args.visibilityMode == "Taskpane") {
// Code that runs whenever the task pane is made visible.
}
});
The deregister function is also asynchronous and returns a promise. So, if you have code
that should not run until after the deregistration is complete, then you should await the
promise returned by the deregister function.

JavaScript

// await the promise from the deregister handler before continuing


await removeVisibilityModeHandler();
// subsequent code here

See also
Configure your Office Add-in to use a shared runtime
Run code in your Office Add-in when the document opens
Localization for Office Add-ins
Article • 07/27/2023

You can implement any localization scheme that's appropriate for your Office Add-in.
The JavaScript API and manifest schema of the Office Add-ins platform provide some
choices. You can use the Office JavaScript API to determine a locale and display strings
based on the locale of the Office application, or to interpret or display data based on
the locale of the data. You can use the manifest to specify locale-specific add-in file
location and descriptive information. Alternatively, you can use Visual Studio and
Microsoft Ajax script to support globalization and localization.

Use the JavaScript API to determine locale-


specific strings
The Office JavaScript API provides two properties that support displaying or interpreting
values consistent with the locale of the Office application and data.

Context.displayLanguage specifies the locale (or language) of the user interface of


the Office application. The following example verifies if the Office application uses
the en-US or fr-FR locale, and displays a locale-specific greeting.

JavaScript

function sayHelloWithDisplayLanguage() {
const myLanguage = Office.context.displayLanguage;
switch (myLanguage) {
case 'en-US':
write('Hello!');
break;
case 'fr-FR':
write('Bonjour!');
break;
}
}

// Function that writes to a div with id='message' on the page.


function write(message) {
document.getElementById('message').innerText += message;
}

Context.contentLanguage specifies the locale (or language) of the data. Extending


the last code sample, instead of checking the displayLanguage property, assign
myLanguage the value of the contentLanguage property, and use the rest of the

same code to display a greeting based on the locale of the data.

JavaScript

const myLanguage = Office.context.contentLanguage;

Control localization from the manifest


The techniques for localizing with the manifest differ depending on whether you are
using the XML manifest or the unified manifest for Microsoft 365 (preview) which is
supported only on Outlook for Windows.

XML Manifest

Every Office Add-in specifies a DefaultLocale element and a locale in its manifest. By
default, the Office Add-in platform and Office client applications apply the values of
the Description, DisplayName, IconUrl, HighResolutionIconUrl, and SourceLocation
elements to all locales. You can optionally support specific values for specific
locales, by specifying an Override child element for each additional locale, for any
of these five elements. The value for the DefaultLocale element and for the Locale
attribute of the Override element is specified according to RFC 3066 , "Tags for the
Identification of Languages." Table 1 describes the localizing support for these
elements.

Table 1. Localization support

Element Localization support

Description Users in each locale you specify can see a localized


description for the add-in in AppSource (or private
catalog).
For Outlook add-ins, users can see the description in the
Exchange Admin Center (EAC) after installation.

DisplayName Users in each locale you specify can see a localized


description for the add-in in AppSource (or private
catalog).
For Outlook add-ins, users can see the display name as a
label for the Outlook add-in button and in the EAC after
installation.
For content and task pane add-ins, users can see the
display name on the ribbon after installing the add-in.
Element Localization support

IconUrl The icon image is optional. You can use the same
override technique to specify a certain image for a
specific culture. If you use and localize an icon, users in
each locale you specify can see a localized icon image for
the add-in.
For Outlook add-ins, users can see the icon in the EAC
after installing the add-in.
For content and task pane add-ins, users can see the
icon on the ribbon after installing the add-in.

HighResolutionIconUrl The high resolution icon image is optional but if it is


Important: This element is specified, it must occur after the IconUrl element. When
available only when using add-in HighResolutionIconUrl is specified, and the add-in is
manifest version 1.1. installed on a device that supports high dpi resolution,
the HighResolutionIconUrl value is used instead of the
value for IconUrl.
You can use the same override technique to specify a
certain image for a specific culture. If you use and
localize an icon, users in each locale you specify can see
a localized icon image for the add-in.
For Outlook add-ins, users can see the icon in the EAC
after installing the add-in.
For content and task pane add-ins, users can see the
icon on the ribbon after installing the add-in.

Resources Important: This Users in each locale you specify can see string and icon
element is available only when resources that you specifically create for the add-in for
using add-in manifest version that locale.
1.1.

SourceLocation Users in each locale you specify can see a webpage that
you specifically design for the add-in for that locale.

7 Note

You can localize the description and display name for only the locales that
Office supports. See Overview of deploying languages for Microsoft 365
Apps for a list of languages and locales for the current release of Office.

Examples
For example, an Office Add-in can specify the DefaultLocale as en-us . For the
DisplayName element, the add-in can specify an Override child element for the
locale fr-fr , as shown below.
XML

<DefaultLocale>en-us</DefaultLocale>
...
<DisplayName DefaultValue="Video player">
<Override Locale="fr-fr" Value="Lecteur vidéo" />
</DisplayName>

7 Note

If you need to localize for more than one area within a language family, such
as de-de and de-at , we recommend that you use separate Override elements
for each area. Using the language name alone, in this case, de , is not
supported across all combinations of Office client applications and platforms.

This means that the add-in assumes the en-us locale by default. Users see the
English display name of "Video player" for all locales unless the client computer's
locale is fr-fr , in which case users would see the French display name "Lecteur
vidéo".

7 Note

You may only specify a single override per language, including for the default
locale. For example, if your default locale is en-us you cannot not specify an
override for en-us as well.

The following example applies a locale override for the Description element. It first
specifies a default locale of en-us and an English description, and then specifies an
Override statement with a French description for the fr-fr locale.

XML

<DefaultLocale>en-us</DefaultLocale>
...
<Description DefaultValue=
"Watch YouTube videos referenced in the emails you receive
without leaving your email client.">
<Override Locale="fr-fr" Value=
"Visualisez les vidéos YouTube référencées dans vos courriers
électronique directement depuis Outlook."/>
</Description>
This means that the add-in assumes the en-us locale by default. Users would see
the English description in the DefaultValue attribute for all locales unless the client
computer's locale is fr-fr , in which case they would see the French description.

In the following example, the add-in specifies a separate image that's more
appropriate for the fr-fr locale and culture. Users see the image DefaultLogo.png
by default, except when the locale of the client computer is fr-fr . In this case,
users would see the image FrenchLogo.png.

XML

<!-- Replace "domain" with a real web server name and path. -->
<IconUrl DefaultValue="https://<domain>/DefaultLogo.png"/>
<Override Locale="fr-fr" Value="https://<domain>/FrenchLogo.png"/>

The following example shows how to localize a resource in the Resources section. It
applies a locale override for an image that is more appropriate for the ja-jp
culture.

XML

<Resources>
<bt:Images>
<bt:Image id="icon1_16x16"
DefaultValue="https://www.contoso.com/icon_default.png">
<bt:Override Locale="ja-jp" Value="https://www.contoso.com/ja-
jp16-icon_default.png" />
</bt:Image>
...

For the SourceLocation element, supporting additional locales means providing a


separate source HTML file for each of the specified locales. Users in each locale you
specify can see a customized webpage that you design for that them.

For Outlook add-ins, the SourceLocation element also aligns to the form factor. This
allows you to provide a separate, localized source HTML file for each corresponding
form factor. You can specify one or more Override child elements in each applicable
settings element (DesktopSettings, TabletSettings, or PhoneSettings). The following
example shows settings elements for the desktop, tablet, and smartphone form
factors, each with one HTML file for the default locale and another for the French
locale.

XML
<DesktopSettings>
<SourceLocation DefaultValue="https://contoso.com/Desktop.html">
<Override Locale="fr-fr"
Value="https://contoso.com/fr/Desktop.html" />
</SourceLocation>
<RequestedHeight>250</RequestedHeight>
</DesktopSettings>
<TabletSettings>
<SourceLocation DefaultValue="https://contoso.com/Tablet.html">
<Override Locale="fr-fr"
Value="https://contoso.com/fr/Tablet.html" />
</SourceLocation>
<RequestedHeight>200</RequestedHeight>
</TabletSettings>
<PhoneSettings>
<SourceLocation DefaultValue="https://contoso.com/Mobile.html">
<Override Locale="fr-fr"
Value="https://contoso.com/fr/Mobile.html" />
</SourceLocation>
</PhoneSettings>

Localize extended overrides

7 Note

This section is not applicable if you are using the unified manifest (preview).

Some extensibility features of Office Add-ins, such as keyboard shortcuts, are configured
with JSON files that are hosted on your server, instead of with the add-in's XML
manifest. This section assumes that you're familiar with extended overrides. See Work
with extended overrides of the manifest and ExtendedOverrides element.

Use the ResourceUrl attribute of the ExtendedOverrides element to point Office to a file
of localized resources. The following is an example.

XML

...
</VersionOverrides>
<ExtendedOverrides Url="https://contoso.com/addin/extended-
overrides.json"
ResourceUrl="https://contoso.com/addin/my-
resources.json">
</ExtendedOverrides>
</OfficeApp>
The extended overrides file then uses tokens instead of strings. The tokens name strings
in the resource file. The following is an example that assigns a keyboard shortcut to a
function (defined elsewhere) that displays the add-in's task pane. Note about this
markup:

The example isn't quite valid. (We add a required additional property to it below.)
The tokens must have the format ${resource.name-of-resource}.

JSON

{
"actions": [
{
"id": "SHOWTASKPANE",
"type": "ExecuteFunction",
"name": "${resource.SHOWTASKPANE_action_name}"
}
],
"shortcuts": [
{
"action": "SHOWTASKPANE",
"key": {
"default": "${resource.SHOWTASKPANE_default_shortcut}"
}
}
]
}

The resource file, which is also JSON-formatted, has a top-level resources property that
is divided into subproperties by locale. For each locale, a string is assigned to each
token that was used in the extended overrides file. The following is an example which
has strings for en-us and fr-fr . In this example, the keyboard shortcut is the same in
both locales, but that won't always be the case, especially when you are localizing for
locales that have a different alphabet or writing system, and hence a different keyboard.

JSON

{
"resources":{
"en-us": {
"SHOWTASKPANE_default_shortcut": {
"value": "CTRL+SHIFT+A",
},
"SHOWTASKPANE_action_name": {
"value": "Show task pane for add-in",
},
},
"fr-fr": {
"SHOWTASKPANE_default_shortcut": {
"value": "CTRL+SHIFT+A",
},
"SHOWTASKPANE_action_name": {
"value": "Afficher le volet de tâche pour add-in",
}
}
}
}

There is no default property in the file that is a peer to the en-us and fr-fr sections.
This is because the default strings, which are used when the locale of the Office host
application doesn't match any of the ll-cc properties in the resources file, must be
defined in the extended overrides file itself. Defining the default strings directly in the
extended overrides file ensures that Office doesn't download the resource file when the
locale of the Office application matches the default locale of the add-in (as specified in
the manifest). The following is a corrected version of the preceding example of an
extended overrides file that uses resource tokens.

JSON

{
"actions": [
{
"id": "SHOWTASKPANE",
"type": "ExecuteFunction",
"name": "${resource.SHOWTASKPANE_action_name}"
}
],
"shortcuts": [
{
"action": "SHOWTASKPANE",
"key": {
"default": "${resource.SHOWTASKPANE_default_shortcut}"
}
}
],
"resources": {
"default": {
"SHOWTASKPANE_default_shortcut": {
"value": "CTRL+SHIFT+A",
},
"SHOWTASKPANE_action_name": {
"value": "Show task pane for add-in",
}
}
}
}
Match date/time format with client locale
You can get the locale of the user interface of the Office client application by using the
displayLanguage property. You can then display date and time values in a format
consistent with the current locale of the Office application. One way to do that is to
prepare a resource file that specifies the date/time display format to use for each locale
that your Office Add-in supports. At run time, your add-in can use the resource file and
match the appropriate date/time format with the locale obtained from the
displayLanguage property.

You can get the locale of the data of the Office client application by using the
contentLanguage property. Based on this value, you can then appropriately interpret or
display date/time strings. For example, the jp-JP locale expresses data/time values as
yyyy/MM/dd , and the fr-FR locale, dd/MM/yyyy .

Use Visual Studio to create a localized and


globalized add-in
If you use Visual Studio to create Office Add-ins, the .NET Framework and Ajax provide
ways to globalize and localize client script files.

You can globalize and use the Date and Number JavaScript type extensions and the
JavaScript Date object in the JavaScript code for an Office Add-in to display values
based on the locale settings on the current browser. For more information, see
Walkthrough: Globalizing a Date by Using Client Script.

You can include localized resource strings directly in standalone JavaScript files to
provide client script files for different locales, which are set on the browser or provided
by the user. Create a separate script file for each supported locale. In each script file,
include an object in JSON format that contains the resource strings for that locale. The
localized values are applied when the script runs in the browser.

Example: Build a localized Office Add-in


This section provides examples that show you how to localize an Office Add-in
description, display name, and UI.

7 Note
To download Visual Studio, see the Visual Studio IDE page . During installation
you'll need to select the Office/SharePoint development workload.

Configure Office to use additional languages for display or editing

To run the sample code provided, configure Office on your computer to use additional
languages so that you can test your add-in by switching the language used for display
in menus and commands, for editing and proofing, or both.

You can use an Office Language pack to install an additional language. For more
information about Language Packs and where to get them, see Language Accessory
Pack for Office .

After you install the Language Accessory Pack, you can configure Office to use the
installed language for display in the UI, for editing document content, or both. The
example in this article uses an installation of Office that has the Spanish Language Pack
applied.

Create an Office Add-in project


You'll need to create a Visual Studio Office Add-in project.

7 Note

If you haven't installed Visual Studio, see the Visual Studio IDE page for
download instructions. During installation you'll need to select the
Office/SharePoint development workload. If you have previously installed Visual
Studio 2019 or later, use the Visual Studio Installer to ensure that the
Office/SharePoint development workload is installed.

1. Choose Create a new project.

2. Using the search box, enter add-in. Choose Word Web Add-in, then select Next.

3. Name your project WorldReadyAddIn and select Create.

4. Visual Studio creates a solution and its two projects appear in Solution Explorer.
The Home.html file opens in Visual Studio.

Localize the text used in your add-in


The text that you want to localize for another language appears in two areas.

Add-in display name and description. This is controlled by entries in the add-in
manifest file.

Add-in UI. You can localize the strings that appear in your add-in UI by using
JavaScript code, for example, by using a separate resource file that contains the
localized strings.

Localize the add-in display name and description

1. In Solution Explorer, expand WorldReadyAddIn, WorldReadyAddInManifest, and


then choose WorldReadyAddIn.xml.

2. In WorldReadyAddInManifest.xml, replace the DisplayName and Description


elements with the following block of code.

7 Note

You can replace the Spanish language localized strings used in this example
for the DisplayName and Description elements with the localized strings for
any other language.

XML

<DisplayName DefaultValue="World Ready add-in">


<Override Locale="es-es" Value="Aplicación de uso internacional"/>
</DisplayName>
<Description DefaultValue="An add-in for testing localization">
<Override Locale="es-es" Value="Una aplicación para la prueba de la
localización"/>
</Description>

3. When you change the display language for Microsoft 365 from English to Spanish,
for example, and then run the add-in, the add-in display name and description are
shown with localized text.

Lay out the add-in UI

1. In Visual Studio, in Solution Explorer, choose Home.html.

2. Replace the <body> element contents in Home.html with the following HTML, and
save the file.
HTML

<body>
<!-- Page content -->
<div id="content-header" class="ms-bgColor-themePrimary ms-font-
xl">
<div class="padding">
<h1 id="greeting" class="ms-fontColor-white"></h1>
</div>
</div>
<div id="content-main">
<div class="padding">
<div class="ms-font-m">
<p id="about"></p>
</div>
</div>
</div>
</body>

The following figure shows the heading (h1) element and the paragraph (p) element
that will display localized text when you complete the remaining steps and run the add-
in.

Figure 1. The add-in UI


Add the resource file that contains the localized strings
The JavaScript resource file contains the strings used for the add-in UI. The HTML for the
sample add-in UI contains an <h1> element that displays a greeting, and a <p> element
that introduces the add-in to the user.

To enable localized strings for the heading and paragraph, you place the strings in a
separate resource file. The resource file creates a JavaScript object that contains a
separate JavaScript Object Notation (JSON) object for each set of localized strings. The
resource file also provides a method for getting back the appropriate JSON object for a
given locale.

Add the resource file to the add-in project


1. In Solution Explorer in Visual Studio, right-click the WorldReadyAddInWeb project
and choose Add > New Item.

2. In the Add New Item dialog box, choose JavaScript File.

3. Enter UIStrings.js as the file name and choose Add.

4. Add the following code to the UIStrings.js file, and save the file.

JavaScript

/* Store the locale-specific strings */

const UIStrings = (function ()


{
"use strict";

const UIStrings = {};

// JSON object for English strings


UIStrings.EN =
{
"Greeting": "Welcome",
"Introduction": "This is my localized add-in."
};

// JSON object for Spanish strings


UIStrings.ES =
{
"Greeting": "Bienvenido",
"Introduction": "Esta es mi aplicación localizada."
};

UIStrings.getLocaleStrings = function (locale)


{
let text;

// Get the resource strings that match the language.


switch (locale)
{
case 'en-US':
text = UIStrings.EN;
break;
case 'es-ES':
text = UIStrings.ES;
break;
default:
text = UIStrings.EN;
break;
}

return text;
};
return UIStrings;
})();

The UIStrings.js resource file creates an object, UIStrings, which contains the localized
strings for your add-in UI.

Localize the text used for the add-in UI

To use the resource file in your add-in, you'll need to add a script tag for it on
Home.html. When Home.html is loaded, UIStrings.js executes and the UIStrings object
that you use to get the strings is available to your code. Add the following HTML in the
head tag for Home.html to make UIStrings available to your code.

HTML

<!-- Resource file for localized strings: -->


<script src="../UIStrings.js" type="text/javascript"></script>

Now you can use the UIStrings object to set the strings for the UI of your add-in.

If you want to change the localization for your add-in based on what language is used
for display in menus and commands in the Office client application, you use the
Office.context.displayLanguage property to get the locale for that language. For
example, if the application language uses Spanish for display in menus and commands,
the Office.context.displayLanguage property will return the language code es-ES.

If you want to change the localization for your add-in based on what language is being
used for editing document content, you use the Office.context.contentLanguage
property to get the locale for that language. For example, if the application language
uses Spanish for editing document content, the Office.context.contentLanguage
property will return the language code es-ES.

After you know the language the application is using, you can use UIStrings to get the
set of localized strings that matches the application language.

Replace the code in the Home.js file with the following code. The code shows how you
can change the strings used in the UI elements on Home.html based on either the
display language of the application or the editing language of the application.

7 Note
To switch between changing the localization of the add-in based on the language
used for editing, uncomment the line of code const myLanguage =
Office.context.contentLanguage; and comment out the line of code const
myLanguage = Office.context.displayLanguage;

JavaScript

/// <reference path="../App.js" />


/// <reference path="../UIStrings.js" />

(function () {
"use strict";

// The initialize function must be run each time a new page is loaded.
Office.initialize = function (reason)
{

$(document).ready(function () {
// Get the language setting for editing document content.
// To test this, uncomment the following line and then comment
out the
// line that uses Office.context.displayLanguage.
// const myLanguage = Office.context.contentLanguage;

// Get the language setting for UI display in the Office


application.
const myLanguage = Office.context.displayLanguage;
let UIText;

// Get the resource strings that match the language.


// Use the UIStrings object from the UIStrings.js file
// to get the JSON object with the correct localized strings.
UIText = UIStrings.getLocaleStrings(myLanguage);

// Set localized text for UI elements.


$("#greeting").text(UIText.Greeting);
$("#about").text(UIText.Introduction);
});
};
})();

Test your localized add-in


To test your localized add-in, change the language used for display or editing in the
Office application and then run your add-in.
1. In Word, choose File > Options > Language. The following figure shows the Word
Options dialog box opened to the Language tab.

Figure 2. Language options in the Word Options dialog box

2. Under Choose Display Language, select the language that you want for display,
for example Spanish, and then choose the up arrow to move the Spanish language
to the first position in the list. Alternatively, to change the language used for
editing, under Choose Editing Languages, choose the language you want to use
for editing, for example, Spanish, and then choose Set as Default.

3. Choose OK to confirm your selection, and then close Word.

4. Press F5 in Visual Studio to run the sample add-in, or choose Debug > Start
Debugging from the menu bar.

5. In Word, choose Home > Show Taskpane.

Once running, the strings in the add-in UI change to match the language used by the
application, as shown in the following figure.

Figure 3. Add-in UI with localized text


See also
Design guidelines for Office Add-ins
Overview of deploying languages for Microsoft 365 Apps
Develop Office Add-ins with Angular
Article • 05/20/2023

This article provides guidance for using Angular 2+ to create an Office Add-in as a
single page application.

7 Note

Do you have something to contribute based on your experience using Angular to


create Office Add-ins? You can contribute to this article in GitHub or provide
your feedback by submitting an issue in the repo.

For an Office Add-ins sample that's built using the Angular framework, see Word Style
Checking Add-in Built on Angular .

Install the TypeScript type definitions


Open a Node.js window and enter the following at the command line.

command line

npm install --save-dev @types/office-js

Bootstrapping must be inside Office.initialize


On any page that calls the Office, Word, or Excel JavaScript APIs, your code must first
assign a function to Office.initialize . (If you have no initialization code, the function
body can just be empty " {} " symbols, but you must not leave the Office.initialize
function undefined. For details, see Initialize your Office Add-in.) Office calls this
function immediately after it has initialized the Office JavaScript libraries.

Your Angular bootstrapping code must be called inside the function that you assign
to Office.initialize to ensure that the Office JavaScript libraries have initialized first.
The following is a simple example that shows how to do this. This code should be in the
main.ts file of the project.

JavaScript

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';


import { AppModule } from './app.module';
Office.initialize = function () {
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
};

Use the hash location strategy in the Angular


application
Navigating between routes in the application might not work if you don't specify the
hash location strategy. You can do this in one of two ways. First, you can specify a
provider for the location strategy in your app module, as shown in the following
example. It goes into the app.module.ts file.

JavaScript

import { LocationStrategy, HashLocationStrategy } from '@angular/common';


// Other imports suppressed for brevity

@NgModule({
providers: [
{ provide: LocationStrategy, useClass: HashLocationStrategy },
// Other providers suppressed
],
// Other module properties suppressed
})
export class AppModule { }

If you define your routes in a separate routing module, there is an alternative way to
specify the hash location strategy. In your routing module's .ts file, pass a configuration
object to the forRoot function that specifies the strategy. The following code is an
example.

JavaScript

import { RouterModule, Routes } from '@angular/router';


// Other imports suppressed for brevity

const routes: Routes = // route definitions go here

@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule]
})
export class AppRoutingModule { }
Use the Office dialog API with Angular
The Office Add-in dialog API enables your add-in to open a page in a nonmodal dialog
box that can exchange information with the main page, which is typically in a task pane.

The displayDialogAsync method takes a parameter that specifies the URL of the page
that should open in the dialog box. Your add-in can have a separate HTML page
(different from the base page) to pass to this parameter, or you can pass the URL of a
route in your Angular application.

It is important to remember, if you pass a route, that the dialog box creates a new
window with its own execution context. Your base page and all its initialization and
bootstrapping code run again in this new context, and any variables are set to their
initial values in the dialog box. So this technique launches a second instance of your
single page application in the dialog box. Code that changes variables in the dialog box
does not change the task pane version of the same variables. Similarly, the dialog box
has its own session storage (the Window.sessionStorage property), which is not
accessible from code in the task pane.

Trigger the UI update


In an Angular app, the UI sometimes does not update. This is because that part of the
code runs out of the Angular zone. The solution is to put the code in the zone, as shown
in the following example.

JavaScript

import { NgZone } from '@angular/core';

export class MyComponent {


constructor(private zone: NgZone) { }

myFunction() {
this.zone.run(() => {
// the codes that need update the UI
});
}
}

Use Observable
Angular uses RxJS (Reactive Extensions for JavaScript), and RxJS introduces Observable
and Observer objects to implement asynchronous processing. This section provides a
brief introduction to using Observables ; for more detailed information, see the official
RxJS documentation.

An Observable is like a Promise object in some ways - it is returned immediately from


an asynchronous call, but it might not resolve until some time later. However, while a
Promise is a single value (which can be an array object), an Observable is an array of

objects (possibly with only a single member). This enables code to call array methods ,
such as concat , map , and filter , on Observable objects.

Push instead of pull


Your code "pulls" Promise objects by assigning them to variables, but Observable
objects "push" their values to objects that subscribe to the Observable . The subscribers
are Observer objects. The benefit of the push architecture is that new members can be
added to the Observable array over time. When a new member is added, all the
Observer objects that subscribe to the Observable receive a notification.

The Observer is configured to process each new object (called the "next" object) with a
function. (It is also configured to respond to an error and a completion notification. See
the next section for an example.) For this reason, Observable objects can be used in a
wider range of scenarios than Promise objects. For example, in addition to returning an
Observable from an AJAX call, the way you can return a Promise , an Observable can be

returned from an event handler, such as the "changed" event handler for a text box.
Each time a user enters text in the box, all the subscribed Observer objects react
immediately using the latest text and/or the current state of the application as input.

Wait until all asynchronous calls have completed


When you want to ensure that a callback only runs when every member of a set of
Promise objects has resolved, use the Promise.all() method.

JavaScript

myPromise.all([x, y, z]).then(
// TODO: Callback logic goes here
)

To do the same thing with an Observable object, you use the Observable.forkJoin()
method.

JavaScript
const source = Observable.forkJoin([x, y, z]);

const subscription = source.subscribe(


x => {
// TODO: Callback logic goes here
},
err => console.log('Error: ' + err),
() => console.log('Completed')
);

Compile the Angular application using the


Ahead-of-Time (AOT) compiler
Application performance is one of the most important aspects of user experience. An
Angular application can be optimized by using the Angular Ahead-of-Time (AOT)
compiler to compile the app at build time. It converts all source code (HTML templates
and TypeScript) into efficient JavaScript code. If you compile your app with the AOT
compiler, no additional compilation will occur at runtime, which results in faster
rendering and faster asynchronous requests for HTML templates. Additionally, the
overall application size will be reduced, because the Angular compiler won't need to be
included in the application distributable.

To use the AOT compiler, add --aot to the ng build or ng serve command:

command line

ng build --aot
ng serve --aot

7 Note

To learn more about the Angular Ahead-of-Time (AOT) compiler, see the official
guide .

Support the Trident webview control if you're


dynamically loading Office.js
Based on the Windows version and the Office desktop client where your add-in is
running, your add-in may be using the Trident webview control provided by Internet
Explorer 11. (For more details, see Browsers and webview controls used by Office Add-
ins.) Angular depends on a few window.history APIs but these APIs don't work in the
Trident webview that is sometimes used to run Office Add-ins in Windows desktop
clients. When these APIs don't work, your add-in may not work properly, for example, it
may load a blank task pane. To mitigate this, Office.js nullifies those APIs. However, if
you're dynamically loading Office.js, AngularJS may load before Office.js. In that case,
you should disable the window.history APIs by adding the following code to your add-
in's index.html page.

JavaScript

<script
type="text/javascript">window.history.replaceState=null;window.history.pushS
tate=null;</script>
Create Office Add-in projects using the
Yeoman Generator
Article • 07/27/2023

The Yeoman Generator for Office Add-ins (also called "Yo Office") is an interactive
Node.js-based command line tool that creates Office Add-in development projects. We
recommend that you use this tool to create add-in projects except when you want the
server-side code of the add-in to be in a .NET-based language (such as C# or VB.Net) or
you want the add-in hosted in Internet Information Server (IIS). In either of the latter two
situations, use Visual Studio to create the add-in.

7 Note

Office add-ins can also be created with the Teams Toolkit (Prerelease version). For
more information about how to do this and the limitations, see Create Office Add-
in projects using the Teams Toolkit (preview).

The projects that the tool creates have the following characteristics.

They have a standard npm configuration that includes a package.json file.


They include several helpful scripts to build the project, start the server, sideload
the add-in in Office, and other tasks.
They use webpack as a bundler and basic task runner.
In development mode, they are hosted on localhost by webpack's Node.js-based
webpack-dev-server, a development-oriented version of the express server that
supports hot-reloading and recompile-on-change.
By default, all dependencies are installed by the tool, but you can postpone the
installation with a command line argument.
They include a complete add-in manifest.
They have a "Hello World"-level add-in that is ready run as soon as the tool has
finished.
They include a polyfill and a transpiler that is configured to transpile TypeScript,
and recent versions of JavaScript, to ES5 JavaScript. These features ensure that the
add-in is supported in all webview runtimes that Office Add-ins might run in,
including Trident (Internet Explorer).

 Tip
If you want to deviate from these choices significantly, such as using a different task
runner or a different server, we recommend that when you run the tool you choose
the Manifest-only option.

Install the generator

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Use the tool


Start the tool with the following command in a system prompt (not a bash window).

command line

yo office

A lot needs to load, so it may take 20 seconds before the tool starts. The tool asks you a
series of questions. For some, you just type an answer to the prompt. For others, you're
given a list of possible answers. If given a list, select one and then select Enter.

The first question asks you to choose between several types of projects. The options are:

Office Add-in Task Pane project


Office Add-in Task Pane project using React framework
Excel Custom Functions using a Shared Runtime
Excel Custom Functions using a JavaScript-only Runtime
Office Add-in Task Pane project supporting single sign-on
Outlook Add-in with the unified manifest for Microsoft 365 (preview)
Office Add-in project containing the manifest only

7 Note

The unified manifest is in preview and is supported only on Outlook on


Windows. It isn't supported yet for a production add-in.
The Office Add-in project containing the manifest only option produces a
project that contains a basic add-in manifest and minimal scaffolding. For
more information about the option, see Manifest-only option.

The next question asks you to choose between TypeScript and JavaScript. (This
question is skipped if you chose the manifest-only option in the preceding question.)
You're then prompted to give the add-in a name. The name you specify will be used in
the add-in's manifest, but you can change it later.

You're then prompted to choose which Office application the add-in should run in.
There are six possible applications to choose from: Excel, OneNote, Outlook,
PowerPoint, Project, and Word. You must choose just one, but you can change the
manifest later to support the additional Office applications. The exception is Outlook. A
manifest that supports Outlook cannot support any other Office application.
After you've answered this question, the generator creates the project and installs the
dependencies. You may see WARN messages in the npm output on screen. You can
ignore these. You may also see messages that vulnerabilities were found. You can ignore
these for now, but you'll eventually need to fix them before your add-in is released to
production. For more information about fixing vulnerabilities, open your browser and
search for "npm vulnerability".

If the creation is successful, you'll see a Congratulations! message in the command


window, followed by some suggested next steps. (If you're using the generator as part
of a quick start or tutorial, ignore the next steps in the command window and continue
with the instructions in the article.)

 Tip

If you want to create the scaffolding of an Office Add-in project, but postpone the
installation of the dependencies, add the --skip-install option to the yo office
command. The following code is an example.

command line

yo office --skip-install

When you're ready to install the dependencies, navigate to the root folder of the
project in a command prompt and enter npm install .
Manifest-only option
This option creates only a manifest for an add-in. The resulting project doesn't have a
Hello World add-in, any of the scripts, or any of the dependencies. Use this option in the
following scenarios.

You want to use different tools from the ones a Yeoman generator project installs
and configures by default. For example, you want to use a different bundler,
transpiler, task runner, or development server.
You want to use a web application development framework, other than React, such
as Vue.

For an example of using the generator with the manifest-only option, see Use Vue to
build an Excel task pane add-in.

Use command line parameters


You can also add parameters to the yo office command. The two most common are:

yo office --details : This will output brief help about all of the other command

line parameters.
yo office --skip-install : This will prevent the generator from installing the

dependencies.

For detailed reference about the command line parameters, see the readme for the
generator at Yeoman generator for Office Add-ins .

Troubleshooting
If you encounter problems using the tool, your first step should be to reinstall it to be
sure that you have the latest version. (See Install the generator for details.) If doing so
doesn't fix the problem, search the issues of the GitHub repo for the tool to see if
anyone else has encountered the same problem and found a solution. If no one has,
create a new issue .
Create Office Add-in projects with
Teams Toolkit (preview)
Article • 07/27/2023

Extensions to the Microsoft 365 Platform are now included under "Teams Apps", even if
the extension is fully outside of the Teams application itself. A primary tool for
developing Teams Apps is Teams Toolkit. You can create Office Add-ins with Teams
Toolkit, with the following restrictions:

Only Outlook Add-ins can be created at this time and only for Outlook on
Windows. We're working hard to enable support in Teams Toolkit for add-ins to
other Office applications and platforms.
Add-ins created with Teams Toolkit use the unified manifest for Microsoft 365
which is currently in preview. We encourage you to experiment with creating add-
ins using the toolkit but don't use the unified manifest for production add-ins.

Install the latest version of Teams Toolkit into Visual Studio Code as described in Install
Teams Toolkit.

Create an Outlook Add-in project


1. Open Visual Studio Code and select Teams Toolkit icon in the Activity Bar.

2. Select Create a new app.

3. In the New Project drop down, select Outlook add-in.


4. In the App Features Using an Outlook Add-in drop down, select Taskpane.

5. In the Workspace folder dialog that opens, select the folder where you want to
create the project.

6. Give a name to the project (with no spaces) when prompted. Teams Toolkit will
create the project with basic files and scaffolding. It will then open the project in a
second Visual Studio Code window. Close the original Visual Studio Code window.

7. In the Visual Studio Code TERMINAL navigate to the root of the project and run
npm install .

8. Before you make changes to the project, verify that you can sideload your Outlook
add-in from Visual Studio Code. Use the following steps:
a. Ensure that your account in your Microsoft 365 developer tenancy is also an
email account in desktop Outlook. If it isn't, follow the guidance in Add an email
account to Outlook .
b. Close Outlook desktop.
c. In Visual Studio Code, open Teams Toolkit.
d. In the ACCOUNTS section, verify that you're signed into Microsoft 365.
e. Select View | Run in Visual Studio Code. In the RUN AND DEBUG drop down
menu, select the option, Outlook Desktop (Edge Chromium), and then press F5.
The project builds and a Node dev-server window opens. This process may take
a couple of minutes. Eventually, Outlook desktop will open.
f. Open the Inbox of your Microsoft 365 account identity and open any message. A
Contoso Add-in tab with two buttons will appear on the Home ribbon (or the
Message ribbon, if you have opened the message in its own window).
g. Click the Show Taskpane button and a task pane opens. Click the Perform an
action button and a small notification appears near the top of the message.
h. To stop debugging and uninstall the add-in, select Run | Stop Debugging in
Visual Studio Code.

Now you can change and develop the project. In places where the guidance in the
Office Add-ins documentation branches depending on what type of manifest is being
used, be sure to follow the guidance for the unified manifest.
Develop Office Add-ins with Visual
Studio Code
Article • 03/14/2023

This article describes how to use Visual Studio Code (VS Code) to develop an Office
Add-in.

7 Note

For information about using Visual Studio to create an Office Add-in, see Develop
Office Add-ins with Visual Studio.

Prerequisites
Visual Studio Code

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Create the add-in project using the Yeoman


generator
If you're using VS Code as your integrated development environment (IDE), you should
create the Office Add-in project with the Yeoman generator for Office Add-ins. The
Yeoman generator creates a Node.js project that can be managed with VS Code or any
other editor.
To create an Office Add-in with the Yeoman generator, follow instructions in the 5-
minute quick start that corresponds to the type of add-in you'd like to create.

Develop the add-in using VS Code


When the Yeoman generator finishes creating the add-in project, open the root folder of
the project with VS Code.

 Tip

On Windows, you can navigate to the root directory of the project via the
command line and then enter code . to open that folder in VS Code. On Mac,
you'll need to add the code command to the path before you can use that
command to open the project folder in VS Code.

The Yeoman generator creates a basic add-in with limited functionality. You can
customize the add-in by editing the manifest, HTML, JavaScript or TypeScript, and CSS
files in VS Code. For a high-level description of the project structure and files in the add-
in project that the Yeoman generator creates, see the Yeoman generator guidance
within the 5-minute quick start that corresponds to the type of add-in you've created.

Test and debug the add-in


Methods for testing, debugging, and troubleshooting Office Add-ins vary by platform.
For more information, see Test and debug Office Add-ins.

Publish the add-in


An Office Add-in consists of a web application and a manifest file. The web application
defines the add-in's user interface and functionality, while the manifest specifies the
location of the web application and defines settings and capabilities of the add-in.

While you're developing your add-in, you can run the add-in on your local web server
( localhost ), but when you're ready to publish it for other users to access, you'll need to
deploy the web application to a web server or web hosting service (for example,
Microsoft Azure) and update the manifest to specify the URL of the deployed
application.

When your add-in is working as desired and you're ready to publish it for other users to
access, complete the following steps.
1. From the command line, in the root directory of your add-in project, run the
following command to prepare all files for production deployment.

command line

npm run build

When the build completes, the dist folder in the root directory of your add-in
project will contain the files that you'll deploy in subsequent steps.

2. Upload the contents of the dist folder to the web server that'll host your add-in.
You can use any type of web server or web hosting service to host your add-in.

3. In VS Code, open the add-in's manifest file, located in the root directory of the
project ( manifest.xml ). Replace all occurrences of https://localhost:3000 with the
URL of the web application that you deployed to a web server in the previous step.

4. Choose the method you'd like to use to deploy your Office Add-in, and follow the
instructions to publish the manifest file.

See also
Core concepts for Office Add-ins
Develop Office Add-ins
Design Office Add-ins
Test and debug Office Add-ins
Publish Office Add-ins
Develop Office Add-ins with Visual
Studio
Article • 03/14/2023

This article describes how to use Visual Studio to develop an Office Add-in. If you've
already created your add-in, you can skip ahead to the Develop the add-in using Visual
Studio section.

7 Note

As an alternative to using Visual Studio, you may choose to use the Yeoman
generator for Office Add-ins and VS Code to create an Office Add-in. For more
information about this choice, see Creating an Office Add-in.

Create the add-in project using Visual Studio


Visual Studio can be used to create Office Add-ins for Excel, Outlook, Word, and
PowerPoint. An Office Add-in project gets created as part of a Visual Studio solution and
uses HTML, CSS, and JavaScript. To create an Office Add-in with Visual Studio, follow
instructions in the quick start that corresponds to the add-in you'd like to create.

Excel quick start


Outlook quick start
Word quick start
PowerPoint quick start

Visual Studio doesn't support creating Office Add-ins for OneNote or Project. To create
Office Add-ins for either of these applications, you'll need to use the Yeoman generator
for Office Add-ins, as described in the OneNote quick start or the Project quick start.

Develop the add-in using Visual Studio


Visual Studio creates a basic add-in with limited functionality. You can customize the
add-in by editing the manifest, HTML, JavaScript, and CSS files in Visual Studio. For a
high-level description of the project structure and files in the add-in project that Visual
Studio creates, see the Visual Studio guidance within the quick start that you completed
to create your add-in.
 Tip

Because an Office Add-in is a web application, you'll need at least basic web
development skills to customize your add-in. If you're new to JavaScript, we
recommend reviewing the Mozilla JavaScript tutorial .

To customize your add-in, you'll need to understand concepts described in the Core
concepts > Develop area of this documentation, as well as concepts described in the
application-specific area of documentation that corresponds to the add-in you're
building (for example, Excel).

Test and debug the add-in


Methods for testing, debugging, and troubleshooting Office Add-ins vary by platform.
For more information, see Debug Office Add-ins in Visual Studio and Test and debug
Office Add-ins.

Publish the add-in


An Office Add-in consists of a web application and a manifest file. The web application
defines the add-in's user interface and functionality, while the manifest specifies the
location of the web application and defines settings and capabilities of the add-in.

While you're developing your add-in in Visual Studio, your add-in runs on your local
web server ( localhost ). When your add-in is working as desired and you're ready to
publish it for other users to access, you'll need to complete the following steps.

1. Deploy the web application to a web server or web hosting service (for example,
Microsoft Azure).
2. Update the manifest to specify the URL of the deployed application.
3. Choose the method you'd like to use to deploy your Office Add-in, and follow the
instructions to publish the manifest file.

See also
Core concepts for Office Add-ins
Develop Office Add-ins
Design Office Add-ins
Test and debug Office Add-ins
Publish Office Add-ins
Get JavaScript IntelliSense in Visual
Studio
Article • 03/14/2023

When you use Visual Studio 2019 and later to develop Office Add-ins, you can use
JSDoc to enable IntelliSense for your JavaScript variables, objects, parameters, and
return values. This article provides an overview of JSDoc and how you can use it to
create IntellSense in Visual Studio. For more details, see JavaScript IntelliSense and
JSDoc support in JavaScript .

Office.js type definitions


You need to provide the definitions of the types in Office.js to Visual Studio. To do this,
you can:

Have a local copy of the Office.js files in a folder in your solution named
\Office\1\ . The Office Add-in project templates in Visual Studio add this local

copy when you create an add-in project.

Use an online version of Office.js by adding a tsconfig.json file to the root of the
web application project in the add-in solution. The file should include the following
content.

JSON

{
"compilerOptions": {
"allowJs": true, // These settings apply to
JavaScript files also.
"noEmit": true // Do not compile the JS (or
TS) files in this project.
},
"exclude": [
"node_modules", // Don't include any JavaScript
found under "node_modules".
"Scripts/Office/1" // Suppress loading all the
JavaScript files from the Office NuGet package.
],
"typeAcquisition": {
"enable": true, // Enable automatic fetching of
type definitions for detected JavaScript libraries.
"include": [ "office-js" ] // Ensure that the "Office-js"
type definition is fetched.
}
}
JSDoc syntax
The basic technique is to precede the variable (or parameter, and so on) with a
comment that identifies its data type. This allows IntelliSense in Visual Studio to infer its
members. The following are examples.

Variable
JavaScript

/** @type {Excel.Range} */


let subsetRange;

Parameter
JavaScript

/** @param {Word.ParagraphCollection} paragraphs */


function myFunc(paragraphs){

}
Return value
JavaScript

/** @returns {Word.Range} */


function myFunc() {

Complex types
JavaScript

/** @typedef {{range: Word.Range, paragraphs: Word.ParagraphCollection}}


MyType

/** @returns {MyType} */


function myFunc() {
}

See also
Develop Office Add-ins with Visual Studio
Debug Office Add-ins in Visual Studio
Convert an Office Add-in project in
Visual Studio to TypeScript
Article • 03/14/2023

You can use the Office Add-in template in Visual Studio to create an add-in that uses
JavaScript, and then convert that add-in project to TypeScript. This article describes this
conversion process for an Excel add-in. You can use the same process to convert other
types of Office Add-in projects from JavaScript to TypeScript in Visual Studio.

Prerequisites
Visual Studio 2022 or later with the Office/SharePoint development workload
installed

 Tip

If you've previously installed Visual Studio, use the Visual Studio Installer to
ensure that the Office/SharePoint development workload is installed. If this
workload is not yet installed, use the Visual Studio Installer to install it.

Excel 2016 or later.

Create the add-in project


1. In Visual Studio, choose Create a new project. If the Visual Studio development
environment is already open, you can create a new project by choosing File > New
> Project on the menu bar.

2. Using the search box, enter add-in. Choose Excel Web Add-in, then select Next.

3. Name your project and select Create.

4. In the Create Office Add-in dialog window, choose Add new functionalities to
Excel, and then choose Finish to create the project.

5. Visual Studio creates a solution and its two projects appear in Solution Explorer.
The Home.html file opens in Visual Studio.

Convert the add-in project to TypeScript


Add Nuget packages
1. Open the Nuget package manager by choosing Tools > Nuget Package Manager
> Manage Nuget Packages for Solution
2. Select the Browse tab and search for Microsoft.TypeScript.MSBuild. Install this
package to the ASP.NET web project, or update it if it's already installed. The
ASP.NET web project has your project name with the text Web appended to the
end. This will ensure the project will transpile to JavaScript when the build runs.
3. On the Browse tab, search for jquery.TypeScript.DefinitelyTyped. Install this
package to the ASP.NET web project, or update it if it's already installed. This will
ensure the jQuery TypeScript definitions are included in your project. The packages
for jQuery appear in a file generated by Visual Studio, called packages.config.

7 Note

In your TypeScript project, you can have a mix of TypeScript and JavaScript files and
your project will compile. This is because TypeScript is a typed superset of
JavaScript that compiles JavaScript.

Create a TypeScript config file


1. In Solution Explorer, right-click the ASP.NET web project and choose Add > New
Item. The ASP.NET web project has your project name with the text Web appended
to the end.

2. In the Add New Item dialog, select TypeScript JSON configuration File to create a
tsconfig.json file and then choose Add.

3. Update the tsconfig.json file to also have an include section as shown in the
following JSON.

JSON

{
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es5"
},
"exclude": [
"node_modules",
"wwwroot"
],
"include": [
"scripts/**/*",
"**/*"
]
}

4. Save the file. For more information on tsconfig.json settings, see What is a
tsconfig.json?

Update the JavaScript files


Change your JavaScript files (.js) to TypeScript files (.ts). Then, make the necessary
changes for them to compile. This section walks through the default files in a new
project.

1. Find the Home.js file and rename it to Home.ts.

2. Find the ./Functions/FunctionFile.js file and rename it to FunctionFile.ts.

3. Find the ./Scripts/MessageBanner.js file and rename it to MessageBanner.ts.

4. In Home.ts, find the line Office.initialize = function (reason) { and add a line
immediately after it to polyfill the global window.Promise , as shown here.

TypeScript

Office.initialize = function (reason) {


// Add the following line.
(window as any).Promise = OfficeExtension.Promise;
...

5. In ./Scripts/MessageBanner.ts, find the line _onResize(null); and replace it with


the following:

TypeScript

_onResize();

The JavaScript files generated by Visual Studio do not contain any TypeScript syntax. You
should consider updating them. For example, the following code shows how to update
the parameters to showNotification to include the string types.

TypeScript
function showNotification(header: string, content: string) {
$("#notification-header").text(header);
$("#notification-body").text(content);
messageBanner.showBanner();
messageBanner.toggleExpansion();
}

Run the converted add-in project


1. In Visual Studio, press F5 or choose the Start button to launch Excel with the Show
Taskpane add-in button displayed on the ribbon. The add-in will be hosted locally
on IIS.

2. In Excel, choose the Home tab, and then choose the Show Taskpane button on the
ribbon to open the add-in task pane.

3. In the worksheet, select the nine cells that contain numbers.

4. Press the Highlight button on the task pane to highlight the cell in the selected
range that contains the highest value.

See also
Promise implementation discussion on StackOverflow
Office Add-in samples on GitHub
Special requirements for add-ins on the
iPad
Article • 03/14/2023

If your add-in uses only Office APIs that are supported on the iPad, then customers can
install it on iPads. (See Specify Office applications and API requirements for more
information.) If the add-in will be marketed through AppSource , then there are some
practices you must follow for add-ins that can be installed on iPads, in addition to the
best practices that apply to all Office Add-ins.

The following table lists the tasks to perform.

7 Note

For information about designing Outlook add-ins that look good and work well in
Outlook on mobile devices, see Add-ins for Outlook on mobile devices.

Task Description Resources

Update Update the JavaScript files (Office.js and app- Update API and manifest version
your add- specific .js files) and the add-in manifest
in to validation file used in your Office Add-in project
support to version 1.1.
Office.js
version 1.1.

Apply iOS Integrate your add-in UI seamlessly with the iOS See note below.
design experience.
best
practices.

Optimize Make your UI responsive to touch inputs in Apply UX design principles


your add- addition to mouse and keyboard.
in for
touch.

Make your Office on iPad is a channel through which you Certification policy 1120.2
add-in can reach more users and promote your
free. services. These new users have the potential to
become your customers.
Task Description Resources

Make your When it's running on the iPad, your add-in must Certification policy 1100.3
add-in be free of in-app purchases, trial offers, UI that
commerce aims to upsell to a non-free version, or links to Your add-in can still have
free on the any online stores where users can purchase or commerce on other platforms.
iPad. acquire other content, apps, or add-ins. Your To do so, test the
Privacy Policy and Terms of Use pages must also Office.context.commerceAllowed
be free of any commerce UI or AppSource links. property and suppress all
commerce when it returns
false .

Submit In Partner Center, on the Product setup page, Make your solutions available in
your add- select the Make my product available on iOS AppSource and within Office
in to and Android (if applicable) check box, and
AppSource. provide your Apple developer ID in Account
settings. Review the Application Provider
Agreement to make sure you understand the
terms.

7 Note

Your add-in can serve an alternate UI based on the device that it is running on. To
detect whether your add-in is running on an iPad, you can use the following APIs.

const isTouchEnabled = Office.context.touchEnabled


const allowCommerce = Office.context.commerceAllowed

On an iPad, touchEnabled returns true and commerceAllowed returns false .

For information on the best UI design practices for iPad, see Designing for iOS .

Best practices for developing Office Add-ins


that can run on iPad
Apply the following best practices for developing add-ins that run on iPad.

Develop and debug the add-in on Windows or Mac and sideload it to an iPad.

You can't develop the add-in directly on an iPad, but you can develop and debug it
on a Windows or Mac computer and sideload it to an iPad for testing. Because an
add-in that runs in Office on iOS or Mac supports the same APIs as an add-in
running in Office on Windows, your add-in's code should run the same way on
these platforms. For details, see Test and debug Office Add-ins and Sideload Office
Add-ins on iPad for testing.

Specify API requirements in your add-in's manifest or with runtime checks.

When you specify API requirements in your add-in's manifest, Office will determine
if the Office client application supports those API members. If the API members are
available in the application, then your add-in will be available. Alternatively, you
can perform a runtime check to determine if a method is available in the
application before using it in your add-in. Runtime checks ensure that your add-in
is always available in the application, and provides additional functionality if the
methods are available. For more information, see Specify Office applications and
API requirements.
Test Office Add-ins
Article • 05/20/2023

This article contains guidance about testing, debugging, and troubleshooting issues
with Office Add-ins.

Test cross-platform and for multiple versions of


Office
Office Add-ins run across major platforms, so you need to test an add-in in all the
platforms where your users might be running Office. This usually includes Office on the
web, Office on Windows (both perpetual and Microsoft 365 subscription), Office on Mac,
Office on iOS, and (for Outlook add-ins) Office on Android. However, there may be
some situations in which you can be sure that none of your users will be working on
some platforms. For example, if you're making an add-in for a company that requires its
users to work with Windows computers and subscription Office, then you don't need to
test for Office on Mac or perpetual Office on Windows.

7 Note

On Windows computers, the version of Windows and Office will determine which
browser or webview control is used by add-ins. For more information, see Browsers
and webview controls used by Office Add-ins. For brevity hereafter, this article
uses "browser control" to mean "browser or webview control".

Add-ins tested for Office on the web


Add-ins are tested for Office on the web with all major modern browsers, including
Microsoft Edge (Chromium-based WebView2), Chrome, and Safari. Accordingly, you
should test on these platforms and browsers before you submit to AppSource. For more
information about validation, see Commercial marketplace certification policies,
especially section 1120.3, and the Office Add-in application and availability page.

Office on the web no longer opens in Internet Explorer or Microsoft Edge Legacy
(EdgeHTML). Consequently, AppSource doesn't test Office on the web on these
browsers. Office still supports these browsers for add-in runtimes, so if you think you've
encountered a bug in how add-ins run in them, please create an issue in the office-js
repository. For more information, see Support older Microsoft webviews and Office
versions and Troubleshoot EdgeHTML and WebView2 (Microsoft Edge) issues.

Add-ins tested for Office on Windows


Some Office versions on Windows still use the webview controls that come with Internet
Explorer and Microsoft Edge Legacy. AppSource tests whether your add-in supports
these browser controls. If your add-in doesn't support these browser controls,
AppSource only issues a warning and doesn't reject your add-in. In this instance, we
recommend configuring a graceful failure message on your add-in for a smoother user
experience. For further guidance, see Support older Microsoft webviews and Office
versions.

Sideload an Office Add-in for testing


You can use sideloading to install an Office Add-in for testing without having to first put
it in an add-in catalog. The procedure for sideloading an add-in varies by platform, and
in some cases, by product as well. The following articles each describe how to sideload
Office Add-ins on a specific platform or within a specific product.

Sideload Office Add-ins on Windows

Sideload Office Add-ins in Office on the web

Sideload Office Add-ins on Mac

Sideload Office Add-ins on iPad

Sideload Outlook add-ins for testing

Unit testing
For information about how to add unit tests to your add-in project, see Unit testing in
Office Add-ins.

Debug an Office Add-in


The procedure for debugging an Office Add-in varies based on your platform and
environment. For more information, see Debug Office Add-ins.

Validate an Office Add-in manifest


For information about how to validate the manifest file that describes your Office Add-in
and troubleshoot issues with the manifest file, see Validate and troubleshoot issues with
your manifest.

Troubleshoot user errors


For information about how to resolve common issues that users may encounter with
your Office Add-in, see Troubleshoot user errors with Office Add-ins.
Overview of debugging Office Add-ins
Article • 06/23/2023

Debugging Office Add-ins is essentially the same as debugging any web application.
However, a single set of tools won't work for all add-in developers. This is because add-
ins can be developed on different operating systems and run cross-platform. This article
helps you find the detailed debugging guidance for your development environment.

 Tip

This article is concerned with debugging in the narrow sense of setting breakpoints
and stepping through code. For guidance on testing and troubleshooting, start
with Test Office Add-ins and Troubleshoot development errors with Office Add-
ins.

7 Note

Although you should test your add-in on all the platforms that you want to support,
you'll only very rarely need to debug on an environment different from your
development computer. For this reason, this article uses "your development
computer" and "your development environment" to refer to the environment on
which you're debugging. If a problem in the code occurs only on a platform other
than the one on your development computer, and you need to set breakpoints or
step through code to solve it, then the environment on which you're debugging
isn't literally your development environment.

Server-side or client-side?
Debugging the server-side code of an Office Add-in is the same as debugging the
server-side of any web application. See the debugging instructions for your IDE or other
tools. The following are examples for some of the most popular tools.

Debug ASP.NET or ASP.NET Core apps in Visual Studio


Debugging Express
Node.js Debugging Guide
Node.js debugging in VS Code
Webpack Debugging
The rest of this article is concerned only with debugging client-side JavaScript (which
may be transpiled from TypeScript).

Special cases
There are some special cases in which the debugging process differs from normal for a
given combination of platform, Office application, and development environment. If
you're debugging any of these special cases, use the links in this section to find the
proper guidance. Otherwise, continue to General guidance.

Debugging the Office.initialize or Office.onReady function: Debug the


initialize and onReady functions.
Debugging an Excel custom function in a non-shared runtime: Custom functions
debugging in a non-shared runtime.
Debugging a function command in a non-shared runtime:
Outlook add-ins on a Windows development computer: Debug function
commands in Outlook add-ins
Other Office application add-ins or Outlook on a Mac development computer:
Debug a function command with a non-shared runtime.
Debugging an event-based Outlook add-in: Debug your event-based Outlook
add-in.
Debugging an add-in in the new Outlook on Windows desktop client (preview):
See the "Debug your add-in" section of Develop Outlook add-ins for the new
Outlook on Windows (preview).
Debugging a Blazor-based add-in: Debug the add-in the same way you would
debug a Blazor web application. See Debug ASP.NET Core Blazor WebAssembly.

General guidance
To find guidance for debugging client-side code, the first variable is the operating
system of your development computer.

Windows
Mac
Linux or other Unix variant

Debug on Windows
The following provides general guidance to debugging on Windows. Debugging on
Windows depends on your IDE.
Visual Studio: Debug using the browser's F12 tools. See Debug Office Add-ins in
Visual Studio.

Visual Studio Code: Debug using the Add-in Debugger Extension for Visual Studio
Code.

Any other IDE (or you don't want to debug inside your IDE): Use the developer
tools that are associated with the webview control that add-ins use on your
development computer. See one of the following:
For the Trident webview: Debug add-ins using developer tools for Internet
Explorer
For the EdgeHTML webview: Debug add-ins using developer tools for Edge
Legacy
For the WebView2 webview: Debug add-ins using developer tools in Microsoft
Edge (Chromium-based)

For information about which runtime is being used, see Browsers and webview controls
used by Office Add-ins and Runtimes in Office Add-ins.

 Tip

In recent versions of Office, one way to identify the webview control that Office is
using is through the personality menu on any add-in where it's available. (The
personality menu isn't supported in Outlook.) Open the menu and select Security
Info. In the Security Info dialog on Windows, the Runtime reports Microsoft Edge,
Microsoft Edge Legacy, or Internet Explorer. The runtime isn't included on the
dialog in older versions of Office.

Debug on Mac
Use the Safari Web Inspector. Instructions are in Debug Office Add-ins on a Mac.

Debug on Linux
There is no desktop version of Office for Linux, so you'll need to sideload the add-in to
Office on the web to test and debug it. Debugging guidance is in Debug add-ins in
Office on the web.

7 Note
We don't recommend that you develop Office Add-ins on a Linux computer except
in the unusual case where you can be sure that all the add-in's users will be
accessing the add-in through Office on the web from a Linux computer.

Debug add-ins in staging or production


To debug an add-in that is already in staging or production, attach a debugger from the
UI of the add-in. For instructions, see Attach a debugger from the task pane.

See also
Runtimes in Office Add-ins
Sideload Office Add-ins for testing from
a network share
Article • 06/23/2023

You can test an Office Add-in in an Office client that's on Windows by publishing the
manifest to a network file share (instructions follow). This deployment option is intended
to be used when you've completed development and testing on a localhost and want to
test the add-in from a non-local server or cloud account.

) Important

Deployment by network share isn't supported for production add-ins. This method
has the following limitations.

The add-in can only be installed on Windows computers.


If a new version of an add-in changes the ribbon, such as by adding a custom
tab or custom button to it, each user will have to reinstall the add-in.

7 Note

If your add-in project was created with a sufficiently recent version of the Yeoman
generator for Office Add-ins, the add-in will automatically sideload in the Office
desktop client when you run npm start .

This article applies only to testing Word, Excel, PowerPoint, and Project add-ins and only
on Windows. If you want to test on another platform or want to test an Outlook add-in,
see one of the following topics to sideload your add-in.

Sideload Office Add-ins in Office on the web for testing


Sideload Office Add-ins on Mac for testing
Sideload Office Add-ins on iPad for testing
Sideload Outlook add-ins for testing

The following video walks you through the process of sideloading your add-in in Office
on the web or desktop using a shared folder catalog.
https://www.youtube-nocookie.com/embed/XXsAw2UUiQo

Share a folder
1. In File Explorer on the Windows computer where you want to host your add-in, go
to the parent folder, or drive letter, of the folder you want to use as your shared
folder catalog.

2. Open the context menu for the folder you want to use as your shared folder
catalog (for example, right-click the folder) and choose Properties.

3. Within the Properties dialog window, open the Sharing tab and then choose the
Share button.

4. Within the Network access dialog window, add yourself and any other users
and/or groups with whom you want to share your add-in. You'll need at least
Read/Write permission to the folder. After you've finished choosing people to
share with, choose the Share button.

5. When you see the Your folder is shared confirmation, make note of the full
network path that's displayed immediately following the folder name. (You'll need
to enter this value as the Catalog Url when you specify the shared folder as a
trusted catalog, as described in the next section of this article.) Choose the Done
button to close the Network access dialog window.

6. Choose the Close button to close the Properties dialog window.

Specify the shared folder as a trusted catalog

Configure the trust manually


1. Open a new document in Excel, Word, PowerPoint, or Project.

2. Choose the File tab, and then choose Options.

3. Choose Trust Center, and then choose the Trust Center Settings button.

4. Choose Trusted Add-in Catalogs.


5. In the Catalog Url box, enter the full network path to the folder that you shared
previously. If you failed to note the folder's full network path when you shared the
folder, you can get it from the folder's Properties dialog window, as shown in the
following screenshot.

6. After you've entered the full network path of the folder into the Catalog Url box,
choose the Add catalog button.

7. Select the Show in Menu check box for the newly-added item, and then choose
the OK button to close the Trust Center dialog window.
8. Choose the OK button to close the Options dialog window.

9. Close and reopen the Office application so your changes will take effect.

Configure the trust with a Registry script


1. In a text editor, create a file named TrustNetworkShareCatalog.reg.

2. Add the following content to the file.

text

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\WEF\TrustedCatalogs\
{-random-GUID-here-}]
"Id"="{-random-GUID-here-}"
"Url"="\\\\-share-\\-folder-"
"Flags"=dword:00000001

3. Use one of the many online GUID generation tools, such as GUID Generator , to
generate a random GUID, and within the TrustNetworkShareCatalog.reg file,
replace the string "-random-GUID-here-" in both places with the GUID. (The
enclosing {} symbols should remain.)
4. Replace the Url value with the full network path to the folder that you shared
previously. (Note that any \ characters in the URL must be doubled.) If you failed
to note the folder's full network path when you shared the folder, you can get it
from the folder's Properties dialog window, as shown in the following screenshot.

5. The file should now look like the following. Save it.

text

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\WEF\TrustedCatalogs\
{01234567-89ab-cedf-0123-456789abcedf}]
"Id"="{01234567-89ab-cedf-0123-456789abcedf}"
"Url"="\\\\TestServer\\OfficeAddinManifests"
"Flags"=dword:00000001

6. Close all Office applications.

7. Run the TrustNetworkShareCatalog.reg just as you would any executable, such as


double-clicking it.

Sideload your add-in


1. Put the manifest XML file of any add-in that you're testing into the shared folder
catalog. Note that you deploy the web application itself to a web server. Be sure to
specify the URL in the <SourceLocation> element of the manifest file.

) Important

While not strictly required in all add-in scenarios, using an HTTPS endpoint for
your add-in is strongly recommended. Add-ins that are not SSL-secured
(HTTPS) generate unsecure content warnings and errors during use. If you
plan to run your add-in in Office on the web or publish your add-in to
AppSource, it must be SSL-secured. If your add-in accesses external data and
services, it should be SSL-secured to protect data in transit. Self-signed
certificates can be used for development and testing, so long as the certificate
is trusted on the local machine.

7 Note

For Visual Studio projects, use the manifest built by the project in the
{projectfolder}\bin\Debug\OfficeAppManifests folder.

2. In Excel, Word, or PowerPoint, select My Add-ins on the Insert tab of the ribbon. In
Project, select My Add-ins on the Project tab of the ribbon.

3. Choose SHARED FOLDER at the top of the Office Add-ins dialog box.

4. Select the name of the add-in and choose Add to insert the add-in.

Remove a sideloaded add-in


You can remove a previously sideloaded add-in by clearing the Office cache on your
computer. Details on how to clear the cache on Windows can be found in the article
Clear the Office cache.

See also
Validate an Office Add-in's manifest
Clear the Office cache
Publish your Office Add-in
Attach a debugger from the task pane
Article • 05/20/2023

In some environments, a debugger can be attached on an Office Add-in that is already


running. This can be useful when you want to debug an add-in that is already in staging
or production. If you are still developing and testing the add-in, see Overview of
debugging Office Add-ins.

The technique described in this article can be used only when the following conditions
are met.

The add-in is running in Office on Windows.


The computer is using a combination of Windows and Office versions that use the
Edge (Chromium-based) webview control, WebView2. To determine which webview
you're using, see Browsers and webview controls used by Office Add-ins.

 Tip

In recent versions of Office, one way to identify the webview control that Office is
using is through the personality menu on any add-in where it's available. (The
personality menu isn't supported in Outlook.) Open the menu and select Security
Info. In the Security Info dialog on Windows, the Runtime reports Microsoft Edge,
Microsoft Edge Legacy, or Internet Explorer. The runtime isn't included on the
dialog in older versions of Office.

To launch the debugger, choose the top right corner of the task pane to activate the
Personality menu (as shown in the red circle in the following image).
Select Attach Debugger. This launches the Microsoft Edge (Chromium-based) developer
tools. Use the tools as described in Debug add-ins using developer tools in Microsoft
Edge (Chromium-based).

See also
Overview of debugging Office Add-ins
Debug add-ins using developer tools in
Internet Explorer
Article • 05/20/2023

This article shows how to debug the client-side code (JavaScript or TypeScript) of your
add-in when the following conditions are met.

You cannot, or don't wish to, debug using tools built into your IDE; or you are
encountering a problem that only occurs when the add-in is run outside the IDE.
Your computer is using a combination of Windows and Office versions that use the
Internet Explorer webview control, Trident.

To determine which browser or webview is being used on your computer, see Browsers
and webview controls used by Office Add-ins.

 Tip

In recent versions of Office, one way to identify the webview control that Office is
using is through the personality menu on any add-in where it's available. (The
personality menu isn't supported in Outlook.) Open the menu and select Security
Info. In the Security Info dialog on Windows, the Runtime reports Microsoft Edge,
Microsoft Edge Legacy, or Internet Explorer. The runtime isn't included on the
dialog in older versions of Office.

7 Note

To install a version of Office that uses Trident or to force your current version to use
Trident, see Switch to the Trident webview.

Debug a task pane add-in using the F12 tools


Windows 10 and 11 include a web development tool called "F12" because it was
originally launched by pressing F12 in Internet Explorer. F12 is now an independent
application used to debug your add-in when it is running in the Internet Explorer
webview control, Trident. The application is not available in earlier versions of Windows.

7 Note
If your add-in has an add-in command that executes a function, the function runs
in a hidden browser runtime process that the F12 tools cannot detect or attach to,
so the technique described in this article cannot be used to debug code in the
function.

The following steps are the instructions for debugging your add-in. If you just want to
test the F12 tools themselves, see Example add-in to test the F12 tools.

1. Sideload and run the add-in.

2. Launch the F12 development tools that corresponds to your version of Office.

For the 32-bit version of Office, use C:\Windows\System32\F12\IEChooser.exe


For the 64-bit version of Office, use
C:\Windows\SysWOW64\F12\IEChooser.exe

IEChooser opens with a window named Choose target to debug. Your add-in will
appear in the window named by the filename of the add-in's home page. In the
following screenshot, it is Home.html . Only processes that are running in Internet
Explorer, or Trident, appear. The tool cannot attach to processes that are running in
other browsers or webviews, including Microsoft Edge.

3. Select your add-in's process; that is, its home page file name. This action will attach
the F12 tools to the process and open the main F12 user interface.

4. Open the Debugger tab.

5. In the upper left of the tab, just below the debugger tool ribbon, there is a small
folder icon. Select this to open a drop down list of the files in the add-in. The
following is an example.
6. Select the file that you want to debug and it opens in the the script (left) pane of
the Debugger tab. If you're using a transpiler, bundler, or minifier, that changes
the name of the file, it will have the final name that is actually loaded, not the
original source file name.

7. Scroll to a line where you want to set a breakpoint and click in the margin to the
left of the line number. You'll see a red dot to the left of the line and a
corresponding line appears in the Breakpoints tab of the bottom right pane. The
following screenshot is an example.
8. Execute functions in the add-in as needed to trigger the breakpoint. When the
breakpoint is hit, a right-pointing arrow appears on the red dot of the breakpoint.
The following screenshot is an example.

 Tip

For more information about using the F12 tools, see Inspect running JavaScript
with the Debugger.

Example add-in to test the F12 tools


This example uses Word and a free add-in from AppSource.

1. Open Word and choose a blank document.


2. On the Insert tab, in the Add-ins group, select My Add-ins to open the Office
Add-ins dialog and then select the STORE tab.
3. Select the QR4Office add-in. It opens in a task pane.
4. Launch the F12 development tools that corresponds to your version of Office as
described in the preceding section.
5. In the F12 window, select Home.html.
6. In the Debugger tab, open the file Home.js as described in the preceding section.
7. Set the breakpoints on lines 310 and 312.
8. In the add-in, select the Insert button. One or the other breakpoint is hit.

Debug a dialog in an add-in


If your add-in uses the Office Dialog API, the dialog runs in a separate process from the
task pane (if any) and the tools must attach to that process. Follow these steps.

1. Run the add-in and the tools.


2. Open the dialog and then select the Refresh button in the tools. The dialog
process is shown. Its name is the file name of the file that is open in the dialog.
3. Select the process to open it and debug just as described in the section Debug a
task pane add-in using the F12 tools.

Switch to the Trident webview


There are two ways to switch the Trident webview. You can run a simple command in a
command prompt, or you can install a version of Office that uses Trident by default. We
recommend the first method. But you should use the second in the following scenarios.

Your project was developed with Visual Studio and IIS. It isn't node.js-based.
You want to be absolutely robust in your testing.
If for any reason the command line tool doesn't work.

Switch via the command line


If your project is node.js-based (that is, not developed with Visual Studio and Internet
Information server (IIS)), you can force Office on Windows to use either the EdgeHTML
webview control that is provided by Edge Legacy or the Trident webview control that is
provided by Internet Explorer to run add-ins, even if you have a combination of
Windows and Office versions that would normally use a more recent webview. For more
information about which browsers and webviews are used by various combinations of
Windows and Office versions, see Browsers and webview controls used by Office Add-
ins.
7 Note

The tool that's used to force the change in webview is supported only in the Beta
subscription channel of Microsoft 365. Join the Microsoft 365 Insider program
and select the Beta Channel option to access Office Beta builds. See also About
Office: What version of Office am I using? .

Strictly, it's the webview switch of this tool (see Step 2) that requires the Beta
channel. The tool has other switches that don't have this requirement.

1. If your project was not created with the Yeoman generator for Office Add-ins tool,
you need to install the office-addin-dev-settings tool. Run the following command
in a command prompt.

command line

npm install office-addin-dev-settings --save-dev

) Important

The office-addin-dev-settings tool is not supported on Mac.

2. Specify the webview that you want Office to use with the following command in a
command prompt in the root of the project. Replace <path-to-manifest> with the
relative path, which is just the manifest filename if it's in the root of the project.
Replace <webview> with either ie or edge-legacy . Note that the options are
named after the browsers in which the webviews originated. The ie option means
"Trident" and the edge-legacy option means "EdgeHTML".

command line

npx office-addin-dev-settings webview <path-to-manifest> <webview>

The following is an example.

command line

npx office-addin-dev-settings webview manifest.xml ie


You should see a message in the command line that the webview type is now set
to IE (or Edge Legacy).

3. When you're finished, set Office to resume using the default webview for your
combination of Windows and Office versions with the following command.

command line

npx office-addin-dev-settings webview <path-to-manifest> default

Install a version of Office that uses Internet Explorer


Use the following procedure to install either a version of Office (downloaded from a
Microsoft 365 subscription) that uses the Microsoft Edge Legacy webview (EdgeHTML)
to run add-ins or a version that uses Internet Explorer (Trident).

1. In any Office application, open the File tab on the ribbon, and then select Office
Account or Account. Select the About host-name button (for example, About
Word).

2. On the dialog that opens, find the full xx.x.xxxxx.xxxxx build number and make a
copy of it somewhere.

3. Download the Office Deployment Tool .

4. Run the downloaded file to extract the tool. You are prompted to choose where to
install the tool.

5. In the folder where you installed the tool (where the setup.exe file is located),
create a text file with the name config.xml and add the following contents.

XML

<Configuration>
<Add OfficeClientEdition="64" Channel="SemiAnnual"
Version="16.0.xxxxx.xxxxx">
<Product ID="O365ProPlusRetail">
<Language ID="en-us" />
</Product>
</Add>
</Configuration>

6. Change the Version value.

To install a version that uses EdgeHTML, change it to 16.0.11929.20946 .


To install a version that uses Trident, change it to 16.0.10730.20348 .

7. Optionally, change the value of OfficeClientEdition to "32" to install 32-bit


Office, and change the Language ID value as needed to install Office in a different
language.

8. Open a command prompt as an administrator.

9. Navigate to the folder with the setup.exe and config.xml files.

10. Run the following command.

command line

setup.exe /configure config.xml

This command installs Office. The process may take several minutes.

11. Clear the Office cache.

) Important

After installation, be sure that you turn off automatic updating of Office, so that
Office isn't updated to a version that doesn't use webview you want to work with
before you've completed using it. This can happen within minutes of installation.
Follow these steps.

1. Start any Office application and open a new document.


2. Open the File tab on the ribbon, and then select Office Account or Account.
3. In the Product Information column, select Update Options, and then select
Disable Updates. If that option isn't available, then Office is already
configured to not update automatically.

When you are finished using the old version of Office, reinstall your newer version by
editing the config.xml file and changing the Version to the build number that you
copied earlier. Then repeat the setup.exe /configure config.xml command in an
administrator command prompt. Optionally, re-enable automatic updates.

See also
Inspect running JavaScript with the Debugger
Using the F12 developer tools
Debug add-ins using developer tools in
Microsoft Edge Legacy
Article • 05/20/2023

This article shows how to debug the client-side code (JavaScript or TypeScript) of your
add-in when the following conditions are met.

You cannot, or don't wish to, debug using tools built into your IDE; or you are
encountering a problem that only occurs when the add-in is run outside the IDE.
Your computer is using a combination of Windows and Office versions that use the
original Edge webview control, EdgeHTML.

 Tip

For information about debugging with Edge Legacy inside Visual Studio Code, see
Microsoft Office Add-in Debugger Extension for Visual Studio Code.

To determine which browser or webview you're using, see Browsers and webview
controls used by Office Add-ins.

 Tip

In recent versions of Office, one way to identify the webview control that Office is
using is through the personality menu on any add-in where it's available. (The
personality menu isn't supported in Outlook.) Open the menu and select Security
Info. In the Security Info dialog on Windows, the Runtime reports Microsoft Edge,
Microsoft Edge Legacy, or Internet Explorer. The runtime isn't included on the
dialog in older versions of Office.

7 Note

To install a version of Office that uses the Edge legacy webview or to force your
current version of Office to use Edge Legacy, see Switch to the Edge Legacy
webview.

Debug a task pane add-in using Microsoft Edge


DevTools Preview
1. Install the Microsoft Edge DevTools Preview . (The word "Preview" is in the name
for historical reasons. There isn't a more recent version.)

7 Note

If your add-in has an add-in command that executes a function, the function
runs in a hidden browser runtime process that the Microsoft Edge DevTools
cannot detect or attach to, so the technique described in this article cannot be
used to debug code in the function.

2. Sideload and run the add-in.

3. Run the Microsoft Edge DevTools.

4. In the tools, open the Local tab. Your add-in will be listed by its name. (Only
processes that are running in EdgeHTML appear on the tab. The tool cannot attach
to processes that are running in other browsers or webviews, including Microsoft
Edge (WebView2) and Internet Explorer (Trident).)

5. Select the add-in name to open it in the tools.

6. Open the Debugger tab.

7. Open the file that you want to debug with the following steps.
a. On the debugger task bar, select Show find in files. This will open a search
window.
b. Enter a line of code from the file you want to debug in the search box. It should
be something that's not likely to be in any other file.
c. Select the refresh button.
d. In the search results, select the line to open the code file in the pane above the
search results.
8. To set a breakpoint, select the line in the code file. The breakpoint is registered in
the Call stack (bottom right) pane. There may also be a red dot by the line in the
code file, but this doesn't appear reliably.

9. Execute functions in the add-in as needed to trigger the breakpoint.

 Tip

For more information about using the tools, see Microsoft Edge (EdgeHTML)
Developer Tools.

Debug a dialog in an add-in


If your add-in uses the Office Dialog API, the dialog runs in a separate process from the
task pane (if any) and the tools must attach to that process. Follow these steps.

1. Run the add-in and the tools.

2. Open the dialog and then select the Refresh button in the tools. The dialog
process is shown. Its name comes from the <title> element in the HTML file that
is open in the dialog.
3. Select the process to open it and debug just as described in the section Debug a
task pane add-in using Microsoft Edge DevTools Preview.

Switch to the Edge Legacy webview


There are two ways to switch the Edge Legacy webview. You can run a simple command
in a command prompt, or you can install a version of Office that uses Edge Legacy by
default. We recommend the first method. But you should use the second in the
following scenarios.

Your project was developed with Visual Studio and IIS. It isn't node.js-based.
You want to be absolutely robust in your testing.
If for any reason the command line tool doesn't work.

Switch via the command line


If your project is node.js-based (that is, not developed with Visual Studio and Internet
Information server (IIS)), you can force Office on Windows to use either the EdgeHTML
webview control that is provided by Edge Legacy or the Trident webview control that is
provided by Internet Explorer to run add-ins, even if you have a combination of
Windows and Office versions that would normally use a more recent webview. For more
information about which browsers and webviews are used by various combinations of
Windows and Office versions, see Browsers and webview controls used by Office Add-
ins.

7 Note
The tool that's used to force the change in webview is supported only in the Beta
subscription channel of Microsoft 365. Join the Microsoft 365 Insider program
and select the Beta Channel option to access Office Beta builds. See also About
Office: What version of Office am I using? .

Strictly, it's the webview switch of this tool (see Step 2) that requires the Beta
channel. The tool has other switches that don't have this requirement.

1. If your project was not created with the Yeoman generator for Office Add-ins tool,
you need to install the office-addin-dev-settings tool. Run the following command
in a command prompt.

command line

npm install office-addin-dev-settings --save-dev

) Important

The office-addin-dev-settings tool is not supported on Mac.

2. Specify the webview that you want Office to use with the following command in a
command prompt in the root of the project. Replace <path-to-manifest> with the
relative path, which is just the manifest filename if it's in the root of the project.
Replace <webview> with either ie or edge-legacy . Note that the options are
named after the browsers in which the webviews originated. The ie option means
"Trident" and the edge-legacy option means "EdgeHTML".

command line

npx office-addin-dev-settings webview <path-to-manifest> <webview>

The following is an example.

command line

npx office-addin-dev-settings webview manifest.xml ie

You should see a message in the command line that the webview type is now set
to IE (or Edge Legacy).
3. When you're finished, set Office to resume using the default webview for your
combination of Windows and Office versions with the following command.

command line

npx office-addin-dev-settings webview <path-to-manifest> default

Install a version of Office that uses Edge Legacy


Use the following procedure to install either a version of Office (downloaded from a
Microsoft 365 subscription) that uses the Microsoft Edge Legacy webview (EdgeHTML)
to run add-ins or a version that uses Internet Explorer (Trident).

1. In any Office application, open the File tab on the ribbon, and then select Office
Account or Account. Select the About host-name button (for example, About
Word).

2. On the dialog that opens, find the full xx.x.xxxxx.xxxxx build number and make a
copy of it somewhere.

3. Download the Office Deployment Tool .

4. Run the downloaded file to extract the tool. You are prompted to choose where to
install the tool.

5. In the folder where you installed the tool (where the setup.exe file is located),
create a text file with the name config.xml and add the following contents.

XML

<Configuration>
<Add OfficeClientEdition="64" Channel="SemiAnnual"
Version="16.0.xxxxx.xxxxx">
<Product ID="O365ProPlusRetail">
<Language ID="en-us" />
</Product>
</Add>
</Configuration>

6. Change the Version value.

To install a version that uses EdgeHTML, change it to 16.0.11929.20946 .


To install a version that uses Trident, change it to 16.0.10730.20348 .
7. Optionally, change the value of OfficeClientEdition to "32" to install 32-bit
Office, and change the Language ID value as needed to install Office in a different
language.

8. Open a command prompt as an administrator.

9. Navigate to the folder with the setup.exe and config.xml files.

10. Run the following command.

command line

setup.exe /configure config.xml

This command installs Office. The process may take several minutes.

11. Clear the Office cache.

) Important

After installation, be sure that you turn off automatic updating of Office, so that
Office isn't updated to a version that doesn't use webview you want to work with
before you've completed using it. This can happen within minutes of installation.
Follow these steps.

1. Start any Office application and open a new document.


2. Open the File tab on the ribbon, and then select Office Account or Account.
3. In the Product Information column, select Update Options, and then select
Disable Updates. If that option isn't available, then Office is already
configured to not update automatically.

When you are finished using the old version of Office, reinstall your newer version by
editing the config.xml file and changing the Version to the build number that you
copied earlier. Then repeat the setup.exe /configure config.xml command in an
administrator command prompt. Optionally, re-enable automatic updates.
Debug add-ins using developer tools in
Microsoft Edge (Chromium-based)
Article • 05/20/2023

This article shows how to debug the client-side code (JavaScript or TypeScript) of your
add-in when the following conditions are met.

You cannot, or don't wish to, debug using tools built into your IDE; or you are
encountering a problem that only occurs when the add-in is run outside the IDE.
Your computer is using a combination of Windows and Office versions that use the
Edge (Chromium-based) webview control, WebView2.

 Tip

For information about debugging with Edge WebView2 (Chromium-based) inside


Visual Studio Code, see Debug add-ins on Windows using Visual Studio Code and
Microsoft Edge WebView2 (Chromium-based).

To determine which webview you're using, see Browsers and webview controls used by
Office Add-ins.

 Tip

In recent versions of Office, one way to identify the webview control that Office is
using is through the personality menu on any add-in where it's available. (The
personality menu isn't supported in Outlook.) Open the menu and select Security
Info. In the Security Info dialog on Windows, the Runtime reports Microsoft Edge,
Microsoft Edge Legacy, or Internet Explorer. The runtime isn't included on the
dialog in older versions of Office.

Debug a task pane add-in using Microsoft Edge


(Chromium-based) developer tools

7 Note

If your add-in has an add-in command that executes a function, the function runs
in a hidden browser runtime process that the Microsoft Edge (Chromium-based)
developer tools cannot be launched from, so the technique described in this article
cannot be used to debug code in the function.

1. Sideload and run the add-in.

2. Run the Microsoft Edge (Chromium-based) developer tools by one of these


methods:

Be sure the add-in's task pane has focus and press Ctrl+Shift+I.
Right-click the task pane to open the context menu and select Inspect, or
open the personality menu and select Attach Debugger. (The personality
menu isn't supported in Outlook.)

3. Open the Sources tab.

4. Open the file that you want to debug with the following steps.
a. On the far right of the tool's top menu bar, select the ... button and then select
Search.
b. Enter a line of code from the file you want to debug in the search box. It should
be something that's not likely to be in any other file.
c. Select the refresh button.
d. In the search results, select the line to open the code file in the pane above the
search results.
5. To set a breakpoint, select the line number of the line in the code file. A red dot
appears by the line in the code file. In the debugger window to the right, the
breakpoint is registered in the Breakpoints drop down.

6. Execute functions in the add-in as needed to trigger the breakpoint.

 Tip

For more information about using the tools, see Microsoft Edge Developer Tools
overview.

Debug a dialog in an add-in


If your add-in uses the Office Dialog API, the dialog runs in a separate process from the
task pane (if any) and the tool must be started from that separate process. Follow these
steps.

1. Run the add-in.

2. Open the dialog and be sure it has focus.


3. Open the Microsoft Edge (Chromium-based) developer tools by one of these
methods:

Press Ctrl+Shift+I or F12.


Right-click the dialog to open the context menu and select Inspect.

4. Use the tool the same as you would for code in a task pane. See Debug a task
pane add-in using Microsoft Edge (Chromium-based) developer tools earlier in this
article.
Debug add-ins on Windows using Visual
Studio Code and Microsoft Edge
WebView2 (Chromium-based)
Article • 12/12/2022

Office Add-ins running on Windows can debug against the Edge Chromium WebView2
runtime directly in Visual Studio Code.

) Important

This article only applies when Office runs add-ins in the Microsoft Edge Chromium
WebView2 runtime, as explained in Browsers and webview controls used by Office
Add-ins. For instructions about debugging in Visual Studio Code against Microsoft
Edge Legacy with the original WebView (EdgeHTML) runtime, see Office Add-in
Debugger Extension for Visual Studio Code.

 Tip

If you cannot, or don't wish to, debug using tools built into Visual Studio Code; or
you are encountering a problem that only occurs when the add-in is run outside
Visual Studio Code, you can debug Edge Chromium WebView2 runtime by using
the Edge (Chromium-based) developer tools as described in Debug add-ins using
developer tools for Microsoft Edge WebView2.

This debugging mode is dynamic, allowing you to set breakpoints while code is running.
See changes in your code immediately while the debugger is attached, all without losing
your debugging session. Your code changes also persist, so you see the results of
multiple changes to your code. The following image shows this extension in action.
Prerequisites
Visual Studio Code
Node.js (version 10+)
Windows 10, 11
A combination of platform and Office application that supports Microsoft Edge
with WebView2 (Chromium-based) as explained in Browsers and webview controls
used by Office Add-ins. If your version of Office from a Microsoft 365 subscription
is earlier than Version 2101, you will need to install WebView2. Use the instructions
for installing it at Microsoft Edge WebView2 / Embed web content ... with
Microsoft Edge WebView2.

Use the Visual Studio Code debugger


These instructions assume you have experience using the command line, understand
basic JavaScript, and have created an Office Add-in project before using the Yeoman
generator for Office Add-ins. If you haven't done this before, consider visiting one of our
tutorials, such as the Excel Office Add-in tutorial.

1. The first step depends on the project and how it was created.

If you want to create a project to experiment with debugging in Visual Studio


Code, use the Yeoman generator for Office Add-ins. Use any one of our quick
start guides, such as the Outlook add-in quick start, in order to do this.
If you want to debug an existing project that was created with Yo Office, skip
to the next step.
If you want to debug an existing project that was not created with Yo Office,
complete the procedure in the Appendix A and then return to the next step
of this procedure.

2. Open VS Code and open your project in it.

3. Choose View > Run or enter Ctrl+Shift+D to switch to debug view.

4. From the RUN AND DEBUG options, choose the Edge Chromium option for your
host application, such as Outlook Desktop (Edge Chromium). Select F5 or choose
Run > Start Debugging from the menu to begin debugging. This action
automatically launches a local server in a Node window to host your add-in and
then automatically opens the host application, such as Excel or Word. This may
take several seconds.

 Tip

If you aren't using a project created with Yo Office, you may be prompted to
adjust a registry key. While in the root folder of your project, run the following
in the command line.

command line

npx office-addin-debugging start <your manifest path>

) Important

If your project was created with older versions of Yo Office, you may see the
following error dialog box about 10 - 30 seconds after you start debugging
(at which point you may have already gone on to another step in this
procedure) and it may be hidden behind the dialog box described in the next
step.

Complete the tasks in Appendix B and then restart this procedure.


5. In the host application, your add-in is now ready to use. Select Show Taskpane or
run any other add-in command. A dialog box will appear with text similar to the
following:

WebView Stop On Load. To debug the webview, attach VS Code to the


webview instance using the Microsoft Debugger for Edge extension, and click
OK to continue. To prevent this dialog from appearing in the future, click
Cancel.

Select OK.

7 Note

If you select Cancel, the dialog won't be shown again while this instance of
the add-in is running. However, if you restart your add-in, you'll see the dialog
again.

6. You're now able to set breakpoints in your project's code and debug. To set
breakpoints in Visual Studio Code, hover next to a line of code and select the red
circle that appears.
7. Run functionality in your add-in that calls the lines with breakpoints. You'll see that
breakpoints have been hit and you can inspect local variables.

7 Note

Breakpoints in calls of Office.initialize or Office.onReady are ignored. For


details about these functions, see Initialize your Office Add-in.

) Important

The best way to stop a debugging session is to select Shift+F5 or choose Run >
Stop Debugging from the menu. This action should close the Node server window
and attempt to close the host application, but there will be a prompt on the host
application asking you whether to save the document or not. Make an appropriate
choice and let the host application close. Avoid manually closing the Node window
or host application. Doing so can cause bugs especially when you are stopping and
starting debugging sessions repeatedly.

If debugging stops working; for example, if breakpoints are being ignored; stop
debugging. Then, if necessary, close all host application windows and the Node
window. Finally, close Visual Studio Code and reopen it.

Appendix A
If your project was not created with Yo Office, you need to create a debug configuration
for Visual Studio Code.

1. Create a file named launch.json in the \.vscode folder of the project if there isn't
one there already.

2. Ensure that the file has a configurations array. The following is a simple example
of a launch.json .

JSON

{
// Other properties may be here.

"configurations": [

// Configuration objects may be here.

]
// Other properties may be here.
}

3. Add the following object to the configurations array.

JSON

{
"name": "$HOST$ Desktop (Edge Chromium)",
"type": "pwa-msedge",
"request": "attach",
"useWebView": true,
"port": 9229,
"timeout": 600000,
"webRoot": "${workspaceRoot}",
"preLaunchTask": "Debug: Excel Desktop",
"postDebugTask": "Stop Debug"
},

4. Replace the placeholder $HOST$ with the name of the Office application that the
add-in runs in; for example, Outlook or Word .

5. Save and close the file.

Appendix B
1. In the error dialog box, select the Cancel button.
2. If debugging doesn't stop automatically, select Shift+F5 or choose Run > Stop
Debugging from the menu.
3. Close the Node window where the local server is running, if it doesn't close
automatically.
4. Close the Office application if it doesn't close automatically.
5. Open the \.vscode\launch.json file in the project.
6. In the configurations array, there are several configuration objects. Find the one
whose name has the pattern $HOST$ Desktop (Edge Chromium) , where $HOST$ is an
Office application that your add-in runs in; for example, Outlook Desktop (Edge
Chromium) or Word Desktop (Edge Chromium) .

7. Change the value of the "type" property from "edge" to "pwa-msedge" .


8. Change the value of the "useWebView" property from the string "advanced" to the
boolean true (note there are no quotation marks around the true ).
9. Save the file.
10. Close VS Code.
See also
Test and debug Office Add-ins
Debug add-ins on Windows using Visual Studio Code and Microsoft Edge legacy
WebView (EdgeHTML)
Debug add-ins using developer tools for Internet Explorer
Debug add-ins using developer tools for Edge Legacy
Debug add-ins using developer tools in Microsoft Edge (Chromium-based)
Attach a debugger from the task pane
Runtimes in Office Add-ins
Microsoft Office Add-in Debugger
Extension for Visual Studio Code
Article • 11/01/2022

Office Add-ins running on Windows can use the Office Add-in Debugger Extension in
Visual Studio Code to debug against Microsoft Edge Legacy with the original WebView
(EdgeHTML) runtime.

) Important

This article only applies when Office runs add-ins in the original WebView
(EdgeHTML) runtime, as explained in Browsers and webview controls used by
Office Add-ins. For instructions about debugging in Visual Studio code against
Microsoft Edge WebView2 (Chromium-based), see Microsoft Office Add-in
Debugger Extension for Visual Studio Code.

 Tip

If you cannot, or don't wish to, debug using tools built into Visual Studio Code; or
you are encountering a problem that only occurs when the add-in is run outside
Visual Studio Code, you can debug Edge Legacy (EdgeHTML) runtime by using the
Edge Legacy developer tools as described in Debug add-ins using developer tools
in Microsoft Edge Legacy.

This debugging mode is dynamic, allowing you to set breakpoints while code is running.
You can see changes in your code immediately while the debugger is attached, all
without losing your debugging session. Your code changes also persist, so you can see
the results of multiple changes to your code. The following image shows this extension
in action.
Prerequisites
Visual Studio Code
Node.js (version 10+)
Windows 10, 11
Microsoft Edge A combination of platform and Office application that supports
Microsoft Edge Legacy with with the original webview (EdgeHTML) as explained in
Browsers and webview controls used by Office Add-ins.

Install and use the debugger


These instructions assume you have experience using the command line, understand
basic JavaScript, and have created an Office Add-in project before using the Yeoman
generator for Office Add-ins. If you haven't done this before, consider visiting one of our
tutorials, like this Excel Office Add-in tutorial.

1. The first step depends on the project and how it was created.

If you want to create a project to experiment with debugging in Visual Studio


Code, use the Yeoman generator for Office Add-ins. Use any one of our quick
start guides, such as the Outlook add-in quick start, in order to do this.
If you want to debug an existing project that was created with Yo Office, skip
to the next step.
If you want to debug an existing project that was not created with Yo Office,
carry out the procedure in the Appendix and then return to the next step of
this procedure.

2. Open VS Code and open your project in it.

3. Within VS Code, select Ctrl+Shift+X to open the Extensions bar. Search for the
"Microsoft Office Add-in Debugger" extension and install it.

4. Choose View > Run or enter Ctrl+Shift+D to switch to debug view.

5. From the RUN AND DEBUG options, choose the Edge Legacy option for your host
application, such as Outlook Desktop (Edge Legacy). Select F5 or choose Run >
Start Debugging from the menu to begin debugging. This action automatically
launches a local server in a Node window to host your add-in and then
automatically opens the host application, such as Excel or Word. This may take
several seconds.

6. In the host application, your add-in is now ready to use. Select Show Taskpane or
run any other add-in command. A dialog box will appear similar to the following:

WebView Stop On Load. To debug the WebView, attach VS Code to the


WebView instance using the Microsoft Debugger for Edge extension, and click
OK to continue. To prevent this dialog from appearing in the future, click
Cancel.

Select OK.

7 Note

If you select Cancel, the dialog won't be shown again while this instance of
the add-in is running. However, if you restart your add-in, you'll see the dialog
again.

7. Set a breakpoint in your project's task pane file. To set breakpoints in Visual Studio
Code, hover next to a line of code and select the red circle that appears.
8. Run functionality in your add-in that calls the lines with breakpoints. You'll see that
breakpoints have been hit and you can inspect local variables.

7 Note

Breakpoints in calls of Office.initialize or Office.onReady are ignored. For


details about these methods, see Initialize your Office Add-in.

) Important

The best way to stop a debugging session is to select Shift+F5 or choose Run >
Stop Debugging from the menu. This action should close the Node server window
and attempt to close the host application, but there will be a prompt on the host
application asking you whether to save the document or not. Make an appropriate
choice and let the host application close. Avoid manually closing the Node window
or host application. Doing so can cause bugs especially when you are stopping and
starting debugging sessions repeatedly.

If debugging stops working; for example, if breakpoints are being ignored; stop
debugging. Then, if necessary, close all host application windows and the Node
window. Finally, close Visual Studio Code and reopen it.
Appendix
If your project was not created with Yo Office, you need to create a debug configuration
for Visual Studio Code.

1. Create a file named launch.json in the \.vscode folder of the project if there isn't
one there already.

2. Ensure that the file has a configurations array. The following is a simple example
of a launch.json .

JSON

{
// Other properties may be here.

"configurations": [

// Configuration objects may be here.

// Other properties may be here.


}

3. Add the following object to the configurations array.

JSON

{
"name": "HOST Desktop (Edge Legacy)",
"type": "office-addin",
"request": "attach",
"url": "https://localhost:3000/taskpane.html?
_host_Info=HOST$Win32$16.01$en-US$$$$0",
"port": 9222,
"timeout": 600000,
"webRoot": "${workspaceRoot}",
"preLaunchTask": "Debug: HOST Desktop",
"postDebugTask": "Stop Debug"
}

4. Replace the placeholder HOST in all three places with the name of the Office
application that the add-in runs in; for example, Outlook or Word .

5. Save and close the file.


See also
Test and debug Office Add-ins
Debug add-ins on Windows using Visual Studio Code and Microsoft Edge
WebView2 (Chromium-based).
Debug add-ins using developer tools for Internet Explorer
Debug add-ins using developer tools for Edge Legacy
Debug add-ins using developer tools in Microsoft Edge (Chromium-based)
Attach a debugger from the task pane
Runtimes in Office Add-ins
Debug Office Add-ins in Visual Studio
Article • 05/20/2023

This article describes how to debug client-side code in Office Add-ins that are created
with one of the Office Add-in project templates in Visual Studio 2022. For information
about debugging server-side code in Office Add-ins, see Overview of debugging Office
Add-ins - Server-side or client-side?.

7 Note

You can't use Visual Studio to debug add-ins in Office on Mac. For information
about debugging on a Mac, see Debug Office Add-ins on a Mac.

Review the build and debug properties


Before you start debugging, review the properties of each project to confirm that Visual
Studio will open the desired Office application and that other build and debug
properties are set appropriately.

Add-in project properties


Open the Properties window for the add-in project to review project properties.

1. In Solution Explorer, choose the add-in project (not the web application project).

2. From the menu bar, choose View > Properties Window.

The following table describes the properties of the add-in project.

Property Description

Start Action Specifies the debug mode for your add-in. This should be set to Microsoft
Edge for an Outlook add-in. For all other Office applications, it should be set to
Office Desktop Client.

Start Specifies what document to open when you start the project. In a new project,
Document this is set to [New Excel Workbook], [New Word Document], or [New
(Excel, PowerPoint Presentation]. To specify a particular document, follow the steps in
PowerPoint, and Use an existing document to debug the add-in.
Word add-ins
only)
Property Description

Web Project Specifies the name of the web project associated with the add-in.

Email Address Specifies the email address of the user account in Exchange Server or Exchange
(Outlook add- Online that you want to use to test your Outlook add-in. If left blank, you'll be
ins only) prompted for the email address when you start debugging.

EWS Url Specifies the Exchange Web Services URL (For example:
(Outlook add- https://www.contoso.com/ews/exchange.aspx ). This property can be left blank.
ins only)

OWA Url Specifies the Outlook on the web URL (For example:
(Outlook add- https://www.contoso.com/owa ). This property can be left blank.
ins only)

Use multi- Specifies the boolean value that indicates whether multi-factor authentication
factor auth should be used. The default is false, but the property has no practical effect. If
(Outlook add- you normally have to provide a second factor to login to the email account,
ins only) you'll be prompted to when you start debugging.

User Name Specifies the name of the user account in Exchange Server or Exchange Online
(Outlook add- that you want to use to test your Outlook add-in. This property can be left
ins only) blank.

Project File Specifies the name of the file containing build, configuration, and other
information about the project.

Project Folder Specifies the location of the project file.

7 Note

For an Outlook add-in, you may choose to specify values for one or more of the
Outlook add-in only properties in the Properties window, but doing so isn't
required.

Web application project properties


Open the Properties window for the web application project to review project
properties.

1. In Solution Explorer, choose the web application project.

2. From the menu bar, choose View > Properties Window.

The following table describes the properties of the web application project that are most
relevant to Office Add-in projects.
Property Description

SSL Specifies whether SSL is enabled on the site. This property should be set to True for
Enabled Office Add-in projects.

SSL URL Specifies the secure HTTPS URL for the site. Read-only.

URL Specifies the HTTP URL for the site. Read-only.

Project Specifies the name of the file containing build, configuration, and other information
File about the project.

Project Specifies the location of the project file. Read-only. The manifest file that Visual
Folder Studio generates at runtime is written to the bin\Debug\OfficeAppManifests folder in
this location.

Debug an Excel, PowerPoint, or Word add-in


project
This section describes how to start and debug an Excel, PowerPoint, or Word add-in.

Start the Excel, PowerPoint, or Word add-in project


Start the project by choosing Debug > Start Debugging from the menu bar or press the
F5 button. Visual Studio will automatically build the solution and start the Office host
application.

When Visual Studio builds the project, it performs the following tasks:

1. Creates a copy of the XML manifest file and adds it to the


_ProjectName_\bin\Debug\OfficeAppManifests directory. The Office application that

hosts your add-in consumes this copy when you start Visual Studio and debug the
add-in.

2. Creates a set of registry entries on your Windows computer that enables the add-
in to appear in the Office application.

3. Builds the web application project, and then deploys it to the local IIS web server
( https://localhost ).

4. If this is the first add-in project that you have deployed to the local IIS web server,
you may be prompted to install a Self-Signed Certificate to the current user's
Trusted Root Certificate store. This is required for IIS Express to display the content
of your add-in correctly.
7 Note

If Office uses the Edge Legacy webview control (EdgeHTML) to run add-ins on your
Windows computer, Visual Studio may prompt you to add a local network loopback
exemption. This is required for the webview control to be able to access the
website deployed to the local IIS web server. You can also change this setting
anytime in Visual Studio under Tools > Options > Office Tools (Web) > Web Add-
In Debugging. To find out what webview control is used on your Windows
computer, see Browsers and webview controls used by Office Add-ins.

Next, Visual Studio does the following:

1. Modifies the SourceLocation element of the XML manifest file (that was copied to
the _ProjectName_\bin\Debug\OfficeAppManifests directory) by replacing the
~remoteAppUrl token with the fully qualified address of the start page (for example,

https://localhost:44302/Home.html ).

2. Starts the web application project in IIS Express.

3. Validates the manifest.

) Important

The Office manifest XSD files that Visual Studio installs are out-of-date. If you
get validation errors for the manifest, your first troubleshooting step should
be to replace one or more of these files with the latest versions. For detailed
instructions, see Manifest schema validation errors in Visual Studio projects.

4. Opens the Office application and sideloads your add-in.

Debug the Excel, PowerPoint, or Word add-in


The best method for debugging an add-in in Visual Studio 2022 depends on whether
the add-in is running in WebView2, which is the webview control that is associated with
Microsoft Edge (Chromium), or an older webview control. To determine which webview
control is being used, see Browsers and webview controls used by Office Add-ins. If your
computer is using WebView2, see Use the built-in Visual Studio debugger. For any other
webview control, see Use the browser developer tools.

Use the built-in Visual Studio debugger


1. Set breakpoints, as needed, in the source JavaScript or TypeScript files. You can do
this either before or after you start the add-in as described in the earlier section
Start the Excel, PowerPoint, or Word add-in project.

2. When the add-in is running, use the add-in's UI to run the code that contains your
breakpoints.

) Important

Breakpoints set in Office.initialize or Office.onReady aren't hit. To debug these


methods, see Debug the initialize and onReady functions.

 Tip

If you encounter any problems, there's more information at Debug a JavaScript or


TypeScript app in Visual Studio.

Use the browser developer tools


1. Follow the steps in the earlier section Start the Excel, PowerPoint, or Word add-in
project.

2. Launch the add-in in the Office application if it isn't already open. For example, if
it's a task pane add-in, it will have added a button to the Home ribbon (for
example, a Show Taskpane button). Select the button on the ribbon.

7 Note

If your add-in isn't sideloaded by Visual Studio, you can sideload it manually.
In Excel, PowerPoint, or Word, choose the Insert tab, and then choose the
down-arrow located to the right of My Add-ins.

In the list of available add-ins, find the Developer Add-ins section and select
the your add-in to register it.

 Tip
The task pane may appear blank when it first opens. If so, it should render
correctly when you launch the debugging tools in a later step.

3. Open the personality menu and then choose Attach a debugger. This will open the
debugging tools for the webview control that Office is using to run add-ins on
your Windows computer. You can set breakpoints and step through code as
described in one of the following articles:

Debug add-ins using developer tools for Internet Explorer


Debug add-ins using developer tools for Edge Legacy
Debug add-ins using developer tools in Microsoft Edge (Chromium-based)

4. To make changes to your code, first stop the debugging session in Visual Studio
and close the Office application. Make your changes, and start a new debugging
session.

Debug an Outlook add-in project


This section describes how to start and debug an Outlook add-in. Debugging is done in
Outlook on the web, not Outlook on Windows.

Start the Outlook add-in project


Start the project by choosing Debug > Start Debugging from the menu bar or press the
F5 button. Visual Studio will automatically build the solution and launch the Outlook
page of your Microsoft 365 tenancy.

When Visual Studio builds the project it performs the following tasks.

1. Prompts you for login credentials. If you're asked to sign in repeatedly or if you
receive an error that you are unauthorized, then Basic Auth may be disabled for
accounts on your Microsoft 365 tenant. In this case, try using a Microsoft account
instead. You can also try setting the property Use multi-factor auth to True in the
Outlook Web Add-in project properties pane. See Add-in project properties.

2. Creates a copy of the XML manifest file and adds it to the


_ProjectName_\bin\Debug\OfficeAppManifests directory. Outlook consumes this

copy when you start Visual Studio and debug the add-in.

3. Builds the web application project, and then deploys it to the local IIS web server
( https://localhost ).
4. If this is the first add-in project that you have deployed to the local IIS web server,
you may be prompted to install a Self-Signed Certificate to the current user's
Trusted Root Certificate store. This is required for IIS Express to display the content
of your add-in correctly.

7 Note

If Office uses the Edge Legacy webview control (EdgeHTML) to run add-ins on your
Windows computer, Visual Studio may prompt you to add a local network loopback
exemption. This is required for the webview control to be able to access the
website deployed to the local IIS web server. You can also change this setting
anytime in Visual Studio under Tools > Options > Office Tools (Web) > Web Add-
In Debugging. To find out what webview control is used on your Windows
computer, see Browsers and webview controls used by Office Add-ins.

Next, Visual Studio does the following:

1. Modifies the SourceLocation element of the XML manifest file (that was copied to
the _ProjectName_\bin\Debug\OfficeAppManifests directory) by replacing the
~remoteAppUrl token with the fully qualified address of the start page (for example,
https://localhost:44302/Home.html ).

2. Starts the web application project in IIS Express.

3. Validates the manifest.

) Important

The Office manifest XSD files that Visual Studio installs are out-of-date. If you
get validation errors for the manifest, your first troubleshooting step should
be to replace one or more of these files with the latest versions. For detailed
instructions, see Manifest schema validation errors in Visual Studio projects.

4. Opens the Outlook page of your Microsoft 365 tenancy in Microsoft Edge.

Debug the Outlook add-in


The best method for debugging an add-in in Visual Studio 2022 depends on whether
the add-in is running in WebView2, which is the webview control that is associated with
Microsoft Edge (Chromium), or an older webview control. To determine which webview
control is being used, see Browsers and webview controls used by Office Add-ins. If your
computer is using WebView2, see Use the built-in Visual Studio debugger. For any other
webview control, see Use the browser developer tools.

Use the built-in Visual Studio debugger

1. Set breakpoints, as needed, in the source JavaScript or TypeScript files. You can do
this either before or after you start the add-in as described in the earlier section
Start the Outlook add-in project.

2. When the add-in is running, use the add-in's UI to run the code that contains your
breakpoints.

 Tip

Sometimes in Outlook on the web, the Visual Studio debugger doesn't attach.
If you get errors by the breakpoints that indicate they won't be hit, use the
browser developer tools to attach to the Visual Studio debugger: After you
have pressed F5 to start debugging and Outlook on the web has opened,
follow the first four steps in the Use the browser developer tools in Outlook
on the web. (Use the instructions for Microsoft Edge (Chromium-based).)
After you set a breakpoint in the browser tools and it's hit, execution will
pause on the breakpoint in both the browser tools and in Visual Studio. This
indicates that the Visual Studio debugger is attached. At this point, you can
close the browser tools and add breakpoints in Visual Studio as you normally
would.
If you encounter any problems, there's more information at Debug a
JavaScript or TypeScript app in Visual Studio.

Use the browser developer tools in Outlook on the web


1. In the Outlook page, select an email message or appointment item to open it in its
own window.

2. Press F12 to open the Edge debugging tool.

3. After the tool is open, launch the add-in. For example, in the toolbar at the top of a
message, select the More apps button, and then select your add-in from the
callout that opens.
4. Use the instructions in one of the following articles to set breakpoints and step
through code. They each have a link to more detailed guidance.

Debug add-ins using developer tools for Edge Legacy


Debug add-ins using developer tools in Microsoft Edge (Chromium-based)

 Tip

To debug code that runs in the Office.initialize function or an


Office.onReady function that runs when the add-in opens, set your

breakpoints, and then close and reopen the add-in. For more information
about these functions, see Initialize your Office Add-in.

5. To make changes to your code, first stop the debugging session in Visual Studio
and close the Outlook pages. Make your changes, and start a new debugging
session.

Use an existing document to debug the add-in


If you have a document that contains test data you want to use while debugging your
Excel, PowerPoint, or Word add-in, Visual Studio can be configured to open that
document when you start the project. To specify an existing document to use while
debugging the add-in, complete the following steps.

1. In Solution Explorer, choose the add-in project (not the web application project).

2. From the menu bar, choose Project > Add Existing Item.
3. In the Add Existing Item dialog box, locate and select the document that you want
to add.

4. Choose the Add button to add the document to your project.

5. In Solution Explorer, choose the add-in project (not the web application project).

6. From the menu bar, choose View > Properties Window.

7. In the Properties window, choose the Start Document list, and then select the
document that you added to the project. The project is now configured to start the
add-in in that document.

Next steps
After your add-in is working as desired, see Deploy and publish your Office Add-in to
learn about the ways you can distribute the add-in to users.
Test your Office Add-in on Trident
Article • 05/20/2023

If you plan to support older versions of Windows and Office, your add-in must work in
the embeddable browser control called "Trident" that's provided by Internet Explorer 11.
You can use a command line to switch from a more modern webview used by add-ins to
Trident for this testing. For information about which versions of Windows and Office use
the Internet Explorer 11 webview control, see Browsers and webview controls used by
Office Add-ins. In this article, "webview" refers to the combination of a webview control
and a JavaScript engine.

) Important

Webviews from Internet Explorer and Microsoft Edge Legacy are still used in
Office Add-ins

Some combinations of platforms and Office versions, including volume-licensed


perpetual versions through Office 2019, still use the webview controls that come
with Internet Explorer 11 (called "Trident") and Microsoft Edge Legacy (called
"EdgeHTML") to host add-ins, as explained in Browsers and webview controls used
by Office Add-ins. Internet Explorer 11 was disabled in Windows 10 and Windows
11 in February 2023, and the UI for launching it was removed; but it's still installed
on with those operating systems. So, Trident and other functionality from Internet
Explorer can still be called programmatically by Office.

We recommend (but don't require) that you support these combinations, at least in
a minimal way, by providing users of your add-in a graceful failure message when
your add-in is launched in these webviews. Keep these additional points in mind:

Office on the web no longer opens in Internet Explorer or Microsoft Edge


Legacy. Consequently, AppSource doesn't test add-ins in Office on the web
on these browsers.
AppSource still tests for combinations of platform and Office desktop versions
that use Trident or EdgeHTML. However, it only issues a warning when the
add-in doesn't support these webviews; the add-in isn't rejected by
AppSource.
The Script Lab tool no longer supports Trident.
If you plan to support older versions of Windows and Office, your add-in must work in
the embeddable browser control called "Trident" that's provided by Internet Explorer 11.
You can use a command line to switch from a more modern webview used by add-ins to
Trident for this testing. For information about which versions of Windows and Office use
the Internet Explorer 11 webview control, see Browsers and webview controls used by
Office Add-ins.

) Important

Trident doesn't support JavaScript versions later than ES5. If you want to use the
syntax and features of ECMAScript 2015 or later, you have to use a transpiler or
polyfill or both. For more information about these options, see Support older
Microsoft webviews and Office versions.

Also, Trident doesn't support some HTML5 features such as media, recording, and
location. To learn more, see Determine the webview the add-in is running in at
runtime.

7 Note

Office on the web can't be opened in Internet Explorer 11, so you can't (and
don't need to) test your add-in on Office on the web with Internet Explorer.

Internet Explorer's Enhanced Security Configuration (ESC) must be turned off


for Office Web Add-ins to work. If you're using a Windows Server computer as
your client when developing add-ins, note that ESC is turned on by default in
Windows Server.

Switch to the Trident webview

 Tip

In recent versions of Office, one way to identify the webview control that Office is
using is through the personality menu on any add-in where it's available. (The
personality menu isn't supported in Outlook.) Open the menu and select Security
Info. In the Security Info dialog on Windows, the Runtime reports Microsoft Edge,
Microsoft Edge Legacy, or Internet Explorer. The runtime isn't included on the
dialog in older versions of Office.
There are two ways to switch the Trident webview. You can run a simple command in a
command prompt, or you can install a version of Office that uses Trident by default. We
recommend the first method, but you should use the second in the following scenarios.

Your project was developed with Visual Studio and IIS. It isn't node.js-based.
You want to be absolutely robust in your testing.
You can't use the Beta channel for Microsoft 365 on your development computer.
You're developing on a Mac.
If for any reason the command line tool doesn't work.

Switch via the command line


If your project is node.js-based (that is, not developed with Visual Studio and Internet
Information server (IIS)), you can force Office on Windows to use either the EdgeHTML
webview control that is provided by Edge Legacy or the Trident webview control that is
provided by Internet Explorer to run add-ins, even if you have a combination of
Windows and Office versions that would normally use a more recent webview. For more
information about which browsers and webviews are used by various combinations of
Windows and Office versions, see Browsers and webview controls used by Office Add-
ins.

7 Note

The tool that's used to force the change in webview is supported only in the Beta
subscription channel of Microsoft 365. Join the Microsoft 365 Insider program
and select the Beta Channel option to access Office Beta builds. See also About
Office: What version of Office am I using? .

Strictly, it's the webview switch of this tool (see Step 2) that requires the Beta
channel. The tool has other switches that don't have this requirement.

1. If your project was not created with the Yeoman generator for Office Add-ins tool,
you need to install the office-addin-dev-settings tool. Run the following command
in a command prompt.

command line

npm install office-addin-dev-settings --save-dev

) Important
The office-addin-dev-settings tool is not supported on Mac.

2. Specify the webview that you want Office to use with the following command in a
command prompt in the root of the project. Replace <path-to-manifest> with the
relative path, which is just the manifest filename if it's in the root of the project.
Replace <webview> with either ie or edge-legacy . Note that the options are
named after the browsers in which the webviews originated. The ie option means
"Trident" and the edge-legacy option means "EdgeHTML".

command line

npx office-addin-dev-settings webview <path-to-manifest> <webview>

The following is an example.

command line

npx office-addin-dev-settings webview manifest.xml ie

You should see a message in the command line that the webview type is now set
to IE (or Edge Legacy).

3. When you're finished, set Office to resume using the default webview for your
combination of Windows and Office versions with the following command.

command line

npx office-addin-dev-settings webview <path-to-manifest> default

Install a version of Office that uses Internet Explorer


Use the following procedure to install either a version of Office (downloaded from a
Microsoft 365 subscription) that uses the Microsoft Edge Legacy webview (EdgeHTML)
to run add-ins or a version that uses Internet Explorer (Trident).

1. In any Office application, open the File tab on the ribbon, and then select Office
Account or Account. Select the About host-name button (for example, About
Word).

2. On the dialog that opens, find the full xx.x.xxxxx.xxxxx build number and make a
copy of it somewhere.
3. Download the Office Deployment Tool .

4. Run the downloaded file to extract the tool. You are prompted to choose where to
install the tool.

5. In the folder where you installed the tool (where the setup.exe file is located),
create a text file with the name config.xml and add the following contents.

XML

<Configuration>
<Add OfficeClientEdition="64" Channel="SemiAnnual"
Version="16.0.xxxxx.xxxxx">
<Product ID="O365ProPlusRetail">
<Language ID="en-us" />
</Product>
</Add>
</Configuration>

6. Change the Version value.

To install a version that uses EdgeHTML, change it to 16.0.11929.20946 .


To install a version that uses Trident, change it to 16.0.10730.20348 .

7. Optionally, change the value of OfficeClientEdition to "32" to install 32-bit


Office, and change the Language ID value as needed to install Office in a different
language.

8. Open a command prompt as an administrator.

9. Navigate to the folder with the setup.exe and config.xml files.

10. Run the following command.

command line

setup.exe /configure config.xml

This command installs Office. The process may take several minutes.

11. Clear the Office cache.

) Important

After installation, be sure that you turn off automatic updating of Office, so that
Office isn't updated to a version that doesn't use webview you want to work with
before you've completed using it. This can happen within minutes of installation.
Follow these steps.

1. Start any Office application and open a new document.


2. Open the File tab on the ribbon, and then select Office Account or Account.
3. In the Product Information column, select Update Options, and then select
Disable Updates. If that option isn't available, then Office is already
configured to not update automatically.

When you are finished using the old version of Office, reinstall your newer version by
editing the config.xml file and changing the Version to the build number that you
copied earlier. Then repeat the setup.exe /configure config.xml command in an
administrator command prompt. Optionally, re-enable automatic updates.

See also
Test and debug Office Add-ins
Sideload Office Add-ins for testing
Debug add-ins using developer tools for Internet Explorer
Attach a debugger from the task pane
Runtimes in Office Add-ins
Window objects that are unsupported in
Office Add-ins
Article • 05/20/2023

For some versions of Windows and Office, add-ins run in a Trident (Internet Explorer 11)
webview runtime. (For details, see Browsers and webview controls used by Office Add-
ins.) Some properties or subproperties of the global window object are not supported in
Trident. These properties are disabled in add-ins to ensure that your add-in provides a
consistent experience to all users, regardless of which browser or webview control the
add-in is using. This also helps AngularJS load properly.

The following is a list of the disabled properties. The list is a work in progress. If you
discover additional window properties that do not work in add-ins, please use the
feedback tool below to tell us.

window.history.pushState

window.history.replaceState

See also
Browsers and webview controls used by Office Add-ins
Sideload Office Add-ins to Office on the
web
Article • 05/20/2023

When you sideload an add-in, you're able to install the add-in without first putting it in
an add-in catalog. This is useful when testing and developing your add-in because you
can see how your add-in will appear and function.

When you sideload an add-in on the web, the add-in's manifest is stored in the
browser's local storage, so if you clear the browser's cache, or switch to a different
browser, you have to sideload the add-in again.

The steps to sideload an add-in on the web vary based on the following factors.

The host application (for example, Excel, Word, Outlook)


What tool created the add-in project (for example, Visual Studio, Yeoman
generator for Office Add-ins, or neither)
Whether you are sideloading to Office on the web with a Microsoft account or with
an account in a Microsoft 365 tenant

In the following list, go to the section or article that matches your scenario. Note the
first scenario in the list applies to Outlook add-ins. The remaining scenarios apply to
non-Outlook add-ins.

If you're sideloading an Outlook add-in, see the article Sideload Outlook add-ins
for testing.

If you created the add-in using the Yeoman generator for Office Add-ins, see
Sideload a Yeoman-created add-in to Office on the web.

If you created the add-in using Visual Studio, see Sideload an add-in on the web
when using Visual Studio.

For all other cases, see one of the following sections.


If you're sideloading to Office on the web with a Microsoft account, see
Manually sideload an add-in to Office on the web.
If you're sideloading to Office on the web with an account in a Microsoft 365
tenant, see Sideload an add-in to Microsoft 365.

Sideload a Yeoman-created add-in to Office on


the web
This process is supported for Excel, OneNote, PowerPoint, and Word only. This example
project assumes you're using a project created with the Yeoman generator for Office
Add-ins.

1. Open Office on the web or OneDrive. Using the Create option, make a
document in Excel, OneNote, PowerPoint, or Word. In this new document, select
Share, select Copy Link, and copy the URL.

2. In the command line starting at the root directory of your project, run the
following command. Replace "{url}" with the URL that you copied.

command line

npm run start:web -- --document {url}

The following are examples.

npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCMfF1

WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

7 Note

If you are developing on a Mac, enclose the {url} in single quotation marks.
Do not do this on Windows.

If your add-in doesn't sideload in the document, manually sideload it by following


the instructions in Manually sideload add-ins to Office on the web.

3. The first time you use this method to sideload an add-in on the web, you'll see a
dialog asking you to enable developer mode. Select the checkbox for Enable
Developer Mode now and select OK.

4. You'll see a second dialog box, asking if you wish to register an Office Add-in
manifest from your computer. Select Yes.
5. Your add-in is installed. If it has an add-in command, it should appear on either the
ribbon or the context menu. If it's a task pane add-in without any add-in
commands, the task pane should appear.

Sideload an add-in on the web when using


Visual Studio
If you're using Visual Studio to develop your add-in, press F5 to open an Office
document in desktop Office, create a blank document, and sideload the add-in. When
you want to sideload to Office on the web, the process to sideload is similar to manual
sideloading to the web. The only difference is that you must update the value of the
SourceURL element, and possibly other elements, in your manifest to include the full
URL where the add-in is deployed.

1. In Visual Studio, choose View > Properties Window.

2. In the Solution Explorer, select the web project. This displays properties for the
project in the Properties window.

3. In the Properties window, copy the SSL URL.

4. In the add-in project, open the manifest XML file. Be sure you're editing the source
XML. For some project types, Visual Studio will open a visual view of the XML
which won't work for the next step.

5. Search and replace all instances of ~remoteAppUrl/ with the SSL URL you just
copied. You'll see several replacements depending on the project type, and the
new URLs will appear similar to https://localhost:44300/Home.html .

6. Save the XML file.

7. In the Solution Explorer, open the context menu of the web project (for example,
by right clicking on it) then choose Debug > Start new instance. This runs the web
project without launching Office.

8. From Office on the web, sideload the add-in using steps described in Manually
sideload an add-in to Office on the web.

Manually sideload an add-in to Office on the


web
This method doesn't use the command line and can be accomplished using commands
only within the host application (such as Excel).

1. Open Office on the web . Open a document in Excel, OneNote, PowerPoint, or


Word.

2. On the Insert tab, in the Add-ins section, choose Office Add-ins.

3. On the Office Add-ins dialog, select the MY ADD-INS tab, choose Manage My
Add-ins, and then Upload My Add-in.

4. Browse to the add-in manifest file, and then select Upload.

5. Verify that your add-in is installed. For example, if it has an add-in command, it
should appear on either the ribbon or the context menu. If it's a task pane add-in
that has no add-in commands, the task pane should appear.
7 Note

To test your Office Add-in with EdgeHTML (Microsoft Edge Legacy), an additional
configuration step is required. In a Windows Command Prompt, run the following
line: npx office-addin-dev-settings appcontainer EdgeWebView --loopback --yes .
This isn't required when Office is using the Chromium-based Edge WebView2. For
more information, see Browsers and webview controls used by Office Add-ins.

) Important

The office-addin-dev-settings tool is not supported on Mac.

Sideload an add-in to Microsoft 365


1. Sign in to your Microsoft 365 account.

2. Open the App Launcher on the left end of the toolbar and select Excel, OneNote,
PowerPoint, or Word, and then create a new document.

3. On the Insert tab, select the Add-ins button.

4. Follow steps 3 - 5 of the section Manually sideload an add-in to Office on the web.

Remove a sideloaded add-in


To remove an add-in sideloaded to Office on the web, simply clear your browser's cache.
If you make changes to your add-in's manifest (for example, update file names of icons
or text of add-in commands), you may need to clear your browser's cache and then re-
sideload the add-in using the updated manifest. Doing so allows Office on the web to
render the add-in as it's described by the updated manifest.

See also
Sideload Office Add-ins on Mac
Sideload Office Add-ins on iPad
Sideload Outlook add-ins for testing
Clear the Office cache
Debug add-ins in Office on the web
Article • 06/27/2023

This article describes how to use Office on the web to debug your add-ins. Use this
technique:

To debug add-ins on a computer that isn't running Windows or the Office desktop
client—for example, if you're developing on a Mac or Linux.
As an alternative debugging process if you can't, or don't wish to, debug in an IDE,
such as Visual Studio or Visual Studio Code.

This article assumes that you have an add-in project that needs to be debugged. If you
just want to practice debugging on the web, create a new project using one of the quick
starts for specific Office applications, such as this quick start for Word.

Debug your add-in


To debug your add-in by using Office on the web:

1. Run the project on localhost and sideload it to a document in Office on the web.
For detailed sideloading instructions, see Manually sideload Office Add-ins on the
web.

2. Open the browser's developer tools. This is usually done by pressing F12. Open the
debugger tool and use it to set breakpoints and watch variables. For detailed help
in using your browser's tool, see one of the following:

Firefox
Safari
Debug add-ins using developer tools in Microsoft Edge (Chromium-based)
Debug add-ins using developer tools for Edge Legacy

7 Note

Office on the web won't open in Internet Explorer.

Potential issues
The following are some issues that you might encounter as you debug.

Some JavaScript errors that you see might originate from Office on the web.
The browser might show an invalid certificate error that you'll need to bypass. The
process for doing this varies with the browser and the various browsers' UIs for
doing this change periodically. You should search the browser's help or search
online for instructions. (For example, search for "Microsoft Edge invalid certificate
warning".) Most browsers will have a link on the warning page that enables you to
click through to the add-in page. For example, Microsoft Edge has a "Go on to the
webpage (Not recommended)" link. But you'll usually have to go through this link
every time the add-in reloads. For a longer lasting bypass, see the help as
suggested.
If you set breakpoints in your code, Office on the web might throw an error
indicating that it's unable to save.

See also
Best practices for developing Office Add-ins
Troubleshoot user errors with Office Add-ins
Sideload Office Add-ins on Mac for
testing
Article • 02/22/2023

To see how your add-in will run on Office on Mac, you can sideload your add-in's
manifest. This action won't enable you to set breakpoints and debug your add-in's code
while it's running, but you can see how it behaves and verify that the UI is usable and
rendering appropriately.

7 Note

To sideload an Outlook add-in, see Sideload Outlook add-ins for testing.

Prerequisites for Office on Mac


A Mac running OS X v10.10 "Yosemite" or later with Office on Mac installed.

Word on Mac version 15.18 (160109).

Excel on Mac version 15.19 (160206).

PowerPoint on Mac version 15.24 (160614).

The manifest .xml file for the add-in you want to test.

Sideload an add-in in Office on Mac


1. Use Finder to sideload the manifest file. Open Finder and then enter
Command+Shift+G to open the Go to folder dialog.

2. Enter one of the following filepaths, based on the application you want to use for
sideloading. If the wef folder doesn't exist on your computer, create it.

For Word:
/Users/<username>/Library/Containers/com.microsoft.Word/Data/Documents/w

ef

For Excel:
/Users/<username>/Library/Containers/com.microsoft.Excel/Data/Documents/

wef
For PowerPoint:
/Users/<username>/Library/Containers/com.microsoft.Powerpoint/Data/Docum
ents/wef

7 Note

The remaining steps describe how to sideload a Word add-in.

3. Copy your add-in's manifest file to this wef folder.

4. Open Word (or restart Word if it's already running), then open a document.

5. On the Word ribbon, choose the Insert tab, and then select My Add-ins
(dropdown menu) in the Add-ins group. On the dropdown menu, choose your
add-in.

) Important
Sideloaded add-ins will not show up in the My Add-ins dialog box. They are
only visible within the drop-down menu (small down-arrow to the right of My
Add-ins on the Insert tab). Sideloaded add-ins are listed under the Developer
Add-ins heading in this menu.

6. Verify that your add-in is displayed in Word.

Remove a sideloaded add-in


You can remove a previously sideloaded add-in by clearing the Office cache on your
computer. Details on how to clear the cache for each platform and application can be
found in the article Clear the Office cache.

See also
Sideload Office Add-ins on iPad for testing
Debug Office Add-ins on a Mac
Sideload Outlook add-ins for testing
Sideload Office Add-ins on iPad for
testing
Article • 07/11/2022

To see how your add-in will run in Office on iOS, you can sideload your add-in's
manifest onto an iPad using iTunes. This action won't enable you to set breakpoints and
debug your add-in's code while it's running, but you can see how it behaves and verify
that the UI is usable and rendering appropriately.

7 Note

To sideload an Outlook add-in, see Sideload Outlook add-ins for testing.

Prerequisites for Office on iOS


A Windows or Mac computer with iTunes installed.

) Important

If you're running macOS Catalina, iTunes is no longer available so you


should follow the instructions in the section Sideload an add-in on Excel or
Word on iPad using macOS Catalina later in this article.

An iPad running iOS 8.2 or later with Excel or Word installed, and a sync cable.

The manifest .xml file for the add-in you want to test.

Sideload an add-in on Excel or Word on iPad


using iTunes
1. Use a sync cable to connect your iPad to your computer. If you're connecting the
iPad to your computer for the first time, you'll be prompted with Trust This
Computer?. Choose Trust to continue.

2. In iTunes, choose the iPad icon below the menu bar.

3. Under Settings on the left side of iTunes, choose Apps.


4. On the right side of iTunes, scroll down to File Sharing, and then choose Excel or
Word in the Add-ins column.

5. At the bottom of the Excel or Word Documents column, choose Add File, and
then select the manifest .xml file of the add-in you want to sideload.

6. Open the Excel or Word app on your iPad. If the Excel or Word app is already
running, choose the Home button, and then close and restart the app.

7. Open a document.

8. Choose Add-ins on the Insert tab. (On the Insert tab, you may need to scroll
horizontally until you see the Add-ins button.) Your sideloaded add-in is available
to insert under the Developer heading in the Add-ins UI.

Sideload an add-in on Excel or Word on iPad


using macOS Catalina

) Important

With the introduction of macOS Catalina, Apple discontinued iTunes on Mac


and integrated functionality required to sideload apps into Finder.
1. Use a sync cable to connect your iPad to your computer. If you're connecting the
iPad to your computer for the first time, you'll be prompted with Trust This
Computer?. Choose Trust to continue. You may also be asked if this is a new iPad
or if you're restoring one.

2. In Finder, under Locations, choose the iPad icon below the menu bar.

3. On the top of the Finder window, click on Files, and then locate Excel or Word.

4. From a different Finder window, drag and drop the manifest.xml file of the add-in
you want to side load onto the Excel or Word file in the first Finder window.

5. Open the Excel or Word app on your iPad. If the Excel or Word app is already
running, choose the Home button, and then close and restart the app.

6. Open a document.

7. Choose Add-ins on the Insert tab. (On the Insert tab, you may need to scroll
horizontally until you see the Add-ins button.) Your sideloaded add-in is available
to insert under the Developer heading in the Add-ins UI.

Remove a sideloaded add-in


You can remove a previously sideloaded add-in by clearing the Office cache on your
computer. Details on how to clear the cache for each platform and application can be
found in the article Clear the Office cache.

See also
Sideload Office Add-ins on Mac for testing
Debug Office Add-ins on a Mac
Sideload Outlook add-ins for testing
Debug Office Add-ins on a Mac
Article • 04/11/2023

Because add-ins are developed using HTML and JavaScript, they are designed to work
across platforms, but there might be subtle differences in how different browsers render
the HTML. This article describes how to debug add-ins running on a Mac.

) Important

Debugging add-ins with Office on Mac is only possible if Office is installed on the
Mac from Office.com , not the Apple app store.

Debugging with Safari Web Inspector on a Mac


If you have add-in that shows UI in a task pane or in a content add-in, you can debug an
Office Add-in using Safari Web Inspector.

To be able to debug Office Add-ins on Mac, you must have Mac OS High Sierra AND
Mac Office Version 16.9.1 (Build 18012504) or later. If you don't have an Office on Mac
build, you can get one by joining the Microsoft 365 developer program .

To start, open a terminal and set the OfficeWebAddinDeveloperExtras property for the
relevant Office application as follows:

defaults write com.microsoft.Word OfficeWebAddinDeveloperExtras -bool true

defaults write com.microsoft.Excel OfficeWebAddinDeveloperExtras -bool true

defaults write com.microsoft.Powerpoint OfficeWebAddinDeveloperExtras -bool

true

defaults write com.microsoft.Outlook OfficeWebAddinDeveloperExtras -bool true

) Important

Mac App Store builds of Office do not support the


OfficeWebAddinDeveloperExtras flag.

Then, open the Office application and sideload your add-in. Right-click the add-in and
you should see an Inspect Element option in the context menu. Select that option and it
will pop the Inspector, where you can set breakpoints and debug your add-in.

7 Note

If you're trying to use the inspector and the dialog flickers, update Office to the
latest version. If that doesn't resolve the flickering, try the following workaround.

1. Reduce the size of the dialog.


2. Choose Inspect Element, which opens in a new window.
3. Resize the dialog to its original size.
4. Use the inspector as required.

Clearing the Office application's cache on a


Mac
Add-ins are often cached in Office on Mac for performance reasons. Normally, the cache
is cleared by reloading the add-in. If more than one add-in exists in the same document,
the process of automatically clearing the cache on reload might not be reliable.

Use the personality menu to clear the cache


You can clear the cache by using the personality menu of any task pane add-in.
However, because the personality menu isn't supported in Outlook add-ins, you can try
the option to clear the cache manually if you're using Outlook.

Choose the personality menu. Then choose Clear Web Cache.

7 Note

You must run macOS Version 10.13.6 or later to see the personality menu.
Clear the cache manually
You can also clear the cache manually by deleting the contents of the
~/Library/Containers/com.Microsoft.OsfWebHost/Data/ folder. Look for this folder via

terminal.

7 Note

If that folder doesn't exist, check for the following folders via terminal and if found,
delete the contents of the folder.

~/Library/Containers/com.microsoft.{host}/Data/Library/Caches/ where

{host} is the Office application (e.g., Excel )

~/Library/Containers/com.microsoft.{host}/Data/Library/Application

Support/Microsoft/Office/16.0/Wef/ where {host} is the Office application

(e.g., Excel )
~/Library/Containers/com.microsoft.Office365ServiceV2/Data/Caches/com.mic

rosoft.Office365ServiceV2/

~/Library/Containers/com.microsoft.Office365ServiceV2/Data/Library/Caches

/com.microsoft.Office365ServiceV2/

To look for these folders via Finder, you must set Finder to show hidden files. Finder
displays the folders inside the Containers directory by product name, such as
Microsoft Excel instead of com.microsoft.Excel.
Clear the Office cache
Article • 03/14/2023

To remove an add-in that you've previously sideloaded on Windows, Mac, or iOS, you
need to clear the Office cache on your computer.

Additionally, if you make changes to your add-in's manifest (for example, update file
names of icons or text of add-in commands), you should clear the Office cache and then
re-sideload the add-in using an updated manifest. Doing so allows Office to render the
add-in as it's described by the updated manifest.

7 Note

To remove a sideloaded add-in from Excel, OneNote, PowerPoint, or Word on the


web, see Sideload Office Add-ins in Office on the web for testing: Remove a
sideloaded add-in.

Clear the Office cache on Windows


There are three ways to clear the Office cache on a Windows computer: automatically,
manually, and using the Microsoft Edge developer tools. The methods are described in
the following subsections.

Automatically
This method is recommended for add-in development computers. If your Office on
Windows version is 2108 or later, the following steps configure the Office cache to be
cleared the next time Office is reopened.

7 Note

The automatic method is not supported for Outlook.

1. From the ribbon of any Office host except Outlook, navigate to File > Options >
Trust Center > Trust Center Settings > Trusted Add-in Catalogs.
2. Select the checkbox Next time Office starts, clear all previously-started web add-
ins cache.
Manually
The manual method for Excel, Word, and PowerPoint is different from Outlook.

Manually clear the cache in Excel, Word, and PowerPoint

To remove all sideloaded add-ins from Excel, Word, and PowerPoint, delete the contents
of the following folder.

%LOCALAPPDATA%\Microsoft\Office\16.0\Wef\

If the following folder exists, delete its contents too.

%userprofile%\AppData\Local\Packages\Microsoft.Win32WebViewHost_cw5n1h2txyew
y\AC\#!123\INetCache\

Manually clear the cache in Outlook


To remove a sideloaded add-in from Outlook, use the steps outlined in Sideload
Outlook add-ins for testing to find the add-in in the Custom add-ins section of the
dialog box that lists your installed add-ins. Choose the ellipsis ( ... ) for the add-in and
then choose Remove to remove that specific add-in.

If this add-in removal doesn't work, then delete the contents of the Wef folder as noted
previously for Excel, Word, and PowerPoint.

If your Outlook add-in uses the Unified manifest for Microsoft 365 (preview), also delete
the following folder.

%userprofile%\AppData\Local\Microsoft\Outlook\HubAppFileCache

Using the Microsoft Edge developer tools


To clear the Office cache on Windows 10 when the add-in is running in Microsoft Edge,
you can use the Microsoft Edge DevTools.
 Tip

If you only want the sideloaded add-in to reflect recent changes to its HTML or
JavaScript source files, you shouldn't need to clear the cache. Instead, just put focus
in the add-in's task pane (by clicking anywhere within the task pane) and then press
Ctrl+F5 to reload the add-in.

7 Note

To clear the Office cache using the following steps, your add-in must have a task
pane. If your add-in is a UI-less add-in -- for example, one that uses the on-send
feature -- you'll need to add a task pane to your add-in that uses the same domain
for SourceLocation, before you can use the following steps to clear the cache.

1. Install the Microsoft Edge DevTools .

2. Open your add-in in the Office client.

3. Run the Microsoft Edge DevTools.

4. In the Microsoft Edge DevTools, open the Local tab. Your add-in will be listed by its
name.

5. Select the add-in name to attach the debugger to your add-in. A new Microsoft
Edge DevTools window will open when the debugger attaches to your add-in.

6. On the Network tab of the new window, select Clear cache.

7. If completing these steps doesn't produce the desired result, try selecting Always
refresh from server.

Clear the Office cache on Mac


Add-ins are often cached in Office on Mac for performance reasons. Normally, the cache
is cleared by reloading the add-in. If more than one add-in exists in the same document,
the process of automatically clearing the cache on reload might not be reliable.

Use the personality menu to clear the cache


You can clear the cache by using the personality menu of any task pane add-in.
However, because the personality menu isn't supported in Outlook add-ins, you can try
the option to clear the cache manually if you're using Outlook.

Choose the personality menu. Then choose Clear Web Cache.

7 Note

You must run macOS Version 10.13.6 or later to see the personality menu.

Clear the cache manually


You can also clear the cache manually by deleting the contents of the
~/Library/Containers/com.Microsoft.OsfWebHost/Data/ folder. Look for this folder via

terminal.

7 Note

If that folder doesn't exist, check for the following folders via terminal and if found,
delete the contents of the folder.

~/Library/Containers/com.microsoft.{host}/Data/Library/Caches/ where

{host} is the Office application (e.g., Excel )


~/Library/Containers/com.microsoft.{host}/Data/Library/Application

Support/Microsoft/Office/16.0/Wef/ where {host} is the Office application

(e.g., Excel )
~/Library/Containers/com.microsoft.Office365ServiceV2/Data/Caches/com.mic

rosoft.Office365ServiceV2/

~/Library/Containers/com.microsoft.Office365ServiceV2/Data/Library/Caches

/com.microsoft.Office365ServiceV2/

To look for these folders via Finder, you must set Finder to show hidden files. Finder
displays the folders inside the Containers directory by product name, such as
Microsoft Excel instead of com.microsoft.Excel.

Clear the Office cache on iOS


To clear the Office cache on iOS, call window.location.reload(true) from JavaScript in
the add-in to force a reload. Alternatively, reinstall Office.

See also
Troubleshoot development errors with Office Add-ins
Debug add-ins using developer tools for Internet Explorer
Debug add-ins using developer tools for Edge Legacy
Debug add-ins using developer tools in Microsoft Edge (Chromium-based)
Debug your add-in with runtime logging
Sideload Office Add-ins for testing
Office Add-ins manifest
Validate an Office Add-in's manifest
Debug your add-in with runtime
logging
Article • 05/05/2023

You can use runtime logging to debug your add-in's manifest as well as several
installation errors. This feature can help you identify and fix issues with your manifest
that are not detected by XSD schema validation, such as a mismatch between resource
IDs. Runtime logging is particularly useful for debugging add-ins that implement add-in
commands and Excel custom functions.

7 Note

The runtime logging feature is currently available for Office 2016 or later on
desktop.

) Important

Runtime Logging affects performance. Turn it on only when you need to debug
issues with your add-in manifest.

Use runtime logging from the command line


Enabling runtime logging from the command line is the fastest way to use this logging
tool. These use npx, which is provided by default as part of npm@5.2.0+. If you have an
earlier version of npm , try Runtime logging on Windows or Runtime logging on Mac
instructions, or install npx .

) Important

The office-addin-dev-settings tool is not supported on Mac.

To enable runtime logging:

command line

npx office-addin-dev-settings runtime-log --enable


To enable runtime logging only for a specific file, use the same command with a
filename:

command line

npx office-addin-dev-settings runtime-log --enable [filename.txt]

To disable runtime logging:

command line

npx office-addin-dev-settings runtime-log --disable

To display whether runtime logging is enabled:

command line

npx office-addin-dev-settings runtime-log

To display help within the command line for runtime logging:

command line

npx office-addin-dev-settings runtime-log --help

Runtime logging on Windows


1. Make sure that you are running Office 2016 desktop build 16.0.7019 or later.

2. Add the RuntimeLogging registry key under


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\Developer\ .

7 Note

If the Developer key (folder) doesn't already exist under


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\ , complete the

following steps to create it.


a. Right-click the WEF key (folder) and select New > Key.
b. Name the new key Developer.
3. Set the default value of the RuntimeLogging key to the full path of the file where
you want the log to be written. The following example run in a .reg file sets the
logging to the C:\ClientLogs\log.txt folder.

registry

[HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Wef\Developer\Runtime
Logging]
@="C:\\ClientLogs\\log.txt"`

7 Note

The directory in which the log file will be written must already exist, and you
must have write permissions to it.

The following image shows what the registry should look like. To turn the feature off,
remove the RuntimeLogging key from the registry.

Runtime logging on Mac


1. Make sure that you are running Office 2016 desktop build 16.27.19071500 or later.
2. Open Terminal and set a runtime logging preference by using the defaults
command:

command line

defaults write <bundle id> CEFRuntimeLoggingFile -string <file_name>

<bundle id> identifies which the host for which to enable runtime logging.

<file_name> is the name of the text file to which the log will be written.

Set <bundle id> to one of the following values to enable runtime logging for the
corresponding application.

com.microsoft.Word
com.microsoft.Excel

com.microsoft.Powerpoint
com.microsoft.Outlook

The following example enables runtime logging for Word and then opens the log file.

command line

defaults write com.microsoft.Word CEFRuntimeLoggingFile -string


"runtime_logs.txt"
open ~/library/Containers/com.microsoft.Word/Data/runtime_logs.txt

7 Note

You'll need to restart Office after running the defaults command to enable
runtime logging.

To turn off runtime logging, use the defaults delete command:

command line

defaults delete <bundle id> CEFRuntimeLoggingFile

The following example will turn off runtime logging for Word.

command line

defaults delete com.microsoft.Word CEFRuntimeLoggingFile


Use runtime logging to troubleshoot issues
with your manifest
To use runtime logging to troubleshoot issues loading an add-in:

1. Sideload your add-in for testing.

7 Note

We recommend that you sideload only the add-in that you are testing to
minimize the number of messages in the log file.

2. If nothing happens and you don't see your add-in (and it's not appearing in the
add-ins dialog box), open the log file.

3. Search the log file for your add-in ID, which you define in your manifest. In the log
file, this ID is labeled SolutionId .

Known issues with runtime logging


You might see messages in the log file that are confusing or that are classified
incorrectly. For example:

The message Medium Current host not in add-in's host list followed by
Unexpected Parsed manifest targeting different host is incorrectly classified as
an error.

If you see the message Unexpected Add-in is missing required manifest fields
DisplayName and it doesn't contain a SolutionId, the error is most likely not related

to the add-in you are debugging.

Any Monitorable messages are expected errors from a system point of view.
Sometimes they indicate an issue with your manifest, such as a misspelled element
that was skipped but didn't cause the manifest to fail.

See also
Office Add-ins manifest
Validate an Office Add-in's manifest
Clear the Office cache
Sideload Office Add-ins for testing
Debug add-ins using developer tools for Internet Explorer
Debug add-ins using developer tools for Edge Legacy
Debug add-ins using developer tools in Microsoft Edge (Chromium-based)
Runtimes in Office Add-ins
Debug a function command with a non-
shared runtime
Article • 08/23/2022

) Important

If your add-in is configured to use a shared runtime, you debug the code behind
the function command just as you would the code behind a task pane. See Debug
Office Add-ins and note that a function command in an add-in with a shared
runtime is not a special case as described in that article.

7 Note

This article assumes that you are familiar with function commands.

Function commands don't have a UI, so a debugger can't be attached to the process in
which the function runs on desktop Office. (Outlook add-ins being developed on
Windows are an exception to this. See Debug function commands in Outlook add-ins on
Windows later in this article.) So function commands, in add-ins with a non-shared
runtime, must be debugged on Office on the web where the function runs in the overall
browser process. Use the following steps.

1. Sideload the add-in in Office on the web, and then select the button or menu item
that runs the function command. This is necessary to load the code file for the
function command.
2. Open the browser's developer tools. This is usually done by pressing F12. The
debugger in the tools attaches to the browser process.
3. Apply breakpoints to the code as needed for the function command.
4. Rerun the function command. The process stops on your breakpoints.

 Tip

For more detailed information, see Debug add-ins in Office on the web.

Debug function commands in Outlook add-ins


on Windows
If your development computer is Windows, there is a way that you can debug a function
command on Outlook desktop. See Debug function commands in Outlook add-ins.

See also
Runtimes in Office Add-ins
Debug the initialize and onReady
functions
Article • 08/23/2022

7 Note

This article assumes that you are familiar with Initialize your Office Add-in.

The paradox of debugging the Office.initialize and Office.onReady functions is that a


debugger can only attach to a process that is running, but these functions run
immediately as the add-in's runtime process starts up, before a debugger can attach. In
most situations, restarting the add-in after a debugger is attached doesn't help because
restarting the add-in closes the original runtime process and the attached debugger and
starts a new process that has no debugger attached.

Fortunately, there is an exception. You can debug these functions using Office on the
web, with the following steps.

1. Sideload and run the add-in in Office on the web. This is usually done by opening
an add-in's task pane or running a function command. The add-in runs in the
overall browser process, not a separate process as it would in desktop Office.
2. Open the browser's developer tools. This is usually done by pressing F12. The
debugger in the tools attaches to the browser process.
3. Apply breakpoints as needed to the code in the Office.initialize or
Office.onReady function.

4. Relaunch the add-in's task pane or the function command just as you did in step 1.
This action does not close the browser process or the debugger. The
Office.initialize or Office.onReady function runs again and processing stops on

your breakpoints.

 Tip

For more detailed information, see Debug add-ins in Office on the web.

See also
Runtimes in Office Add-ins
Error handling with the application-
specific JavaScript APIs
Article • 06/27/2023

When you build an add-in using the application-specific Office JavaScript APIs, be sure
to include error handling logic to account for runtime errors. Doing so is critical, due to
the asynchronous nature of the APIs.

Best practices
In our code samples and Script Lab snippets, you'll notice that every call to Excel.run ,
PowerPoint.run , or Word.run is accompanied by a catch statement to catch any errors.
We recommend that you use the same pattern when you build an add-in using the
application-specific APIs.

JavaScript

$("#run").click(() => tryCatch(run));

async function run() {


await Excel.run(async (context) => {
// Add your Excel JavaScript API calls here.

// Await the completion of context.sync() before continuing.


await context.sync();
console.log("Finished!");
});
}

/** Default helper for invoking an action and handling errors. */


async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user through
your add-in's UI.
console.error(error);
}
}

API errors
When an Office JavaScript API request doesn't run successfully, the API returns an error
object that contains the following properties.

code: The code property of an error message contains a string that is part of
OfficeExtension.ErrorCodes or {application}.ErrorCodes where {application}
represents Excel, PowerPoint, or Word. For example, the error code
"InvalidReference" indicates that the reference is not valid for the specified
operation. Error codes are not localized.

message: The message property of an error message contains a summary of the


error in the localized string. The error message isn't intended for consumption by
end users; you should use the error code and appropriate business logic to
determine the error message that your add-in shows to end users.

debugInfo: When present, the debugInfo property of the error message provides
additional information that you can use to understand the root cause of the error.

7 Note

If you use console.log() to print error messages to the console, those messages
are only visible on the server. End users don't see those error messages in the add-
in task pane or anywhere in the Office application. To report errors to the user, see
Error notifications.

Error codes and messages


The following tables list the errors that application-specific APIs may return.

7 Note

The following tables list error messages you may encounter while using the
application-specific APIs. If you're working with the Common API, see Office
Common API error codes to learn about relevant error messages.

Error code Error message Notes

AccessDenied You cannot perform the None.


requested operation.

ActivityLimitReached Activity limit has been None.


reached.
Error code Error message Notes

ApiNotAvailable The requested API is not None.


available.

ApiNotFound The API you are trying to use None.


could not be found. It may be
available in a newer version
of the Office application. See
Office client application and
platform availability for Office
Add-ins for more
information.

BadPassword The password you supplied is None.


incorrect.

Conflict Request could not be None.


processed because of a
conflict.

ContentLengthRequired A Content-length HTTP None.


header is missing.

GeneralException There was an internal error None.


while processing the request.

InsertDeleteConflict The insert or delete operation None.


attempted resulted in a
conflict.

InvalidArgument The argument is invalid or None.


missing or has an incorrect
format.

InvalidBinding This object binding is no None.


longer valid due to previous
updates.

InvalidOperation The operation attempted is None.


invalid on the object.

InvalidReference This reference is not valid for None.


the current operation.

InvalidRequest Cannot process the request. None.

InvalidSelection The current selection is None.


invalid for this operation.
Error code Error message Notes

ItemAlreadyExists The resource being created None.


already exists.

ItemNotFound The requested resource None.


doesn't exist.

MemoryLimitReached The memory limit has been None.


reached. Your action could
not be completed.

NotImplemented The requested feature isn't This could mean the API is
implemented. in preview or only
supported on a particular
platform (such as online-
only). See Office client
application and platform
availability for Office Add-
ins for more information.

RequestAborted The request was aborted None.


during run time.

RequestPayloadSizeLimitExceeded The request payload size has This error only occurs in
exceeded the limit. See the Office on the web.
Resource limits and
performance optimization for
Office Add-ins article for
more information.

ResponsePayloadSizeLimitExceeded The response payload size This error only occurs in


has exceeded the limit. See Office on the web.
the Resource limits and
performance optimization for
Office Add-ins article for
more information.

ServiceNotAvailable The service is unavailable. None.

Unauthenticated Required authentication None.


information is either missing
or invalid.

UnsupportedFeature The operation failed because None.


the source worksheet
contains one or more
unsupported features.

UnsupportedOperation The operation being None.


attempted is not supported.
Excel-specific error codes and messages

Error code Error message Notes

EmptyChartSeries The attempted None.


operation failed
because the chart
series is empty.

FilteredRangeConflict The attempted None.


operation causes a
conflict with a
filtered range.

FormulaLengthExceedsLimit The bytecode of the This error occurs in both Excel on the
applied formula web and on desktop.
exceeds the
maximum length
limit. For Office on
32-bit machines, the
bytecode length limit
is 16384 characters.
On 64-bit machines,
the bytecode length
limit is 32768
characters.

GeneralException Various. The data types APIs return


GeneralException errors with dynamic
error messages. These messages
reference the cell that is the source of
the error, and the problem that is
causing the error, such as: "Cell A1 is
missing the required property type ."

InactiveWorkbook The operation failed None.


because multiple
workbooks are open
and the workbook
being called by this
API has lost focus.
Error code Error message Notes

InvalidOperationInCellEditMode The operation isn't None.


available while Excel
is in Edit cell mode.
Exit Edit mode by
using the Enter or
Tab keys, or by
selecting another
cell, and then try
again.

MergedRangeConflict Cannot complete the None.


operation. A table
can't overlap with
another table, a
PivotTable report,
query results,
merged cells, or an
XML Map.

NonBlankCellOffSheet Microsoft Excel can't None.


insert new cells
because it would
push non-empty cells
off the end of the
worksheet. These
non-empty cells
might appear empty
but have blank
values, some
formatting, or a
formula. Delete
enough rows or
columns to make
room for what you
want to insert and
then try again.
Error code Error message Notes

OperationCellsExceedLimit The attempted If the TableColumnCollection.add API


operation affects triggers this error, confirm that there
more than the limit is no unintentional data within the
of 33554000 cells. worksheet but outside of the table. In
particular, check for data in the right-
most columns of the worksheet.
Remove the unintended data to
resolve this error. One way to verify
how many cells that an operation
processes is to run the following
calculation: (number of table rows) x
(16383 - (number of table columns)) .
The number 16383 is the maximum
number of columns that Excel
supports.

This error only occurs in Excel on the


web.

PivotTableRangeConflict The attempted None.


operation causes a
conflict with a
PivotTable range.

RangeExceedsLimit The cell count in the None.


range has exceeded
the maximum
supported number.
See the Resource
limits and
performance
optimization for
Office Add-ins article
for more information.

RefreshWorkbookLinksBlocked The operation failed None.


because the user
hasn't granted
permission to refresh
external workbook
links.

UnsupportedSheet This sheet type does None.


not support this
operation, since it is
a Macro or Chart
sheet.
Word-specific error codes and messages

Error code Error message Notes

SearchDialogIsOpen The search dialog is open. None.

SearchStringInvalidOrTooLong The search string is invalid The search string maximum is


or too long. 255 characters.

Error notifications
How you report errors to users depends on the UI system you're using.

If you're using React as the UI system, use the Fluent UI components and design
elements. We recommend that error messages be conveyed with a Dialog
component. If the error is in the user's input, configure the Input component to
display the error as bold red text.

7 Note

The Alert component can also be used to report errors to users, but it's
currently in preview and shouldn't be used in a production add-in. For
information about its release status, see the Fluent UI React v9 Component
Roadmap .

If you're not using React for the UI, consider using the older Fabric UI components
implemented directly in HTML and JavaScript. Some example templates are in the
Office-Add-in-UX-Design-Patterns-Code repository. Take a look especially in the
dialog and navigation subfolders. The sample Excel-Add-in-SalesLeads uses a
message banner.

See also
OfficeExtension.Error object
Office Common API error codes
Office Common API error codes
Article • 12/27/2022

This article documents the error messages you might encounter while using the
Common API model. These error codes don't apply to application-specific APIs, such as
the Excel JavaScript API or the Word JavaScript API.

See API models to learn more about the differences between the Common API and
application-specific API models.

Error codes
The following table lists the error codes, names, and messages displayed, and the
conditions they indicate.

Error.code Error.name Error.message Condition

1000 Invalid The specified The coercion type is not supported in the Office
Coercion coercion type is application. (For example, OOXML and HTML
Type not supported coercion types are not supported in Excel.)

1001 Data Read The current The user's current selection is not supported
Error selection is not (that is, it is something different than the
supported. supported coercion types).

1002 Invalid The specified The solution developer provided an


Coercion coercion type is incompatible combination of coercion type and
Type not compatible binding type.
for this binding
type.

1003 Data Read The specified The user supplies invalid column or row counts.
Error rowCount or
columnCount
values are
invalid.

1004 Data Read The current The current selection is not supported for the
Error selection is not specified coercion type by this application.
compatible for
the specified
coercion type.
Error.code Error.name Error.message Condition

1005 Data Read The specified The user supplies invalid startRow or startCol
Error startRow or values.
startColumn
values are
invalid.

1006 Data Read Coordinate The user tries to get partial data from a non-
Error parameters uniform table (that is, a table that has merged
cannot be used cells).
with coercion
type "Table"
when the table
contains merged
cells.

1007 Data Read The size of the The user tries to get a document larger than the
Error document is too size currently supported.
large.

1008 Data Read The requested The user requests to read data beyond the data
Error data set is too limits defined by the Office application.
large.

1009 Data Read The specified file The user sends an invalid file type.
Error type is not
supported.

2000 Data Write The supplied An unsupported data object is supplied.


Error data object type
is not supported.

2001 Data Write Cannot write to The user's current selection is not supported for
Error the current a write operation. (For example, when the user
selection. selects an image.)

2002 Data Write The supplied Multiple cells are selected (and the selection
Error data object is shape does not match the shape of the data).
not compatible Multiple cells are selected (and the selection
with the shape dimensions do not match the dimensions of the
or dimensions of data).
the current
selection.
Error.code Error.name Error.message Condition

2003 Data Write The set A single cell is selected and the supplied data
Error operation failed object overwrites data in the worksheet.
because the
supplied data
object will
overwrite data.

2004 Data Write The supplied The user supplies an object larger than the
Error data object does current selection size.
not match the
size of the
current selection.

2005 Data Write The specified The user supplies invalid startRow or startCol
Error startRow or values.
startColumn
values are
invalid.

2006 Invalid The format of The solution developer supplies an invalid HTML
Format Error the specified or OOXML string, a malformed HTML string, or
data object is an invalid OOXML string.
not valid.

2007 Invalid Data The type of the The solution developer supplies a data object
Object specified data not compatible with the specified coercion type.
object is not
compatible with
the current
selection.

2008 Data Write TBD TBD


Error

2009 Data Write The specified The user tries to set data beyond the data limits
Error data object is defined by the Office application.
too large.

2010 Data Write Coordinate The user tries to set partial data from a non-
Error parameters uniform table (that is, a table that has merged
cannot be used cells).
with coercion
type Table when
the table
contains merged
cells.
Error.code Error.name Error.message Condition

3000 Binding Cannot bind to The user's selection is not supported for binding.
Creation the current (For example, the user is selecting an image or
Error selection. other non-supported object.)

3001 Binding TBD TBD


Creation
Error

3002 Invalid The specified The developer tries to bind to a non-existing or


Binding Error binding does not removed binding.
exist.

3003 Binding Noncontiguous The user is making multiple selections.


Creation selections are
Error not supported.

3004 Binding A binding There are several conditions under which this
Creation cannot be might happen. Please see the "Binding creation
Error created with the error conditions" section later in this article.
current selection
and the specified
binding type.

3005 Invalid Operation is not The developer sends an add row or add column
Binding supported on operation on a binding type that is not of
Operation this binding coercion type table .
type.

3006 Binding The named item The named item cannot be found. No content
Creation does not exist. control or table with that name exists.
Error

3007 Binding Multiple objects Collision error: more than one content control
Creation with the same with the same name exists, and fail on collision is
Error name were set to true .
found.

3008 Binding The specified Named item can't be bound to type. For
Creation binding type is example, a content control contains text, but the
Error not compatible developer tried to bind by using coercion type
with the table .
supplied named
item.

3009 Invalid The binding type Used for backward compatibility.


Binding is not supported.
Operation
Error.code Error.name Error.message Condition

3010 Unsupported The selected The developer is trying to use the addRowsAsync
Binding content needs to or deleteAllDataValuesAsync method of the
Operation be in table TableBinding object on data of coercion type
format. Format matrix .
the data as a
table and try
again.

4000 Read The specified A nonexistent setting name is supplied.


Settings setting name
Error does not exist.

4001 Save The settings Settings could not be saved.


Settings could not be
Error saved.

4002 Settings Settings could Settings are stale and developer indicated not to
Stale Error not be saved override settings.
because they are
stale.

5000 Settings The operation is The operation is not supported in the current
Stale Error not supported. Office application. For example,
document.getSelectionAsync is called from
Outlook.

5001 Internal Error An internal error Refers to an internal error condition, which can
has occurred. occur for any of the following reasons.

An add-in being used by another user sharing


the workbook created a binding at
approximately the same time, and your add-
in needs to retry binding.

An unknown error occurred.

The operation failed.

Access was denied because the user is not a


member of an authorized role.

Access was denied because secure, encrypted


communication is required.

Data is stale and the user needs to confirm


enabling the queries to refresh it.

The site collection CPU quota has been


exceeded.
Error.code Error.name Error.message The site collection memory quota has been
Condition
exceeded.

The session memory quota has been


exceeded.

The workbook is in an invalid state and the


operation can't be performed.

The session has timed out due to inactivity


and the user needs to reload the workbook.

The maximum number of allowed sessions


per user has been exceeded.

The operation was canceled by the user.

The operation can't be completed because it


is taking too long.

The request can't be completed and needs to


be retried.

The trial period of the product has expired.

The session has timed out due to inactivity.

The user doesn't have permission to perform


the operation on the specified range.

The user's regional settings don't match the


current collaboration session.

The user is no longer connected and must


refresh or re-open the workbook.

The requested range doesn't exist in the


sheet.

The user doesn't have permission to edit the


workbook.

The workbook can't be edited because it is


locked.

The session can't auto save the workbook.

The session can't refresh its lock on the


workbook file.

The request can't be processed and needs to


be retried.
Error.code Error.name Error.message Condition

The user's sign-in information couldn't be


verified and needs to be re-entered.

The user has been denied access.

The shared workbook needs to be updated.

5002 Permission The requested The solution developer submits a set operation,
Denied operation is not but the document is in a mode that does not
allowed on the allow modifications, such as 'Restrict Editing'.
current
document mode.

5003 Event The specified The solution developer tries to register or


Registration event type is not unregister a handler to an event that does not
Error supported by exist.
the current
object.

5004 Invalid API Invalid API call in An invalid call is made for the context, for
call the current example, trying to use a CustomXMLPart object in
context. Excel.

5005 Data Stale Operation failed The data on the server needs to be refreshed.
because the data
is stale on the
server.

5006 Session The document The session has timed out.


Timeout session timed
out. Reload the
document.

5007 Invalid API The enumeration The enumeration is not supported in the current
call is not supported context.
in the current
context.

5009 Permission Access Denied The add-in does not have permission to call the
Denied specific API.

5012 Invalid Or Your Office The session between the Office client and server
Timed Out browser session has expired or the date, time, or time zone is
Session has expired or is incorrect on your computer.
invalid. To
continue, refresh
the page.
Error.code Error.name Error.message Condition

6000 Invalid node The specified The CustomXmlPart node was not found.
node was not
found.

6100 Custom XML Custom XML Invalid API call.


error error

7000 Invalid Id The specified Id Invalid ID.


does not exist.

7001 Invalid The object is The user can find the object, but cannot navigate
navigation located in a to it. (For example, in Word, the binding is to the
place where header, footer, or a comment.)
navigation is not
supported.

7002 Invalid The object is The user is trying to navigate to a locked or


navigation locked or protected range.
protected.

7004 Invalid The operation The user is trying to navigate to an index that is
navigation failed because out of range.
the Index is out
of range.

8000 Missing We couldn't The cellFormat method is missing some


Parameter format the table parameters. For example, there are missing cells,
cell because format, or tableOptions parameters.
some parameter
values are
missing. Double-
check the
parameters and
try again.

8010 Invalid value One or more of The common cells reference enumeration is not
the cells defined. For example, All, Data, Headers.
parameters have
values that aren't
allowed. Double-
check the values
and try again.
Error.code Error.name Error.message Condition

8011 Invalid value One or more of One of the values in tableOptions is invalid.
the tableOptions
parameters have
values that aren't
allowed. Double-
check the values
and try again.

8012 Invalid value One or more of One of the values in the format is invalid.
the format
parameters have
values that aren't
allowed. Double-
check the values
and try again.

8020 Out of range The row index The row index is more than the biggest row
value is out of index of the table or less than 0.
the allowed
range. Use a
positive value (0
or higher) that's
less than the
number of rows.

8021 Out of range The column The column index is more than the biggest
index value is column index of the table or less than 0.
out of the
allowed range.
Use a positive
value (0 or
higher) that's
less than the
number of
columns.

8022 Out of range The value is out Some of the values in the format are out of the
of the allowed supported ranges.
range.

9016 Permission Permission Access is denied.


denied denied

9020 Generic An internal error Refers to an internal error condition, which can
Response has occurred. occur for any number of reasons.
Error
Error.code Error.name Error.message Condition

9021 Save Error Connection error The item couldn't be saved. This could be due to
occurred while a server connection error if using Online Mode in
trying to save Outlook desktop, or due to an attempt to re-
the item on the save a draft item that was deleted from the
server. Exchange server.

9022 Message In The EWS ID The EWS ID for the current message couldn't be
Different cannot be retrieved as the message may have been moved
Store Error retrieved or the sending mailbox may have changed.
because the
message is
saved in another
store.

9041 Network The user is no The user no longer has network or internet
error longer access.
connected to the
network. Please
check your
network
connection and
try again.

9043 Attachment The attachment The API doesn't support the attachment type.
Type Not type is not For example, item.getAttachmentContentAsync
Supported supported. throws this error if the attachment is an
embedded image in Rich Text Format, or if it's an
item type other than an email or calendar item
(such as a contact or task item).

9057 Size Limit A maximum of When updating roaming settings via


Exceeded 32KB is available Office.context.roamingSettings.set, the size
for the settings cannot exceed 32KB. See Office.RoamingSettings
of each add-in. interface.

12002 Not Not applicable. One of the following:


applicable. - No page exists at the URL that was passed to
displayDialogAsync .
- The page that was passed to
displayDialogAsync loaded, but the dialog box
was directed to a page that it cannot find or
load, or it has been directed to a URL with invalid
syntax. Thrown within the dialog and triggers a
DialogEventReceived event in the host page.
Error.code Error.name Error.message Condition

12003 Not Not applicable. The dialog box was directed to a URL with the
applicable. HTTP protocol. HTTPS is required. Thrown within
the dialog and triggers a DialogEventReceived
event in the host page.

12004 Not Not applicable. The domain of the URL passed to


applicable. displayDialogAsync is not trusted. The domain
must be the same domain as the host page
(including protocol and port number). Thrown by
call of displayDialogAsync .

12005 Not Not applicable. The URL passed to displayDialogAsync uses the
applicable. HTTP protocol. HTTPS is required. Thrown by call
of displayDialogAsync . (In some versions of
Office, the error message returned with 12005 is
the same one returned for 12004.)

12006 Not Not applicable. The dialog box was closed, usually because the
applicable. user chooses the X button. Thrown within the
dialog and triggers a DialogEventReceived event
in the host page.

12007 Not Not applicable. A dialog box is already opened from this host
applicable. window. A host window, such as a task pane, can
only have one dialog box open at a time. Thrown
by call of displayDialogAsync .

12009 Not Not applicable. The user chose to ignore the dialog box. This
applicable. error can occur in online versions of Office,
where users may choose not to allow an add-in
to present a dialog. Thrown by call of
displayDialogAsync .

12011 Not Not applicable. The user's browser is configured in a way that
applicable. blocks popups. This error can occur in Office on
the web if the browser is Safari and it's
configured to block popups or the browser is
Edge Legacy and the add-in domain is in a
different security zone from the domain the
dialog is trying to open. Thrown by call of
displayDialogAsync .

13nnn Not Not applicable. See Causes and handling of errors from
applicable. getAccessToken.

Binding creation error conditions


When a binding is created in the API, indicate the binding type that you want to use.
The following tables lists the binding types and the resulting binding behaviors that are
expected.

Behavior in Excel
The following table summarizes binding behavior in Excel.

Specified Actual Selection Behavior


Binding
Type

Matrix Range of cells (including within a table, A binding of type matrix is created on
and single cell) the selected cells. No modification in
the document is expected.

Matrix Text selected in the cell A binding of type matrix is created on


the whole cell. No modification in the
document is expected.

Matrix Multiple selection/invalid selection (For The binding cannot be created.


example, user selects a picture, object, or
Word Art.)

Table Range of cells (includes single cell) The binding cannot be created.

Table Range of cell within a table (includes single A binding is created in the whole table.
cell within a table, or the whole table, or
text within a cell in a table)

Table Half selection in a table and half selection The binding cannot be created.
outside the table

Table Text selected in the cell (not in the table.) The binding cannot be created.

Table Multiple selection/invalid selection (For The binding cannot be created.


example, user selects a picture, object,
Word Art, etc.)

Text Range of cells The binding cannot be created.

Text Range of cells within a table The binding cannot be created.

Text Single cell A binding of type text is created.

Text Single cell within a table A binding of type text is created.

Text Text selected in the cell A binding of type text in the whole cell
is created.
Behavior in Word
The following table summarizes binding behavior in Word.

Specified Actual Selection Behavior


Binding
Type

Matrix Text The binding cannot be created.

Matrix Whole table A binding of type matrix is created.Document is


changed and a content control must wrap the table.

Matrix Range within a table The binding cannot be created.

Matrix Invalid selection (for The binding cannot be created.


example, multiple, invalid
objects, etc.)

Table Text The binding cannot be created.

Table Whole table A binding of type text is created.

Table Range within a table The binding cannot be created.

Table Invalid selection (for The binding cannot be created.


example, multiple, invalid
objects, etc.)

Text Whole table A binding of type text is created.

Text Range within a table The binding cannot be created.

Text Multiple selection The last selection will be wrapped with a content control
and a binding to that control. A content control of type
text is created.

Text Invalid selection (for The binding cannot be created.


example, multiple, invalid
objects, etc.)

See also
Office Add-ins development lifecycle
Understanding the Office JavaScript API
Error handling with the application-specific JavaScript APIs
Troubleshoot error messages for single sign-on (SSO)
Troubleshoot development errors with Office Add-ins
Troubleshoot user errors with Office
Add-ins
Article • 03/21/2023

At times your users might encounter issues with Office Add-ins that you develop. For
example, an add-in fails to load or is inaccessible. Use the information in this article to
help resolve common issues that your users encounter with your Office Add-in.

You can also use Fiddler to identify and debug issues with your add-ins.

Common errors and troubleshooting steps


The following table lists common error messages that users might encounter and steps
that your users can take to resolve the errors.

Error message Resolution

App error: Catalog could not be Verify firewall settings."Catalog" refers to AppSource.
reached This message indicates that the user cannot access
AppSource.

APP ERROR: This app could not be Verify that the latest Office updates are installed, or
started. Close this dialog to ignore the update with the Windows Installer.
problem or click "Restart" to try again.

Error: Object doesn't support property Confirm that Internet Explorer is not running in
or method 'defineProperty' Compatibility Mode. Go to Tools > Compatibility View
Settings.

Sorry, we couldn't load the app Make sure that the browser supports HTML5 local
because your browser version is not storage, or reset your Internet Explorer settings. For
supported. Click here for a list of information about supported browsers, see
supported browser versions. Requirements for running Office Add-ins.

When installing an add-in, you see "Error


loading add-in" in the status bar
1. Close Office.
2. Verify that the manifest is valid. See Validate an Office Add-in's manifest.
3. Restart the add-in.
4. Install the add-in again.
You can also give us feedback: if using Excel on Windows or Mac, you can report
feedback to the Office extensibility team directly from Excel. To do this, select File >
Feedback > Send a Frown. Sending a frown provides the necessary logs to understand
the issue.

Outlook add-in doesn't work correctly


If an Outlook add-in running on Windows and using Internet Explorer is not working
correctly, try turning on script debugging in Internet Explorer.

Go to Tools > Internet Options > Advanced.


Under Browsing, uncheck Disable script debugging (Internet Explorer) and
Disable script debugging (Other).

We recommend that you uncheck these settings only to troubleshoot the issue. If you
leave them unchecked, you will get prompts when you browse. After the issue is
resolved, check Disable script debugging (Internet Explorer) and Disable script
debugging (Other) again.

Add-in doesn't activate in Office


If the add-in doesn't activate when the user performs the following steps.

1. Signs in with their Microsoft account in the Office application.

2. Enables two-step verification for their Microsoft account.

3. Verifies their identity when prompted when they try to insert an add-in.

Verify that the latest Office updates are installed, or update with the Windows Installer.

Add-in dialog box cannot be displayed


When using an Office Add-in, the user is asked to allow a dialog box to be displayed.
The user chooses Allow, and the following error message occurs.

"The security settings in your browser prevent us from creating a dialog box. Try a
different browser, or configure your browser so that [URL] and the domain shown in
your address bar are in the same security zone."
Affected browsers Affected platforms

Microsoft Edge Office on the web

To resolve the issue, end users or administrators can add the domain of the add-in to
the list of trusted sites in the Microsoft Edge browser.

) Important

Do not add the URL for an add-in to your list of trusted sites if you don't trust the
add-in.

To add a URL to your list of trusted sites:

1. In Control Panel, go to Internet options > Security.


2. Select the Trusted sites zone, and choose Sites.
3. Enter the URL that appears in the error message, and choose Add.
4. Try to use the add-in again. If the problem persists, verify the settings for the other
security zones and ensure that the add-in domain is in the same zone as the URL
that is displayed in the address bar of the Office application.

This issue occurs when the Dialog API is used in pop-up mode. To prevent this issue
from occurring, use the displayInFrame flag. This requires that your page support
display within an iframe. The following example shows how to use the flag.

JavaScript

Office.context.ui.displayDialogAsync(startAddress, {displayInIFrame:true},
callback);

Add-in won't upgrade


You may see the following error when deploying an updated manifest for your add-in:
ADD-IN WARNING: This add-in is currently upgrading. Please close the current
message or appointment, and re-open in a few moments.

When you add features or fix bugs in your add-in, you'll need to deploy the updates. If
your add-in is deployed by one or more admins to their organizations, some manifest
changes will require the admin to consent to the updates. Users will be blocked from
the add-in until consent is granted. The following manifest changes will require the
admin to consent again.

Changes to requested permissions.


Additional scopes.
Additional Outlook events.

See also
Troubleshoot development errors with Office Add-ins
Troubleshoot development errors with
Office Add-ins
Article • 08/01/2023

Here's a list of common issues you may encounter while developing an Office Add-in.

 Tip

Clearing the Office cache often fixes issues related to stale code. This guarantees
the latest manifest is uploaded, using the current file names, menu text, and other
command elements. To learn more, see Clear the Office cache.

Add-in doesn't load in task pane or other issues


with the add-in manifest
See Validate an Office Add-in's manifest and Debug your add-in with runtime logging to
debug add-in manifest issues.

Changes to add-in commands including ribbon


buttons and menu items do not take effect
Clearing the cache helps ensure the latest version of your add-in's manifest is being
used. To clear the Office cache, follow the instructions in Clear the Office cache. If you're
using Office on the web, clear your browser's cache through the browser's UI.

Add-in commands from old development add-


ins stay on ribbon even after the cache is
cleared
Sometimes buttons or menus from an add-in that you were developing in the past
appears on the ribbon when you run an Office application even after you have cleared
the cache. Try these techniques:

If you develop add-ins on more than one computer and your user settings are
synchronized across the computers, try clearing the Office cache on all the
computers. Shut down all Office applications on all the computers, and then clear
the cache on all of them before you open any Office application on any of them.
If you published the manifest of the old add-in to a network share, shut down all
Office applications, clear the cache, and then be sure that the manifest for the add-
in is removed from the shared folder.

Changes to static files, such as JavaScript,


HTML, and CSS do not take effect
The browser may be caching these files. To prevent this, turn off client-side caching
when developing. The details will depend on what kind of server you are using. In most
cases, it involves adding certain headers to the HTTP Responses. We suggest the
following set.

Cache-Control: "private, no-cache, no-store"


Pragma: "no-cache"
Expires: "-1"

For an example of doing this in an Node.JS Express server, see this app.js file . For an
example in an ASP.NET project, see this cshtml file .

If your add-in is hosted in Internet Information Server (IIS), you could also add the
following to the web.config.

XML

<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge"
cacheControlMaxAge="0.00:00:00" cacheControlCustom="must-revalidate" />
</staticContent>

If these steps don't seem to work at first, you may need to clear the browser's cache. Do
this through the UI of the browser. Sometimes the Edge cache isn't successfully cleared
when you try to clear it in the Edge UI. If that happens, run the following command in a
Windows Command Prompt.

Bash

del /s /f /q
%LOCALAPPDATA%\Packages\Microsoft.Win32WebViewHost_cw5n1h2txyewy\AC\#!123\IN
etCache\
Changes made to property values don't happen
and there is no error message
Check the reference documentation for the property to see if it is read-only. Also, the
TypeScript definitions for Office JS specify which object properties are read-only. If you
attempt to set a read-only property, the write operation will fail silently, with no error
thrown. The following example erroneously attempts to set the read-only property
Chart.id. See also Some properties cannot be set directly.

JavaScript

// This will do nothing, since `id` is a read-only property.


myChart.id = "5";

Getting error: "This add-in is no longer


available"
The following are some of the causes of this error. If you discover additional causes,
please tell us with the feedback tool at the bottom of the page.

If you are using Visual Studio, there may be a problem with the sideloading. Close
all instances of the Office host and Visual Studio. Restart Visual Studio and try
pressing F5 again.
The add-in's manifest has been removed from its deployment location, such as
Centralized Deployment, a SharePoint catalog, or a network share.
The value of the ID element in the manifest has been changed directly in the
deployed copy. If for any reason, you want to change this ID, first remove the add-
in from the Office host, then replace the original manifest with the changed
manifest. You many need to clear the Office cache to remove all traces of the
original. See the Clear the Office cache article for instructions on clearing the cache
for your operating system.
The add-in's manifest has a resid that is not defined anywhere in the Resources
section of the manifest, or there is a mismatch in the spelling of the resid
between where it is used and where it is defined in the <Resources> section.
There is a resid attribute somewhere in the manifest with more than 32
characters. A resid attribute, and the id attribute of the corresponding resource
in the <Resources> section, cannot be more than 32 characters.
The add-in has a custom Add-in Command but you are trying to run it on a
platform that doesn't support them. For more information, see Add-in commands
requirement sets.

Add-in doesn't work on Edge but it works on


other browsers
See Troubleshoot EdgeHTML and WebView2 (Microsoft Edge) issues.

Excel add-in throws errors, but not consistently


See Troubleshoot Excel add-ins for possible causes.

Word add-in throws errors or displays broken


behavior
See Troubleshoot Word add-ins for possible causes.

Manifest schema validation errors in Visual


Studio projects
If you're using newer features that require changes to the manifest file, you may get
validation errors in Visual Studio. For example, when adding the <Runtimes> element to
implement the shared runtime, you may see the following validation error.

The element 'Host' in namespace


'http://schemas.microsoft.com/office/taskpaneappversionoverrides' has invalid child
element 'Runtimes' in namespace
'http://schemas.microsoft.com/office/taskpaneappversionoverrides'

If this occurs, you can update the XSD files that Visual Studio uses to the latest versions.
The latest schema versions are at [MS-OWEMXML]: Appendix A: Full XML Schema.

Locate the XSD files


1. Open your project in Visual Studio.
2. In Solution Explorer, open the manifest.xml file. The manifest is typically in the first
project under your solution.
3. Select View > Properties Window (F4).
4. Set the cursor selection in the manifest.xml so that the Properties window shows
the XML Document properties.
5. In the Properties window, select the Schemas property, then select the ellipsis (...)
to open the XML Schemas editor. Here you can find the exact folder location of all
schema files your project uses.

Update the XSD files


1. Open the XSD file you want to update in a text editor. The schema name from the
validation error will correlate to the XSD file name. For example, open
TaskPaneAppVersionOverridesV1_0.xsd.
2. Locate the updated schema at [MS-OWEMXML]: Appendix A: Full XML Schema. For
example, TaskPaneAppVersionOverridesV1_0 is at taskpaneappversionoverrides
Schema.
3. Copy the text into your text editor.
4. Save the updated XSD file.
5. Restart Visual Studio to pick up the new XSD file changes.

You can repeat the previous process for any additional schemas that are out-of-date.

When working offline, no Office APIs work


When you're loading the Office JavaScript Library from a local copy instead of from the
CDN, the APIs may stop working if the library isn't up-to-date. If you have been away
from a project for a while, reinstall the library to get the latest version. The process
varies according to your IDE. Choose one of the following options based on your
environment.

Visual Studio: See Update to the latest Office JavaScript API library.
Any other IDE: See the npm packages @microsoft/office-js and @types/office-
js .

See also
Debug add-ins in Office on the web
Sideload an Office Add-in on Mac
Sideload an Office Add-in on iPad
Debug Office Add-ins on a Mac
Microsoft Office Add-in Debugger Extension for Visual Studio Code
Validate an Office Add-in's manifest
Debug your add-in with runtime logging
Troubleshoot user errors with Office Add-ins
Runtimes in Office Add-ins
Microsoft Q&A (office-js-dev)
Avoid using the context.sync method in
loops
Article • 03/14/2023

7 Note

This article assumes that you're beyond the beginning stage of working with at
least one of the four application-specific Office JavaScript APIs—for Excel, Word,
OneNote, and Visio—that use a batch system to interact with the Office document.
In particular, you should know what a call of context.sync does and you should
know what a collection object is. If you're not at that stage, please start with
Understanding the Office JavaScript API and the documentation linked to under
"application-specific" in that article.

For some programming scenarios in Office Add-ins that use one of the application-
specific API models (for Excel, Word, OneNote, and Visio), your code needs to read,
write, or process some property from every member of a collection object. For example,
an Excel add-in that needs to get the values of every cell in a particular table column or
a Word add-in that needs to highlight every instance of a string in the document. You
need to iterate over the members in the items property of the collection object; but, for
performance reasons, you need to avoid calling context.sync in every iteration of the
loop. Every call of context.sync is a round trip from the add-in to the Office document.
Repeated round trips hurt performance, especially if the add-in is running in Office on
the web because the round trips go across the internet.

7 Note

All examples in this article use for loops but the practices described apply to any
loop statement that can iterate through an array, including the following:

for

for of

while

do while

They also apply to any array method to which a function is passed and applied to
the items in the array, including the following:
Array.every

Array.forEach

Array.filter

Array.find

Array.findIndex

Array.map

Array.reduce

Array.reduceRight

Array.some

Writing to the document


In the simplest case, you are only writing to members of a collection object, not reading
their properties. For example, the following code highlights in yellow every instance of
"the" in a Word document.

7 Note

It's generally a good practice to put have a final context.sync just before the
closing "}" character of the application run function (such as Excel.run , Word.run ,
etc.). This is because the run function makes a hidden call of context.sync as the
last thing it does if, and only if, there are queued commands that have not yet been
synchronized. The fact that this call is hidden can be confusing, so we generally
recommend that you add the explicit context.sync . However, given that this article
is about minimizing calls of context.sync , it is actually more confusing to add an
entirely unnecessary final context.sync . So, in this article, we leave it out when
there are no unsynchronized commands at the end of the run .

JavaScript

await Word.run(async function (context) {


let startTime, endTime;
const docBody = context.document.body;

// search() returns an array of Ranges.


const searchResults = docBody.search('the', { matchWholeWord: true });
searchResults.load('font');
await context.sync();

// Record the system time.


startTime = performance.now();
for (let i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.highlightColor = '#FFFF00';

await context.sync(); // SYNCHRONIZE IN EACH ITERATION


}

// await context.sync(); // SYNCHRONIZE AFTER THE LOOP

// Record the system time again then calculate how long the operation
took.
endTime = performance.now();
console.log("The operation took: " + (endTime - startTime) + "
milliseconds.");
})

The preceding code took 1 full second to complete in a document with 200 instances of
"the" in Word on Windows. But when the await context.sync(); line inside the loop is
commented out and the same line just after the loop is uncommented, the operation
took only a 1/10th of a second. In Word on the web (with Edge as the browser), it took 3
full seconds with the synchronization inside the loop and only 6/10ths of a second with
the synchronization after the loop, about five times faster. In a document with 2000
instances of "the", it took (in Word on the web) 80 seconds with the synchronization
inside the loop and only 4 seconds with the synchronization after the loop, about 20
times faster.

7 Note

It's worth asking whether the synchronize-inside-the-loop version would execute


faster if the synchronizations ran concurrently, which could be done by simply
removing the await keyword from the front of the context.sync() . This would
cause the runtime to initiate the synchronization and then immediately start the
next iteration of the loop without waiting for the synchronization to complete.
However, this is not as good a solution as moving the context.sync out of the loop
entirely for these reasons.

Just as the commands in a synchronization batch job are queued, the batch
jobs themselves are queued in Office, but Office supports no more than 50
batch jobs in the queue. Any more triggers errors. So, if there are more than
50 iterations in a loop, there is a chance that the queue size is exceeded. The
greater the number of iterations, the greater the chance of this happening.
"Concurrently" does not mean simultaneously. It would still take longer to
execute multiple synchronization operations than to execute one.
Concurrent operations are not guaranteed to complete in the same order in
which they started. In the preceding example, it doesn't matter what order the
word "the" gets highlighted, but there are scenarios where it's important that
the items in the collection be processed in order.

Read values from the document with the split


loop pattern
Avoiding context.sync s inside a loop becomes more challenging when the code must
read a property of the collection items as it processes each one. Suppose your code
needs to iterate all the content controls in a Word document and log the text of the first
paragraph associated with each control. Your programming instincts might lead you to
loop over the controls, load the text property of each (first) paragraph, call
context.sync to populate the proxy paragraph object with the text from the document,

and then log it. The following is an example.

JavaScript

Word.run(async (context) => {


const contentControls = context.document.contentControls.load('items');
await context.sync();

for (let i = 0; i < contentControls.items.length; i++) {


const paragraph =
contentControls.items[i].getRange('Whole').paragraphs.getFirst();
paragraph.load('text');
await context.sync();
console.log(paragraph.text);
}
});

In this scenario, to avoid having a context.sync in a loop, you should use a pattern we
call the split loop pattern. Let's see a concrete example of the pattern before we get to a
formal description of it. Here's how the split loop pattern can be applied to the
preceding code snippet. Note the following about this code.

There are now two loops and the context.sync comes between them, so there's
no context.sync inside either loop.
The first loop iterates through the items in the collection object and loads the text
property just as the original loop did, but the first loop cannot log the paragraph
text because it no longer contains a context.sync to populate the text property
of the paragraph proxy object. Instead, it adds the paragraph object to an array.
The second loop iterates through the array that was created by the first loop, and
logs the text of each paragraph item. This is possible because the context.sync
that came between the two loops populated all the text properties.

JavaScript

Word.run(async (context) => {


const contentControls = context.document.contentControls.load("items");
await context.sync();

const firstParagraphsOfCCs = [];


for (let i = 0; i < contentControls.items.length; i++) {
const paragraph =
contentControls.items[i].getRange('Whole').paragraphs.getFirst();
paragraph.load('text');
firstParagraphsOfCCs.push(paragraph);
}

await context.sync();

for (let i = 0; i < firstParagraphsOfCCs.length; i++) {


console.log(firstParagraphsOfCCs[i].text);
}
});

The preceding example suggests the following procedure for turning a loop that
contains a context.sync into the split loop pattern.

1. Replace the loop with two loops.


2. Create a first loop to iterate over the collection and add each item to an array
while also loading any property of the item that your code needs to read.
3. Following the first loop, call context.sync to populate the proxy objects with any
loaded properties.
4. Follow the context.sync with a second loop to iterate over the array created in the
first loop and read the loaded properties.

Process objects in the document with the


correlated objects pattern
Let's consider a more complex scenario where processing the items in the collection
requires data that isn't in the items themselves. The scenario envisions a Word add-in
that operates on documents created from a template with some boilerplate text.
Scattered in the text are one or more instances of the following placeholder strings: "
{Coordinator}", "{Deputy}", and "{Manager}". The add-in replaces each placeholder with
some person's name. The UI of the add-in is not important to this article. For example, it
could have a task pane with three text boxes, each labeled with one of the placeholders.
The user enters a name in each text box and then presses a Replace button. The handler
for the button creates an array that maps the names to the placeholders, and then
replaces each placeholder with the assigned name.

You don't need to actually produce an add-in with this UI to experiment with the code.
You can use the Script Lab tool to prototype the important code. Use the following
assignment statement to create the mapping array.

JavaScript

const jobMapping = [
{ job: "{Coordinator}", person: "Sally" },
{ job: "{Deputy}", person: "Bob" },
{ job: "{Manager}", person: "Kim" }
];

The following code shows how you might replace each placeholder with its assigned
name if you used context.sync inside loops.

JavaScript

Word.run(async (context) => {

for (let i = 0; i < jobMapping.length; i++) {


let options = Word.SearchOptions.newObject(context);
options.matchWildCards = false;
let searchResults = context.document.body.search(jobMapping[i].job,
options);
searchResults.load('items');

await context.sync();

for (let j = 0; j < searchResults.items.length; j++) {


searchResults.items[j].insertText(jobMapping[i].person,
Word.InsertLocation.replace);

await context.sync();
}
}
});

In the preceding code, there is an outer and an inner loop. Each of them contains a
context.sync . Based on the very first code snippet in this article, you probably see that
the context.sync in the inner loop can simply be moved after the inner loop. But that
would still leave the code with a context.sync (two of them actually) in the outer loop.
The following code shows how you can remove context.sync from the loops. We
discuss the code below.

JavaScript

Word.run(async (context) => {

const allSearchResults = [];


for (let i = 0; i < jobMapping.length; i++) {
let options = Word.SearchOptions.newObject(context);
options.matchWildCards = false;
let searchResults = context.document.body.search(jobMapping[i].job,
options);
searchResults.load('items');
let correlatedSearchResult = {
rangesMatchingJob: searchResults,
personAssignedToJob: jobMapping[i].person
}
allSearchResults.push(correlatedSearchResult);
}

await context.sync()

for (let i = 0; i < allSearchResults.length; i++) {


let correlatedObject = allSearchResults[i];

for (let j = 0; j < correlatedObject.rangesMatchingJob.items.length;


j++) {
let targetRange = correlatedObject.rangesMatchingJob.items[j];
let name = correlatedObject.personAssignedToJob;
targetRange.insertText(name, Word.InsertLocation.replace);
}
}

await context.sync();
});

Note the code uses the split loop pattern.

The outer loop from the preceding example has been split into two. (The second
loop has an inner loop, which is expected because the code is iterating over a set
of jobs (or placeholders) and within that set it is iterating over the matching
ranges.)
There is a context.sync after each major loop, but no context.sync inside any
loop.
The second major loop iterates through an array that is created in the first loop.
But the array created in the first loop does not contain only an Office object as the first
loop did in the section Reading values from the document with the split loop pattern.
This is because some of the information needed to process the Word Range objects is
not in the Range objects themselves but instead comes from the jobMapping array.

So, the objects in the array created in the first loop are custom objects that have two
properties. The first is an array of Word Ranges that match a specific job title (that is, a
placeholder string) and the second is a string that provides the name of the person
assigned to the job. This makes the final loop easy to write and easy to read because all
of the information needed to process a given range is contained in the same custom
object that contains the range. The name that should replace
correlatedObject.rangesMatchingJob.items[ j] is the other property of the same object:
correlatedObject.personAssignedToJob.

We call this variation of the split loop pattern the correlated objects pattern. The
general idea is that the first loop creates an array of custom objects. Each object has a
property whose value is one of the items in an Office collection object (or an array of
such items). The custom object has other properties, each of which provides information
needed to process the Office objects in the final loop. See the section Other examples of
these patterns for a link to an example where the custom correlating object has more
than two properties.

One further caveat: sometimes it takes more than one loop just to create the array of
custom correlating objects. This can happen if you need to read a property of each
member of one Office collection object just to gather information that will be used to
process another collection object. (For example, your code needs to read the titles of all
the columns in an Excel table because your add-in is going to apply a number format to
the cells of some columns based on that column's title.) But you can always keep the
context.sync s between the loops, rather than in a loop. See the section Other examples
of these patterns for an example.

Other examples of these patterns


For a very simple example for Excel that uses Array.forEach loops, see the
accepted answer to this Stack Overflow question: Is it possible to queue more than
one context.load before context.sync?
For a simple example for Word that uses Array.forEach loops and doesn't use
async / await syntax, see the accepted answer to this Stack Overflow question:
Iterating over all paragraphs with content controls with Office JavaScript API .
For an example for Word that is written in TypeScript, see the sample Word Add-in
Angular2 Style Checker , especially the file word.document.service.ts . It has a
mixture of for and Array.forEach loops.
For an advanced Word sample, import this gist into the Script Lab tool. For
context in using the gist, see the accepted answer to the Stack Overflow question
Document not in sync after replace text . This sample creates a custom
correlating object type that has three properties. It uses a total of three loops to
construct the array of correlated objects, and two more loops to do the final
processing. There are a mixture of for and Array.forEach loops.
Although not strictly an example of the split loop or correlated objects patterns,
there is an advanced Excel sample that shows how to convert a set of cell values to
other currencies with just a single context.sync . To try it, open the Script Lab tool
and navigate to the Currency Converter sample.

When should you not use the patterns in this


article?
Excel cannot read more than 5 MB of data in a given call of context.sync . If this limit is
exceeded, an error is thrown. (See the "Excel add-ins section" of Resource limits and
performance optimization for Office Add-ins for more information.) It's very rare that
this limit is approached, but if there's a chance that this will happen with your add-in,
then your code should not load all the data in a single loop and follow the loop with a
context.sync . But you still should avoid having a context.sync in every iteration of a

loop over a collection object. Instead, define subsets of the items in the collection and
loop over each subset in turn, with a context.sync between the loops. You could
structure this with an outer loop that iterates over the subsets and contains the
context.sync in each of these outer iterations.
Resource limits and performance
optimization for Office Add-ins
Article • 04/11/2023

To create the best experience for your users, ensure that your Office Add-in performs
within specific limits for CPU core and memory usage, reliability, and, for Outlook add-
ins, the response time for evaluating regular expressions. These run-time resource usage
limits apply to add-ins running in Office clients on Windows and OS X, but not on
mobile apps or in a browser.

You can also optimize the performance of your add-ins on desktop and mobile devices
by optimizing the use of resources in your add-in design and implementation.

Resource usage limits for add-ins


Run-time resource usage limits apply to all types of Office Add-ins. These limits help
ensure performance for your users and mitigate denial-of-service attacks. Be sure to test
your Office Add-in on your target Office application by using a range of possible data,
and measure its performance against the following run-time usage limits.

CPU core usage - A single CPU core usage threshold of 90%, observed three times
in default 5-second intervals.

The default interval for an Office client to check CPU core usage is every 5 seconds.
If the Office client detects the CPU core usage of an add-in is above the threshold
value, it displays a message asking if the user wants to continue running the add-
in. If the user chooses to continue, the Office client does not ask the user again
during that edit session. Administrators might want to use the AlertInterval
registry key to raise the threshold to reduce the display of this warning message if
users run CPU-intensive add-ins.

Memory usage - A default memory usage threshold that is dynamically


determined based on the available physical memory of the device.

By default, when a Office client detects that physical memory usage on a device
exceeds 80% of the available memory, the client starts monitoring the add-in's
memory usage, at a document level for content and task pane add-ins, and at a
mailbox level for Outlook add-ins. At a default interval of 5 seconds, the client
warns the user if physical memory usage for a set of add-ins at the document or
mailbox level exceeds 50%. This memory usage limit uses physical rather than
virtual memory to ensure performance on devices with limited RAM, such as
tablets. Administrators can override this dynamic setting with an explicit limit by
using the MemoryAlertThreshold Windows registry key as a global setting, ir
adjust the alert interval by using the AlertInterval key as a global setting.

Crash tolerance - A default limit of four crashes for an add-in.

Administrators can adjust the threshold for crashes by using the


RestartManagerRetryLimit registry key.

Application blocking - Prolonged unresponsiveness threshold of 5 seconds for an


add-in.

This affects the user's experiences of the add-in and the Office application. When
this occurs, the Office application automatically restarts all the active add-ins for a
document or mailbox (where applicable), and warns the user as to which add-in
became unresponsive. Add-ins can reach this threshold when they do not regularly
yield processing while performing long-running tasks. There are techniques to
ensure that blocking does not occur. Administrators cannot override this threshold.

Outlook add-ins
If any Outlook add-in exceeds the preceding thresholds for CPU core or memory usage,
or tolerance limit for crashes, Outlook disables the add-in. The Exchange Admin Center
displays the disabled status of the app.

7 Note

Even though only the Outlook rich clients and not Outlook on the web or mobile
devices monitor resource usage, if a rich client disables an Outlook add-in, that
add-in is also disabled for use in Outlook on the web and mobile devices.

In addition to the CPU core, memory, and reliability rules, Outlook add-ins should
observe the following rules on activation.

Regular expressions response time - A default threshold of 1,000 milliseconds for


Outlook to evaluate all regular expressions in the manifest of an Outlook add-in.
Exceeding the threshold causes Outlook to retry evaluation at a later time.

Using a group policy or application-specific setting in the Windows registry,


administrators can adjust this default threshold value of 1,000 milliseconds in the
OutlookActivationAlertThreshold setting.
Regular expressions re-evaluation - A default limit of three times for Outlook to
reevaluate all the regular expressions in a manifest. If evaluation fails all three
times by exceeding the applicable threshold (which is either the default of 1,000
milliseconds or a value specified by OutlookActivationAlertThreshold, if that
setting exists in the Windows registry), Outlook disables the Outlook add-in. The
Exchange Admin Center displays the disabled status, and the add-in is disabled for
use in the Outlook rich clients, and Outlook on the web and mobile devices.

Using a group policy or application-specific setting in the Windows registry,


administrators can adjust this number of times to retry evaluation in the
OutlookActivationManagerRetryLimit setting.

Excel add-ins
If you're building an Excel add-in, be aware of the following size limitations when
interacting with the workbook.

Excel on the web has a payload size limit for requests and responses of 5MB.
RichAPI.Error will be thrown if that limit is exceeded.
A range is limited to five million cells for get operations.

If you expect user input to exceed these limits, be sure to check the data before calling
context.sync() . Split the operation into smaller pieces as needed. Be sure to call

context.sync() for each sub-operation to avoid those operations getting batched

together again.

These limitations are typically exceeded by large ranges. Your add-in might be able to
use RangeAreas to strategically update cells within a larger range. For more information
about working with RangeAreas , see Work with multiple ranges simultaneously in Excel
add-ins. For additional information about optimizing payload size in Excel, see Payload
size limit best practices.

Task pane and content add-ins


If any content or task pane add-in exceeds the preceding thresholds on CPU core or
memory usage, or tolerance limit for crashes, the corresponding Office application
displays a warning for the user. At this point, the user can do one of the following:

Restart the add-in.


Cancel further alerts about exceeding that threshold. Ideally, the user should then
delete the add-in from the document; continuing the add-in would risk further
performance and stability issues.
Verify resource usage issues in the Telemetry
Log
Office provides a Telemetry Log that maintains a record of certain events (loading,
opening, closing, and errors) of Office solutions running on the local computer,
including resource usage issues in an Office Add-in. If you have the Telemetry Log set
up, you can use Excel to open the Telemetry Log in the following default location on
your local drive.

%Users%\<Current user>\AppData\Local\Microsoft\Office\16.0\Telemetry

For each event that the Telemetry Log tracks for an add-in, there is a date/time of the
occurrence, event ID, severity, and short descriptive title for the event, the friendly name
and unique ID of the add-in, and the application that logged the event. You can refresh
the Telemetry Log to see the current tracked events. The following table shows examples
of Outlook add-ins that were tracked in the Telemetry log.

Date/Time Event Severity Title File ID Application


ID

10/8/2022 7 Not add-in manifest Who's 69cc567c-6737- Outlook


5:57:10 PM applicable downloaded Who 4c49-88dd-
successfully 123334943a22

10/8/2022 7 Not add-in manifest LinkedIn 333bf46d-7dad- Outlook


5:57:01 PM applicable downloaded 4f2b-8cf4-
successfully c19ddc78b723

The following table lists the events that the Telemetry Log tracks for Office Add-ins in
general.

Event Title Severity Description


ID

7 Add-in Not The manifest of the Office Add-in was successfully loaded
manifest applicable and read by the Office application.
downloaded
successfully

8 Add-in Critical The Office application was unable to load the manifest file for
manifest did the Office Add-in from the SharePoint catalog, corporate
not catalog, or AppSource.
download
Event Title Severity Description
ID

9 Add-in Critical The Office application loaded the Office Add-in manifest, but
markup could not read the HTML markup of the app.
could not be
parsed

10 Add-in used Critical The Office Add-in used more than 90% of the CPU resources
too much over a finite period of time.
CPU

15 Add-in Not Outlook add-ins search the subject line and message of an e-
disabled applicable mail to determine whether they should be displayed by using
due to a regular expression. The Outlook add-in listed in the File
string search column was disabled by Outlook because it timed out
time-out repeatedly while trying to match a regular expression.

18 Add-in Not The Office application was able to close the Office Add-in
closed applicable successfully.
successfully

19 Add-in Critical The Office Add-in had a problem that caused it to fail. For
encountered more details, look at the Microsoft Office Alerts log using
runtime the Windows Event Viewer on the computer that
error encountered the error.

20 Add-in Critical The licensing information for the Office Add-in could not be
failed to verified and may have expired. For more details, look at the
verify Microsoft Office Alerts log using the Windows Event Viewer
licensing on the computer that encountered the error.

For more information, see Deploying Telemetry Dashboard and Troubleshooting Office
files and custom solutions with the telemetry log.

Design and implementation techniques


While the resources limits on CPU and memory usage, crash tolerance, UI
responsiveness apply to Office Add-ins running only on the rich clients, optimizing the
usage of these resources and battery should be a priority if you want your add-in to
perform satisfactorily on all supporting clients and devices. Optimization is particularly
important if your add-in carries out long-running operations or handles large data sets.
The following list suggests some techniques to break up CPU-intensive or data-intensive
operations into smaller chunks so that your add-in can avoid excessive resource
consumption and the Office application can remain responsive.
In a scenario where your add-in needs to read a large volume of data from an
unbounded dataset, you can apply paging when reading the data from a table, or
reduce the size of data in each shorter read operation, rather than attempting to
complete the read in one single operation. You can do this through the
setTimeout method of the global object to limit the duration of input and
output. It also handles the data in defined chunks instead of randomly unbounded
data. Another option is to use async to handle your Promises.

If your add-in uses a CPU-intensive algorithm to process a large volume of data,


you can use web workers to perform the long-running task in the background
while running a separate script in the foreground, such as displaying progress in
the user interface. Web workers do not block user activities and allow the HTML
page to remain responsive. For an example of web workers, see The Basics of Web
Workers . See Web Workers for more information about the Web Workers API.

If your add-in uses a CPU-intensive algorithm but you can divide the data input or
output into smaller sets, consider creating a web service, passing the data to the
web service to off-load the CPU, and wait for an asynchronous callback.

Test your add-in against the highest volume of data you expect, and restrict your
add-in to process up to that limit.

Performance improvements with the application-specific


APIs
The performance tips in Using the application-specific API model provide guidance
when using the application-specific APIs for Excel, OneNote, Visio, and Word. In
summary, you should:

Only load necessary properties.


Minimize the number of sync() calls. Read Avoid using the context.sync method in
loops for further information on how to manage sync calls in your code.
Minimize the number of proxy objects created. You can also untrack proxy objects,
as described in the next section.

Untrack unneeded proxy objects

Proxy objects persist in memory until RequestContext.sync() is called. Large batch


operations may generate a lot of proxy objects that are only needed once by the add-in
and can be released from memory before the batch executes.
The untrack() method releases the object from memory. This method is implemented
on many application-specific API proxy objects. Calling untrack() after your add-in is
done with the object should yield a noticeable performance benefit when using large
numbers of proxy objects.

7 Note

Range.untrack() is a shortcut for

ClientRequestContext.trackedObjects.remove(thisRange). Any proxy object can be


untracked by removing it from the tracked objects list in the context.

The following Excel code sample fills a selected range with data, one cell at a time. After
the value is added to the cell, the range representing that cell is untracked. Run this
code with a selected range of 10,000 to 20,000 cells, first with the cell.untrack() line,
and then without it. You should notice the code runs faster with the cell.untrack() line
than without it. You may also notice a quicker response time afterwards, since the
cleanup step takes less time.

JavaScript

Excel.run(async (context) => {


const largeRange = context.workbook.getSelectedRange();
largeRange.load(["rowCount", "columnCount"]);
await context.sync();

for (let i = 0; i < largeRange.rowCount; i++) {


for (let j = 0; j < largeRange.columnCount; j++) {
let cell = largeRange.getCell(i, j);
cell.values = [[i *j]];

// Call untrack() to release the range from memory.


cell.untrack();
}
}

await context.sync();
});

Note that needing to untrack objects only becomes important when you're dealing with
thousands of them. Most add-ins will not need to manage proxy object tracking.

See also
Privacy and security for Office Add-ins
Limits for activation and JavaScript API for Outlook add-ins
Performance optimization using the Excel JavaScript API
Unit testing in Office Add-ins
Article • 02/22/2023

Unit tests check your add-in's functionality without requiring network or service
connections, including connections to the Office application. Unit testing server-side
code, and client-side code that does not call the Office JavaScript APIs, is the same in
Office Add-ins as it is in any web application, so it requires no special documentation.
But client-side code that calls the Office JavaScript APIs is challenging to test. To solve
these problems, we have created a library to simplify the creation of mock Office objects
in unit tests: Office-Addin-Mock . The library makes testing easier in the following
ways:

The Office JavaScript APIs must initialize in a webview control in the context of an
Office application (Excel, Word, etc.), so they cannot be loaded in the process in
which unit tests run on your development computer. The Office-Addin-Mock
library can be imported into your test files, which enables the mocking of Office
JavaScript APIs inside the node.js process in which the tests run.
The application-specific APIs have load and sync methods that must be called in a
particular order relative to other functions and to each other. Moreover, the load
method must be called with certain parameters depending on what what
properties of Office objects are going to be read in by code later in the function
being tested. But unit testing frameworks are inherently stateless, so they cannot
keep a record of whether load or sync was called or what parameters were passed
to load . The mock objects that you create with the Office-Addin-Mock library have
internal state that keeps track of these things. This enables the mock objects to
emulate the error behavior of actual Office objects. For example, if the function
that is being tested tries to read a property that was not first passed to load , then
the test will return an error similar to what Office would return.

The library doesn't depend on the Office JavaScript APIs and it can be used with any
JavaScript unit testing framework, such as:

Jest
Mocha
Jasmine

The examples in this article use the Jest framework. There are examples using the Mocha
framework at the Office-Addin-Mock home page .

Prerequisites
This article assumes that you are familiar with the basic concepts of unit testing and
mocking, including how to create and run test files, and that you have some experience
with a unit testing framework.

 Tip

If you are working with Visual Studio, we recommend that you read the article Unit
testing JavaScript and TypeScript in Visual Studio for some basic information
about JavaScript unit testing in Visual Studio and then return to this article.

Install the tool


To install the library, open a command prompt, navigate to the root of your add-in
project, and then enter the following command.

command line

npm install office-addin-mock --save-dev

Basic usage
1. Your project will have one or more test files. (See the instructions for your test
framework and the example test files in Examples below.) Import the library, with
either the require or import keyword, to any test file that has a test of a function
that calls the Office JavaScript APIs, as shown in the following example.

JavaScript

const OfficeAddinMock = require("office-addin-mock");

2. Import the module that contains the add-in function that you want to test with
either the require or import keyword. The following is an example that assumes
your test file is in a subfolder of the folder with your add-in's code files.

JavaScript

const myOfficeAddinFeature = require("../my-office-add-in");

3. Create a data object that has the properties and subproperties that you need to
mock to test the function. The following is an example of an object that mocks the
Excel Workbook.range.address property and the Workbook.getSelectedRange
method. This isn't the final mock object. Think of it as a seed object that is used by
OfficeMockObject to create the final mock object.

JavaScript

const mockData = {
workbook: {
range: {
address: "C2:G3",
},
getSelectedRange: function () {
return this.range;
},
},
};

4. Pass the data object to the OfficeMockObject constructor. Note the following
about the returned OfficeMockObject object.

It is a simplified mock of an OfficeExtension.ClientRequestContext object.


The mock object has all the members of the data object and also has mock
implementations of the load and sync methods.
The mock object will mimic crucial error behavior of the
ClientRequestContext object. For example, if the Office API you are testing

tries to read a property without first loading the property and calling sync ,
then the test will fail with an error similar to what would be thrown in
production runtime: "Error, property not loaded".

JavaScript

const contextMock = new OfficeAddinMock.OfficeMockObject(mockData);

7 Note

Full reference documentation for the OfficeMockObject type is at Office-


Addin-Mock .

5. In the syntax of your test framework, add a test of the function. Use the
OfficeMockObject object in place of the object that it mocks, in this case the

ClientRequestContext object. The following continues the example in Jest. This


example test assumes that the add-in function that is being tested is called
getSelectedRangeAddress , that it takes a ClientRequestContext object as a
parameter, and that it is intended to return the address of the currently selected
range. The full example is later in this article.

JavaScript

test("getSelectedRangeAddress should return the address of the range",


async function () {
expect(await getSelectedRangeAddress(contextMock)).toBe("C2:G3");
});

6. Run the test in accordance with documentation of the test framework and your
development tools. Typically, there is a package.json file with a script that executes
the test framework. For example, if Jest is the framework, package.json would
contain the following:

JSON

"scripts": {
"test": "jest",
-- other scripts omitted --
}

To run the test, enter the following in a command prompt in the root of the
project.

command line

npm test

Examples
The examples in this section use Jest with its default settings. These settings support
CommonJS modules. See the Jest documentation for how to configure Jest and
node.js to support ECMAScript modules and to support TypeScript. To run any of these
examples, take the following steps.

1. Create an Office Add-in project for the appropriate Office host application (for
example, Excel or Word). One way to do this quickly is to use the Yeoman
generator for Office Add-ins.
2. In the root of the project, install Jest .
3. Install the office-addin-mock tool.
4. Create a file exactly like the first file in the example and add it to the folder that
contains the project's other source files, often called \src .
5. Create a subfolder to the source file folder and give it an appropriate name, such
as \tests .
6. Create a file exactly like the test file in the example and add it to the subfolder.
7. Add a test script to the package.json file, and then run the test, as described in
Basic usage.

Mocking the Office Common APIs


This example assumes an Office Add-in for any host that supports the Office Common
APIs (for example, Excel, PowerPoint, or Word). The add-in has one of its features in a file
named my-common-api-add-in-feature.js . The following shows the contents of the file.
The addHelloWorldText function sets the text "Hello World!" to whatever is currently
selected in the document; for example; a range in Word, or a cell in Excel, or a text box
in PowerPoint.

JavaScript

const myCommonAPIAddinFeature = {

addHelloWorldText: async () => {


const options = { coercionType: Office.CoercionType.Text };
await Office.context.document.setSelectedDataAsync("Hello World!",
options);
}
}

module.exports = myCommonAPIAddinFeature;

The test file, named my-common-api-add-in-feature.test.js is in a subfolder, relative to


the location of the add-in code file. The following shows the contents of the file. Note
that the top level property is context , an Office.Context object, so the object that is
being mocked is the parent of this property: an Office object. Note the following about
this code:

The OfficeMockObject constructor does not add all of the Office enum classes to
the mock Office object, so the CoercionType.Text value that is referenced in the
add-in method must be added explicitly in the seed object.
Because the Office JavaScript library isn't loaded in the node process, the Office
object that is referenced in the add-in code must be declared and initialized.

JavaScript

const OfficeAddinMock = require("office-addin-mock");


const myCommonAPIAddinFeature = require("../my-common-api-add-in-feature");
// Create the seed mock object.
const mockData = {
context: {
document: {
setSelectedDataAsync: function (data, options) {
this.data = data;
this.options = options;
},
},
},
// Mock the Office.CoercionType enum.
CoercionType: {
Text: {},
},
};

// Create the final mock object from the seed object.


const officeMock = new OfficeAddinMock.OfficeMockObject(mockData);

// Create the Office object that is called in the addHelloWorldText


function.
global.Office = officeMock;

/* Code that calls the test framework goes below this line. */

// Jest test
test("Text of selection in document should be set to 'Hello World'", async
function () {
await myCommonAPIAddinFeature.addHelloWorldText();
expect(officeMock.context.document.data).toBe("Hello World!");
});

Mocking the Outlook APIs


Although strictly speaking, the Outlook APIs are part of the Common API model, they
have a special architecture that is built around the Mailbox object, so we have provided
a distinct example for Outlook. This example assumes an Outlook that has one of its
features in a file named my-outlook-add-in-feature.js . The following shows the
contents of the file. The addHelloWorldText function sets the text "Hello World!" to
whatever is currently selected in the message compose window.

JavaScript

const myOutlookAddinFeature = {

addHelloWorldText: async () => {


Office.context.mailbox.item.setSelectedDataAsync("Hello World!");
}
}
module.exports = myOutlookAddinFeature;

The test file, named my-outlook-add-in-feature.test.js is in a subfolder, relative to the


location of the add-in code file. The following shows the contents of the file. Note that
the top level property is context , an Office.Context object, so the object that is being
mocked is the parent of this property: an Office object. Note the following about this
code:

The host property on the mock object is used internally by the mock library to
identify the Office application. It's mandatory for Outlook. It currently serves no
purpose for any other Office application.
Because the Office JavaScript library isn't loaded in the node process, the Office
object that is referenced in the add-in code must be declared and initialized.

JavaScript

const OfficeAddinMock = require("office-addin-mock");


const myOutlookAddinFeature = require("../my-outlook-add-in-feature");

// Create the seed mock object.


const mockData = {
// Identify the host to the mock library (required for Outlook).
host: "outlook",
context: {
mailbox: {
item: {
setSelectedDataAsync: function (data) {
this.data = data;
},
},
},
},
};

// Create the final mock object from the seed object.


const officeMock = new OfficeAddinMock.OfficeMockObject(mockData);

// Create the Office object that is called in the addHelloWorldText


function.
global.Office = officeMock;

/* Code that calls the test framework goes below this line. */

// Jest test
test("Text of selection in message should be set to 'Hello World'", async
function () {
await myOutlookAddinFeature.addHelloWorldText();
expect(officeMock.context.mailbox.item.data).toBe("Hello World!");
});
Mocking the Office application-specific APIs
When you are testing functions that use the application-specific APIs, be sure that you
are mocking the right type of object. There are two options:

Mock a OfficeExtension.ClientRequestObject. Do this when the function that is


being tested meets both of the following conditions:
It doesn't call a Host. run function, such as Excel.run.
It doesn't reference any other direct property or method of a Host object.

Mock a Host object, such as Excel or Word. Do this when the preceding option isn't
possible.

Examples of both types of tests are in the subsections below.

7 Note

The Office-Addin-Mock library doesn't currently support mocking collection type


objects, which are all the objects in the application-specific APIs that are named on
the pattern *Collection, such as WorksheetCollection. We are working hard to add
this support to the library.

Mocking a ClientRequestContext object


This example assumes an Excel add-in that has one of its features in a file named my-
excel-add-in-feature.js . The following shows the contents of the file. Note that the
getSelectedRangeAddress is a helper method called inside the callback that is passed to

Excel.run .

JavaScript

const myExcelAddinFeature = {

getSelectedRangeAddress: async (context) => {


const range = context.workbook.getSelectedRange();
range.load("address");

await context.sync();

return range.address;
}
}
module.exports = myExcelAddinFeature;

The test file, named my-excel-add-in-feature.test.js is in a subfolder, relative to the


location of the add-in code file. The following shows the contents of the file. Note that
the top level property is workbook , so the object that is being mocked is the parent of an
Excel.Workbook : a ClientRequestContext object.

JavaScript

const OfficeAddinMock = require("office-addin-mock");


const myExcelAddinFeature = require("../my-excel-add-in-feature");

// Create the seed mock object.


const mockData = {
workbook: {
range: {
address: "C2:G3",
},
// Mock the Workbook.getSelectedRange method.
getSelectedRange: function () {
return this.range;
},
},
};

// Create the final mock object from the seed object.


const contextMock = new OfficeAddinMock.OfficeMockObject(mockData);

/* Code that calls the test framework goes below this line. */

// Jest test
test("getSelectedRangeAddress should return address of selected range",
async function () {
expect(await
myOfficeAddinFeature.getSelectedRangeAddress(contextMock)).toBe("C2:G3");
});

Mocking a host object


This example assumes a Word add-in that has one of its features in a file named my-
word-add-in-feature.js . The following shows the contents of the file.

JavaScript

const myWordAddinFeature = {

insertBlueParagraph: async () => {


return Word.run(async (context) => {
// Insert a paragraph at the end of the document.
const paragraph = context.document.body.insertParagraph("Hello World",
Word.InsertLocation.end);

// Change the font color to blue.


paragraph.font.color = "blue";

await context.sync();
});
}
}

module.exports = myWordAddinFeature;

The test file, named my-word-add-in-feature.test.js is in a subfolder, relative to the


location of the add-in code file. The following shows the contents of the file. Note that
the top level property is context , a ClientRequestContext object, so the object that is
being mocked is the parent of this property: a Word object. Note the following about
this code:

When the OfficeMockObject constructor creates the final mock object, it will
ensure that the child ClientRequestContext object has sync and load methods.
The OfficeMockObject constructor does not add a run function to the mock Word
object, so it must be added explicitly in the seed object.
The OfficeMockObject constructor does not add all of the Word enum classes to
the mock Word object, so the InsertLocation.end value that is referenced in the
add-in method must be added explicitly in the seed object.
Because the Office JavaScript library isn't loaded in the node process, the Word
object that is referenced in the add-in code must be declared and initialized.

JavaScript

const OfficeAddinMock = require("office-addin-mock");


const myWordAddinFeature = require("../my-word-add-in-feature");

// Create the seed mock object.


const mockData = {
context: {
document: {
body: {
paragraph: {
font: {},
},
// Mock the Body.insertParagraph method.
insertParagraph: function (paragraphText, insertLocation) {
this.paragraph.text = paragraphText;
this.paragraph.insertLocation = insertLocation;
return this.paragraph;
},
},
},
},
// Mock the Word.InsertLocation enum.
InsertLocation: {
end: "end",
},
// Mock the Word.run function.
run: async function(callback) {
await callback(this.context);
},
};

// Create the final mock object from the seed object.


const wordMock = new OfficeAddinMock.OfficeMockObject(mockData);

// Define and initialize the Word object that is called in the


insertBlueParagraph function.
global.Word = wordMock;

/* Code that calls the test framework goes below this line. */

// Jest test set


describe("Insert blue paragraph at end tests", () => {

test("color of paragraph", async function () {


await myWordAddinFeature.insertBlueParagraph();

expect(wordMock.context.document.body.paragraph.font.color).toBe("blue");
});

test("text of paragraph", async function () {


await myWordAddinFeature.insertBlueParagraph();
expect(wordMock.context.document.body.paragraph.text).toBe("Hello
World");
});
})

7 Note

Full reference documentation for the OfficeMockObject type is at Office-Addin-


Mock .

See also
Office-Addin-Mock npm page installation point.
The open source repo is Office-Addin-Mock .
Jest
Mocha
Jasmine
Usability testing for Office Add-ins
Article • 11/01/2022

A great add-in design takes user behaviors into account. Because your own
preconceptions influence your design decisions, it’s important to test designs with real
users to make sure that your add-ins work well for your customers.

You can run usability tests in different ways. For many add-in developers, remote,
unmoderated usability studies are the most time and cost effective. Several popular
testing services make this easy; the following are some examples.

UserTesting.com
Optimalworkshop.com
Userzoom.com

These testing services help you to streamline test plan creation and remove the need to
seek out participants or moderate the tests.

You need only five participants to uncover most usability issues in your design.
Incorporate small tests regularly throughout your development cycle to ensure that your
product is user-centered.

7 Note

We recommend that you test the usability of your add-in across multiple platforms.
To publish your add-in to AppSource, it must work on all platforms that support
the methods that you define.

1. Sign up for a testing service


For more information, see Selecting an Online Tool for Unmoderated Remote User
Testing .

2. Develop your research questions


Research questions define the objectives of your research and guide your test plan. Your
questions will help you identify participants to recruit and the tasks they will perform.
Make your research questions as specific as you can. You can also seek to answer
broader questions.
The following are some examples of research questions.

Specific

Do users notice the "free trial" link on the landing page?


When users insert content from the add-in to their document, do they understand
where in the document it is inserted?

Broad

What are the biggest pain points for the user in our add-in?
Do users understand the meaning of the icons in our command bar, before they
click on them?
Can users easily find the settings menu?

It’s important to get data on the entire user journey – from discovering your add-in, to
installing and using it. Consider research questions that address the following aspects of
the add-in user experience.

Finding your add-in in AppSource


Choosing to install your add-in
First-run experience
Ribbon commands
Add-in UI
How the add-in interacts with the document space of the Office application
How much control the user has over any content insertion flows

For more information, see Gathering factual responses vs. subjective data .

3. Identify participants to target


Remote testing services can give you control over many characteristics of your test
participants. Think carefully about what kinds of users you want to target. In your early
stages of data collection, it might be better to recruit a wide variety of participants to
identify more obvious usability issues. Later, you might choose to target groups like
advanced Office users, particular occupations, or specific age ranges.

4. Create the participant screener


The screener is the set of questions and requirements you will present to prospective
test participants to screen them for your test. Keep in mind that participants for services
like UserTesting.com have a financial interest in qualifying for your test. It's a good idea
to include trick questions in your screener if you want to exclude certain users from the
test.

For example, if you want to find participants who are familiar with GitHub, to filter out
users who might misrepresent themselves, include fakes in the list of possible answers.

Which of the following source code repositories are you familiar with?
a. SourceShelf [Reject]
b. CodeContainer [Reject]
c. GitHub [Must select]
d. BitBucket [May select]
e. CloudForge [May select]

If you are planning to test a live build of your add-in, the following questions can screen
for users who will be able to do this.

This test requires you to have the latest version of Microsoft PowerPoint. Do you have
the latest version of PowerPoint?
a. Yes [Must select]
b. No [Reject]
c. I don’t know [Reject]

This test requires you to install a free add-in for PowerPoint, and create a free account
to use it. Are you willing to install an add-in and create a free account?
a. Yes [Must select]
b. No [Reject]

For more information, see Screener Questions Best Practices .

5. Create tasks and questions for participants


Try to prioritize what you want tested so that you can limit the number of tasks and
questions for the participant. Some services pay participants only for a set amount of
time, so you want to make sure not to go over.

Try to observe participant behaviors instead of asking about them, whenever possible. If
you need to ask about behaviors, ask about what participants have done in the past,
rather than what they would expect to do in a situation. This tends to give more reliable
results.

The main challenge in unmoderated testing is making sure your participants understand
your tasks and scenarios. Your directions should be clear and concise. Inevitably, if there
is potential for confusion, someone will be confused.
Don't assume that your user will be on the screen they’re supposed to be on at any
given point during the test. Consider telling them what screen they need to be on to
start the next task.

For more information, see Writing Great Tasks .

6. Create a prototype to match the tasks and


questions
You can either test your live add-in, or you can test a prototype. Keep in mind that if you
want to test the live add-in, you need to screen for participants that have the latest
version of Office, are willing to install the add-in, and are willing to sign up for an
account (unless you have logon credentials to provide them.) You'll then need to make
sure that they successfully install your add-in.

On average, it takes about 5 minutes to walk users through how to install an add-in. The
following is an example of clear, concise installation steps. Adjust the steps based on the
specifics of your test.

Please install the (insert your add-in name here) add-in for PowerPoint, using the
following instructions.

1. Open Microsoft PowerPoint.


2. Select Blank Presentation.
3. Go to Insert > My Add-ins.
4. In the popup window, choose Store.
5. Type (Add-in name) in the search box.
6. Choose (Add-in name).
7. Take a moment to look at the Store page to familiarize yourself with the add-in.
8. Choose Add to install the add-in.

You can test a prototype at any level of interaction and visual fidelity. For more complex
linking and interactivity, consider a prototyping tool like InVision . If you just want to
test static screens, you can host images online and send participants the corresponding
URL, or give them a link to an online PowerPoint presentation.

7. Run a pilot test


It can be tricky to get the prototype and your task/question list right. Users might be
confused by tasks, or might get lost in your prototype. You should run a pilot test with
1-3 users to work out the inevitable issues with the test format. This will help to ensure
that your questions are clear, that the prototype is set up correctly, and that you’re
capturing the type of data you’re looking for.

8. Run the test


After you order your test, you will get email notifications when participants complete it.
Unless you’ve targeted a specific group of participants, the tests are usually completed
within a few hours.

9. Analyze results
This is the part where you try to make sense of the data you’ve collected. While
watching the test videos, record notes about problems and successes the user has.
Avoid trying to interpret the meaning of the data until you have viewed all the results.

A single participant having a usability issue is not enough to warrant making a change
to the design. Two or more participants encountering the same issue suggests that
other users in the general population will also encounter that issue.

In general, be careful about how you use your data to draw conclusions. Don’t fall into
the trap of trying to make the data fit a certain narrative; be honest about what the data
actually proves, disproves, or simply fails to provide any insight about. Keep an open
mind; user behavior frequently defies designer’s expectations.

See also
How to Conduct Usability Testing
Best Practices for UserTesting
Minimizing Bias
Validate an Office Add-in's manifest
Article • 04/14/2023

You may want to validate your add-in's manifest file to ensure that it's correct and
complete. Validation can also identify issues that are causing the error "Your add-in
manifest is not valid" when you attempt to sideload your add-in. This article describes
multiple ways to validate the manifest file.

7 Note

For details about using runtime logging to troubleshoot issues with your add-in's
manifest, see Debug your add-in with runtime logging.

Validate your manifest with the Yeoman


generator for Office Add-ins
If you used the Yeoman generator for Office Add-ins to create your add-in, you can also
use it to validate your project's manifest file. Run the following command in the root
directory of your project.

command line

npm run validate


7 Note

To access this functionality, your add-in project must be created using the Yeoman
generator for Office Add-ins version 1.1.17 or later.

Validate your manifest with office-addin-


manifest
If you didn't use the Yeoman generator for Office Add-ins to create your add-in, you can
validate the manifest by using office-addin-manifest .

1. Install Node.js .

2. Open a command prompt and install the validator with the following command.

command line

npm install -g office-addin-manifest

3. Run the following command in the root directory of your project.

command line

npm run validate


7 Note

If this command is not available or not working, run the following command
instead to force the use of the latest version of the office-addin-manifest tool
(replacing MANIFEST_FILE with the name of the manifest file).

command line

npx office-addin-manifest validate MANIFEST_FILE

Office Store validation


The validate command also does Office Store validation but allows developer
information like localhost URLs. If you'd like to run production-level Office Store
validation, then run the following command.

command line

npm run validate -- -p

If you're having trouble with that command, try the following (replacing MANIFEST_FILE
with the name of the manifest file).

command line

npx office-addin-manifest validate -p MANIFEST_FILE

Validate your manifest against the XML schema


You can validate the manifest file against the XML Schema Definition (XSD) files. This will
ensure that the manifest file follows the correct schema, including any namespaces for
the elements you are using. If you copied elements from other sample manifests double
check that you also include the appropriate namespaces. You can use an XML schema
validation tool to perform this validation.

To use a command-line XML schema validation tool to


validate your manifest
1. Install tar and libxml , if you haven't already.
2. Run the following command. Replace XSD_FILE with the path to the manifest XSD
file, and replace XML_FILE with the path to the manifest XML file.

command line

xmllint --noout --schema XSD_FILE XML_FILE

See also
Office Add-ins manifest
Clear the Office cache
Debug your add-in with runtime logging
Sideload Office Add-ins for testing
Debug add-ins using developer tools for Internet Explorer
Debug add-ins using developer tools for Edge Legacy
Debug add-ins using developer tools in Microsoft Edge (Chromium-based)
Deploy and publish Office Add-ins
Article • 08/15/2023

You can use one of several methods to deploy your Office Add-in for testing or
distribution to users. The deployment method can also affect which platforms surface
your add-in.

Method Use...

Sideloading As part of your development process, to test your add-in running on


Windows, iPad, Mac, or in a browser. (Not for production add-ins.)

Network share As part of your development process, to test your add-in running on Windows
after you have published the add-in to a server other than localhost. (Not for
production add-ins or for testing on iPad, Mac, or the web.)

AppSource To distribute your add-in publicly to users.

Microsoft 365 In a cloud deployment, to distribute your add-in to users in your organization
admin center by using the Microsoft 365 admin center. This is done through Integrated
Apps or Centralized Deployment.

SharePoint In an on-premises environment, to distribute your add-in to users in your


catalog organization.

Exchange server In an on-premises or online environment, to distribute Outlook add-ins to


users.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).

Deployment options by Office application and


add-in type
The deployment options that are available depend on the Office application that you're
targeting and the type of add-in you create.
Deployment options for Word, Excel, and PowerPoint
add-ins

Extension Sideloading Network AppSource Microsoft 365 SharePoint


point share admin center catalog*

Content Supported Supported Supported Supported Supported

Task pane Supported Supported Supported Supported Supported

Command Supported Supported Supported Supported Not available

* SharePoint catalogs do not support Office on Mac.

Deployment options for Outlook add-ins

Extension point Sideloading AppSource Exchange server

Mail app Supported Supported Supported

Command Supported Supported Supported

Production deployment methods


The following sections provide additional information about the deployment methods
that are most commonly used to distribute production Office Add-ins to users within an
organization.

Deploy updates
When you add features or fix bugs in your add-in, you'll need to deploy the updates. If
your add-in is deployed by one or more admins to their organizations, some manifest
changes will require the admin to consent to the updates. Users will be blocked from
the add-in until consent is granted. The following manifest changes will require the
admin to consent again.

Changes to requested permissions. See Requesting permissions for API use in add-
ins and Understanding Outlook add-in permissions.
Additional or changed Scopes. (Not applicable if the add-in uses the unified
manifest for Microsoft 365.)
Additional or changed Outlook events.
7 Note

Whenever you make a change to the manifest, you must raise the version number
of the manifest.

If the add-in uses the XML manifest, see Version element.


If the add-in uses the unified manifest, see version property.

For information about how end users acquire, insert, and run add-ins, see Start using
your Office Add-in .

Integrated Apps via the Microsoft 365 admin center


The Microsoft 365 admin center makes it easy for an administrator to deploy Office
Add-ins to users and groups in their organization. Add-ins deployed via the admin
center are available to users in their Office applications right away, with no client
configuration required. You can use Integrated Apps to deploy internal add-ins as well
as add-ins provided by ISVs. Integrated Apps also shows admins add-ins and other apps
bundled together by same ISV, giving them exposure to the entire experience across the
Microsoft 365 platform.

When you link your Office Add-ins, Teams apps, SPFx apps, and other apps together,
you create a single software as a service (SaaS) offering for your customers. For general
information about this process, see How to plan a SaaS offer for the commercial
marketplace. For specifics on how to create Integrated Apps, see Configure Microsoft
365 App integration.

For more information on the Integrated Apps deployment process, see Test and deploy
Microsoft 365 Apps by partners in the Integrated apps portal.

) Important

Customers in sovereign or government clouds don't have access to Integrated


Apps. They will use Centralized Deployment instead. Centralized Deployment is a
similar deploy method, but doesn't expose connected add-ins and apps to the
admin. For more information, see Determine if Centralized Deployment of add-ins
works for your organization.

SharePoint app catalog deployment


A SharePoint app catalog is a special site collection that you can create to host Word,
Excel, and PowerPoint add-ins. Because SharePoint catalogs don't support new add-in
features implemented in the VersionOverrides node of the manifest, including add-in
commands, we recommend that you use Centralized Deployment via the admin center if
possible. Add-in commands deployed via a SharePoint catalog open in a task pane by
default.

If you are deploying add-ins in an on-premises environment, use a SharePoint catalog.


For details, see Publish task pane and content add-ins to a SharePoint catalog.

7 Note

SharePoint catalogs do not support Office on Mac. To deploy Office Add-ins to Mac
clients, you must submit them to AppSource.

Outlook add-in deployment


For on-premises and online environments that do not use the Azure AD identity service,
you can deploy Outlook add-ins via the Exchange server.

Outlook add-in deployment requires:

Microsoft 365, Exchange Online, or Exchange Server 2013 or later


Outlook 2013 or later

To assign add-ins to tenants, use the Exchange admin center to upload a manifest
directly, either from a file or a URL, or add an add-in from AppSource. To assign add-ins
to individual users, you must use Exchange PowerShell. For details, see Add-ins for
Outlook in Exchange Server.

GoDaddy Microsoft 365 SKUs


Microsoft 365 subscriptions provided by GoDaddy have limited support for add-ins.
The following options are not supported:

Deployment through the Microsoft Admin Center.


Deployment through Exchange servers.
Acquiring add-ins from AppSource.

See also
Sideload Outlook add-ins for testing
Submit to AppSource
AppSource
Design guidelines for Office Add-ins
Create effective AppSource listings
Troubleshoot user errors with Office Add-ins
What is the Microsoft commercial marketplace?
Microsoft Dev Center app publishing page
Publish an add-in developed with Visual
Studio Code
Article • 03/28/2023

This article describes how to publish an Office Add-in that you created using the
Yeoman generator and developed with Visual Studio Code (VS Code) or any other
editor.

7 Note

For information about publishing an Office Add-in that you created using
Visual Studio, see Publish your add-in using Visual Studio.
For information about publishing an Office Add-in that you created using
Teams Toolkit, see Deploy Teams app to the cloud and Deploy your first
Teams app. This article is about Teams tab apps, but it is applicable to Office
Add-ins created with Teams Toolkit.

Publishing an add-in for other users to access


An Office Add-in consists of a web application and a manifest file. The web application
defines the add-in's user interface and functionality, while the manifest specifies the
location of the web application and defines settings and capabilities of the add-in.

While you're developing, you can run the add-in on your local web server ( localhost ).
When you're ready to publish it for other users to access, you'll need to deploy the web
application and update the manifest to specify the URL of the deployed application.

When your add-in is working as desired, you can publish it directly through Visual
Studio Code using the Azure Storage extension.

Using Visual Studio Code to publish

7 Note

These steps only work for projects created with the Yeoman generator, and that use
the XML-formatted manifest. They do not apply if you created the add-in using the
Teams Toolkit or created it with the Yeoman generator and it uses the unified
manifest for Microsoft 365.

1. Open your project from its root folder in Visual Studio Code (VS Code).

2. Select View > Extensions (Ctrl+Shift+X) to open the Extensions view.

3. Search for the Azure Storage extension and install it.

4. Once installed, an Azure icon is added to the Activity Bar. Select it to access the
extension. If the Activity Bar is hidden, open it by selecting View > Appearance >
Activity Bar.

5. Select Sign in to Azure to sign in to your Azure account. If you don't already have
an Azure account, create one by selecting Create an Azure Account. Follow the
provided steps to set up your account.

6. Once you're signed in, you'll see your Azure storage accounts appear in the
extension. If you don't already have a storage account, create one using the Create
Storage Account option in the command palette. Name your storage account a
globally unique name, using only 'a-z' and '0-9'. Note that by default, this creates a
storage account and a resource group with the same name. It automatically puts
the storage account in West US. This can be adjusted online through your Azure
account .
7. Right-click your storage account and select Configure Static Website. You'll be
asked to enter the index document name and the 404 document name. Change
the index document name from the default index.html to taskpane.html . You may
also change the 404 document name but are not required to.

8. Right-click your storage account again and this time select Browse Static Website.
From the browser window that opens, copy the website URL.

9. Open your project's manifest file ( manifest.xml ) and change all references to your
localhost URL (such as https://localhost:3000 ) to the URL you've copied. This
endpoint is the static website URL for your newly created storage account. Save
the changes to your manifest file.

10. Open a command line prompt or terminal window and go to the root directory of
your add-in project. Run the following command to prepare all files for production
deployment.

command line

npm run build

When the build completes, the dist folder in the root directory of your add-in
project will contain the files that you'll deploy in subsequent steps.

11. In VS Code, go to the Explorer and Right-click the dist folder, and select Deploy to
Static Website via Azure Storage. When prompted, select the storage account you
created previously.

12. When deployment is complete, right-click the storage account that you created
previously and select Browse Static Website. This opens the static web site and
displays the task pane.

13. Finally, sideload the manifest file and the add-in will load from the static web site
you just deployed.

Deploy custom functions for Excel


If your add-in has custom functions, there are a few more steps to enable them on the
Azure Storage account. First, enable CORS so that Office can access the functions.json
file.

1. Right-click the Azure storage account and select Open in Portal.

2. In the Settings group, select Resource sharing (CORS). You can also use the search
box to find this.
3. Create a new CORS rule with the following settings.

Property Value

Allowed origins *

Allowed methods GET

Allowed headers *

Exposed headers Access-Control-Allow-Origin

Max age 200

4. Select Save.

U Caution

This CORS configuration assumes all files on your server are publicly available to all
domains.

Next, add a MIME type for JSON files.

1. Create a new file in the /src folder named web.config.

2. Insert the following XML and save the file.

XML

<?xml version="1.0"?>
<configuration>
<system.webServer>
<staticContent>
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
</system.webServer>
</configuration>

3. Open the webpack.config.js file.

4. Add the following code in the list of plugins to copy the web.config into the
bundle when the build runs.

JavaScript

new CopyWebpackPlugin({
patterns: [
{
from: "src/web.config",
to: "src/web.config",
},
],
}),

5. Open a command line prompt and go to the root directory of your add-in project.
Then, run the following command to prepare all files for deployment.

command line

npm run build

When the build completes, the dist folder in the root directory of your add-in
project will contain the files that you'll deploy.

6. To deploy, in the VS Code Explorer, Right-click the dist folder, and select Deploy
to Static Website via Azure Storage. When prompted, select the storage account
you created previously. If you already deployed the dist folder, you'll be prompted
if you want to overwrite the files in the Azure storage with the latest changes.

Deploy updates
When you add features or fix bugs in your add-in, you'll need to deploy the updates. If
your add-in is deployed by one or more admins to their organizations, some manifest
changes will require the admin to consent to the updates. Users will be blocked from
the add-in until consent is granted. The following manifest changes will require the
admin to consent again.

Changes to requested permissions.


Additional scopes.
Additional Outlook events.

See also
Develop Office Add-ins with Visual Studio Code
Deploy and publish your Office Add-in
Cross-Origin Resource Sharing (CORS) support for Azure Storage
Deploy a single sign-on (SSO) Office
Add-in to Microsoft Azure App Service
Article • 03/28/2023

Office Add-ins that use SSO require server-side code. To support server-side code in
deployment you need to use Azure App Service. Follow the steps in this article to deploy
your Office Add-in to Azure App Service for staging or deployment.

Prerequisites
The steps in this article work for an Office Add-in created by the Yeoman Generator for
Office Add-ins using the Office Add-in Task Pane project supporting single sign-on
(localhost) project type. Be sure you have configured the add-in project so that it runs

on localhost successfully. For more information, see the Single sign-on (SSO) quick start.

7 Note

For information about deploying an Office Add-in that you created using Teams
Toolkit, see Deploy Teams app to the cloud and Deploy your first Teams app. Add-
ins created with the Teams Toolkit use the unified manifest for Microsoft 365
(preview). At this time, SSO-enabled add-ins that use this manifest can be
published only by sideloading. For more information about publication of add-ins
and sideloading, see Deploy and publish Office Add-ins.

The steps in this article also require:

An Azure account. Get a trial subscription at Microsoft Azure .


Azure Account extension for VS Code.
Azure App Service extension for VS Code.

Create the App Service app


The following steps set up a basic deployment of the Office Add-in. There are multiple
ways to configure deployment that aren't covered in this documentation. For additional
options on how to configure your deployment, see Deployment Best Practices.

Sign in to Azure
1. Open your Office Add-in project in VS Code.

2. Select the Azure logo in the Activity Bar . If the Activity Bar is hidden, open it by
selecting View > Appearance > Activity Bar.

3. In the App Service explorer, select Sign in to Azure... and follow the instructions.

Configure the App Service app


App Service supports various versions of Node.js on both Linux and Windows. Select the
tab for the one you'd like to use and then follow the instructions to create your App
Service app.

Deploy to Windows

1. Right-click on App Services and select Create new Web App... Advanced.

2. Type a globally unique name for your web app and press Enter. The name
must be unique across all of Azure and use only alphanumeric characters ('A-
Z', 'a-z', and '0-9') and hyphens ('-').

3. Select the resource group you want to use. If you don't have a resource group,
select Create a new resource group, then enter a name for the resource
group, such as AppServiceQS-rg.

4. Select the Node 16 LTS runtime stack.


5. Select Windows for the operating system.

6. Select the location you want to serve your app from. For example, West
Europe.

7. Select the App Service plan you want to use. If you don't have an App Service
plan, select Create new App Service plan, then enter a name for the plan
(such as AppServiceQS-plan), then select F1 Free.

8. For Select an Application Insights resource for your app, select Skip for now
and wait the resources to be provisioned in Azure. When prompted to deploy,
don't deploy the add-in yet. You'll do that in a later step.

9. In the App Service explorer in Visual Studio code, expand the node for the
new app, right-click Application Settings, and select Add New Setting:

10. Enter SCM_DO_BUILD_DURING_DEPLOYMENT for the setting key.

11. Enter true for the setting value.

This app setting enables build automation at deploy time, which automatically
detects the start script and generates the web.config with it.

1. Right-click your App Service app and select Open in Portal.


2. When the portal opens in the browser, copy the domain name of the URL (not the
https:// part) from the Overview pane and save it. You'll need it in later steps.

Update package.json
1. Open the package.json file. Then replace the start command in the "scripts"
section with the following entry.
JSON

"start": "node middletier.js",

2. Find the "prestart" entry in the '"scripts"' section and delete it. This section is not
needed for this deployment.

3. Save the file.

Update webpack.config.js
1. Open the webpack.config.js file.

2. Set the urlDev and urlProd constants to the following values. (Note that the
https protocol is not specified.) This will cause webpack to replace localhost:3000

with your web site domain name in the /dist/manifest.xml file.

JavaScript

const urlDev = "localhost:3000";


const urlProd = "<your-web-site-domain-name>";

3. Find the first CopyWebpackPlugin section and update it to also copy the
package.json file to the dist folder as shown in the following example.

JavaScript

new CopyWebpackPlugin({
patterns: [
{
from: "assets/*",
to: "assets/[name][ext][query]",
},
{
from: "package.json",
to: "package.json",
},
{
from: "manifest*.xml",
to: "[name]" + "[ext]",
transform(content) {
if (dev) {
return content;
} else {
return content.toString().replace(new RegExp(urlDev,
"g"), urlProd);
}
},
},
],
}),

4. Save the file.

Update manifest
1. Open the manifest.xml file.

2. Replace <SupportUrl DefaultValue="https://www.contoso.com/help"/> with the URL


of your web site help page.

3. Replace <AppDomain>https://www.contoso.com</AppDomain> with the URL of your


web site.

4. In the <Scopes> section near the bottom of the file, add the openid scope as
shown in the following XML.

XML

<Scopes>
<Scope>User.Read</Scope>
<Scope>profile</Scope>
<Scope>openid</Scope>
</Scopes>

5. Save the file.

Update fallbackauthdialog.js (or


fallbackauthdialog.ts)
1. Open the src/helpers/fallbackauthdialog.js file, or
src/helpers/fallbackauthdialog.ts if your project uses TypeScript.
2. Find the redirectUri on line 24 and change the value to use your App Service app
URL you saved previously. For example, redirectUri: "https://contoso-
sso.azurewebsites.net/fallbackauthdialog.html",

3. Save the file.

Update .ENV
The .ENV file contains a client secret. For the purposes of learning in this article you can
deploy the .ENV file to Azure. However for a production deployment, you should move
the secret and any other confidential data into Azure Key Vault.

1. Open the .ENV file.


2. Set the NODE_ENV variable to the value production .
3. Save the file.

Update app.js (or app.ts)


The app.js (or app.ts) requires several minor changes to run correctly in a deployment.
It's easiest to just replace the file with an updated version for deployment.

1. Open the src/middle-tier/app.js file, or src/middle-tier/app.ts if your project uses


TypeScript.

2. Replace the entire file contents with the following code.

JavaScript

/*
* Copyright (c) Microsoft. All rights reserved. Licensed under the MIT
license. See full license in root of repo. -->
*
* This file is the main Node.js server file that defines the express
middleware.
*/

require("dotenv").config();
import * as createError from "http-errors";
import * as path from "path";
import * as cookieParser from "cookie-parser";
import * as logger from "morgan";
import express from "express";
import { getUserData } from "./msgraph-helper";
import { validateJwt } from "./ssoauth-helper";

/* global console, process, require, __dirname */

const app = express();


const port = process.env.PORT;

app.set("port", port);

// view engine setup


app.set("views", path.join(__dirname, "views"));
app.set("view engine", "pug");

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

/* Turn off caching when developing */


if (process.env.NODE_ENV !== "production") {
app.use(express.static(path.join(process.cwd(), "dist"), { etag:
false }));

app.use(function (req, res, next) {


res.header("Cache-Control", "private, no-cache, no-store, must-
revalidate");
res.header("Expires", "-1");
res.header("Pragma", "no-cache");
next();
});
} else {
// In production mode, let static files be cached.
app.use(express.static(path.join(process.cwd())));
console.log("static set up: " + path.join(process.cwd()));
}

const indexRouter = express.Router();


indexRouter.get("/", function (req, res) {
res.sendFile("/taskpane.html", { root: __dirname });
});

// Route APIs
indexRouter.get("/getuserdata", validateJwt, getUserData);

app.use("/", indexRouter);

// Catch 404 and forward to error handler


app.use(function (req, res, next) {
console.log("error 404");
next(createError(404));
});

// error handler
app.use(function (err, req, temp, res) {
// set locals, only providing error in development
console.log("error 500");
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};

// render the error page


res.status(err.status || 500).send({
message: err.message,
});
});

app.listen(process.env.PORT, () => console.log("Server listening on


port: " + process.env.PORT));
3. Save the file.

Update app registration


We recommend you create multiple app registrations for localhost, staging, and
deployment testing. The following steps ensure that the app registration you use for
deployment correctly uses the App Service app URL.

1. In the Azure portal, open your app registration. Note that the app registration may
be in a different account than your App Service app. Be sure to sign in to the
correct account.

2. In the left sidebar, select Authentication.

3. On the Authentication pane, find the


https://localhost:3000/fallbackauthdialog.html and change it to use the App
Service app URL you saved previously. For example,
https://contoso.sso.azurewebsites.net/fallbackauthdialog.html .

4. Save the change.


5. In the left sidebar, select Expose an API.

6. Edit the Application ID URI field and replace localhost:3000 with the domain from
the App Service app URL you saved previously.

7. Save the changes.

Build and deploy


Once the files and app registration are updated, you can deploy the add-in.

1. In VS Code open the terminal and run the command npm run build . This will build
a folder named dist that you can deploy.
2. In the VS Code Explorer browse to the dist folder. Right-click the dist folder and
select Deploy to Web App...
3. When prompted to select a resource, select the App Service app you created
previously.
4. When prompted if you are sure, select Deploy.
5. When prompted to always deploy the workspace, choose Yes.

If you make additional code changes, you'll need to run npm run build again and
redeploy the project.

Deploy updates
When you add features or fix bugs in your add-in, you'll need to deploy the updates. If
your add-in is deployed by one or more admins to their organizations, some manifest
changes will require the admin to consent to the updates. Users will be blocked from
the add-in until consent is granted. The following manifest changes will require the
admin to consent again.

Changes to requested permissions.


Additional scopes.
Additional Outlook events.

Test the deployment


Sideload the ./dist/manifest.xml file and test the functionality of the add-in in Office.
For more information, see Sideload an Office Add-in for testing.

7 Note
If your app registration is on a different tenant than where you sideload the add-in,
the add-in will not have admin consent for Microsoft Graph. To test in this scenario
you need an admin to centrally deploy the add-in on the Microsoft 365 tenant. For
more information, see Deploy add-ins in the Microsoft 365 admin center

If you encounter any deployment issues, see the Azure App Service troubleshooting
documentation. If you use central deployment, or plan to deploy to App Source, we
recommend you validate the Office Add-in's manifest using the '-p' option for
production.

Next steps
Deploy to App Service using GitHub Actions
Deployment Best Practices
App Service documentation
Azure community support
Create a Node.js web app in Azure
Publish your add-in using Visual Studio
Article • 02/09/2023

Your Office Add-in package contains an XML manifest file that you'll use to publish the
add-in. You'll have to publish the web application files of your project separately. This
article describes how to deploy your web project and package your add-in by using
Visual Studio 2019.

7 Note

For information about publishing an Office Add-in that you created using the
Yeoman generator and developed with Visual Studio Code or any other editor, see
Publish an add-in developed with Visual Studio Code.

To deploy your web project using Visual Studio


2019
Complete the following steps to deploy your web project using Visual Studio 2019.

1. From the Build tab, choose Publish [Name of your add-in].

2. In the Pick a publish target window, choose one of the options to publish to your
preferred target. Each publish target requires you to include more information to
get started, such as an Azure Virtual Machine or folder location. Once you have
specified a publish location and filled in all of the information required, select
Publish

7 Note

Picking a publish target specifies the server you are deploying to, the
credentials needed to sign in to the server, the databases to deploy, and other
deployment options.

3. For more information about deployment steps for each publish target option, see
First look at deployment in Visual Studio.
To package and publish your add-in using IIS,
FTP, or Web Deploy using Visual Studio 2019
Complete the following steps to package your add-in using Visual Studio 2019.

1. From the Build tab, choose Publish [Name of your add-in].

2. In the Pick a publish target window, choose IIS, FTP, etc, and select Configure.
Next, select Publish.

3. A wizard appears that will help guide you through the process. Ensure the publish
method is your preferred method, such as Web Deploy.

4. In the Destination URL box, enter the URL of the website that will host the content
files of your add-in, and then select Next. If you plan to submit your add-in to
AppSource, you can choose the Validate Connection button to identify any issues
that will prevent your add-in from being accepted. You should address all issues
before you submit your add-in to the store.

5. Confirm any settings desired including File Publish Options and select Save.

) Important

While not strictly required in all add-in scenarios, using an HTTPS endpoint for
your add-in is strongly recommended. Add-ins that are not SSL-secured
(HTTPS) generate unsecure content warnings and errors during use. If you
plan to run your add-in in Office on the web or publish your add-in to
AppSource, it must be SSL-secured. If your add-in accesses external data and
services, it should be SSL-secured to protect data in transit. Self-signed
certificates can be used for development and testing, so long as the certificate
is trusted on the local machine. Azure websites automatically provide an
HTTPS endpoint.

You can now upload your XML manifest to the appropriate location to publish your add-
in. You can find the XML manifest in OfficeAppManifests in the app.publish folder. For
example:

%UserProfile%\Documents\Visual Studio
2019\Projects\MyApp\bin\Debug\app.publish\OfficeAppManifests

Deploy updates
When you add features or fix bugs in your add-in, you'll need to deploy the updates. If
your add-in is deployed by one or more admins to their organizations, some manifest
changes will require the admin to consent to the updates. Users will be blocked from
the add-in until consent is granted. The following manifest changes will require the
admin to consent again.

Changes to requested permissions.


Additional scopes.
Additional Outlook events.

See also
Publish your Office Add-in
Make your solutions available in AppSource and within Office
Host an Office Add-in on Microsoft
Azure
Article • 02/09/2023

The simplest Office Add-in is made up of an XML manifest file and an HTML page. The
XML manifest file describes the add-in's characteristics, such as its name, what Office
desktop clients it can run in, and the URL for the add-in's HTML page. The HTML page is
contained in a web app that users interact with when they install and run your add-in
within an Office client application. You can host the web app of an Office Add-in on any
web hosting platform, including Azure.

This article describes how to deploy an add-in web app to Azure and sideload the add-
in for testing in an Office client application.

Prerequisites
1. Install Visual Studio 2019 and choose to include the Azure development
workload.

7 Note

If you've previously installed Visual Studio 2019, use the Visual Studio
Installer to ensure that the Azure development workload is installed.

2. Install Office.

7 Note

If you don't already have Office, you can register for a free 1-month trial .

3. Obtain an Azure subscription.

7 Note

If don't already have an Azure subscription, you can get one as part of your
Visual Studio subscription or register for a free trial .
Step 1: Create a shared folder to host your add-
in XML manifest file
1. Open File Explorer on your development computer.

2. Right-click the C:\ drive and then choose New > Folder.

3. Name the new folder AddinManifests.

4. Right-click the AddinManifests folder and then choose Share with > Specific
people.

5. In File Sharing, choose the drop-down arrow and then choose Everyone > Add >
Share.

7 Note

In this walkthrough, you're using a local file share as a trusted catalog where you'll
store the add-in XML manifest file. In a real-world scenario, you might instead
choose to deploy the XML manifest file to a SharePoint catalog or publish the
add-in to AppSource.

Step 2: Add the file share to the Trusted Add-


ins catalog
1. Start Word and create a document.

7 Note

Although this example uses Word, you can use any Office application that
supports Office Add-ins such as Excel, Outlook, PowerPoint, or Project.

2. Choose File > Options.

3. In the Word Options dialog box, choose Trust Center and then choose Trust
Center Settings.

4. In the Trust Center dialog box, choose Trusted Add-in Catalogs. Enter the
universal naming convention (UNC) path for the file share you created earlier as
the Catalog URL (for example, \\YourMachineName\AddinManifests), and then
choose Add catalog.
5. Select the check box for Show in Menu.

7 Note

When you store an add-in XML manifest file on a share that is specified as a
trusted web add-in catalog, the add-in appears under Shared Folder in the
Office Add-ins dialog box when the user navigates to the Insert tab on the
ribbon and chooses My Add-ins.

6. Close Word.

Step 3: Create a web app in Azure using the


Azure portal
To create the web app using the Azure portal, complete the following steps.

1. Log on to the Azure portal using your Azure credentials.

2. Under Azure Services select Web Apps.

3. On the App Service page, select Add. Provide this information:

Choose the Subscription to use for creating this site.

Choose the Resource Group for your site. If you create a new group, you also
need to name it.

Enter a unique App name for your site. Azure verifies that the site name is
unique across the azureweb apps.net domain.

Choose whether to publish using code or a docker container.

Specify a Runtime stack.

Choose the OS for your site.

Choose a Region.

Choose the App Service plan to use for creating this site.

Choose Create.

4. The next page will let you know that your deployment is underway and when it
completes. When it is completed, select Go to resource.
5. In the Overview section, choose the URL that is displayed under URL. Your browser
opens and displays a webpage with the message "Your App Service app is up and
running."

) Important

While not strictly required in all add-in scenarios, using an HTTPS endpoint for
your add-in is strongly recommended. Add-ins that are not SSL-secured
(HTTPS) generate unsecure content warnings and errors during use. If you
plan to run your add-in in Office on the web or publish your add-in to
AppSource, it must be SSL-secured. If your add-in accesses external data and
services, it should be SSL-secured to protect data in transit. Self-signed
certificates can be used for development and testing, so long as the certificate
is trusted on the local machine. Azure websites automatically provide an
HTTPS endpoint.

Step 4: Create an Office Add-in in Visual Studio


1. Start Visual Studio as an administrator.

2. Choose Create a new project.

3. Using the search box, enter add-in.

4. Choose Word Web Add-in as the project type, and then choose Next to accept
the default settings.

Visual Studio creates a basic Word add-in that you'll be able to publish as-is, without
making any changes to its web project. To make an add-in for a different Office
application, such as Excel, repeat the steps and choose a project type with your desired
Office application.

Step 5: Publish your Office Add-in web app to


Azure
1. With your add-in project open in Visual Studio, expand the solution node in
Solution Explorer, then select App Service.

2. Right-click the web project and then choose Publish. The web project contains
Office Add-in web app files so this is the project that you publish to Azure.
3. On the Publish tab:

Choose Microsoft Azure App Service.

Choose Select Existing.

Choose Publish.

4. Visual Studio publishes the web project for your Office Add-in to your Azure web
app. When Visual Studio finishes publishing the web project, your browser opens
and shows a webpage with the text "Your App Service app has been created." This
is the current default page for the web app.

5. Copy the root URL (for example: https://YourDomain.azurewebsites.net ); you'll


need it when you edit the add-in manifest file later in this article.

Step 6: Edit and deploy the add-in XML


manifest file
1. In Visual Studio with the sample Office Add-in open in Solution Explorer, expand
the solution so that both projects show.

2. Expand the Office Add-in project (for example WordWebAddIn), right-click the
manifest folder, and then choose Open. The add-in XML manifest file opens.

3. In the XML manifest file, find and replace all instances of "~remoteAppUrl" with
the root URL of the add-in web app on Azure. This is the URL that you copied
earlier after you published the add-in web app to Azure (for example:
https://YourDomain.azurewebsites.net ).

4. Choose File and then choose Save All. Next, Copy the add-in XML manifest file (for
example, WordWebAddIn.xml).

5. Using the File Explorer program, browse to the network file share that you created
in Step 1: Create a shared folder and paste the manifest file into the folder.

Step 7: Insert and run the add-in in the Office


client application
1. Start Word and create a document.

2. On the ribbon, choose Insert > My Add-ins.


3. In the Office Add-ins dialog box, choose SHARED FOLDER. Word scans the folder
that you listed as a trusted add-ins catalog (in Step 2: Add the file share to the
Trusted Add-ins catalog) and shows the add-ins in the dialog box. You should see
an icon for your sample add-in.

4. Choose the icon for your add-in and then choose Add. A Show Taskpane button
for your add-in is added to the ribbon.

5. On the ribbon of the Home tab, choose the Show Taskpane button. The add-in
opens in a task pane to the right of the current document.

6. Verify that the add-in works by selecting some text in the document and choosing
the Highlight! button in the task pane.

Deploy updates
When you add features or fix bugs in your add-in, you'll need to deploy the updates. If
your add-in is deployed by one or more admins to their organizations, some manifest
changes will require the admin to consent to the updates. Users will be blocked from
the add-in until consent is granted. The following manifest changes will require the
admin to consent again.

Changes to requested permissions. See Requesting permissions for API use in add-
ins and Understanding Outlook add-in permissions.
Additional or changed Scopes. (Not applicable if the add-in uses the unified
manifest for Microsoft 365.)
Additional or changed Outlook events.

7 Note

Whenever you make a change to the manifest, you must raise the version number
of the manifest.

If the add-in uses the XML manifest, see Version element.


If the add-in uses the unified manifest, see version property.

See also
Publish your Office Add-in
Publish your add-in using Visual Studio
Make your solutions available in
Microsoft AppSource and within Office
Article • 03/03/2023

Microsoft AppSource provides a convenient location for you to upload new Office and
SharePoint Add-ins, Microsoft Teams apps, and Power BI visuals that provide solutions
for both consumers and businesses. When you add your app solution to Microsoft
AppSource, you also make it available in the in-product experience within Office. To
include your solution in Microsoft AppSource and within Office, you submit it to Partner
Center . You need to create an individual or company account and, if applicable, add
payout information. For details, see the following:

Create a developer account . After you create your account, it goes through an
approval process.
For details about the registration process, see Opening a developer account.
For details about managing settings and additional users in your Partner Center
account, see Manage account users.
Submit your solution to Microsoft AppSource via Partner Center.

7 Note

Office VSTO add-ins and COM add-ins cannot be submitted to Microsoft


AppSource. For more about the distinction between types of Office add-ins, see
How are Office Add-ins different from COM and VSTO add-ins?.

For information about how to submit, validate, and publish Microsoft Teams apps, see
Teams apps submission details.

For information about how to submit, validate, and publish your SharePoint Framework
(SPFx) solution apps, see SPFx solutions submission details.

For information about how to submit Power BI custom visuals to Microsoft AppSource,
see Publish custom visuals.

Approval process
After your account is approved, you can submit your solution to Partner Center. You can
make changes at any point before you submit your solution for approval. During the
approval process, you can make changes to your submission, but you can’t submit them
for publishing until your current submission is complete.

7 Note

If you're submitting a Microsoft Teams app, see Tips for a successful app
submission. This will help to ensure timely approval of your submission.

In order for your submission to be approved:

It must be free of viruses. If you need virus detection software, see the Microsoft
Safety & Security Center .
It must not contain inadmissible or offensive material.
It must be stable and functional.
Any material that you associate with your apps or add-ins, such as descriptions and
support documentation, must be accurate. Use correct spelling, capitalization,
punctuation, and grammar in your descriptions and materials.
If you want a tailored experience for users in a regional store, you can add
additional languages so that your add-in appears in another language store with
localized metadata. Your service and your add-in manifest must be updated
appropriately. You must also provide descriptions for each language you add.
If your free app or add-in contains an in-app purchase, the Microsoft AppSource
listing for your add-in will reflect this by stating 'Additional purchase may be
required' under the pricing options.

For more details about Microsoft AppSource requirements, see the Certification policies.

Certification process
After you submit your solution:

1. Your submission goes through a series of automated checks to ensure that it


complies with the certification policies.

2. The validation team reviews your submission. This can take 3-5 working days,
depending on the volume of submissions in the queue. (Teams and SPFx apps
submissions are validated in 24 hours.)

7 Note

The validation team tests Office Add-ins on all the platforms that the add-in is
required to support. For details about supported platforms, see the Office
Add-ins host and platform availability page.

For a seamless certification experience, provide detailed test notes with your
submission, including:

Information about any sample data your app or add-in needs.


Configuration instructions, if required.
Information about a test or demo account that your app or add-in needs.

7 Note

Because our team is located in multiple time zones, we request that you do
not configure test accounts that require developer interaction before we can
test.

3. When the certification process is complete, you receive a message to let you know
that either your submission is approved, or you need to make changes and
resubmit it.

Check the status of your submission in Partner Center


You can also follow these steps to check the status of your submission in Partner Center.

1. Sign in to Partner Center .

2. In the Offer alias column, select the Office add-in or app you want.

3. On the Product overview page, the status of your submission will be one of the
following:

Pre-processing

Certification

Published

7 Note

After your product is certified, there might be a delay before it is


published. After certification, a product typically appears in Microsoft
AppSource within one hour.
4. If the status is Attention needed, your submission needs changes to be approved.
For details about the required changes, on the Product overview page, select View
report.

If you make changes after your submission is certified, it must go through the
certification process again.

If you have general questions about policies, processes, or validation requirements, you
can engage with the Microsoft AppSource validation team via Stack Overflow . Tag
your question with "Office-Store". Please be aware that the validation team will not be
able to discuss individual submission results on Stack Overflow.

Microsoft 365 App Compliance


After your solution is published through Partner Center, you can begin the Microsoft
365 App Compliance program. This program is optional and is designed to allow you to
reach the level of security that meets the needs of your customers. To complete the
Publisher Attestation within Partner Center, click the App Compliance button in the
Office Store section. For details, see the User guide.

See also
Office Add-ins
SharePoint Add-ins
Microsoft Teams developer platform
Visuals in Power BI
Microsoft 365 App Compliance
Commercial marketplace certification
policies
Article • 03/14/2023

Document version: 1.33

Document date: May 20, 2022

7 Note

For a summary of recent changes to these policies, see Change history.

Table of contents
100 General

100.1 Value proposition and offer requirements


100.1.1 Title
100.1.2 Summary
100.1.3 Description
100.1.4 Non-English content
100.1.7 Active and visible presence
100.2 Discoverability
100.2.1 Categories and Industries
100.2.2 Keywords
100.2.3 Competencies (consulting services offers only)
100.2.4 Listing organization
100.3 Graphic elements
100.4 Acquisition, pricing, and terms
100.5 Offer information
100.6 Personal information
100.7 Accurate source
100.8 Significant value
100.10 Inappropriate content
100.11 Security
100.12 Functionality
100.13 Business requirements
100.14 Testability
200 Virtual Machines

200.1 Technical Requirements


200.2 Business Requirements
200.3 VM Image Requirements
200.3.1 General
200.3.2 Windows
200.3.3 Linux
200.4 VM Image Generalization
200.5 Security
200.6 Testing and Certification

220 Network Virtual Appliances (NVAs)

220.1 NVA Internal Service Error


220.2 NVA VHD access
220.3 NVA boot performance
220.6 NVA High availability
220.7 NVA VNET Peering
220.8 NVA Accelerated networking
220.9 NVA Multi-NIC basic
220.10 NVA Network Disruption

300 Azure Applications

300.1 Value proposition and offer requirements


300.2 Acquisition, pricing, and terms
300.3 Functionality
300.4 Technical requirements
300.4.1 Code
300.4.2 Security
300.4.3 Variables
300.4.4 Parameters
300.4.5 Resources
300.4.6 CUID (createUIDef)
300.4.7 Deployment artifacts
300.4.8 VM image references and disks

400 Azure container offers

400.1 Technical requirements


400.2 Business requirements
400.3 Security
600 IoT Edge Modules

600.1 Offer Information


600.2 Plan Information
600.3 Technical Requirements

700 Managed Services

700.1 Value proposition and offer requirements


700.3 Graphic elements
700.4 Business requirements
700.5 Plan details
700.6 Plan manifest details

800 Consulting Services

800.1 Value proposition


800.2 Eligibility requirements
800.2.2 Primary Product: Dynamics 365 Apps on Dataverse and Power Apps
800.2.3 Primary Product: Dynamics 365 Finance and Operations
800.2.4 Primary Product: Dynamics 365 Customer Insights
800.2.5 Primary Product: Dynamics 365 Business Central
800.2.6 Power BI
800.2.7 Power Apps
800.2.9 Power Automate
800.2.10 Power Virtual Agents
800.2.12 Primary Product: Dynamics 365 Mixed Reality
800.3 Title
800.4 Summary and description
800.4.1 Quality
800.4.2 Content
800.5 Supporting documents

1000 Software as a Service (SaaS)

1000.1 Value proposition and offer requirements


1000.2 Offer targets
1000.3 Authentication options
1000.4 SaaS Fulfillment and Metering APIs
1000.5 Microsoft 365 App and Add-In Linking

1100 Microsoft 365

1100.1 General content


1100.3 Selling additional features
1100.4 Predictable behavior
1100.5 Customer control
1100.6 Global audience
1100.7 Easy identification
1100.8 Preserving functionality

1120 Office Add-ins: Word, Excel, PowerPoint, and Outlook

1120.1 Offer requirements


1120.2 Mobile requirements
1120.3 Functionality
1120.4 Outlook add-ins functionality
1120.5 Excel custom functions
1120.5.1 Offer information and support contacts
1120.5.2 Security
1120.5.3 Functionality
1120.5.4 Validation

1140 Teams

1140.1 Value proposition and offer requirements


1140.1.1 App name
1140.1.2 Workplace appropriateness
1140.1.3 Other platforms and services
1140.1.4 Access to services
1140.3 Security
1140.3.1 Financial Transactions
1140.3.2 Bots and messaging extensions
1140.3.3 External domains
1140.4 Functionality
1140.4.1 General
1140.4.2 Tabs
1140.4.3 Bots
1140.4.4 Messaging extensions
1140.4.5 Task modules
1140.4.6 Meeting extensions
1140.4.7 Notification APIs
1140.4.8 Mobile Experience
1140.5 Teams apps linked to Software as a Service (SaaS) offers
1140.5.1 Manifest and metadata requirements
1140.5.2 Purchasing and managing subscriptions
1140.5.3 Testability and technical requirements
1140.6 Publisher Attestation
1140.7 Advertising
1140.8 Teams apps extensible across Microsoft 365
1140.8.1 General

1160 SharePoint add-in

1160.1 Security
1160.2 Functionality

1170 SharePoint Framework Solutions

1170.1 Value proposition and offer requirements


1170.2 Security
1170.3 Functionality
1170.4 Branding and advertising
1170.5 Validation

1180 Power BI visuals

1180.1 Acquisition, pricing, and terms


1180.2 Functionality

1200 Power BI visuals additional certification

1200.1 Certification requirements


1200.1.1 Code repository
1200.1.2 Code quality
1200.1.3 Code security
1200.1.4 Code functionality
1200.1.5 Update without Advanced Certification
1200.2 Duplicate offers

1240 Power BI Template Apps

1240.1 Value proposition and offer requirements


1240.2 Technical validation

1400 Dynamics 365 Business Central

1400.3 Acquisition, pricing, and terms


1400.5 Technical requirements
1400.6 Business Central version functionality
1420 Dynamics 365 apps on Dataverse and Power Apps

1420.1 Value proposition and offer requirements


1420.2 Acquisition, pricing, and terms
1420.3 Content requirements
1420.4 Functionality
1420.5 Technical requirements
1420.6 Code validation
1420.7 Deployment validation
1420.8 Functionality validation
1420.9 Security validation
1420.10 Sitemap validation

1440 Dynamics 365 Operations Apps

1440.1 Content requirements


1440.2 Technical requirements
1440.3 Code validation
1440.4 Deployment validation
1440.5 Functionality validation

3000 Requirements for Co-sell Status Tiers

3000.1 Co-sell ready status


3000.2 Azure IP co-sell benefit
3000.3 Azure IP co-sell program deal registration
3000.3.1 Segmentation
3000.3.2 Opportunity age
3000.3.3 Solution tagging
3000.3.4 Timeline
3000.3.5 Contract Parameters
3000.4 Azure benefit eligibility for customers (Microsoft Azure Consumption
Commitment))

4000 Microsoft 365 Application Compliance

100 General
These General policies apply to all offer types. Additional policies for each specific offer
type are listed below by offer type. Please be sure you review both policy sections for
the type of offer you are developing for the marketplace.
The types of offer types supported by the marketplace can be found in the publishing
guide by offer type and the Microsoft 365 AppSource documentation. All offers and
your publisher activities on Partner Center are subject to the Microsoft Publisher
Agreement .

100.1 Value proposition and offer requirements


Your listing must clearly, concisely, and accurately communicate the value proposition
and requirements for your offer. Your offer should represent a distinct product. Your
offer must leverage the appropriate offer type as represented in the publishing guide by
offer type.

100.1.1 Title
All offers and plans must have an accurate and descriptive title. If your offer is promoted
on a website outside of the commercial marketplace, the title on the promotional
website should match the title in the marketplace. If your product includes repackaged
open-source software or software that was originally created by a vendor other than
you, the product title must indicate the value added by your repackaging that
distinguishes it. If there is no additional intellectual property added to the product, the
product title must include the seller’s name (for example: <Product> with support by
<Seller>).

100.1.2 Summary
Offers must have a concise, well written summary of the offer and its intended use. This
summary will be shown on the commercial marketplace search results screen and is
limited to 100 characters.

100.1.3 Description

All offers and plans must have a description that identifies the intended audience, briefly
and clearly explains its unique and distinct value, identifies supported Microsoft
products and other supported software, and includes any prerequisites or requirements
for its use. The description should not simply repeat the offer summary.

You must clearly describe any limitations, conditions or exceptions to the functionality,
features, and deliverables described in the listing and related materials before the
customer acquires your offer. The capabilities you declare must relate to the core
functions and description of your offer.
Your listing, including the description, metadata, and any other provided content, should
describe your offer’s capabilities, strengths, and what makes it desirable, including any
compatibility with other offers. Comparative marketing, including using competitor
logos or trademarks in your offer listing, or including tags or other metadata referencing
competing offers or marketplaces, is not allowed.

100.1.4 Non-English content


Commercial marketplace content (including Storefront text, documents, screenshots,
Terms of Use, and Privacy Policy) is not required to be in English. If your offer is
published with non-English content, the description must begin or end with the English
phrase, "This application is available in <a list of languages including the language of
your offer content>." It is also acceptable to provide a Useful Link URL to offer content
in a language other than the one used in the marketplace content.

If your offer supports multiple languages, all offer and marketplace listing content
should be localized for each supported language. Offers listed in multiple languages
must be easily identified and understood.

100.1.7 Active and visible presence


You must maintain an active presence in the marketplace. Offers submitted to the
marketplace must be commercially available and under active development and/or
supported until they are removed from the marketplace.

Each offer submitted to the marketplace must have at least one public plan, which may
be Contact Me, Bring Your Own License (BYOL), or Get It Now (Transact). Private plans
are not allowed without a corresponding public plan.

100.2 Discoverability
To help customers discover offers, categories, industries, keywords, and consulting
service competencies must accurately identify your expertise. The description of your
listing must be relevant to the selected categories and industries.

100.2.1 Categories and Industries


Select the most relevant category or categories in alignment with your offer’s value
proposition. The description should help a customer understand how your offer is
applicable to the selected categories. A maximum of two categories can be selected.
Select an industry only if your offer is designed to solve a specific need or industry
scenario. The industry and/or vertical your offer targets must be included in either the
short or long description. Customers should be able to understand from the description
how your offer helps them solve a specific industry scenario.

100.2.2 Keywords

Keywords must be relevant to the offer and any supported products. Adding competitor
names or products as keywords is not permitted. Categories and titles should not be
added as keywords.

100.2.3 Competencies (consulting services offers only)


You must provide accurate current information needed to validate your competencies
and qualifications when you submit your offer.

100.2.4 Listing organization

Products from a single publisher with multiple or related versions of the same product
must be grouped under a single listing and the multiple or related versions captured as
distinct plans.

100.3 Graphic elements


Graphic elements help customers identify the source and understand the features of
your offer. When used, graphic elements must be current, accurate, easy to understand,
and related to your offer. Graphic elements include:

Logo
Logos are uploaded as a .png file between 216- and 350-pixels square. This
logo appears on the offer listing page in Azure Marketplace or AppSource.
An optional 48-pixel square logo may be added later to replace the
autogenerated small logo. This logo appears in Azure Marketplace search
results or on the AppSource main page and search results.
Images, including screenshots
Images must be 1280x720 pixel .png files.
Images should be of good quality: high resolution, sharp, with legible and
readable text.
Comparative marketing, including using competitor logos or trademarks, is not
allowed.
Videos
Videos must be hosted on YouTube or Vimeo; no other video hosts are allowed.
Videos must be publicly viewable and embeddable.
Videos and their thumbnail images should be of good quality: high resolution,
understandable, and related to the offer.
Video links must lead directly to the individual video page. No short URLs,
"human readable" redirects, or other obfuscating services may be used. Account
pages, playlists, or other collection pages are not allowed.

100.4 Acquisition, pricing, and terms


Customers need to understand how to evaluate and acquire your offer. Your listing must
accurately describe:

How you are providing your offer (for example, as a limited time trial or as a
purchase)
Pricing, including currency
Variable pricing structures
Features or content that require an extra charge, whether through in-app or add-in
purchases or through other means. Your description must also disclose any
dependencies on additional services, accounts, or hardware. Offers cannot have
any dependencies on any product or component that is no longer supported or
commercially available.

Pricing models must conform to the pricing models supported by the marketplace.

All purchase transactions associated with your offer must begin by using a starting point
in the commercial marketplace listing, such as the Contact Me or Get It Now buttons.

Microsoft provides limited native application programming interfaces (APIs) to support


in-offer purchases. If your in-offer purchases are not possible with Microsoft's APIs, you
may use any third-party payment system for those purchases.

Within the offer listing, you may not redirect or up-sell customers to software or services
outside the marketplace. This restriction does not apply to support services that
publishers sell outside the marketplace.

You may not promote the availability of your offer on other cloud marketplaces within
the offer listing.

The commercial marketplace does not currently support the sale of hardware or
professional services. Any offers for hardware must be transacted outside of the
marketplace. Charges for services such as support included with your offer and billed
through the marketplace may only amount to an ancillary component (less than 10%) of
the total price charged to end customers.

100.5 Offer information


Customers need to know how to find out more about your offer. Include relevant offer
information:

Terms and conditions


Should describe the legal terms between you and your customers governing
use of your offer
Privacy policy
Should detail any of your applicable collection, use, and storage of customer
data
Documentation
Should be available, detailed, instructive, and current
"Learn More" links to additional offer information

Links must be functional, accurate, and must not jeopardize or compromise user
security. For example, a link must not spontaneously download a file.

100.6 Personal information


Customers and partners care about the security of their personal information. Personal
Information includes all information or data that identifies or could be used to identify a
person, or that is associated with such information or data. Your listing must not include
third-party personal information without authorization. Your listing must include a link
to your privacy policy for the listed offer.

100.7 Accurate source


Customers want to know who they are dealing with and expect clarity about the offers
and relationships they rely on. All content in your offer and associated metadata must
be either originally created by the offer provider, appropriately licensed from the third-
party rights holder, used as permitted by the rights holder, or used as otherwise
permitted by law. Offers must be unique and cannot duplicate an offer made available
by another publisher on the marketplace.

When referring to Microsoft trademarks and the names of Microsoft software, products,
and services, follow Microsoft Trademark and Brand Guidelines .
References to Business Programs participation or eligibility are not allowed. Example
(but not limited to) references to Microsoft Azure Consumption Commitment (MACC),
Partner Co-sell, Co-sell Prioritized, IP Co-sell, MPN Competency, Cloud Solution Partner
Designation. Such references must not be included anywhere in the metadata of your
offer.

100.8 Significant value


Offers must provide enough value to justify the investment it takes to learn and use
them. Your offer should provide significant benefits such as enhanced efficiency,
innovative features, or strategic advantages. Simple utilities, offers with limited scope, or
offers that duplicate offerings in well-served areas are not a good fit for the commercial
marketplace. Offers must provide a useable software solution.

100.10 Inappropriate content


Customers expect offers to be free of inappropriate, harmful, or offensive content. Your
offer must not contain or provide access to such content including, but not limited to
content that:

Facilitates or glamorizes harmful activities in the real world.


Might pose a risk of harm to the safety, health, or comfort of any person or to
property.
Is defamatory, libelous, slanderous, or threatening.
Is potentially sensitive or offensive or that advocates discrimination, hatred, or
violence based on membership in a particular racial, ethnic, national, linguistic,
religious, or other social group, or based on a person's gender, age, or sexual
orientation.
Facilitates or glamorizes excessive or irresponsible use of alcohol or tobacco
products, drugs, or weapons.
Contains sexually explicit or pornographic content.
Encourages, facilitates, or glamorizes illegal activity in the real world, including
piracy of copyrighted content.
Includes excessive or gratuitous profanity or obscenity.
Is offensive in any country/region to which your offer is targeted. Content may be
considered offensive in certain countries/regions because of local laws or cultural
norms.

100.11 Security
Customers want to be confident that offers are safe and secure. Your offer must not
jeopardize or compromise user security, the security of the Azure service, or related
services or systems. These are related criteria:

If your offer collects credit card information, or uses a third-party payment


processor that collects credit card information, the payment processing must meet
the current PCI Data Security Standard (PCI DSS).
Your offer must not install or launch executable code on the user's environment
beyond what is identified in or may reasonably be expected from the offer listing.
You must report suspected security events, including security incidents and
vulnerabilities of your Marketplace software and service offerings, at the earliest
opportunity.
Your offer should not share any application credentials or access information
publicly in the product description page.

100.12 Functionality
Customers expect offers to deliver what they promise. Your offer must provide the
functionality, features, and deliverables described in your listing and related materials.

If your offer has trial and paid versions, trial functionality must reasonably resemble the
paid version.

Offer user interfaces should not look unfinished. All UI should be intuitive and obvious
in purpose, without requiring users to read support documentation for basic tasks.

Your offer should be reasonably responsive. Long wait or processing times should be
accompanied by some form of warning or loading indicator.

100.13 Business requirements


Offers you submit to the marketplace must meet applicable business requirements
including:

Specific qualification or approval by Microsoft as needed


Appropriately targeting customer segments, categories, or industries
Appropriate configuration including offer type and billing

100.14 Testability
Your offer submission must include any necessary instructions and resources for
successful certification of your offer.
200 Virtual Machines
To ensure that customers have a clear and accurate understanding of your offer, please
follow these additional listing requirements for Virtual Machines (VM) offers.

200.1 Technical Requirements


Offers you publish to the Marketplace must meet the following technical requirements:

The Azure Resource Manager (RM) module may still be used but is being
deprecated. We recommend using the Azure PowerShell Az module instead.

In addition to your solution domain, your engineering team should have knowledge on
the following Microsoft technologies:

Basic understanding of Azure Services


Working knowledge of Azure Virtual Machines, Azure Storage and Azure
Networking
Working knowledge of Azure Resource Manager
Working knowledge of JSON

200.2 Business Requirements


The publisher must be registered through Partner Center and approved for the VM
billing plan .

The App Description must match the application included in the Virtual Machine and
must have been tested for primary functionality after deployment of the VM image in
Microsoft Azure.

Usage/distribution of third-party software and consumption of services must be in


compliance with all respective redistribution licensing.

200.3 VM Image Requirements


As a VM image contains one operating system disk and zero or more data disks, one
Virtual Hard Drive (VHD) is needed per disk. Even blank data disks require a VHD to be
created. You must configure the VM operating system (OS), the VM size, ports to open,
and up to 15 attached data disks. Regardless of which OS you use, add only the
minimum number of data disks needed by the stock keeping unit (SKU).

200.3.1 General
VM image must be provided in the form of a VHD file and built on an Azure-approved
base image.

VM image must be deployable and able to provision on Azure from either the Azure
Portal or PowerShell scripts.

Must support deployment of the image with at least the publisher recommended Azure
VM Size.

While additional configuration steps may be required by the application, deployment of


the VM image allows the VM to be fully provisioned and the OS to start properly.

Image should support enablement of VM Extensions including Azure Diagnostics and


Monitoring.

Disk count in a new image version cannot be changed. A new SKU must be defined to
reconfigure data disks in the image. Publishing a new image version with different disk
counts will have the potential of breaking subsequent deployments based on the new
image version in cases of auto-scaling, automatic deployments of solutions through
Azure Resource Manager templates, and other scenarios.

Image must be well-formed including standard footer.

VHD image must be submitted via a valid and available Shared Access Signature (SAS)
URI.

Choose one or both of the Azure PowerShell or Azure command-line interface (CLI)
scripting environments to help manage VHDs and VMs.

Image size must be an exact multiple of 1MB.

OS Architecture must be 64 bits.

Image must have been deprovisioned. See Configure the Azure-Hosted VM: Generalize
the Image.

200.3.2 Windows

OS disk size validation should be between 30GB and 250GB.

Data disk size should be between 1GB and 1023GB.

Support Compatibility with Serial Console: Windows: Registry.

Application must not have a dependency on the D: drive for persistent data. Azure offers
the D: drive as temporary storage only and data could be lost.
Application usage of the data drive must not have a dependency on C: or D: drive letter
designations. Azure reserves C: and D: drive letter designations.

Build lean and limit possible cloud compatibility issues by avoiding dependency and not
including specialized Windows Server roles and features such as Failover Cluster, DHCP,
Hyper-V, Remote Access, Rights Management Services, Windows Deployment Services,
BitLocker Drive Encryption on OS disk, and Network Load Balancing Windows Internet
Name Service.

200.3.3 Linux

No swap partition on the OS disk. Swap can be requested for creation on the local
resource disk by the Linux Agent. It is recommended that a single root partition is
created for the OS disk.

Leverage Endorsed Linux distributions on Azure: /azure/virtual-


machines/linux/endorsed-distros. Custom images may be subject to additional
validation steps and requiring specific approval from Microsoft.

OS disk size validation should be between 30GB and 1023GB.

Data disk size validation should be between 1GB and 1023GB.

Support Compatibility with Serial Console – parameter Linux: console=ttyS0.

The latest Azure Linux Agent should be installed using the repair manager (RPM) or
Debian package. You may also use the manual install process, but the installer packages
are recommended and preferred.

No swap partition on the OS disk. Swap can be requested for creation on the local
resource disk by the Linux Agent. It is recommended that a single root partition is
created for the OS disk.

Choose one or both of the Azure PowerShell or Azure command-line interface (CLI)
scripting environments to help manage VHDs and VMs.

Secure Shell (SSH) server should be included by default.

VM image must get booted and rebooted successfully.

VM images should be able to connect to network. DNS name should be resolved


successfully.

VM image should not contain any pre-existing users and password keys for them.
Microsoft Hyper-V virtual network driver 'hv_netvsc' should be loaded and reloaded
gracefully.

200.4 VM Image Generalization


All images in the Azure Marketplace must be reusable in a generic fashion. To achieve
this reusability, the operating system VHD must be generalized, an operation that
removes all instance-specific identifiers and software drivers from a VM. The operating
system VHD for your VM image must be based on an Azure-approved base image that
contains Windows Server or SQL Server.

If you are installing an OS manually, then you must size your primary VHD in your VM
image. For Windows, the operating system VHD should be created as a 127-128 GB
fixed-format VHD. For Linux, this VHD should be created as a 30-50 GB fixed-format
VHD.

Windows OS disks are generalized with the sysprep tool. If you subsequently update or
reconfigure the OS, you must rerun sysprep .

Ensure that Azure Support can provide our partners with serial console output when
needed and provide adequate timeout for OS disk mounting from cloud storage.
Images must have the following parameters added to the Kernel Boot Line:
console=ttyS0 earlyprintk=ttyS0 rootdelay=300 .

200.5 Security
Ensure that you have updated the OS and all installed services with all the latest security
and maintenance patches. Your offer should maintain a high level of security for your
solution images in the Marketplace.

All the latest security patches for the Linux distribution must be installed and industry
guidelines to secure the VM image for the specific Linux distribution must be followed.
It is recommended that Logical Volume Manager (LVM) should not be used.

Images should not include significant Common Vulnerability and Exposures. Verify the
following:

Windows must have the latest security patches.


Linux minimum kernel versions including latest security patches.
Latest versions of required libraries should be included:
OpenSSL v1.0 or greater.
Python 2.6+ is highly recommended.
Python pyasn1 package if not already installed.
Linux Azure Agent 2.2.10 and above should be installed.
Firewall rules must be disabled unless application functionally relies on them, such
as for a firewall appliance.
The source code and resulting VM image must be scanned for malware and
verified to be malware free.
All code that is considered suspicious (such as penetration tests and exploits) shall
be identified and disclosed to limit false positive detections by malware
monitoring tools.
All non-OS scheduled tasks shall be well identified to limit exposure to CRON job
type malware.
Limit the attack surface by keeping a minimal footprint with only necessary
Windows Server roles, features, services, and networking ports.
Applications should not have a dependency on restricted usernames such as
'Administrator,' 'root', and 'admin'.
Bash/Shell history entries must be cleared.
Azure expects the VM images to be free of any known vulnerability to keep
customer workloads secure.

Your offer should use a secure OS base image.

The VHD used for the source of any image based on Windows Server must be
from the Windows Server OS images provided through Microsoft Azure.
Do not use the solution VHD (such as the C: drive) to store persistent information.
The VHD image must only include necessary locked accounts that do not have
default passwords that would allow interactive login; no back doors are allowed.
All sensitive information, such as test SSH keys, known hosts file, log files, and
unnecessary certificates, must be removed from the VHD image.

200.6 Testing and Certification


After you create and deploy your Virtual Machine (VM), you must test and submit the
VM image for Azure Marketplace certification with the Certification Test Tool .
Instructions for using the tool are available at the Certify your VM image page. If any of
the tests fail, your image is not certified. In this case, review the requirements and failure
messages, make the indicated changes, and rerun the test.

During the publishing process, you must provide a uniform resource identifier (URI) for
each virtual hard disk (VHD) associated with your SKUs. Microsoft needs access to these
VHDs during the certification process. When generating shared access signature (SAS)
URIs for your VHDs, adhere to the following requirements:
Only unmanaged VHDs are supported.
List and Read permissions are sufficient; do not provide Write or Delete access.
The duration for access (expiry date) should be a minimum of three weeks from
when the SAS URI is created.
To safeguard against Coordinated Universal Time (UTC) variations, set the start
date to one day before the current date; for example, if the current date is October
6, 2019, select 10/5/2019.

Review and verify each generated SAS URI by using the following checklist. Verify that:

The URI is of the form: <blob-service-endpoint-url> + /vhds/ + <vhd-name>? +


<sas-connection-string>

The URI contains your VHD image filename, including the filename extension
" .vhd "
Towards the middle of the URI, sp=rl appears; this string indicates that Read and
List access is specified
After that point, sr=c also appears; this string indicates that container-level access
is specified

You can use the VM Self-test Service API to pre-validate that a Virtual Machine (VM)
meets the latest Azure Marketplace publishing requirements.

Preview images are stored during the testing and preview phase of the offer publication
process and are not visible to customers. Microsoft may remove inactive images from
storage.

220 Network Virtual Appliances (NVAs)


An NVA offer submitted to Azure Marketplace goes through the certification process. To
ensure the offer is certified and published on Azure Marketplace, it must meet all the
following requirements.

220.1 NVA Internal Service Error


NVA Offers are qualified by a certification service that can sometimes result in an
internal error. In this case, publisher will see an error message with Policy ID as 220.1
and no action is needed by the publisher.

220.2 NVA VHD Access


The certification process begins by accessing the Virtual Hard Disk (VHD) image of your
offer. Ensure the VHD can be accessed without any issue.

The correct SAS URL is provided for the VHD


The VHD is accessible
The NVA image is generalized

220.3 NVA boot performance


Please ensure your virtual appliance supports up to 8 network interfaces and that it is
functional within 20 minutes of being started.

220.6 NVA High Availability


One of the most common architectures is to deploy NVAs in a High Availability
configuration using a Load Balancer. To pass this test, ensure the following requirements
are met:

The NVA is reachable through the Internal load balancer's frontend IP


For High Availability configurations, the appliance must be compatible with the HA
Ports feature on the Internal Load Balancer

220.7 NVA VNET Peering


NVA products must support virtual network peering and global virtual network
peering (Learn more about VNet peering)

220.8 NVA Accelerated Networking


Please ensure the following requirements are met for Accelerated Networking testing:

Accelerated Networking can be enabled and disabled on a NIC in the NVA VM


Traffic is allowed through the NVA NICs on which Accelerated Networking is
enabled and disabled

220.9 NVA Multi-NIC basic


Ensure that when NVA is deployed with multiple NICs, the private IP address and MAC
address of the NVA Network Interface Cards (NICs) are unchanged after redeployment.

220.10 NVA Network Disruption


On a VM deployed using the NVA image, verify that after configuring a Network
Security Group (NSG) to block all incoming traffic, the VM status remains Running.

300 Azure Applications


The policies listed in this section apply only to Azure Applications offers.

300.1 Value proposition and offer requirements


The Azure Application offer type must be used when the following conditions are
required:

You deploy a subscription-based solution for your customer using either a VM or


an entire IaaS-based solution.
If you or your customer requires that the solution be managed by a partner, then
the Azure Application SKU type should be used.

Container offers are not supported for Azure applications in the commercial
marketplace.

Custom meters may only be used for the consumption or usage of software (for
example, counting messages sent through an email platform or consuming credits that
indicate software usage).

300.2 Acquisition, pricing, and terms


The resources will be provisioned in the customer's Azure subscription. Pay-as-you-go
(PAYGO) virtual machines will be transacted with the customer via Microsoft, billed via
the customer's Azure subscription (PAYGO).

In the case of bring-your-own-license, Microsoft will bill infrastructure costs incurred in


the customer subscription, and you will transact your software licensing fees to the
customer directly.

300.3 Functionality
VMs must be built on Windows or Linux.

Azure applications must be deployable through the commercial marketplace.

300.4 Technical requirements


For more details on the following requirements, see the Azure Resource Manager
Templates best practices guide .

300.4.1 Code

Code must pass the best practice tests.

Code must address any comments provided as part of the code review in the
certification process.

300.4.2 Security

All firewall rules and network security groups (NSGs) must be reasonable for the
application.

Role-based access control (RBAC) assignments should use the least privilege required
and must have a justification for "owner".

Passwords in createUIDef must have a minimum of 12 characters or use the default


settings.

Managed Service Identity (MSIs) must be assigned a role. Unused MSIs must be
removed.

300.4.3 Variables
Any declared variables must be used.

300.4.4 Parameters
Any declared parameters must be used.

Values such as username and password (secrets) must always be parameterized.

Any defaultValue supplied for a parameter must be valid for all users in the default
deployment configuration.

Do not provide default values for user names, passwords (or anything that requires
a SecureString , or anything that will increase the attack surface area of the
application.

Templates must have a parameter named location for the primary location of resources.
The default value of this parameter must be [ resourceGroup().location ].
The location parameter must not contain allowedValues . Location values may be
restricted in CUID but not the template.

Do not use allowedValues for lists of things that are meant to be inclusive (for example,
all VM SKUs). allowedValues should only be used for exclusive scenarios. Overusing
allowedValues will block deployment in some scenarios. Resources without built-in

controls in createUIDef may only be populated with values that can be validated in
createUIDef .

300.4.5 Resources
Top-level template properties must be in the following order:

JSON

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/...",
"contentVersion": "1.0.0.0",
"apiProfile": "...",
"parameters": {},
"functions": {},
"variables": {},
"resources": [],
"outputs": {},
}

All empty or null properties that are not required must be excluded from the templates.

Resource IDs must be constructed using one of the resourceId() functions.

Any reference to a property of a resource must be done using the reference() function.

Hard-coded or partially hard-coded URIs or endpoints are not allowed.

The apiVersion specified for a resource type must be no more than 24 months old. A
preview apiVersion must not be used if a later version (preview or non-preview) is
available.

The apiVersion property must be a literal value.

Each VM extension resource must have the autoUpgradeMinorVersion property set to


true.

Any secureStrings used by extensions must use protectedSettings .


300.4.6 CUID ( createUIDef )

Regex validation of textbox controls must match the intent of the control and properly
validate the input.

All properties must be output for each control in createUIDef .

300.4.7 Deployment artifacts

All of the artifacts needed for deployment must be included in the zip file submitted for
publishing.

mainTemplate.json and createUIDefinition.json must be in the root of the folder.

Applications that create resources for which there is no createUIDefinition element


must not prompt for input of any names or properties of these resources that cannot be
validated.

Scripts, templates, or other artifacts required during deployment must be staged to


enable a consistent deployment experience throughout the development and test life-
cycle, including command line deployment with the scripts provided at the root of the
repository. To do this, two standard parameters must be defined:

_artifactsLocation – The base URI where all artifacts for the deployment will be

staged. The defaultValue must be [ deployment().properties.templateLink.uri ].


_artifactsLocationSasToken – The sasToken required to access
_artifactsLocation . The default value should be an empty string ( "" ) for

scenarios where the _artifactsLocation is not secured.

300.4.8 VM image references and disks


All imageReference objects for virtual machines or virtual machine scale sets must use
core platform images, or images that are available in the commercial marketplace.
Custom images cannot be used.

An imageReference using an image from the commercial marketplace cannot be a


preview or staged version of the image in production deployments.

An imageReference using an image from the commercial marketplace must include


information about the image in the plan object of the virtual machine.

If a template contains an imageReference using a platform image, the version property


must be the latest version.
VM sizes must be selected using the VM SizeSelector control in createUIDef , and
passed to the template as a parameter.

VM sizes in allowed values must match the storage type selection (premium, standard,
or standard SSD).

OS Disks and Data Disks must use implicit managed disks.

400 Azure container offers


When publishing an offer in Partner Center, ensure you follow the policies listed below.
This ensures customers can easily find and deploy your offer securely and easily in the
commercial marketplace.

400.1 Technical requirements


Adhere to the following technical requirements to ensure successful submission of your
offer:

For ‘Kubernetes App’ offers,

Ensure your application can be deployed using a helm chart .


The application must be deployable to Linux environment.
The images must be of amd64 architecture.
All the image repos and digest details must be included in the chart. No
additional charts or images can be downloaded at runtime.
Package your application artifacts as a CNAB bundle.
Published application must be deployable on Azure Kubernetes service.

For ‘Container Image’ offers,

Ensure your container product is deployable on AKS clusters and Azure Container
Instance.

400.2 Business requirements


Publishing an Azure container offer requires the following:

Usage/distribution of third-party software and consumption of services must


follow all respective redistribution licensing.

For ‘Kubernetes App’ offers,


Select a billing model (percore, pereverycoreincluster) per offer/plan. Add labels to
pods relevant for percore billing.
Add the term KubernetesApps to offer description to make it easily discoverable by
customers.

400.3 Security
Your product must not jeopardize or compromise user security, or the security or
functionality. You are solely responsible for all product safety testing, and the
implementation of any appropriate feature safeguards.

Your product must not contain or enable malware as defined by the Microsoft
criteria for Unwanted and Malicious Software.

Your product must address all known vulnerabilities.

Microsoft performs regular security validations on container offers. If vulnerabilities are


identified in a published offer, Microsoft reserves the right to hide/deprecate the offer
(with or without advanced notification to the publisher) to ensure the safety of our
customers. You can republish your offer after the vulnerabilities are remedied. When
possible, we will notify you of any vulnerabilities identified and provide a timeline for
you to fix them.

600 IoT Edge Modules


To ensure that customers have a clear and accurate understanding of your offer, please
follow these additional listing requirements for IoT Edge Modules offers.

600.1 Offer Information


Offers must link to supported IoT Edge devices in the IoT device catalog. General-
purpose modules must link to the device catalog with the text "List of compatible IoT
Edge certified devices" linked to https://aka.ms/iot-edge-certified .

600.2 Plan Information


SKU metadata must meet the following requirements:

Manifest and image tags must be properly formatted and consistent. The "latest"
tag must be listed.

Defaults must be accurate:


Routes must be specific and use the proper syntax.
Twin desired properties value syntax must be proper non-escaped JSON,
without arrays or values exceeding 512 characters, and with a maximum four (4)
levels of nested hierarchy.
Environment variable value must be less than 512 characters.
createOptions value must be proper non-escaped JSON, without values
exceeding 512 characters, and not granting privileged rights unless absolutely
necessary.

600.3 Technical Requirements


Containers must meet the following requirements:

The "latest" tag must be a manifest tag available in the container registry.
All image tags referred to by manifest tags must be present in the registry.
All version tags must be immutable.

The module must start, run, and remain stable with the default options.

The "latest" tag must run with the default configuration options on all claimed
supported OS/architectures. For general-purpose modules, this means supporting x64,
arm32, and arm64 under both Linux and Windows (x64 platform only).

Modules that include the IoT SDK and are set to the PartnerId.OfferIdPlanId must send
telemetry.

700 Managed Services


The policies listed in this section apply only to Managed Services offers.

700.1 Value proposition and offer requirements


The term "managed service" or "managed services" must be included somewhere in the
offer description.

Managed services offers must have the primary purpose of providing services that
manage customers' use of Azure. Offerings, with the primary purpose of selling licenses
or subscriptions to software or a platform, must instead be listed as an application.

700.3 Graphic elements


No text other than official logo marks may be used in logo images.
Logo backgrounds should not be black, white, or gradients. If a transparent background
is used for the required logos, logo elements should not be black, white, or blue. Hero
logos may not use transparent backgrounds.

700.4 Business requirements


You must have Solutions Partner designation for Infrastructure (Azure) or Security.

OR,

Retained benefits in the following competency based on competencies held as of


September 30, 2022: Silver or Gold competency in Cloud Platform or Security.

700.5 Plan details


Plan titles, summaries, and descriptions must be descriptive of the plan.

The billing model for plans must be "Bring your own license".

700.6 Plan manifest details


Manifest version numbers must be in the n.n.n format (for example, 1.2.5).

800 Consulting Services


The policies listed in this section apply only to Consulting Services offers.

800.1 Value proposition


Consulting Services must be fixed in scope and have a defined outcome. Offers with the
primary purpose of selling licenses or subscriptions to software or a platform must
instead be listed as an application.

800.2 Eligibility requirements

Primary Eligibility Requirement(s)


Product:
Azure

Azure To publish a Consulting Service offer in Commercial Marketplace, you must be


enrolled into Microsoft Cloud Partner Program, and have qualified score in at
least one of the following areas:
Primary Eligibility Requirement(s)
Product:
Azure

Data & AI (Azure), Infrastructure (Azure), Digital & App Innovation (Azure),
Security
OR
Retained benefits in the following competency based on competencies held as
of September 30, 2022:
Silver or Gold competency in at least one of the following areas:
Application Development, Application Integration, Application Lifecycle
Management, Cloud Platform, Data Analytics, Data Center, Data Platform,
DevOps or Security.

Primary Product: Dynamics 365 Eligibility Requirement(s)

Dynamics 365 Business Central Solutions Partner designation for Business Applications OR
at least two associated Business Central customer
deployments
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Enterprise Resource Planning
and serving at least three customers
OR
Must have published a Business Central application in
Microsoft AppSource.

Dynamics 365 Customer Solutions Partner designation for Business Applications


Engagement Applications (Sales, OR
Marketing, Customer Service, Field Have at least two associated Common Data Service
Service, HR) customer deployments
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Business Applications –
Customer Engagement Option.

Dynamics 365 Customer Insights Solutions Partner designation for Business Applications
OR
Have at least two associated Customer Insights customer
deployments.

Dynamics 365 Customer Voice Solutions Partner designation for Business Applications
OR
Primary Product: Dynamics 365 Eligibility Requirement(s)

Have at least two associated Common Data Service


customer deployments.

Dynamics 365 Finance and Solutions Partner designation for Business Applications
Operations Applications (Finance, OR
Supply Chain Management, At least two associated Finance and Operations, Retail,
Commerce, Project Service and/or Core HR customer deployments
Automation) OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Business Applications –
Unified Operations Option.

Power BI Solutions Partner designation for Business Applications or


Data and AI (Azure)
OR
Have at least two associated Power BI customer
deployments
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Must be a Solution Partner in the Business Intelligence
Partner Program with a Silver or Gold competency in
Data Analytics and five [Microsoft Certified: Power BI Data
Analyst Associates]s in the organization. Details in section
800.2.6 below.

Power Apps Solutions Partner designation for Business Applications or


Digital and App Innovation (Azure)
OR
Have at least two PAL associated Power Apps customer
deployments.

Power Automate Solutions Partner designation for Business Applications or


Digital and App Innovation (Azure)
OR
Have at least two PAL associated Power Automate
customer deployments.

Power Virtual Agents Solutions Partner designation for Business Applications or


Digital and App Innovation (Azure) or Data & AI (Azure)
OR
Have at least two PAL associated Power Automate
customer deployments.
Primary Solution Path Eligibility Requirement(s)
Product:
Microsoft
365

Adoption and To publish a Consulting Service offer in Commercial


Change Marketplace, you must be enrolled into Microsoft Cloud
Management Partner Program, and have qualified score in Solutions Partner
Designation for Modern Work
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity.

Calling for To publish a Consulting Service offer in Commercial


Microsoft Teams Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
Designation for Modern Work
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity or
Communications.

Cloud Security To publish a Consulting Service offer in Commercial


Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Security
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Security.

Compliance To publish a Consulting Service offer in Commercial


Advisory Marketplace, you must be enrolled into Microsoft Cloud
Services Partner Program, and have qualified score in Solutions Partner
designation for Security
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Security or Enterprise Mobility
Management.

Device To publish a Consulting Service offer in Commercial


Deployment Marketplace, you must be enrolled into Microsoft Cloud
and Partner Program, and have qualified score in Solutions Partner
Management designation for Modern Work (if Windows or Devices), Security
(if Enterprise Mobility Management
OR
Retained benefits in the following competency based on
Primary Solution Path Eligibility Requirement(s)
Product:
Microsoft
365

competencies held as of September 30, 2022:


Silver or Gold competency in Enterprise Mobility Management
or Windows and Devices.

Firstline Workers To publish a Consulting Service offer in Commercial


Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work (if Cloud Productivity), Security
(if Enterprise Mobility Management)
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity, Enterprise
Mobility Management, or Security.

Identity & To publish a Consulting Service offer in Commercial


Access Marketplace, you must be enrolled into Microsoft Cloud
Management Partner Program, and have qualified score in Solutions Partner
designation for Security
OR
Retained benefits in the following legacy competency based
on competencies held as of September 30, 2022:
Silver or Gold competency in Security or Enterprise Mobility
Management.

Information To publish a Consulting Service offer in Commercial


Protection & Marketplace, you must be enrolled into Microsoft Cloud
Governance Partner Program, and have qualified score in Solutions Partner
designation for Security
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Security or Enterprise Mobility
Management.

Insider Risk To publish a Consulting Service offer in Commercial


Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified a score in Solutions
Partner designation for Security
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Security or Enterprise Mobility
Management.
Primary Solution Path Eligibility Requirement(s)
Product:
Microsoft
365

Knowledge and To publish a Consulting Service offer in Commercial


Insights Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Collaboration and Content or
Cloud Productivity.

Meetings for To publish a Consulting Service offer in Commercial


Microsoft Teams Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity or
Communications.

Meeting Rooms To publish a Consulting Service offer in Commercial


for Microsoft Marketplace, you must be enrolled into Microsoft Cloud
Teams Partner Program, and have qualified score in Solutions Partner
designation for Modern Work
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity or
Communications.

Microsoft 365 To publish a Consulting Service offer in Commercial


Live Events Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity or
Communications.

Mobile Device To publish a Consulting Service offer in Commercial


Management Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work (if Windows & Devices), Security
(if Enterprise Mobility Management)
Primary Solution Path Eligibility Requirement(s)
Product:
Microsoft
365

OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Enterprise Mobility Management
or Windows & Devices.

Power Platform To publish a Consulting Service offer in Commercial


for Teams Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work (if Cloud Productivity), Business
Applications (if Cloud Business Applications)
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity or Cloud
Business Applications.

Teams Custom To publish a Consulting Service offer in Commercial


Solutions Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work (if Cloud Productivity), Business
Applications (if Cloud Business Applications), Digital & App
Innovation (Azure) (if Application Development / App
Integration)
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity, Cloud
Business Applications, Application Development, or App
Integration.

Teamwork To publish a Consulting Service offer in Commercial


Deployment Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity or Small and
Midmarket Cloud Solutions.

Threat To publish a Consulting Service offer in Commercial


Protection Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Security
Primary Solution Path Eligibility Requirement(s)
Product:
Microsoft
365

OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Security.

Workplace To publish a Consulting Service offer in Commercial


Analytics Marketplace, you must be enrolled into Microsoft Cloud
Partner Program, and have qualified score in Solutions Partner
designation for Modern Work (if Cloud Productivity), Data & AI
(Azure) (if Data Analytics)
OR
Retained benefits in the following competency based on
competencies held as of September 30, 2022:
Silver or Gold competency in Cloud Productivity or Data
Analytics.

For more information on meeting these prerequisites, see the Consulting Services
prerequisites.

The following sections provide more detail on publishing requirements for "Power" offer
types noted in the table above.

800.2.2 Primary Product: Dynamics 365 Apps on


Dataverse and Power Apps (Sales, Marketing, Customer
Service, Field Service, HR)
To publish a Dynamics 365 Apps on Dataverse and Power Apps consulting service offer
in the marketplace, you must be Enrolled in the Microsoft Cloud Partner Program as a
Business Applications solutions partner.

OR

Have at least two associated Common Data Service customer deployments in the
trailing 12 months (either through DPOR, CPOR, or OSU).

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications

OR,
Alternatively, you may have retained benefits listed below. Please note we will be
retiring the below legacy competencies starting September 1st, 2023. From this date on,
all partners will be required to become a Solutions Partner. You can learn more here
Introduction to the Solutions partner program - Partner Center | Microsoft Learn.

Retained benefits in the legacy Competency [silver or gold status in Cloud Business
Applications Competency in Customer Engagement option] as described in the
800.2.2 Eligibility requirements, based on your competency held as of September
30, 2022.

For more information please see: Certification Policies

800.2.3 Primary Product: Dynamics 365 Finance and


Operations Applications (Finance, Supply Chain
Management, Commerce, Project Operations)
To publish a Dynamics 365 Finance and Operations Consulting Service offer in the
Marketplace you must be Enrolled in the Microsoft Cloud Partner Program as a Business
Applications Solution Partner.

OR

Have at least two associated Finance and Operations, Retail, and/or Core HR customer
deployments in the trailing 12 months (either through DPOR, CPOR, or OSU).

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications.

OR,

Alternatively, you may have retained benefits in the Legacy Competency [silver or gold
status in Cloud Business Applications Competency in Unified Operations option] as
described in the 800.2.3 Eligibility requirements, based on your competency held as of
September 30, 2022.

For more information please see: Certification Policies

800.2.4 Primary Product: Dynamics 365 Customer Insights


To publish a Dynamics 365 Customer Insights consulting service offer in the
marketplace, you must be Enrolled in the Microsoft Cloud Partner Program as a Business
Applications Solution Partner.
OR

Have at least one associated Customer Insights customer deployments via PAL in the
trailing 12 months.

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications.

For more information on validating your customer deployment projects using PAL,
please see Link a partner ID to your account that’s used to manage customers.

OR,

Alternatively, you may meet the legacy requirements listed below. Please note we will be
retiring the below legacy requirements starting September 1st, 2023. From this date on,
all partners will be required to become a Solutions Partner. You can learn more here
Introduction to the Solutions partner program - Partner Center | Microsoft Learn

Legacy partner requirements include at least one in-production implementation of


Dynamics 365 Customer Insights with 50,000 or more unified profiles and
refreshed at least once a month.

For more information please see: Certification Policies

800.2.5 Primary Product: Dynamics 365 Business Central


To publish a Dynamics 365 Business Central Consulting Service offer in the Marketplace
you must be Enrolled in the Microsoft Cloud Partner Program as a Business Applications
Solution Partner.
OR have at least two associated Dynamics 365 Business Central customer deployments
in the trailing 12 months (either through DPOR, CPOR,or OSU).

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications. OR Alternatively, you may have retained benefits listed
below. Please note we will be retiring the below legacy competencies starting
September 1st, 2023. From this date on, all partners will be required to become a
Solutions Partner. You can learn more here Introduction to the Solutions partner
program - Partner Center | Microsoft Learn.
- Retained benefits in the legacy Competency (silver or gold Enterprise Resource
Planning) along with at least three customers or a published Business Central
application in Microsoft AppSource.

800.2.6 Power BI
To publish a Power BI consulting service offer in the marketplace, you must Enrolled in
the Microsoft Cloud Partner Program as a Solutions Partner for Business Applications OR
Solutions Partner for Data & AI.

OR

Have at least two associated Power BI customer deployments via PAL in the trailing 12
months.

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications.

OR,

Alternatively, you may meet the legacy requirements listed below. Please note we will be
retiring the below legacy requirements starting September 1st, 2023. From this date on,
all partners will be required to become a Solutions Partner. You can learn more here
Introduction to the Solutions partner program - Partner Center | Microsoft Learn.

Legacy partner requirements include attaining the Showcase Partner status in the
Business Intelligence Partner Program . Below are the requirements:

1. Attain your competency or solutions partner designation.

Your company must have an active Silver or Gold Microsoft Data Analytics
Competency
OR Solutions Partner for Data & AI or Solutions Partner for Business
Applications.

2. Get certified

Five Microsoft Certified: Data Analyst Associates*OR


Five Microsoft Certified: Power BI Data Analyst Associates**

3. Request status change

Email pbiptnr@microsoft.com with attached proofs of program tier


requirements.
Receive updated status.
On review approval, submit a Power BI Consulting Service offer.

Read more details about Announcing a new name for the Data Analyst Associate
certification

Exam DA-100 / Microsoft Certified: Data Analyst Associate certification will be


retired on March 31, 2022 and accepted until March 31, 2023. ** Exam PL-300 /
Microsoft Certified: Power BI Data Analyst Associate certification will be accepted
starting February 28, 2022.

For more information please see: Certification Policies

800.2.7 Power Apps


To publish a Power Apps Consulting Service offer in the marketplace you must be
Enrolled in the Microsoft Cloud Partner Program as a Business Applications Solution
Partner or Digital and App Innovation (Azure).

OR

Have at least two PAL associated Power Apps customer deployments in the trailing 12
months.

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications.

For more information on validating your customer deployment projects using PAL,
please seeLink a partner ID to your Power Platform and Dynamics Customer Insights
accounts.

OR,

Alternatively, you may meet the legacy requirements below. Please note we will be
retiring the below legacy requirements starting September 1st, 2023. From this date on,
all partners will be required to become a Solutions Partner. You can learn more here
Introduction to the Solutions partner program - Partner Center | Microsoft Learn.

Legacy partner requirements include: at least one associated Power Apps customer
deployment project validated using the Partner Admin Link (PAL).

OR

meet both the following Competency and Certification requirements:

Competency: Your company must have at least one of the following active Gold
competencies: - Cloud Business Applications - Cloud Platform - Small and Mid-Market
Cloud Solutions - Cloud Productivity - Application Integration - Application
Development - Data Analytics AND Certification: Your company must have individuals
pass the following certifications: - Five must achieve MSFT Certified: Power Platform
Functional Consultant Associate AND - Two must achieve MSFT Certified: Power
Platform Developer Associate AND - One must achieve MSFT Certified: Power Platform
Solution Architect Expert*.
Certifications can be held by the same or different individuals.

Exam MB-600: Microsoft Dynamics 365 + Power Platform Solution Architect will be
accepted until June 30, 2022.

800.2.9 Power Automate


To publish a Power Automate Consulting Service offer in the marketplace, you must be
Enrolled in the Microsoft Cloud Partner Program as a Business Applications Solution
Partner or Digital and App Innovation (Azure).

OR

Have at least two PAL associated Power Automate customer deployments in the trailing
12 months.

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications.

For more information on validating your customer deployment projects using PAL,
please see Link a partner ID to your Power Platform and Dynamics Customer Insights
accounts.

OR,

Alternatively, you may meet the legacy requirements below. Please note we will be
retiring the below legacy requirements starting September 1st, 2023. From this date on,
all partners will be required to become a Solutions Partner. You can learn more here
Introduction to the Solutions partner program - Partner Center | Microsoft Learn.

Legacy partner requirements include:

at least one associated Power Automate customer deployment project validated


using the Partner Admin Link (PAL).

OR meet both the following Competency and Certification requirements: Competency:


Your company must have at least one of the following active Gold competencies: Cloud
Business Applications

Cloud Platform
Small and Mid-Market Cloud Solutions
Cloud Productivity
Application Integration
Application Development
Data Analytics AND Certification: Your company must have individuals pass the
following certifications:
Five must achieve MSFT Certified: Power Platform Functional Consultant
Associate AND · - Two must achieve MSFT Certified: Power Platform Developer
Associate AND · - One must achieve MSFT Certified: Power Platform Solution
Architect Expert*.

Certifications can be held by the same or different individuals.

Exam MB-600: Microsoft Dynamics 365 + Power Platform Solution Architect will be
accepted until June 30, 2022

800.2.10 Power Virtual Agents


To publish a Power Virtual Agents Consulting Service offer in the marketplace, you must
be Enrolled in the Microsoft Cloud Partner Program as a Business Applications Solution
Partner or Digital and App Innovation (Azure) or Data & AI (Azure).
OR

Have at least two PAL associated Power Automate customer deployments in the trailing
12 months.

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications.

OR,

Alternatively, you may meet the legacy requirements below. Please note we will be
retiring the below legacy requirements starting September 1st, 2023. From this date on,
all partners will be required to become a Solutions Partner. You can learn more here
Introduction to the Solutions partner program - Partner Center | Microsoft Learn.

Legacy partner requirements include:

Meet both the following Competency and Certification requirements:

Competency: Your company must have at least one of the following active Gold
competencies: - Cloud Business Applications - Cloud Platform - Small and Mid-Market
Cloud Solutions - Cloud Productivity - Application Integration - Application
Development - Data Analytics AND Certification: Five individuals must achieve MSFT
Certified: Power Platform Functional Consultant Associate.

800.2.11 Dynamics 365 Customer Voice


To publish a Dynamics 365 Customer Voice Consulting Service offer in the marketplace,
you must be Enrolled in the Microsoft Cloud Partner Program as a Business Applications
Solution Partner.

OR

Have at least two associated Common Data Service customer deployments in the
trailing 12 months (either through DPOR, CPOR, or OSU).

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications

OR,

Alternatively, you may meet the legacy requirements listed below. Please note we will be
retiring the below legacy requirements starting September 1st, 2023. From this date on,
all partners will be required to become a Solutions Partner. You can learn more here
Introduction to the Solutions partner program - Partner Center | Microsoft Learn.

Legacy partner requirements include completing the Customer Voice training


available at Create surveys with Dynamics 365 Customer Voice.

For more information please see: Certification Policies

800.2.12 Primary Product: Dynamics 365 Mixed Reality


To publish a Dynamics 365 Mixed Reality consulting service, offer in the marketplace,
you must be Enrolled in the Microsoft Cloud Partner Program as a Business Applications
Solution Partner.

OR

If the offer is focused on Guides, you should have at least one associated Guides
customer deployment in the trailing 12 months. If the offer is Remote Assist, you must
have at least one associated Remote Assist customer deployment in the trailing 12
months.

For more on information on how to become a Solutions Partner please see Solutions
Partner for Business Applications

800.3 Title
Your Title must follow the format "Offer Name: Duration Service type." For example,
"CompanyX - Database Security: 2-wk Implementation."
Your offer Title must not include your company name unless it is also a product name.
For example, "CompanyX 3-Wk Assessment."

The offer type must match the type specified during submission.

800.4 Summary and description


The Summary and Description must provide enough detail for customers to clearly
understand your offer, including:

Service deliverables and outcomes.


Agendas for workshops longer than one day.
Detailed itemization of briefing and workshop topics.

Any Applicable Products and keywords defined during submission must be directly
relevant to the offer.

If mentioned in the summary or description, the offer type must match the type
specified during submission.

800.4.1 Quality
Duplicate Description The descriptions cannot be the same for multiple offers. Each
description should accurately represent and differentiate the services associated with
the offers. 7For more information, please see:

Online store offer details


Offer Listings

Missing Estimated Price Rationale If you provide an estimated price, an explanation of


why it is estimated and what factors influence the final price must be included in the
description. Please update the description with this information and resubmit your offer.
Example: Price is based on scope of work

For more information, please see:

Online store offer details


Offer Listings

Extraneous Content in Description Your description includes a notable amount of


marketing or promotional information not directly relevant to the offer. Please remove
the extraneous content and resubmit your offer. For more information, please see:

Online store offer details


Offer Listings

800.4.2 Content
Summary

Please briefly describe the purpose or goal of your offer in 200 characters or fewer. Your
summary cannot be the same text as the title of the offer. This will be displayed in the
search box and must be different from the name of the offer.

Example: Free 2 hour assessment on how to use Office 365 and SharePoint with Power
Apps, Power Automate, Dataverse, and Power BI by industry leading partner, trainer, and
thought leaders. See Offer Listings.

Primary Product Content

Explain how the primary product is part of this offer by specifically mentioning it and
making it clear. Our goal is not to just publish your offer, but to drive more leads that
will help move your business forward. It needs to be clear to the potential customer how
your service is going to help their business. See Primary products and online stores.

Deliverables and Outcomes

Your description needs to have deliverables and outcomes using Markdown language
for bullet points. Examples:

markdown

### Deliverables
* List of applicable solutions that can be built using Office 365,
SharePoint, Power Apps, Flow, Power BI, > and Dataverse
* Skill gap assessment of your current staff
* License review
* Recommendations on where and how to start using Power Apps, Flow, CDS, and
Power BI with SharePoint and > Office 365

You may format your description using Markdown formatting (### Header, *
Bullet, *Italics*, **Bold**) or simple HTML. Partner Center also allows rich
text formatting.

If you are using HTML, check the PREVIEW before you go live. You may want to switch to
Markdown formatting if the bullets aren’t rendering properly.

See Offer Listings.


Agenda

Workshops longer than a day should include a clear daily or weekly agenda in the
description. Please see examples below:

markdown

### Agenda
* Day 1: Dashboard-in-a-Day using Power BI
* Day 2: Design the reporting platform for your enterprise
* Day 3: Develop and deploy the relevant visualizations on the Power BI
reporting platform
* Day 4 & 5: Remote Support

Or

### Weekly Agenda


* Week 1: Design the reporting platform for your enterprise
* Week 2: Develop and deploy the relevant visualizations on the Power BI
reporting platform
* Week 3: Remote Support

You may format your description using Markdown formatting (### Header, *
Bullet, *Italics*, **Bold**) or simple HTML. Partner Center also allows
rich text formatting.

If you are using HTML, check the PREVIEW before you go live. You may want to switch to
Markdown formatting if the bullets aren’t rendering properly. See Offer Listings.

Topics for Briefings

Briefings should include at least four bullets with information on topics to be covered,
using Markdown formatting for the bullet points. Examples:

markdown

**What does this Briefing include?**

* Review your existing Microsoft Excel or Microsoft Access based business


processes
* Discuss your current goals and limitations
* The alternatives and approaches with Power Platform
* Review the licensing options and costs
* Discuss the "Citizen Developer" capabilities
You may format your description using HTML. Links to external resources (such as
license agreements) should be properly HTML formatted for readability and useability
(“clickability”) instead of being plain text URL strings. If you do so, check the Preview
before you go live. If you are using HTML, check the PREVIEW before you go live. You
may want to switch to Markdown formatting if the bullets aren’t rendering properly.
See Offer Listings.

Omits Microsoft Cloud Solutions Aspects

The description of your offer must clearly state how it leverages or relates to [Choose
one: Microsoft Azure, Microsoft Power BI, etc.] cloud services value propositions, and
state how the offer is providing a professional service for the selected primary product.
Update the description and resubmit your offer.

See Offer Listings.

Contact Info in Description

The description of your offer should not contain contact information. However, it may
direct customers to the "Contact Me" button on the offer page to start a discussion.
Update the description and resubmit your offer

800.5 Supporting documents


Your listing may include supporting documents with further information for your offer.
Documents may feature Microsoft competing products only in the context of migration
to Microsoft products.

1000 Software as a Service (SaaS)


The policies listed in this section apply only to SaaS offers.

1000.1 Value proposition and offer requirements


For your SaaS offer to be listed on Azure Marketplace, it must be primarily platformed
on Microsoft Azure.

The following are generally supported scenarios:

Hosted - your entire product and all components are hosted in your Azure
infrastructure for Azure customers.
Migrations to Azure - your product migration tooling has Azure as its only
destination for migration, but can run on-premises or on another cloud as a
migration source.
Backup or data replication to Azure - your backup, replication, or data product
replicates data only to Azure, while the product’s control plane can run on-
premises or on other clouds.
Azure platformed SaaS with external dependencies - your product compute
and/or data plane runs on Azure, but smaller control planes or support
infrastructure, such as logging, run on-premises or on another cloud. In this case,
your Azure-hosted compute and/or data plane must be the resource whose
consumption increases the fastest when your users increase their consumption
Azure platformed SaaS with smaller, peripheral agents - your product's compute
and/or data plane runs on Azure. Your product's monitoring or security agents can
run on-premises or on another cloud or on the customer’s tenant, but they must
send data to an Azure-hosted environment for storage and analysis.

If your product requires components to be deployed in the customer’s Azure tenant, the
following requirements apply:

The components being deployed to the customer’s Azure tenant must be


deployed through an Azure Marketplace offer.
The components must be provisioned in a secure manner. For example, using
Azure Role Based Access Control (RBAC), Azure Active Directory Service Principals,
or Azure Lighthouse.

For your SaaS offer to be listed on AppSource, it must meet these criteria:

Integrate with or extend a Microsoft service or product and your offer must
describe how it does so.
Accept single sign-on from work accounts from any company or organization that
has Azure Active Directory (AAD). More information is at Get AppSource certified
for Azure Active Directory.

1000.2 Offer targets


Offer categories may only include Internet of things (IoT), if the offer supports Azure IoT
Services such as IoT Hub or Device Provisioning Service (DPS), and the partner has been
approved by the IoT team.

1000.3 Authentication options


If you choose to sell through Microsoft, the marketplace buyer must be able to activate
their subscription using the Azure Active Directory (Azure AD) log in information that
they used to purchase your marketplace offer. This means that your offer landing page
and your application must allow the marketplace buyer to log in using Azure AD Single
Sign-On (SSO) . If you process transactions independently using the Get it now or Free
trial options, the marketplace user that acquires your offer must be able to log in to
your application using Azure AD SSO.

Offers must support both Azure AD and Microsoft Account (MSA) types.

You must limit your Microsoft Graph API request(s) to use only the "User.Read"
permissions during the marketplace subscription activation process. Requests requiring
additional permissions can be made after the subscription activation process has been
completed. See Microsoft’s guidance on incremental consent to learn more.

1000.4 SaaS Fulfillment and Metering APIs


Correctly integrating with the SaaS Fulfillment APIs is a requirement for creating and
publishing a transactable SaaS offer in Partner Center. This integration should be
maintained for as long as the offer is in Marketplace.

See the technical SaaS fulfillment APIs guidelines.

Please bear in mind that while SaaS metering is optional, the fulfillment API docs do not
include the metering service docs. You must additionally integrate with the metering API
if you have a SaaS offer that uses meters.

1000.5 Microsoft 365 App and Add-In Linking


Microsoft 365 apps and add-ins linked to your SaaS offer must extend your SaaS offer's
user experience and functionality. In addition:

You must be the publisher of both the SaaS offer and the app or add-in(s), or
You must provide written authorization from the publisher of the SaaS offer or app
or add-in to which you are trying to link your offer.

1100 Microsoft 365


The policies listed in this section apply only to Microsoft 365 offers, formerly known as
Office 365 offers.

1100.1 General content


Your offer listing must only describe your app or add-in, and not include advertising for
other offers.
Your offer description must disclose any app or add-in features or content that require
an extra charge, whether through in-app or add-in purchases or through other means.

If your product offers in-app purchases, you must select the "My product requires
purchase of a service or offers additional in-app purchases" check box on the Product
Setup tab when submitting your offer via Partner Center.

Office Add-ins must have a clear value proposition and provide a seamless first run
experience (FRE). If users must sign in or sign up to use the add-in, the value proposition
must be clear to the user before they do so.

1100.3 Selling additional features


Apps or add-ins running on mobile must not offer any additional features or content for
sale.

1100.4 Predictable behavior


Your app or add-in must not make unexpected changes to a user's document.

Your app or add-in must not launch functionality outside of the app or add-in
experience without the explicit permission of the user.

Your app experience must not prompt a user to disclose the credentials of a Microsoft
identity (for example, Microsoft 365 (formerly called Office 365) or Microsoft Azure
Organizational Account, Microsoft Account, or Windows Domain Account) except
through Microsoft approved OAuth flow, where your app is authorized to act on behalf
of the user.

1100.5 Customer control


Your app or add-in must obtain consent to publish personal information.

Your app or add-in must not obtain, store, pass, or transmit customer information or
content without notifying the user.

Your app or add-in must be secured with a valid and trusted SSL certificate (HTTPS).

Your app or add-in may not open pop-up windows unless they are triggered by explicit
user action. Pop-up windows must not be blocked by the browser's pop-up blocker
when the blocker is set to the default value.
Your app or add-in may not request unreasonably high permissions or full-control
permission.

Your app or add-in must have a correctly sized and formatted icon specified in the
package or manifest.

Add-ins that depend on external accounts or services must provide a clear and simple
sign in/sign out and sign-up experience.

Apps or add-ins that target larger organizations or enterprises:

Do not require a sign-in experience for external accounts or services if sign-ups are
managed by the enterprise outside of the app or add-in and not by the individual
user.
Do not require a seamless first run experience and value proposition but must
include an email contact or link in the UI so users can learn more about your
services.
Please refer to this blog post to learn more.

1100.6 Global audience


You must provide details on the offer submission form if your app or add-in calls,
supports, contains, or uses cryptography.

1100.7 Easy identification


You must specify language support for your app or add-in within the package manifest.
The primary language selected when you submit your offer must be one of the specified
supported languages. The app or add-in experience must be reasonably similar in each
supported language.

The title may not include your brand or service unless your offer targets a larger
organization or enterprise.

Microsoft Teams apps may not include the brand or service in the title.

Your app or add-in must not be a duplicate of an app or add-in you have already
submitted.

1100.8 Preserving functionality


If you update your app or add-in's pricing or licensing terms, you must continue to offer
the original functionality to the existing user base at the original pricing. New pricing
and/or licensing terms may only apply to new users.

If you update your pricing from free to paid, existing users must receive the same
level of functionality as before the update.
If you update site license pricing from free to paid or not supported, existing users
must continue to be supported for free.
Apps or add-ins may convert from free to subscription pricing as long as existing
users receive the same level of functionality as before the update. Converting from
paid to subscription pricing is not currently supported.

1120 Office Add-ins: Word, Excel, PowerPoint,


and Outlook
The policies listed in this section apply only to Office Add-in offers.

1120.1 Offer requirements


All Office Add-ins must use the latest version of the Microsoft-hosted Office.js file at
https://appsforoffice.microsoft.com/lib/1/hosted/office.js .

All Office Add-ins must use the latest manifest schema.

Specify a valid Support URL in the SupportURL element of your add-in manifest.

A high-resolution icon is mandatory.

Source location must point to a valid web address.

The version number in the app package updates must be incremented.

1120.2 Mobile requirements


Office Add-ins also available on iOS or Android:

Must not include any in-app purchases, trial offers, UI that aims to up-sell to paid
versions, or links to any online stores where users can purchase or acquire other
content, apps, or add-ins.
The iOS or Android version of the add-in must not show any UI or language or
link to any other apps, add-ins, or website that ask the user to pay. If the add-in
requires an account, accounts may only be created if there is no charge; the use
of the term "free" or "free account" is not allowed. You may determine whether
the account is active indefinitely or for a limited time, but if the account expires,
no UI, text, or links indicating the need to pay may be shown.
The associated Privacy Policy and Terms of Use pages must also be free of any
commerce UI or Store links.
Must comply with the Outlook add-in design guidelines.

For Office Add-ins also available on iOS:

You must accept Apple's Terms and Conditions by selecting the appropriate
checkbox on the Partner Center app submission form.
Your add-in must be compliant with all relevant Apple App Store policies.
You must provide a valid Apple ID.

Outlook add-ins with mobile support receive additional design review during validation,
which adds to the required validation time. Outlook add-in design guidelines (link
above) describes how your offer will be evaluated during the design review.

1120.3 Functionality
Add-ins must follow design guidelines without impeding the customer experience
within the host application.

Your app or add-in must be fully functional with the supported operating systems,
browsers, and devices for Office 2016, SharePoint 2013, and Office 365.

Your add-in will be tested and evaluated on Windows 10 (build 1903+ on Edge
Legacy and earlier builds prior to 1903 with Internet Explorer 11).
All features must work on a touch-only device without a physical keyboard or
mouse.
Your app or add-in must not utilize deprecated functionality.
Your add-in may not alter or promote the alteration of Office or SharePoint except
via the Office and SharePoint add-ins model.

Add-ins must be compatible with the latest versions of Microsoft Edge, Google Chrome,
Mozilla Firefox, and Apple Safari (macOS). Internet Explorer (IE) in Windows is still used
in many Office configurations as noted in Browsers used by Office Add-ins. We
recommend supporting IE, but if your add-in does not, you should advise users to install
the latest Office version. For details, see Determine at runtime if the add-in is running in
Internet Explorer.

Add-ins must work in all Office applications specified in the Hosts element in the add-in
manifest.
Add-ins must work across all platforms that support methods defined in the
Requirements element in the add-in manifest, with the following platform-specific
requirements.

Add-ins must support Office on web and Mac applications compatible with the
APIs listed in the Requirements element.
Add-ins that support iOS must be fully functional on the latest iPad device using
the latest version of iOS.
Add-ins that use the task pane manifest must support add-in commands.
Content add-ins for PowerPoint may not activate their content (such as play audio
or video) until after the JavaScript API for Office Office.initialize event has been
called. This ensures that content display will correctly synchronize with
presentations.

To help ensure an efficient validation process, if your add-in supports Single Sign-On,
you must provide certification test notes explaining how your add-in uses SSO and what
functionality in the add-in uses it. This information is required to ensure the validation
team can test the fallback implementation. Offers that support Single Sign-On (SSO)
must follow the SSO guidelines and include a fallback authentication method.

1120.4 Outlook add-ins functionality


The policies listed in this section apply only to Outlook add-in offers.

All Outlook add-ins must support Outlook on the web (Modern).


Outlook on the web (Classic) is preferred but optional for requirement sets of 1.5
or lower.
Outlook add-ins must not include the CustomPane extension point in the
VersionOverrides node.

Outlook add-ins that support mobile must allow users to log on separately for
each email account added to the Outlook app.
Add-in commands must be supported if your add-in is shown on every message or
appointment, whether in read or compose mode.
If your add-in manifest includes the SupportPinning element for read mode of a
message and/or appointment, the pinned content of the add-in must not be static
and must clearly display data related to the message and/or appointment that is
open or selected in the mailbox.
Outlook add-ins must not include the ItemSend event in the Events extension
point.
If your add-in can use the AppendOnSend feature, you must include a disclosure in
your offer description noting in what conditions the option is used and what
information is being inserted (for example, "If configured to do so, this add-in
appends legal disclaimers to email sent by the user").
If your add-in uses the Event-based Activation feature, you must include a
disclosure in your offer description noting what information is being inserted in
what events or conditions (for example, "Defined Signature will be inserted in Mail
subject on composing new e-mail"). To help ensure an efficient validation process,
when submitting your offer you must provide certification test notes explaining
how to configure and test scenarios for auto launch events in your add-in.
Add-ins must not include the "Block" SendMode when using LaunchEvents
"OnMessageSend" and/or "onAppointmentSend".

1120.5 Excel custom functions


The policies listed in this section apply only to Excel offers.

1120.5.1 Offer information and support contacts

Your custom functions metadata must have the helpUrl property set.

1120.5.2 Security
To help to ensure the security of your app and users, your custom functions HTML,
JavaScript, and JSON metadata files must be hosted on the same domain.

1120.5.3 Functionality

Add-ins that contain custom functions must support add-in commands. This is to ensure
that users can easily discover your add-in.

Your add-in must work across all platforms that support custom functions.

After an add-in is approved using the EquivalentAddins tag in the manifest, all future
updates to the add-in must include this tag. This tag ensures that your custom functions
save in XLL-compatible mode.

1120.5.4 Validation

To help ensure an efficient validation process, if your add-in contains custom functions,
you must provide certification test notes for at least one custom function to validate
them on submission.
1140 Teams
The policies listed in this section apply only to Teams offers.

Refer to the Teams store validation guidelines to get a better understanding of these
policies and to increase the likelihood of your app passing the Microsoft Teams store
validation process.

1140.1 Value proposition and offer requirements

1140.1.1 App Name


Teams app names must not copy or mimic the title of an existing Teams app or other
offer in the commercial marketplace.

Common nouns must be prefixed or suffixed with the publisher’s name (for example,
"XYZ Tasks" rather than "Tasks").

1140.1.2 Workplace appropriateness


All content should be suitable for general workplace consumption. Apps must be
collaborative and designed for multiple participants. Apps catering to team bonding and
socializing needs of Microsoft Teams users may be published. Such apps should not
require intense time investment or perceptively impact productivity.

1140.1.3 Other platforms and services


Teams apps must focus on the Teams experience and must not include names, icons, or
imagery of other similar chat-based collaborative platforms or services unless the apps
provide specific interoperability.

1140.1.4 Access to services


If your app requires an account or service, you must provide a clear way for the user to
sign in, sign out, and sign up across all capabilities in your app. Teams apps that depend
on authentication to an external service to allow content sharing in channels, must
clearly state in their help documentation or similar location how a user can disconnect
or unshare any shared content if the same feature is supported on the external service.
The ability to unshare the content does not have to be present in the Teams app, but the
process should be clearly documented, and the documentation should be accessible
from within the app.
1140.3 Security

1140.3.1 Financial transactions


Financial transaction details must not be transmitted to users through a bot interface.
Apps may only receive payment information through a user interface linked to a secure
purchase API. Apps may only link to secure payment services if the link is disclosed in
the App's terms of use, privacy policy, app description, and any profile page or
associated website before the user agrees to use the app.

No payment shall be made through an app for goods or services prohibited by General
policy 100.10 Inappropriate content.

1140.3.2 Bots and messaging extensions


Bots and Messaging Extensions must follow privacy notice requirements as
communicated in the Developer Code of Conduct for the Microsoft Bot Framework and
must operate in accordance with the requirements set forth in the Microsoft Bot
Framework Online Services Agreement and Developer Code of Conduct for the
Microsoft Bot Framework.

1140.3.3 External domains


Domains outside of your organization's control (including wildcards) and tunneling
services cannot be included in the valid domains of your manifest, except in the
following conditions:

If you are using OAuthCard , Token.botframework.com must be in the valid domains


list.
Teams apps that require their own SharePoint URLs to function may include
{teamsitedomain} in their valid domain list.

Teams apps built on the Microsoft Power Platform may include


apps.powerapps.com in their valid domain list, to enable app to be accessible
within Teams.

1140.4 Functionality

1140.4.1 General
App packages must be correctly formatted and conform to the latest release of the
manifest schema.

Apps may not launch functionality outside of the Microsoft Teams app experience
without the explicit permission of the user.

Graph API permissions requested by apps should align with business scenarios.

Compatibility: Teams apps must be fully functional on the latest versions of the
following operating systems and browsers:

Microsoft Windows
macOS
Microsoft Edge
Google Chrome
iOS
Android

For other unsupported operating systems and browsers, apps must provide a graceful
failure message.

Response time: Teams apps must respond within a reasonable time frame.

Tabs must load within two seconds or display a loading message or warning.
Bots must respond to user commands within two seconds or display a typing
indicator.
Messaging extensions must respond to user commands within two seconds.
Notifications based on user actions must be displayed within two seconds.

App listing must contain a minimum of 3 screenshots depicting the app functionality in
Teams. Screenshots must also depict app functionality in the Teams mobile clients,
where supported.

Videos provided in the app listing must not be more than 90 seconds in duration and
must only show how the app works in Teams. You must turn off ads in YouTube/Vimeo
settings before submitting the video link in Partner Center.

You must provide test accounts and / or fully configured test environments that are valid
in perpetuity (till app is live on the Teams store) for continuous health evaluation of your
app.

Apps from the same developer offering the same functionality must share an app listing
unless;

privacy compliance requirements mandate separate app listings or


required to support government cloud.

1. Apps with Artificial intelligence(AI)-generated content must


meet below requirements:

App must not generate, contain, or provide access to inappropriate, harmful, or


offensive Artificial intelligence(AI)-generated content consistent with existing
commercial marketplace policies outlined in 100.10.
App must provide mechanism for app users to report inappropriate, harmful, or
offensive content to the developer.
Developer must take timely action on reported concerns.
App must clearly describe Artificial intelligence (AI) functionality before the
customer acquires the offer consistent with policy 100.1.3 & prompt user to review
the info as a part of in-app functionality.

2. Apps using facial recognition capabilities are subject to the


following policies:

App must not allow use of facial recognition capabilities to identify an individual to
be used by or for a police department in the United States.
Developers of apps utilizing facial recognition or emotional inference technologies
must provide a prominent tag or indication of each of these capabilities in the app
description.
This policy pertains only to apps that use facial expressions or facial movements
to infer emotional states, such as anger, disgust, happiness, sadness, surprise,
fear, or other terms commonly used to describe the emotional state of a person.
It does not pertain to apps that use facial expressions and movements to detect
and classify only individual facial elements, such as smiles or raised eyebrows.
The key distinction is between the detection of facial expressions or movements
as visual signals versus the inference of an emotional state.

1140.4.2 Tabs
Teams apps must follow Teams tab design guidelines without impeding the customer
experience within the host application.

Tab experiences must provide value beyond hosting an existing website.


Tabs should not have excessive chrome or layered navigation.
Tabs must not provide navigation that conflicts with the primary Teams navigation.
Tabs should not allow users to navigate outside of Teams for the core experience.
Content in channel tabs must be contextually the same for all members of the
channel and must not be scoped for individual use.
Configurable tabs are collaborative spaces and should have focused functionality.
Tab configuration must happen in the configuration screen, which must clearly
explain the value of the experience and how to configure.

1140.4.3 Bots
Teams apps must follow Teams bot design guidelines without impeding the customer
experience within the host application.

Bot information in the app manifest must be consistent with the bot's Bot Framework
metadata (bot name, logo, privacy link, and terms of service link).

Bots must be responsive and fail gracefully.

Bots must not spam users by sending multiple messages in short succession. Avoid
multi turn conversations in a bot response.

Bots in personal scope must send a welcome message on first launch.

Bots in collaborative scope must send a welcome message on first launch if the app has
a complex configuration workflow.

Welcome message must follow Bot welcome message guidelines.

Bots in collaborative scope must provide user interaction value in the same scope.

At least one bot command must be listed in the manifest for each scope supported by
the bot. Listed bot commands must contain clear command title and description.

Bot commands must not include special characters such as “/”

1140.4.4 Messaging extensions

Teams apps must follow Teams messaging extension design guidelines without
impeding the customer experience within the host application.

Action Commands:

For action commands triggered from a chat message or channel post, calls to
action in apps must incorporate the host app name instead of only using a generic
verb (for example, "Start a Skype Meeting" rather than "Start Meeting", "Upload file
to DocuSign" rather than "Upload file", and so on).
Action commands triggered from a chat message or channel post should leverage
the context of the conversation and must not ask users to re-enter this
information.

Search commands:

Search commands in messaging extensions should enable users to see real-time


search results as they type text.
Search commands in messaging extensions must provide text that helps users
search effectively.

Preview links (link unfurling):

Do not add domains that are outside your control (either absolute URLs or wildcards).
For example, yourapp.onmicrosoft.com is valid but *.onmicrosoft.com is not valid. Top-
level domains are also prohibited (for example, *.com or *.org ).

1140.4.5 Task modules

Teams apps must follow Teams task module design guidelines without impeding the
customer experience within the host application.

Task modules should not embed an entire app. Task modules should only display the
components required to complete a specific action.

1140.4.6 Meeting extensions


Teams apps must follow Teams meeting extension design guidelines without impeding
the customer experience within the host application. Teams meeting apps must provide
a responsive in-meeting experience aligned with the Teams meeting experience. If an
app offers a pre-meeting or post-meeting experience, these must be relevant to the
meeting workflow. In-meeting experience must not take users outside the Teams
meeting for core experiences. Apps must provide value beyond only offering custom
Together Mode scenes in Teams meetings.

1140.4.7 Notification APIs


Notifications must provide value to the user.

Apps must not spam users by sending multiple notifications in quick succession.

Users must not be redirected outside of Teams when clicking a notification.


1140.4.8 Mobile experience
Teams apps should offer an appropriate cross device mobile experience.

App experiences on iOS and Android:

Must not include any in-app purchases, trial offers, UI that aims to up-sell to paid
versions, or links to any online stores where users can purchase or acquire other
content, apps, or add-ins.
Must not show any UI or language or link to any other apps, add-ins, or websites
that ask the user to pay. If the add-in requires an account, accounts may only be
created if there is no charge; the use of the term "free" or "free account" is not
allowed. You may determine whether the account is active indefinitely or for a
limited time, but if the account expires, no UI, text, or links indicating the need to
pay may be shown.
The associated Privacy Policy and Terms of Use pages must also be free of any
commerce UI or Store links.

You must provide a valid Apple App Store Connect Team ID in your Partner Center
account to enable users to acquire and install your app from the Teams App Store on
Teams mobile clients.

1140.5 Teams app linked to Software as a Service (SaaS)


offers
The following additional requirements apply for Teams apps linked to a Software as a
Service (SaaS) offer.

1140.5.1 Manifest and metadata requirements


The manifest for the Teams app must completely and accurately define the
SubscriptionOffer details and OfferID of the linked SaaS offer.
The SaaS offer linked to the Teams app must meet all requirements defined in
1000 Software as a Service (SaaS).
The SaaS offer linked to the Teams app must be live in AppSource and must have
at least one plan with pricing.
Available pricing plans for the SaaS offer must use a per-user pricing model.
Offer metadata should match across the Teams manifest, the Teams app listing in
AppSource, and the SaaS offer in AppSource.
Plan descriptions and pricing details must provide enough information for users to
clearly understand the offer listings.
Any limitations or specialized purchase flows must be clearly called out in the app
metadata and pricing plan details.

1140.5.2 Purchasing and managing subscriptions


Users must be able to complete the end-to-end purchase flows with adequate
information at each step.

Users completing a purchase must be able to activate and configure the


subscription in the SaaS application.
Users completing a purchase must be able to manage licenses, including assigning
and removing licenses, reassigning licenses among users, and authorizing users to
manage licenses.
Any modifications in purchased licenses or plans must be reflected in the SaaS
application with correct license counts, subscription details, and user assignments.

Admin users must be able to complete end-to-end bulk purchase flows from the
Microsoft Teams Admin Center.

After successful purchase and assignment of licenses, your offer must provide enough
value to justify the purchase and users must have access to the subscribed plan features
in Teams.

1140.5.3 Testability and technical requirements


For testing purposes, your Teams app submission to Partner Center must include an
end-to-end (E2E) functional document, linked SaaS offer configuration steps, and
instructions for license and user management as part of the Notes for Certification.

The offer must meet all the technical requirements for Teams apps linked to a SaaS offer.

1140.6 Publisher Attestation


Teams apps must complete Publisher Attestation in Partner Center.

1140.7 Advertising
Teams apps may not include advertising.

1140.8 Teams apps extensible across Microsoft 365


1140.8.1 General
App packages must be correctly formatted and conform to manifest schema 1.13 or
later

Teams apps extensible across Microsoft 365 must be fully responsive and fully
functional on the latest versions of these clients:

Outlook for Windows and Outlook for the web


Microsoft Office on Desktop, Web and Android
Microsoft Teams on Desktop and Web
Microsoft Teams on Android and iOS

Screenshots in the app listing must depict app functionality in all the supported clients.

The app’s listing description must be relevant to all the supported clients (Microsoft
Teams, Microsoft Outlook and Microsoft Office)

The app’s support URL content must be relevant to all the supported clients (Microsoft
Teams, Microsoft Outlook and Microsoft Office)

The app’s Get Started/Sign-in/Sign-out/Sign-up/Help/Permissions and all other way


forward screens must contain content that is relevant to all the supported clients
(Microsoft Teams, Microsoft Outlook and Microsoft Office)

1160 SharePoint add-in


The policies listed in this section apply only to SharePoint add-in offers.

1160.1 Security
Add-ins must not have any unauthenticated pages or APIs, except for the error page.

An unauthenticated error page should not link to other pages or other protected
resources of the add-in.

If the solution requires full trust (formerly known as high trust) permissions, you will
need to follow the guidelines from this Developer Blog post.

1160.2 Functionality
SharePoint add-ins must be fully functional with Windows 7, Windows 10, all versions of
Internet Explorer 11 and later, and the latest versions of Microsoft Edge, Google
Chrome, and Mozilla Firefox.

Add-ins designed for the modern SharePoint experience are not required to support the
classic SharePoint experience. If your add-in supports only the classic experience, you
must include that limitation in your add-in description.

Add-ins must not have remote debugging settings enabled. The manifest for your add-
in must not include the DebugInfo element.

1170 SharePoint Framework Solutions


The policies listed in this section apply only to SharePoint Framework Solutions offers.

1170.1 Value proposition and offer requirements


SharePoint Framework (SPFx) solutions must clearly declare in the description the type
of SPFx components that are included in the package.

1170.2 Security
SharePoint Framework solutions can request any permissions with the solution manifest.
High permissions requests will need to be justified and clarified as part of the solution
submission process.

Needed permissions must be automatically configurable with the manifest file.

1170.3 Functionality
SharePoint Framework (SPFx) solutions must be correctly formatted and conform to the
latest SPFx versions.

Solutions must be fully functional with Windows 10 and the latest versions of Microsoft
Edge, Google Chrome, and Mozilla Firefox. Solutions are only required to be tested in
the non-root site of a modern SharePoint site. Test SPFx solutions on /appsmod only.

Response times must be reasonable. Responses that take more than three seconds must
display a loading message or warning.

1170.4 Branding and advertising


SharePoint Framework solutions may not include advertising.
1170.5 Validation
SharePoint Framework (SPFx) solutions must be submitted with full configuration
instructions in the Notes for Certification.

Offers should include the E2E functional document. Alternatively, SPFx solution
functionality demonstration video links can be included in the Notes for Certification.

1180 Power BI visuals


The policies listed in this section apply only to Power BI offers.

1180.1 Acquisition, pricing, and terms


Power BI visuals must be free but can offer additional purchases.

If your Power BI visual offers additional purchases, it must comply with the In-App
Purchase (IAP) guidelines.

1180.2 Functionality
Your visual must support Power BI Desktop, Power BI Online, Power BI mobile apps, and
Power BI Windows universal apps. It must be compatible with supported operating
systems, browsers, and devices for Power BI, including touch-only devices without a
physical keyboard or mouse.

All visuals must support the context menu (right click menu). You can learn more about
the context menu here.

Your visual must support the core functions of Power BI for that visual type, including
but not limited to pinning to dashboard, filtering focus mode, and formatting various
data types.

Data type support will be evaluated based on the following tests:

String values
Empty values
Negative values
Lots of rows (at least 20,000 rows)
Large numbers of 16 digits

Your visual must not launch functionality outside of the visual experience without the
explicit permission of the user.
Your visual and its description must not prompt the user to install any additional files.

Your visual must not prompt a user to disclose the credentials of a Microsoft identity (for
example, Microsoft 365 (formerly called Office 365) or Microsoft Azure Organizational
Account, Microsoft Account, or Windows Domain Account) except through Microsoft
approved OAuth flow, where your visual is authorized to act on behalf of the user.

Your visual may not open pop-up windows unless they are triggered by explicit user
action. Pop-up windows must not be blocked by the browser's pop-up blocker when the
blocker is set to the default value.

Your visual may not request unreasonably high permissions or full-control permission.

Visuals that depend on external accounts or services must provide a clear and simple
sign in/sign out and sign-up experience.

Power BI visuals must be accompanied by a sample file in .pbix format. The version and
content of the .pbiviz file should match the corresponding visual contained in the
.pbix sample file. For the best user experience, consider adding Hits and Tips for using

the visual to the sample file.

1200 Power BI visuals additional certification


The policies listed in this section apply only to Power BI visuals offers.

1200.1 Certification requirements

1200.1.1 Code repository


Your visual source code must conform to the visual code repository requirements.

The code repository for your visual should be available and correctly formatted.

1200.1.2 Code quality


Your source code should be readable, maintainable, expose no functionality errors, and
correspond to the provided visual's package.

1200.1.3 Code security

Your source code should comply with all security and privacy policies. Source code must
be safe and not pass or transmit customer data externally.
1200.1.4 Code functionality
Running visual development related commands on top of your visual source code
should not return any errors.

Visual consumption should not expose any errors or failures and must ensure the
functionality of any previous version is preserved.

1200.1.5 Update without advanced certification


Power BI Visual additional certification does not apply automatically to updated visuals.
All updates to certified Power BI Visuals must also be certified as part of the submission
process.

1200.2 Duplicate offers


Visuals that rely on access to external services or resources are not eligible to be
certified Power BI visuals.

You may submit duplicate versions of visuals to the Marketplace: a non-certified version
that uses external services or resources, and a certified version that does not use
external services or resources. The offer that accesses external services or resources
must clearly state so in the description.

1240 Power BI Template Apps


To ensure customers have an accurate understanding of your offer, please follow these
additional listing requirements for Power BI App offers.

1240.1 Value Proposition and Offer Requirements


Offer titles must not include "(Preview)".

Descriptions and summaries should not use the deprecated term "content packs". New
app offers may use the term "template apps".

1240.2 Technical Validation


Publisher ownership of the offer must be verifiable.

Offer updates should use the same Power BI tenant and workspace as previous offer
versions.
Power BI apps may not be published more than once via different offers.

Offers should successfully install within two minutes and load within thirty seconds.

All UI aspects should be publication ready. This includes:

Overall look and feel


Reports and dashboards
Titles
Visuals and text
Logos and graphics
Content (static and generated)

Organization-specific Power BI visuals may not be used.

Sample data is not supported for new (not yet published) offers. Offers must be able to
connect with customer data.

When "Connect Data" is available, test accounts, parameters, and/or additional


instructions must be included in the offer's validation instructions, and custom
parameters should not produce any errors.

1400 Dynamics 365 Business Central


The policies listed in this section apply only to Dynamics 365 Business Central offers.

1400.3 Acquisition, pricing, and terms


Add-on apps may be either "Free", "Free or Trial", or "Contact me". Connect and
Localization apps must be listed as "Contact Me".

See Industries, categories, app type.

Supported countries must be selected on submission.

See Supported countries, languages, app Version, and app release date.

1400.5 Technical requirements


Offers must meet all of the requirements in the technical validation checklist.

1400.6 Business Central version functionality


Your add-on apps must function and your data must upgrade to the current version of
Business Central Online. Add-on updates require recertification which must be
submitted with sufficient lead time prior to a Business Central Online update release.

1420 Dynamics 365 apps on Dataverse and


Power Apps
The policies listed in this section apply only to Dynamics 365 apps on Dataverse and
Power Apps offers.

1420.1 Value proposition and offer requirements


Any feature changes between updates must be reflected in the marketing materials
submitted to the Marketplace.

1420.2 Acquisition, pricing, and terms


Offers must be listed as either Free, Trial, Contact Me, or ISV app license management.

1420.3 Content requirements


The Offer Description field must be in plain text or simple HTML format and must
include the full product name.

1420.4 Functionality
Any feature changes between updates must be re-certified when the offer is re-
submitted. The entire solution package must be submitted with each update to ensure
dependency issues are covered. The version number must be incremented with each
update.

1420.5 Technical requirements


Offers must support:

Dynamics 365 v9.1 or above


Unified Client Interface (UCI) (Model Driven Apps only)

Offers must only use publicly available APIs.


Submitted packages must contain all required artifacts for publication on the
Marketplace.

The end-to-end (E2E) functional document must be updated with functional scenarios
and the user/admin journey.

Commercial marketplace offers must be recertified within six months of their last
successful publish.

1420.6 Code validation


Canvas apps and Common Data Services solutions will have their code validated to
check for the following:

Static formula errors and warnings.


Runtime errors (may occur once the app is opened in Run mode to view).
Accessibility errors.

See App certification checklist.

1420.7 Deployment validation


Offers will be installed to a PowerApps Environment using Package Deployer to ensure
that:

Canvas apps successfully connect through provided connectors.


All Common Data Service components (entities, web resources, plug-ins, and other
components) are available.
All associated components are properly removed upon uninstall.

See App certification checklist.

1420.8 Functionality validation


Offers should include the E2E functional document. Video links may be emailed as an
alternative.

1420.9 Security validation


Apps will be checked for:

Connections to external data sources or connections that require access. Proper


connection details should be included in the E2E functional document.
External connections out of PowerApps connectors.

Custom code provided inside Package Deployer will be validated before offer approval
and checked for retrieval of customer data from the target environment.

1420.10 Sitemap validation


Published app customizations must not change or remove any out of box (OOB) site
map.

1440 Dynamics 365 Operations Apps


The policies listed in this section apply only to Dynamics 365 Operations Apps offers.

See Requirements for publishing apps on AppSource.

1440.1 Content requirements


The following requirements must be met in the offer listing:

The Offer Description field must be in HTML format and must include the full
product name.
The Contact Email and Phone Number fields must have valid, working values.
The Storefront Details > App Choice field must be set to "Contact me".
The CAR file must be uploaded using the Technical Info > Validation asset(s) field.

1440.2 Technical requirements


Offers must support the most current version of Dynamics 365.

Commercial marketplace offers must be recertified within six months of their last
successful publish.

1440.3 Code validation


Offers must be validated to verify that custom code meets Microsoft guidelines.

Before submission, publishers must:

Successfully create a CAR without any localization, accessibility, performance, or


security issues.
Publish a solution package with all the required artifacts to LCS and create a
solution identifier (GUID) for the solution.

1440.4 Deployment validation


Offers must be validated to verify that a solution package can be successfully bundled
and delivered in a Microsoft Dynamics 365 for Finance and Operations environment.

Before submission, publishers must:

Reference best practice information in the Migrate and Create methodology


section of LCS.
Successfully deploy at least one Finance and Operations environment without any
errors.
The environment configuration must be the same as the partner's reference
environment.
Partner-supplied master and reference data must be able to be successfully
pushed into the Finance and Operations environment without any errors.

1440.5 Functionality validation


During the functionality demonstration:

A user must be able to sign in to the environment without any errors.


Business transactions must be able to be completed as defined in the package
scope without any errors.

3000 Requirements for Co-sell Status Tiers


The policies listed in this section apply only to offers pursuing Co-sell status.
Requirements are tiered: all 3000.1 Co-sell ready status requirements must be satisfied
in order to be eligible for 3000.2 Azure IP co-sell eligible status.

3000.1 Co-sell ready status


Your offer must be listed on the commercial marketplace.

Your offer must include a complete set of collateral documents (bill of materials),
including a solution/offer pitch deck and a solution/offer one-pager.

You must have a business profile in Partner Center.


Your offer must have a dedicated sales contact for each region (country or geographical
area) in which you would like to be eligible to co-sell.

Services partners must be enrolled in the Microsoft Cloud Partner Program and
completed a Solutions Partner Designation .

3000.2 Azure IP co-sell status


Offers must be built on or built for Azure.

Azure Applications, Azure Containers, IoT Edge Modules, SaaS, and VMs must meet the
following requirements:

Your organization must meet or exceed $100,000 USD of Azure consumed revenue
over a trailing 12-month period, or your offer must have a cumulative marketplace
billed revenue of $100,000 USD.
For SaaS offers: your product must be primarily platformed on Microsoft Azure.
For non-SaaS offers: must be deployed in Azure.
Your offer must be transactable and listed as a "Sell through Microsoft” on the
Azure Marketplace.

3000.3 Azure IP Co-Sell Program Deal Registration


Referral eligibility for IP Co-sell for Azure requires having an IP co-sell eligible offer that
meets the following requirements and registering the deal through Partner Center.

3000.3.1 Segmentation

Deals must be signed with an Enterprise or SMC-Corporate customer.

3000.3.2 Opportunity Age

The opportunity must be created by the partner (through Partner Center) or by


Microsoft (through MSX) and, except for partner-led deals, shared before the contract
sign date or marketplace transaction date.

3000.3.3 Solution Tagging


The Azure IP Co-sell incentive tag must be added.

3000.3.4 Timeline
Completed deals must be registered within 60 days after signing.

3000.3.5 Contract Parameters


The input contract value must be based on the entirety of the contract, not the
annualized value. The registered value of the offer should match the total contract value
and currency agreed upon by you and your customer.

The deal must be at least $25,000 USD in annual revenue, including your products,
licenses, and IP solution related services, and should exclude any trial component, EA,
and hardware components.

Start and end dates are required for the contract duration. If the contract term is 6 or
more years or there is no defined term or end date, the perpetual partner license should
be selected.

3000.4 Azure benefit eligibility for customer (Microsoft


Azure Consumption Commitment)
Marketplace purchases that contribute towards a customer's Microsoft Azure
consumption commitment (MACC) are limited to Azure benefit-eligible offers and can
only be used to purchase solutions that are primarily platformed on or extend Microsoft
Cloud products or infrastructure. To participate in this benefit, your offer(s) need to
reach Azure IP co-sell status.

4000 Microsoft 365 Application Compliance


M365 Certification requirements can be found on the M365 App Compliance page.
Most requirements must be met for successful certification. If you are interested in
applying for M365 Certification, please contact appcert@microsoft.com. If any
significant changes happen after you receive certification, the team must be notified of
the changes.

Once certified, information on M365 Certification for your application is available at


https://aka.ms/appcertification .

Next steps
Visit the commercial marketplace publishing guide.

Return to the commercial marketplace policies and terms.


Publish task pane and content add-ins
to a SharePoint app catalog
Article • 11/10/2021

An app catalog is a dedicated site collection in a SharePoint web application or


SharePoint Online tenancy that hosts document libraries for Office and SharePoint Add-
ins. To make Office Add-ins accessible to users within their organization, administrators
can upload Office Add-ins manifest files to the app catalog for their organization. When
an administrator registers an app catalog as a trusted catalog, users can insert the add-
in from the insertion UI in an Office client application.

) Important

App catalogs on SharePoint do not support add-in features that are


implemented in the VersionOverrides node of the Office add-in XML
manifest, such as add-in commands.
If you’re targeting a cloud or hybrid environment, we recommend that you
use Integrated Apps via the Microsoft 365 admin center to publish your
add-ins.
App catalogs on SharePoint are not supported in Office on Mac. To deploy
Office Add-ins to Mac clients, you must submit them to AppSource.

Create an app catalog


Complete the steps in one of the following sections to create an app catalog with on-
premises SharePoint Server or on Microsoft 365.

To create an app catalog for on-premises SharePoint


Server
To create the SharePoint app catalog, follow the instructions at Configure the App
Catalog site for a web application.

Once you have created the app catalog follow the steps to publish an Office Add-in.

To create an app catalog on Microsoft 365


To create the SharePoint app catalog, follow the instructions at Create the App Catalog
site collection. Once you have created the app catalog, follow the steps in the next
section to publish an Office Add-in.

Publish an Office Add-in


Complete the steps in one of the following sections to publish an Office Add-in to an
app catalog on Microsoft 365 or on-premises SharePoint Server.

To publish an Office Add-in to a SharePoint app catalog


on Microsoft 365
1. Go to the Active sites page of the new SharePoint admin center and sign in with
an account that has admin permissions for your organization.

7 Note

If you have Microsoft 365 operated by 21Vianet (China), sign in to the


Microsoft 365 admin center , then browse to the SharePoint admin center
and open the More features page.

2. Open the app catalog site by selecting its URL in the URL column.

7 Note

If you just created the app catalog site in the previous section, it can take a
few minutes for the site to finish setting up.

3. Choose Distribute apps for Office.

4. In the Apps for Office page, choose New.

5. In the Add a document dialog, select the Choose Files button.

6. Locate and specify the manifest file to upload and choose Open.

7. In the Add a document dialog, choose OK.

To publish an add-in to an app catalog with on-premises


SharePoint Server
1. Open the Central Administration page.
2. In the left task pane, choose Apps.
3. On the Apps page, under App Management, choose Manage App Catalog.
4. On the Manage App Catalog page, make sure you have the right web application
selected in the Web Application Selector.
5. Choose the URL under the Site URL to open the app catalog site.
6. Choose Distribute apps for Office.
7. In the Apps for Office page, choose New.
8. In the Add a document dialog, select the Choose Files button.
9. Locate and specify the XML manifest file to upload and choose Open.
10. In the Add a document dialog, choose OK.

Insert Office Add-ins from the app catalog


For online Office applications, you can find Office Add-ins from the app catalog by
completing the following steps.

1. Open the online Office application (Excel, PowerPoint, or Word).


2. Create or open a document.
3. Choose Insert > Add-ins.
4. In the Office Add-ins dialog, choose the MY ORGANIZATION tab. The Office Add-
ins are listed.
5. Choose an Office Add-in and then choose Add.

For Office applications on the desktop, you can find Office Add-ins from the app catalog
by completing the following steps.

1. Open the desktop Office application (Excel, Word, or PowerPoint)

2. Choose File > Options > Trust Center > Trust Center Settings > Trusted Add-in
Catalogs.

3. Enter the URL of the SharePoint app catalog in the Catalog Url box and choose
Add catalog. Use the shorter form of the URL. For example, if the URL of the
SharePoint app catalog is:

https://<domain>/sites/<AddinCatalogSiteCollection>/AgaveCatalog

Specify just the URL of the parent site collection:

https://<domain>/sites/<AddinCatalogSiteCollection>

4. Close and reopen the Office application.


5. Choose Insert > Get Add-ins.

6. In the Office Add-ins dialog, choose the MY ORGANIZATION tab. The Office Add-
ins are listed.

7. Choose an Office Add-in and then choose Add.

Alternatively, an administrator can specify an app catalog on SharePoint by using Group


Policy. The relevant policy settings are available in the Administrative Template files
(ADMX/ADML) for Microsoft 365 Apps, Office LTSC 2021, Office 2019, and Office 2016
and be found under User Configuration\Policies\Administrative Templates\Microsoft
Office 2016\Security Settings\Trust Center\Trusted Catalogs.
Guidance for deploying Office Add-ins
on government clouds
Article • 05/30/2023

Microsoft provides the government cloud options for our privacy-sensitive customers in
local, state, and national government organizations. This gives partners opportunities to
target critical customers with their Office Add-ins. Due to the more restricted nature of
these environments, which is important for the customers’ privacy and security needs,
not all resources that are typically available in a standard production environment are
available within these clouds.

For partners providing their Office Add-ins to customers in these restricted cloud
environments, there are important differences from the standard public cloud
environment that must be considered. The following information gives the details that
require special handling by developers writing Office Add-ins that target customers in
these environments.

All sovereign environments


For all government cloud (i.e. Sovereign Cloud) environments, the public Office Store is
not available. This means that end-users cannot acquire Office Add-ins directly from the
public store. Administrators are also unable to deploy Office Add-ins directly from the
public store into their Admin Portal. Instead, you must work with administrators to
ensure the following:

The required resources and services for your solution are available inside the cloud
boundary. Either you work with the tenant administrators to provision your service
and resources inside of the cloud boundary, or you work with the network
administrator to enable access to your resources that reside outside of the cloud
boundary.

The resources the Office Add-in accesses conform to the requirements of the
government cloud from which they are being accessed. They also must conform to
any additional requirements from the customer tenant for which the solution is
being provisioned. These requirements include the transfer, management, and
storage of potentially sensitive data, as well as code and resource security and
access vetting procedures.

The Office Add-in manifest that describes the solution and its source location as
applicable to the particular government cloud deployment is obtained from the
partner and ingested for deployment to the appropriate users via the Admin
Portal.

Centralized deployment of add-ins outside of the store is still supported.

US Government Community Cloud (GCC)


In addition to requirements applicable to all Sovereign Clouds, each Sovereign Cloud
environment has its own considerations that may affect Office Add-ins targeting the
environment. GCC is the least restrictive of the government cloud environments and
some of the resources required by the add-in are available from their usual production
endpoints in this environment. One such resource is the Office JavaScript API library.
Solution partners can continue to reference the public Office.js resource as they do with
their public production solution.

GCC High (GCCH), US Department of Defense


(DOD), or other sovereign managed clouds
These government clouds remain internet-connected, but the set of public endpoints
made available is severely restricted. One such restricted endpoint is the public endpoint
for loading the Office JavaScript API library. The public CDN location for Office.js will not
be accessible from within these environments. However, there will be an internal, per-
cloud Microsoft Office CDN provisioned with the same resource. This means the
endpoint URL to access Office.js will be different and your Office Add-in may need some
level of customization to run. Given the additional restrictions, it's likely that any solution
provided to customers will require hosting provider services within the environment as
well, necessitating additional customizations. You'll need to determine the best way to
provide your solution to customers, such that it conforms to the additional restrictions
imposed on services running within the boundaries of these environments. The Office
JavaScript Library CDN URLs are as follows:

GCC High:
https://appsforoffice.gcch.cdn.office.net/appsforoffice/lib/1/hosted/office.js
DOD: https://appsforoffice.dod.cdn.office.net/appsforoffice/lib/1/hosted/office.js

Airgapped Sovereign Clouds


These government clouds are essentially disconnected from the public internet entirely.
Any resource that would normally be accessed from public resources must instead be
custom-provisioned inside these cloud environments. In the GCCH and DOD clouds
mentioned previously, most (if not all) solution providers will need to provision their
services and resources inside the cloud. There is an option to make firewall exceptions
that allows access to public services and resources. However, this bypass is not possible
in airgapped clouds. As with the GCCH and DOD clouds, there will be a Microsoft Office
CDN provisioned inside each cloud environment that hosts required resources such as
the Office.js library. You'll need to work closely with customer tenant administrators to
determine the best way to provide your services and resources in a way that conforms
to the strict access requirements for airgapped Sovereign Clouds.

Office 365 operated by 21Vianet


21Vianet operates and manages an Office 365 service powered by licensed Microsoft
technologies to provide Office 365 services for China compliant with local laws and
regulations. Add-ins developed for use within this cloud environment should use
corresponding CDN. Use
https://appsforoffice.cdn.partner.office365.cn/appsforoffice/lib/1/hosted/office.j
s instead of the standard CDN reference. This ensures continued compliance and

provides better add-in performance.


Maintain your Office Add-in
Article • 03/14/2023

After you publish your add-in, you should keep it up to date with any important
changes from upstream libraries. Patching security issues is critical to building customer
trust. Since these changes have no effect on the published manifest, your customers
won't need to take any actions to get the latest versions of your add-in.

Breaking changes in Office.js


The Microsoft 365 Developer Platform is committed to ensuring the compatibility of
your add-in. We strive to avoid making breaking changes to the API surface and
behavior. However, there are cases where we need to make breaking updates for the
sake of security or reliability. In those rare cases, the following steps are taken to ensure
users of your add-in are unaffected.

Announcements that describe the impacted features and recommended changes


are made on the Microsoft 365 Developer Blog .
If your add-in is published in AppSource, you'll be contacted through the
information you provided.
Where possible, admins of impacted Microsoft 365 tenants (including developer
tenants) will be contacted through Message Center. It's the responsibility of the
admin to contact providers of add-in solutions published outside of AppSource.

Deprecation policy
APIs or tools with better alternatives may be deprecated. Microsoft undergoes a best
effort to declare something as deprecated at least 24 months in advance of retiring it.
Similarly, for individual APIs that are generally available (GA), Microsoft declares an API
as deprecated at least 24 months in advance of removing it from the GA version.

Deprecation doesn't necessarily mean the feature or API will be removed and unusable
by developers. It does show that after the 24-month time period, Microsoft will no
longer support the API or feature.

When an API is marked as deprecated, we strongly recommend that you migrate to the
latest version as soon as possible. In some cases, we'll announce that new applications
must start to use the new APIs a short time after the original APIs are deprecated. In
those cases, only active applications that currently use the deprecated APIs can continue
to use them.
) Important

The 24-month deprecation period will be accelerated if waiting that long poses a
security risk for your add-in or Microsoft.

App Assure
Microsoft’s App Assure service fulfills Microsoft’s promise of application compatibility:
your apps will work on Windows and Microsoft 365 Apps. App Assure engineers are
available to help resolve any issues you might experience at no additional cost.

If you do encounter an app compatibility issue, App Assure engineers will work with you
to help you resolve the issue. Our experts will:

Help you troubleshoot and identify a root cause.


Provide guidance to help you remediate the application compatibility issue.
Engage with independent software vendors (ISVs) on your behalf to remediate
some part of their app, so that it’s functional on the most modern version of our
products.
Work with Microsoft product engineering teams to fix product bugs.

To learn more about App Assure, watch Bring your apps to Microsoft Edge with App
Assure: tips and tricks . To submit your request for app compatibility with App Assure,
complete the Microsoft FastTrack Registration form or send an email to
achelp@microsoft.com.

Changes to Yeoman templates and web


dependencies
The Yeoman Generator for Office Add-ins relies on a number of libraries from Microsoft
and others. These libraries are updated independently of any Microsoft 365 activity. Any
projects created with the generator should be kept up to date as you develop, publish,
and maintain your add-in. The following tools can help ensure your project is using
secure versions of any dependent libraries.

npm audit
Dependabot and other GitHub security features

This guidance also applies to copies of samples taken from the Office Add-in code
samples and other sources.
office.js NPM package
The office-js NPM package is a copy of what is hosted on the Office.js content
delivery network (CDN). It's intended for scenarios where direct access to the CDN isn't
possible. The NPM package isn't intended to provide versioned references to office.js.
We strongly recommend always using the CDN to ensure you're using the latest version
of the Office JavaScript APIs.

Current best practices


While we strive to maintain backwards compatibility, the patterns and practices we
recommend continually evolve. Our documentation strives to present the current best
practices. To stay informed of new features that may improve your existing functionality,
join our monthly Office Add-ins Community Call.

Deploy updates
When you add features or fix bugs in your add-in, you'll need to deploy the updates. If
your add-in is deployed by one or more admins to their organizations, some manifest
changes will require the admin to consent to the updates. Users will be blocked from
the add-in until consent is granted. The following manifest changes will require the
admin to consent again.

Changes to requested permissions.


Additional scopes.
Additional Outlook events.

Community engagement
As updates are proposed for the Microsoft 365 Developer Platform, we will be listening
for feedback. Please report concerns, potential consequences, or other questions to the
channels listed in Office Add-ins additional resources.
Excel add-ins documentation
With Excel add-ins, you can use familiar web technologies such as HTML, CSS, and
JavaScript to build a solution that can run in Excel across multiple platforms, including
on Windows, Mac, iPad, and in a web browser. Learn how to build, test, debug, and
publish Excel add-ins.

About Excel add-ins

e OVERVIEW

What are Excel add-ins?

f QUICKSTART

Build your first Excel add-in

Explore APIs with Script Lab

c HOW-TO GUIDE

Use the Excel JavaScript API to interact with objects and workbook content

Use data types in an Excel add-in (preview)

Add custom functions to Excel

Test and debug an Excel add-in

Deploy and publish an Excel add-in

Key Office Add-ins concepts

e OVERVIEW

Office Add-ins platform overview

p CONCEPT

Core concepts for Office Add-ins

Design Office Add-ins


Develop Office Add-ins

Resources

i REFERENCE

Ask questions

Request features

Report issues

Office Add-ins additional resources


Excel add-ins overview
Article • 04/11/2023

An Excel add-in allows you to extend Excel application functionality across multiple
platforms including Windows, Mac, iPad, and in a browser. Use Excel add-ins within a
workbook to:

Interact with Excel objects, read and write Excel data.


Extend functionality using web based task pane or content pane
Add custom ribbon buttons or contextual menu items
Add custom functions
Provide richer interaction using dialog window

The Office Add-ins platform provides the framework and Office.js JavaScript APIs that
enable you to create and run Excel add-ins. By using the Office Add-ins platform to
create your Excel add-in, you'll get the following benefits.

Cross-platform support: Excel add-ins run in Office on the web, Windows, Mac,
and iPad.
Centralized deployment: Admins can quickly and easily deploy Excel add-ins to
users throughout an organization.
Use of standard web technology: Create your Excel add-in using familiar web
technologies such as HTML, CSS, and JavaScript.
Distribution via AppSource: Share your Excel add-in with a broad audience by
publishing it to AppSource .

7 Note

Excel add-ins are different from COM and VSTO add-ins, which are earlier Office
integration solutions that run only in Office on Windows. Unlike COM add-ins, Excel
add-ins do not require you to install any code on a user's device, or within Excel.

Components of an Excel add-in


An Excel add-in includes two basic components: a web application and a configuration
file, called a manifest file.

The web application uses the Office JavaScript API to interact with objects in Excel, and
can also facilitate interaction with online resources. For example, an add-in can perform
any of the following tasks.
Create, read, update, and delete data in the workbook (worksheets, ranges, tables,
charts, named items, and more).
Perform user authorization with an online service by using the standard OAuth 2.0
flow.
Issue API requests to Microsoft Graph or any other API.

The web application can be hosted on any web server, and can be built using client-side
frameworks (such as Angular, React, jQuery) or server-side technologies (such as
ASP.NET, Node.js, PHP).

The manifest is a configuration file that defines how the add-in integrates with Office
clients by specifying settings and capabilities such as:

The URL of the add-in's web application.


The add-in's display name, description, ID, version, and default locale.
How the add-in integrates with Excel, including any custom UI that the add-in
creates (ribbon buttons, context menus, and so on).
Permissions that the add-in requires, such as reading and writing to the document.

To enable end users to install and use an Excel add-in, you must publish its manifest
either to AppSource or to an add-ins catalog. For details about publishing to
AppSource, see Make your solutions available in AppSource and within Office.

Capabilities of an Excel add-in


In addition to interacting with the content in the workbook, Excel add-ins can add
custom ribbon buttons or menu commands, insert task panes, add custom functions,
open dialog boxes, and even embed rich, web-based objects such as charts or
interactive visualizations within a worksheet.

Add-in commands
Add-in commands are UI elements that extend the Excel UI and start actions in your
add-in. You can use add-in commands to add a button on the ribbon or an item to a
context menu in Excel. When users select an add-in command, they initiate actions such
as running JavaScript code, or showing a page of the add-in in a task pane.
For more information about command capabilities, supported platforms, and best
practices for developing add-in commands, see Add-in commands for Excel, Word, and
PowerPoint.

Task panes
Task panes are interface surfaces that typically appear on the right side of the window
within Excel. Task panes give users access to interface controls that run code to modify
the Excel document or display data from a data source.

For more information about task panes, see Task panes in Office Add-ins. For a sample
that implements a task pane in Excel, see Excel Add-in JS WoodGrove Expense Trends .
Custom functions
Custom functions enable developers to add new functions to Excel by defining those
functions in JavaScript as part of an add-in. Users within Excel can access custom
functions just as they would any native function in Excel, such as SUM() .

For more information about custom functions, see Create custom functions in Excel.

Dialog boxes
Dialog boxes are surfaces that float above the active Excel application window. You can
use dialog boxes for tasks such as displaying sign-in pages that can't be opened directly
in a task pane, requesting that the user confirm an action, or hosting videos that might
be too small if confined to a task pane. To open dialog boxes in your Excel add-in, use
the Dialog API.
For more information about dialog boxes and the Dialog API, see Use the Dialog API in
your Office Add-ins.

Content add-ins
Content add-ins are surfaces that you can embed directly into Excel documents. You can
use content add-ins to embed rich, web-based objects such as charts, data
visualizations, or media into a worksheet or to give users access to interface controls
that run code to modify the Excel document or display data from a data source. Use
content add-ins when you want to embed functionality directly into the document.
For more information about content add-ins, see Content Office Add-ins. For a sample
that implements a content add-in in Excel, see Excel Content Add-in Humongous
Insurance in GitHub.

JavaScript APIs to interact with workbook


content
An Excel add-in interacts with objects in Excel by using the Office JavaScript API, which
includes two JavaScript object models:

Excel JavaScript API: Introduced with Office 2016, the Excel JavaScript API provides
strongly-typed Excel objects that you can use to access worksheets, ranges, tables,
charts, and more.

Common API: Introduced with Office 2013, the Common API enables you to access
features such as UI, dialogs, and client settings that are common across multiple
types of Office applications. The limited functionality for Excel interaction in the
Common API has been replaced by the Excel JavaScript API.

Next steps
Get started by creating your first Excel add-in. Then, learn about the core concepts of
building Excel add-ins.

See also
Office Add-ins platform overview
Learn about Microsoft 365 Developer Program
Developing Office Add-ins
Excel JavaScript object model in Office Add-ins
Excel JavaScript API reference
Build an Excel task pane add-in
Article • 03/28/2023

In this article, you'll walk through the process of building an Excel task pane add-in.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Create the add-in project
Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Excel

After you complete the wizard, the generator creates the project and installs
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a basic task pane add-in. If you'd like to explore the components of your
add-in project, open the project in your code editor and review the files listed
below. When you're ready to try out your add-in, proceed to the next section.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in. To learn more about the manifest.xml file, see
Office Add-ins XML manifest.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and the Office client
application.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your
add-in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're
developing. If you're prompted to install a certificate after you run one of
the following commands, accept the prompt to install the certificate that
the Yeoman generator provides. You may also have to run your command
prompt or terminal as an administrator for the changes to be made.

 Tip
If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server

To test your add-in in Excel, run the following command in the root
directory of your project. This starts the local web server and opens Excel
with your add-in loaded.

command line

npm start

To test your add-in in Excel on a browser, run the following command in


the root directory of your project. When you run this command, the local
web server starts. Replace "{url}" with the URL of an Excel document on
your OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single


quotation marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798Bpuhwl

uxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP
If your add-in doesn't sideload in the document, manually sideload it by
following the instructions in Manually sideload add-ins to Office on the
web.

3. In Excel, choose the Home tab, and then choose the Show Taskpane button
on the ribbon to open the add-in task pane.

4. Select any range of cells in the worksheet.

5. At the bottom of the task pane, choose the Run link to set the color of the
selected range to yellow.

Next steps
Congratulations, you've successfully created an Excel task pane add-in! Next, learn
more about the capabilities of an Excel add-in and build a more complex add-in by
following along with the Excel add-in tutorial.

Code samples
Excel "Hello world" add-in : Learn how to build a simple Office Add-in with only a
manifest, HTML web page, and a logo.

See also
Office Add-ins platform overview
Develop Office Add-ins
Excel JavaScript object model in Office Add-ins
Excel add-in code samples
Excel JavaScript API reference
Using Visual Studio Code to publish
Use React to build an Excel task pane
add-in
Article • 03/28/2023

In this article, you'll walk through the process of building an Excel task pane add-in
using React and the Excel JavaScript API.

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Create the add-in project


Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note
When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project using React framework
Choose a script type: TypeScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Excel

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample code
for a basic task pane add-in. If you'd like to explore the key components of your add-in
project, open the project in your code editor and review the files listed below. When
you're ready to try out your add-in, proceed to the next section.
The manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in. To learn more about the manifest.xml file, see Office
Add-ins XML manifest.
The ./src/taskpane/taskpane.html file defines the HTML framework of the task
pane, and the files within the ./src/taskpane/components folder define the various
parts of the task pane UI.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in
the task pane.
The ./src/taskpane/components/App.tsx file contains the Office JavaScript API
code that facilitates interaction between the task pane and Excel.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your add-
in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

 Tip

If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server


To test your add-in in Excel, run the following command in the root directory
of your project. This starts the local web server and opens Excel with your
add-in loaded.

command line

npm start

To test your add-in in Excel on a browser, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of an Excel document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM
fF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

3. In Excel, choose the Home tab, and then choose the Show Taskpane button on the
ribbon to open the add-in task pane.
4. Select any range of cells in the worksheet.

5. At the bottom of the task pane, choose the Run link to set the color of the selected
range to yellow.

Next steps
Congratulations, you've successfully created an Excel task pane add-in using React!
Next, learn more about the capabilities of an Excel add-in and build a more complex
add-in by following along with the Excel add-in tutorial.

Excel add-in tutorial


See also
Excel add-in tutorial
Excel JavaScript object model in Office Add-ins
Excel add-in code samples
Excel JavaScript API reference
Using Visual Studio Code to publish
Use Vue to build an Excel task pane
add-in
Article • 05/02/2023

In this article, you'll walk through the process of building an Excel task pane add-in
using Vue and the Excel JavaScript API.

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Install the Vue CLI globally. From the terminal, run the following command.

command line

npm install -g @vue/cli

Generate a new Vue app


To generate a new Vue app, use the Vue CLI.
command line

vue create my-add-in

Then, select the Default preset for "Vue 3" (if you prefer, choose "Vue 2").

Generate the manifest file


Each add-in requires a manifest file to define its settings and capabilities.

1. Navigate to your app folder.

command line

cd my-add-in

2. Use the Yeoman generator to generate the manifest file for your add-in.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as appropriate. If you
choose Exit in response to the second prompt, you'll need to run the yo
office command again when you're ready to create your add-in project.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in project containing the manifest only
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Excel
After completion, the wizard creates a My Office Add-in folder containing a
manifest.xml file. You'll use the manifest to sideload and test your add-in.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Secure the app


While not strictly required in all add-in scenarios, using an HTTPS endpoint for your
add-in is strongly recommended. Add-ins that are not SSL-secured (HTTPS) generate
unsecure content warnings and errors during use. If you plan to run your add-in in
Office on the web or publish your add-in to AppSource, it must be SSL-secured. If your
add-in accesses external data and services, it should be SSL-secured to protect data in
transit. Self-signed certificates can be used for development and testing, so long as the
certificate is trusted on the local machine.

1. Enable HTTPS for your app. In the root folder of the Vue project, create a
vue.config.js file with the following contents.

JavaScript

const fs = require("fs");
const path = require("path");
const homedir = require('os').homedir()
module.exports = {
devServer: {
port: 3000,
https: {
key: fs.readFileSync(path.resolve(`${homedir}/.office-addin-dev-
certs/localhost.key`)),
cert: fs.readFileSync(path.resolve(`${homedir}/.office-addin-dev-
certs/localhost.crt`)),
ca: fs.readFileSync(path.resolve(`${homedir}/.office-addin-dev-
certs/ca.crt`)),
}
}
}

2. Install the add-in's certificates.

command line

npx office-addin-dev-certs install

Explore the project


The add-in project that you've created with the Yeoman generator contains sample code
for a basic task pane add-in. If you'd like to explore the key components of your add-in
project, open the project in your code editor and review the files listed below. When
you're ready to try out your add-in, proceed to the next section.

The manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in. To learn more about the manifest.xml file, see Office
Add-ins XML manifest.
The ./src/App.vue file contains the HTML markup for the task pane, the CSS that's
applied to the content in the task pane, and the Office JavaScript API code that
facilitates interaction between the task pane and Excel.

Update the app


1. Open the ./public/index.html file and add the following <script> tag immediately
before the </head> tag.

HTML

<script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js">
</script>
2. Open manifest.xml and find the <bt:Urls> tags inside the <Resources> tag.
Locate the <bt:Url> tag with the ID Taskpane.Url and update its DefaultValue
attribute. The new DefaultValue is https://localhost:3000/index.html . The entire
updated tag should match the following line.

HTML

<bt:Url id="Taskpane.Url"
DefaultValue="https://localhost:3000/index.html" />

3. Open ./src/main.js and replace the contents with the following code.

JavaScript

import { createApp } from 'vue'


import App from './App.vue'

window.Office.onReady(() => {
createApp(App).mount('#app');
});

4. Open ./src/App.vue and replace the file contents with the following code.

HTML

<template>
<div id="app">
<div class="content">
<div class="content-header">
<div class="padding">
<h1>Welcome</h1>
</div>
</div>
<div class="content-main">
<div class="padding">
<p>
Choose the button below to set the color of the selected
range to
green.
</p>
<br />
<h3>Try it out</h3>
<button @click="onSetColor">Set color</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
onSetColor() {
window.Excel.run(async context => {
const range = context.workbook.getSelectedRange();
range.format.fill.color = 'green';
await context.sync();
});
}
}
};
</script>

<style>
.content-header {
background: #2a8dd4;
color: #fff;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 80px;
overflow: hidden;
}

.content-main {
background: #fff;
position: fixed;
top: 80px;
left: 0;
right: 0;
bottom: 0;
overflow: auto;
}

.padding {
padding: 15px;
}
</style>

Start the dev server


1. Start the dev server.

command line

npm run serve


2. In a web browser, navigate to https://localhost:3000 (notice the https ). If the
page on https://localhost:3000 is blank and without any certificate errors, it
means that it's working. The Vue App is mounted after Office is initialized, so it
only shows things inside of an Excel environment.

Try it out
1. Run your add-in and sideload the add-in within Excel. Follow the instructions for
the platform you'll be using:

Windows: Sideload Office Add-ins on Windows


Web browser: Sideload Office Add-ins to Office on the web
iPad: Sideload Office Add-ins on iPad
Mac: Sideload Office Add-ins on Mac

2. Open the add-in task pane in Excel. On the Home tab, choose the Show Taskpane
button.

3. Select any range of cells in the worksheet.

4. Set the color of the selected range to green. In your add-in's task pane, choose the
Set color button.
Next steps
Congratulations, you've successfully created an Excel task pane add-in using Vue! Next,
learn more about the capabilities of an Excel add-in and build a more complex add-in
by following along with the Excel add-in tutorial.

Excel add-in tutorial

See also
Office Add-ins platform overview
Develop Office Add-ins
Excel JavaScript object model in Office Add-ins
Excel add-in code samples
Excel JavaScript API reference
Using Visual Studio Code to publish
Tutorial: Create an Excel task pane add-
in
Article • 03/28/2023

In this tutorial, you'll create an Excel task pane add-in that:

" Creates a table
" Filters and sorts a table
" Creates a chart
" Freezes a table header
" Protects a worksheet
" Opens a dialog

 Tip

If you've already completed the Build an Excel task pane add-in quick start using
the Yeoman generator, and want to use that project as a starting point for this
tutorial, go directly to the Create a table section to start this tutorial.

If you want a completed version of this tutorial, head over to the Office Add-ins
samples repo on GitHub .

Prerequisites
Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Office connected to a Microsoft 365 subscription (including Office on the web).


7 Note

If you don't already have Office, you can join the Microsoft 365 developer
program to get a free, 90-day renewable Microsoft 365 subscription to use
during development.

Create your add-in project


Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: JavaScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Excel
After you complete the wizard, the generator creates the project and installs supporting
Node components. You may need to manually run npm install in the root folder of
your project if something fails during the initial setup.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Create a table
In this step of the tutorial, you'll programmatically test that your add-in supports the
user's current version of Excel, add a table to a worksheet, populate the table with data,
and format it.

Code the add-in


1. Open the project in your code editor.

2. Open the file ./src/taskpane/taskpane.html. This file contains the HTML markup
for the task pane.

3. Locate the <main> element and delete all lines that appear after the opening
<main> tag and before the closing </main> tag.

4. Add the following markup immediately after the opening <main> tag.

HTML

<button class="ms-Button" id="create-table">Create Table</button><br/>


<br/>

5. Open the file ./src/taskpane/taskpane.js. This file contains the Office JavaScript API
code that facilitates interaction between the task pane and the Office client
application.

6. Remove all references to the run button and the run() function by doing the
following:

Locate and delete the line document.getElementById("run").onclick = run; .


Locate and delete the entire run() function.

7. Within the Office.onReady function call, locate the line if (info.host ===
Office.HostType.Excel) { and add the following code immediately after that line.

Note:

This code adds an event handler for the create-table button.


The createTable function is wrapped in a call to tryCatch (both functions will
be added next step). This allows any errors generated by the Office JavaScript
layer to be handled separate from your service code.

JavaScript

// Assign event handlers and other initialization logic.


document.getElementById("create-table").onclick = () =>
tryCatch(createTable);

8. Add the following functions to the end of the file. Note:

Your Excel.js business logic will be added to the function that is passed to
Excel.run . This logic does not execute immediately. Instead, it is added to a
queue of pending commands.

The context.sync method sends all queued commands to Excel for


execution.

The tryCatch function will be used by all the functions interacting with the
workbook from the task pane. Catching Office JavaScript errors in this fashion
is a convenient way to generically handle any uncaught errors.

7 Note

The following code uses ES6 JavaScript, which is not compatible with older
versions of Office that use the Trident (Internet Explorer 11) browser engine.
For information on how to support those platforms in production, see
Support older Microsoft webviews and Office versions. Join the Microsoft
365 developer program to get a free, 90-day renewable Microsoft 365
subscription, with the latest Office applications, to use during development.

JavaScript

async function createTable() {


await Excel.run(async (context) => {
// TODO1: Queue table creation logic here.

// TODO2: Queue commands to populate the table with data.

// TODO3: Queue commands to format the table.

await context.sync();
});
}

/** Default helper for invoking an action and handling errors. */


async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user
through your add-in's UI.
console.error(error);
}
}

9. Within the createTable() function, replace TODO1 with the following code. Note:

The code creates a table by using the add method of a worksheet's table
collection, which always exists even if it is empty. This is the standard way
that Excel.js objects are created. There are no class constructor APIs, and you
never use a new operator to create an Excel object. Instead, you add to a
parent collection object.

The first parameter of the add method is the range of only the top row of the
table, not the entire range the table will ultimately use. This is because when
the add-in populates the data rows (in the next step), it will add new rows to
the table instead of writing values to the cells of existing rows. This is a
common pattern, because the number of rows a table will have is often
unknown when the table is created.

Table names must be unique across the entire workbook, not just the
worksheet.

JavaScript

const currentWorksheet =
context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.add("A1:D1", true
/*hasHeaders*/);
expensesTable.name = "ExpensesTable";
10. Within the createTable() function, replace TODO2 with the following code. Note:

The cell values of a range are set with an array of arrays.

New rows are created in a table by calling the add method of the table's row
collection. You can add multiple rows in a single call of add by including
multiple cell value arrays in the parent array that is passed as the second
parameter.

JavaScript

expensesTable.getHeaderRowRange().values =
[["Date", "Merchant", "Category", "Amount"]];

expensesTable.rows.add(null /*add at the end*/, [


["1/1/2017", "The Phone Company", "Communications", "120"],
["1/2/2017", "Northwind Electric Cars", "Transportation",
"142.33"],
["1/5/2017", "Best For You Organics Company", "Groceries", "27.9"],
["1/10/2017", "Coho Vineyard", "Restaurant", "33"],
["1/11/2017", "Bellows College", "Education", "350.1"],
["1/15/2017", "Trey Research", "Other", "135"],
["1/15/2017", "Best For You Organics Company", "Groceries",
"97.88"]
]);

11. Within the createTable() function, replace TODO3 with the following code. Note:

The code gets a reference to the Amount column by passing its zero-based
index to the getItemAt method of the table's column collection.

7 Note

Excel.js collection objects, such as TableCollection ,


WorksheetCollection , and TableColumnCollection have an items

property that is an array of the child object types, such as Table or


Worksheet or TableColumn ; but a *Collection object is not itself an

array.

The code then formats the range of the Amount column as Euros to the
second decimal. Learn more about the Excel number format syntax in the
article Number format codes /

Finally, it ensures that the width of the columns and height of the rows is big
enough to fit the longest (or tallest) data item. Notice that the code must get
Range objects to format. TableColumn and TableRow objects do not have

format properties.

JavaScript

expensesTable.columns.getItemAt(3).getRange().numberFormat =
[['\u20AC#,##0.00']];
expensesTable.getRange().format.autofitColumns();
expensesTable.getRange().format.autofitRows();

12. Verify that you've saved all of the changes you've made to the project.

Test the add-in


1. Complete the following steps to start the local web server and sideload your add-
in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

 Tip

If you're testing your add-in on Mac, run the following command in the root
directory of your project before proceeding. When you run this command, the
local web server starts.

command line

npm run dev-server

To test your add-in in Excel, run the following command in the root directory
of your project. This starts the local web server (if it's not already running)
and opens Excel with your add-in loaded.

command line
npm start

To test your add-in in Excel on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of an Excel document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. In Excel, choose the Home tab, and then choose the Show Taskpane button on the
ribbon to open the add-in task pane.
3. In the task pane, choose the Create Table button.

Filter and sort a table


In this step of the tutorial, you'll filter and sort the table that you created previously.

Filter the table


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the create-table button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="filter-table">Filter Table</button><br/>


<br/>

3. Open the file ./src/taskpane/taskpane.js.


4. Within the Office.onReady function call, locate the line that assigns a click handler
to the create-table button, and add the following code after that line.

JavaScript

document.getElementById("filter-table").onclick = () =>
tryCatch(filterTable);

5. Add the following function to the end of the file.

JavaScript

async function filterTable() {


await Excel.run(async (context) => {

// TODO1: Queue commands to filter out all expense categories


except
// Groceries and Education.

await context.sync();
});
}

6. Within the filterTable() function, replace TODO1 with the following code. Note:

The code first gets a reference to the column that needs filtering by passing
the column name to the getItem method, instead of passing its index to the
getItemAt method as the createTable method does. Since users can move

table columns, the column at a given index might change after the table is
created. Hence, it is safer to use the column name to get a reference to the
column. We used getItemAt safely in the preceding tutorial, because we used
it in the very same method that creates the table, so there is no chance that a
user has moved the column.

The applyValuesFilter method is one of several filtering methods on the


Filter object.

JavaScript

const currentWorksheet =
context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.getItem('ExpensesTable');
const categoryFilter =
expensesTable.columns.getItem('Category').filter;
categoryFilter.applyValuesFilter(['Education', 'Groceries']);
Sort the table
1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the filter-table button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="sort-table">Sort Table</button><br/><br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the filter-table button, and add the following code after that line.

JavaScript

document.getElementById("sort-table").onclick = () =>
tryCatch(sortTable);

5. Add the following function to the end of the file.

JavaScript

async function sortTable() {


await Excel.run(async (context) => {

// TODO1: Queue commands to sort the table by Merchant name.

await context.sync();
});
}

6. Within the sortTable() function, replace TODO1 with the following code. Note:

The code creates an array of SortField objects, which has just one member
since the add-in only sorts on the Merchant column.

The key property of a SortField object is the zero-based index of the


column used for sorting. The rows of the table are sorted based on the values
in the referenced column.

The sort member of a Table is a TableSort object, not a method. The


SortField s are passed to the TableSort object's apply method.
JavaScript

const currentWorksheet =
context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.getItem('ExpensesTable');
const sortFields = [
{
key: 1, // Merchant column
ascending: false,
}
];

expensesTable.sort.apply(sortFields);

7. Verify that you've saved all of the changes you've made to the project.

Test the add-in


1. If the local web server is already running and your add-in is already loaded in Excel,
proceed to step 2. Otherwise, start the local web server and sideload your add-in:

To test your add-in in Excel, run the following command in the root directory
of your project. This starts the local web server (if it's not already running)
and opens Excel with your add-in loaded.

command line

npm start

To test your add-in in Excel on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of an Excel document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document
https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp

npm run start:web -- --document https://contoso-my.sharepoint-


df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. If the add-in task pane isn't already open in Excel, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.

3. If the table you added previously in this tutorial is not present in the open
worksheet, choose the Create Table button in the task pane.

4. Choose the Filter Table button and the Sort Table button, in either order.

Create a chart
In this step of the tutorial, you'll create a chart using data from the table that you
created previously, and then format the chart.

Chart a chart using table data


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the sort-table button, and add the following
markup after that line.
HTML

<button class="ms-Button" id="create-chart">Create Chart</button><br/>


<br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the sort-table button, and add the following code after that line.

JavaScript

document.getElementById("create-chart").onclick = () =>
tryCatch(createChart);

5. Add the following function to the end of the file.

JavaScript

async function createChart() {


await Excel.run(async (context) => {

// TODO1: Queue commands to get the range of data to be


charted.

// TODO2: Queue command to create the chart and define its


type.

// TODO3: Queue commands to position and format the chart.

await context.sync();
});
}

6. Within the createChart() function, replace TODO1 with the following code. Note
that in order to exclude the header row, the code uses the Table.getDataBodyRange
method to get the range of data you want to chart instead of the getRange
method.

JavaScript

const currentWorksheet =
context.workbook.worksheets.getActiveWorksheet();
const expensesTable = currentWorksheet.tables.getItem('ExpensesTable');
const dataRange = expensesTable.getDataBodyRange();
7. Within the createChart() function, replace TODO2 with the following code. Note
the following parameters.

The first parameter to the add method specifies the type of chart. There are
several dozen types.

The second parameter specifies the range of data to include in the chart.

The third parameter determines whether a series of data points from the
table should be charted row-wise or column-wise. The option auto tells Excel
to decide the best method.

JavaScript

const chart = currentWorksheet.charts.add('ColumnClustered', dataRange,


'Auto');

8. Within the createChart() function, replace TODO3 with the following code. Most of
this code is self-explanatory. Note:

The parameters to the setPosition method specify the upper left and lower
right cells of the worksheet area that should contain the chart. Excel can
adjust things like line width to make the chart look good in the space it has
been given.

A "series" is a set of data points from a column of the table. Since there is
only one non-string column in the table, Excel infers that the column is the
only column of data points to chart. It interprets the other columns as chart
labels. So there will be just one series in the chart and it will have index 0. This
is the one to label with "Value in €".

JavaScript

chart.setPosition("A15", "F30");
chart.title.text = "Expenses";
chart.legend.position = "Right";
chart.legend.format.fill.setSolidColor("white");
chart.dataLabels.format.font.size = 15;
chart.dataLabels.format.font.color = "black";
chart.series.getItemAt(0).name = 'Value in \u20AC';

9. Verify that you've saved all of the changes you've made to the project.

Test the add-in


1. If the local web server is already running and your add-in is already loaded in Excel,
proceed to step 2. Otherwise, start the local web server and sideload your add-in:

To test your add-in in Excel, run the following command in the root directory
of your project. This starts the local web server (if it's not already running)
and opens Excel with your add-in loaded.

command line

npm start

To test your add-in in Excel on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of an Excel document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. If the add-in task pane isn't already open in Excel, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.
3. If the table you added previously in this tutorial is not present in the open
worksheet, choose the Create Table button, and then the Filter Table button and
the Sort Table button, in either order.

4. Choose the Create Chart button. A chart is created and only the data from the
rows that have been filtered are included. The labels on the data points across the
bottom are in the sort order of the chart; that is, merchant names in reverse
alphabetical order.

Freeze a table header


When a table is long enough that a user must scroll to see some rows, the header row
can scroll out of sight. In this step of the tutorial, you'll freeze the header row of the
table that you created previously, so that it remains visible even as the user scrolls down
the worksheet.

Freeze the table's header row


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the create-chart button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="freeze-header">Freeze Header</button>


<br/><br/>
3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the create-chart button, and add the following code after that line.

JavaScript

document.getElementById("freeze-header").onclick = () =>
tryCatch(freezeHeader);

5. Add the following function to the end of the file.

JavaScript

async function freezeHeader() {


await Excel.run(async (context) => {

// TODO1: Queue commands to keep the header visible when the


user scrolls.

await context.sync();
});
}

6. Within the freezeHeader() function, replace TODO1 with the following code. Note:

The Worksheet.freezePanes collection is a set of panes in the worksheet that


are pinned, or frozen, in place when the worksheet is scrolled.

The freezeRows method takes as a parameter the number of rows, from the
top, that are to be pinned in place. We pass 1 to pin the first row in place.

JavaScript

const currentWorksheet =
context.workbook.worksheets.getActiveWorksheet();
currentWorksheet.freezePanes.freezeRows(1);

7. Verify that you've saved all of the changes you've made to the project.

Test the add-in


1. If the local web server is already running and your add-in is already loaded in Excel,
proceed to step 2. Otherwise, start the local web server and sideload your add-in:
To test your add-in in Excel, run the following command in the root directory
of your project. This starts the local web server (if it's not already running)
and opens Excel with your add-in loaded.

command line

npm start

To test your add-in in Excel on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of an Excel document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM
fF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. If the add-in task pane isn't already open in Excel, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.

3. If the table you added previously in this tutorial is present in the worksheet, delete
it.
4. In the task pane, choose the Create Table button.

5. In the task pane, choose the Freeze Header button.

6. Scroll down the worksheet far enough to see that the table header remains visible
at the top even when the higher rows scroll out of sight.

Protect a worksheet
In this step of the tutorial, you'll add a button to the ribbon that toggles worksheet
protection on and off.

Configure the manifest to add a second ribbon button


1. Open the manifest file ./manifest.xml.

2. Locate the <Control> element. This element defines the Show Taskpane button on
the Home ribbon you have been using to launch the add-in. We're going to add a
second button to the same group on the Home ribbon. In between the closing
</Control> tag and the closing </Group> tag, add the following markup.

XML

<Control xsi:type="Button" id="<!--TODO1: Unique (in manifest) name for


button -->">
<Label resid="<!--TODO2: Button label -->" />
<Supertip>
<Title resid="<!-- TODO3: Button tool tip title -->" />
<Description resid="<!-- TODO4: Button tool tip description --
>" />
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="<!-- TODO5: Specify the type of action-->">
<!-- TODO6: Identify the function.-->
</Action>
</Control>

3. Within the XML you just added to the manifest file, replace TODO1 with a string that
gives the button an ID that is unique within this manifest file. Since our button is
going to toggle protection of the worksheet on and off, use "ToggleProtection".
When you are done, the opening tag for the Control element should look like this:

XML

<Control xsi:type="Button" id="ToggleProtection">

4. The next three TODO s set resource IDs, or resid s. A resource is a string (with a
maximum length of 32 characters), and you'll create these three strings in a later
step. For now, you need to give IDs to the resources. The button label should read
"Toggle Protection", but the ID of this string should be "ProtectionButtonLabel", so
the Label element should look like this:

XML

<Label resid="ProtectionButtonLabel" />

5. The SuperTip element defines the tool tip for the button. The tool tip title should
be the same as the button label, so we use the very same resource ID:
"ProtectionButtonLabel". The tool tip description will be "Click to turn protection of
the worksheet on and off". But the resid should be "ProtectionButtonToolTip". So,
when you are done, the SuperTip element should look like this:

XML

<Supertip>
<Title resid="ProtectionButtonLabel" />
<Description resid="ProtectionButtonToolTip" />
</Supertip>

7 Note

In a production add-in, you would not want to use the same icon for two
different buttons; but to simplify this tutorial, we'll do that. So the Icon
markup in our new Control is just a copy of the Icon element from the
existing Control .
6. The Action element inside the original Control element has its type set to
ShowTaskpane , but our new button isn't going to open a task pane; it's going to run

a custom function that you create in a later step. So, replace TODO5 with
ExecuteFunction , which is the action type for buttons that trigger custom

functions. The opening tag for the Action element should look like this:

XML

<Action xsi:type="ExecuteFunction">

7. The original Action element has child elements that specify a task pane ID and a
URL of the page that should be opened in the task pane. But an Action element of
the ExecuteFunction type has a single child element that names the function that
the control executes. You'll create that function in a later step, and it will be called
toggleProtection . So, replace TODO6 with the following markup.

XML

<FunctionName>toggleProtection</FunctionName>

The entire Control markup should now look like the following:

XML

<Control xsi:type="Button" id="ToggleProtection">


<Label resid="ProtectionButtonLabel" />
<Supertip>
<Title resid="ProtectionButtonLabel" />
<Description resid="ProtectionButtonToolTip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>toggleProtection</FunctionName>
</Action>
</Control>

8. Scroll down to the Resources section of the manifest.

9. Add the following markup as a child of the bt:ShortStrings element.

XML
<bt:String id="ProtectionButtonLabel" DefaultValue="Toggle Worksheet
Protection" />

10. Add the following markup as a child of the bt:LongStrings element.

XML

<bt:String id="ProtectionButtonToolTip" DefaultValue="Click to protect


or unprotect the current worksheet." />

11. Save the file.

Create the function that protects the sheet


1. Open the file .\commands\commands.js.

2. Add the following function immediately after the action function. Note that we
specify an args parameter to the function and the very last line of the function
calls args.completed . This is a requirement for all add-in commands of type
ExecuteFunction. It signals the Office client application that the function has
finished and the UI can become responsive again.

JavaScript

async function toggleProtection(args) {


try {
await Excel.run(async (context) => {

// TODO1: Queue commands to reverse the protection status


of the current worksheet.

await context.sync();
});
} catch (error) {
// Note: In a production add-in, you'd want to notify the user
through your add-in's UI.
console.error(error);
}

args.completed();
}

3. Add the following line immediately after the function to register it.

JavaScript
Office.actions.associate("toggleProtection", toggleProtection);

4. Within the toggleProtection function, replace TODO1 with the following code. This
code uses the worksheet object's protection property in a standard toggle pattern.
The TODO2 will be explained in the next section.

JavaScript

const sheet = context.workbook.worksheets.getActiveWorksheet();

// TODO2: Queue command to load the sheet's "protection.protected"


property from
// the document and re-synchronize the document and task pane.

if (sheet.protection.protected) {
sheet.protection.unprotect();
} else {
sheet.protection.protect();
}

Add code to fetch document properties into the task


pane's script objects
In each function that you've created in this tutorial until now, you queued commands to
write to the Office document. Each function ended with a call to the context.sync()
method, which sends the queued commands to the document to be executed. However,
the code you added in the last step calls the sheet.protection.protected property . This
is a significant difference from the earlier functions you wrote, because the sheet object
is only a proxy object that exists in your task pane's script. The proxy object doesn't
know the actual protection state of the document, so its protection.protected property
can't have a real value. To avoid an exception error, you must first fetch the protection
status from the document and use it set the value of sheet.protection.protected . This
fetching process has three steps.

1. Queue a command to load (that is; fetch) the properties that your code needs to
read.

2. Call the context object's sync method to send the queued command to the
document for execution and return the requested information.

3. Because the sync method is asynchronous, ensure that it has completed before
your code calls the properties that were fetched.
These steps must be completed whenever your code needs to read information from the
Office document.

1. Within the toggleProtection function, replace TODO2 with the following code.
Note:

Every Excel object has a load method. You specify the properties of the
object that you want to read in the parameter as a string of comma-delimited
names. In this case, the property you need to read is a subproperty of the
protection property. You reference the subproperty almost exactly as you

would anywhere else in your code, with the exception that you use a forward
slash ('/') character instead of a "." character.

To ensure that the toggle logic, which reads sheet.protection.protected ,


doesn't run until after the sync is complete and the
sheet.protection.protected has been assigned the correct value that is

fetched from the document, it must come after the await operator ensures
sync has completed.

JavaScript

sheet.load('protection/protected');
await context.sync();

When you are done, the entire function should look like the following:

JavaScript

async function toggleProtection(args) {


try {
await Excel.run(async (context) => {
const sheet =
context.workbook.worksheets.getActiveWorksheet();

sheet.load('protection/protected');
await context.sync();

if (sheet.protection.protected) {
sheet.protection.unprotect();
} else {
sheet.protection.protect();
}

await context.sync();
});
} catch (error) {
// Note: In a production add-in, you'd want to notify the user
through your add-in's UI.
console.error(error);
}

args.completed();
}

2. Verify that you've saved all of the changes you've made to the project.

Test the add-in


1. Close all Office applications, including Excel (or close the browser tab if you're
using Excel on the web).

2. Clear the Office cache. This is necessary to completely clear the old version of the
add-in from the client application. Instructions for this process are in the article
Clear the Office cache.

3. If the local web server is already running, stop it by entering the following
command in the command prompt. This should close the node command window.

command line

npm stop

4. Because your manifest file has been updated, you must sideload your add-in again,
using the updated manifest file. Start the local web server and sideload your add-
in.

To test your add-in in Excel, run the following command in the root directory
of your project. This starts the local web server (if it's not already running)
and opens Excel with your add-in loaded.

command line

npm start

To test your add-in in Excel on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of an Excel document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note
If you are developing on a Mac, enclose the {url} in single quotation
marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

5. On the Home tab in Excel, choose the Toggle Worksheet Protection button. Note
that most of the controls on the ribbon are disabled (and visually grayed-out) as
seen in the following screenshot.

6. Select a cell and try to edit its content. Excel displays an error message indicating
that the worksheet is protected.

7. Choose the Toggle Worksheet Protection button again, and the controls are
reenabled, and you can change cell values again.

Open a dialog
In this final step of the tutorial, you'll open a dialog in your add-in, pass a message from
the dialog process to the task pane process, and close the dialog. Office Add-in dialogs
are nonmodal: a user can continue to interact with both the document in the Office
application and with the host page in the task pane.
Create the dialog page
1. In the ./src folder that's located at the root of the project, create a new folder
named dialogs.

2. In the ./src/dialogs folder, create new file named popup.html.

3. Add the following markup to popup.html. Note:

The page has an <input> field where the user will enter their name, and a
button that will send this name to the task pane where it will display.

The markup loads a script named popup.js that you will create in a later step.

It also loads the Office.js library because it will be used in popup.js.

HTML

<!DOCTYPE html>
<html>
<head lang="en">
<title>Dialog for My Office Add-in</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-
scale=1">

<!-- For more information on Fluent UI, visit


https://developer.microsoft.com/fluentui. -->
<link rel="stylesheet"
href="https://static2.sharepointonline.com/files/fabric/office-ui-
fabric-core/9.6.1/css/fabric.min.css"/>

<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js">
</script>
<script type="text/javascript" src="popup.js"></script>

</head>
<body style="display:flex;flex-direction:column;align-
items:center;justify-content:center">
<p class="ms-font-xl">ENTER YOUR NAME</p>
<input id="name-box" type="text"/><br/><br/>
<button id="ok-button" class="ms-Button">OK</button>
</body>
</html>

4. In the ./src/dialogs folder, create new file named popup.js.

5. Add the following code to popup.js. Note the following about this code.
Every page that calls APIs in the Office.js library must first ensure that the
library is fully initialized. The best way to do that is to call the
Office.onReady() function. The call of Office.onReady() must run before any

calls to Office.js; hence the assignment is in a script file that is loaded by the
page, as it is in this case.

JavaScript

Office.onReady((info) => {
// TODO1: Assign handler to the OK button.
});

// TODO2: Create the OK button handler.

6. Replace TODO1 with the following code. You'll create the sendStringToParentPage
function in the next step.

JavaScript

document.getElementById("ok-button").onclick = () =>
tryCatch(sendStringToParentPage);

7. Replace TODO2 with the following code. The messageParent method passes its
parameter to the parent page, in this case, the page in the task pane. The
parameter must be a string, which includes anything that can be serialized as a
string, such as XML or JSON, or any type that can be cast to a string. This also adds
the same tryCatch method used in taskpane.js for error handling.

JavaScript

function sendStringToParentPage() {
const userName = document.getElementById("name-box").value;
Office.context.ui.messageParent(userName);
}

/** Default helper for invoking an action and handling errors. */


async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user
through your add-in's UI.
console.error(error);
}
}
7 Note

The popup.html file, and the popup.js file that it loads, run in an entirely separate
browser runtime process from the add-in's task pane. If popup.js was transpiled
into the same bundle.js file as the app.js file, then the add-in would have to load
two copies of the bundle.js file, which defeats the purpose of bundling. Therefore,
this add-in does not transpile the popup.js file at all.

Update webpack config settings


Open the file webpack.config.js in the root directory of the project and complete the
following steps.

1. Locate the entry object within the config object and add a new entry for popup .

JavaScript

popup: "./src/dialogs/popup.js"

After you've done this, the new entry object will look like this.

JavaScript

entry: {
polyfill: "@babel/polyfill",
taskpane: "./src/taskpane/taskpane.js",
commands: "./src/commands/commands.js",
popup: "./src/dialogs/popup.js"
},

2. Locate the plugins array within the config object and add the following object to
the end of that array.

JavaScript

new HtmlWebpackPlugin({
filename: "popup.html",
template: "./src/dialogs/popup.html",
chunks: ["polyfill", "popup"]
})

After you've done this, the new plugins array will look like this.
JavaScript

plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: "taskpane.html",
template: "./src/taskpane/taskpane.html",
chunks: ['polyfill', 'taskpane']
}),
new CopyWebpackPlugin([
{
to: "taskpane.css",
from: "./src/taskpane/taskpane.css"
}
]),
new HtmlWebpackPlugin({
filename: "commands.html",
template: "./src/commands/commands.html",
chunks: ["polyfill", "commands"]
}),
new HtmlWebpackPlugin({
filename: "popup.html",
template: "./src/dialogs/popup.html",
chunks: ["polyfill", "popup"]
})
],

3. If the local web server is running, stop it by entering the following command in the
command prompt. This should close the node command window.

command line

npm stop

4. Run the following command to rebuild the project.

command line

npm run build

Open the dialog from the task pane


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the freeze-header button, and add the following
markup after that line.
HTML

<button class="ms-Button" id="open-dialog">Open Dialog</button><br/>


<br/>

3. The dialog will prompt the user to enter a name and pass the user's name to the
task pane. The task pane will display it in a label. Immediately after the button that
you just added, add the following markup.

HTML

<label id="user-name"></label><br/><br/>

4. Open the file ./src/taskpane/taskpane.js.

5. Within the Office.onReady function call, locate the line that assigns a click handler
to the freeze-header button, and add the following code after that line. You'll
create the openDialog method in a later step.

JavaScript

document.getElementById("open-dialog").onclick = openDialog;

6. Add the following declaration to the end of the file. This variable is used to hold an
object in the parent page's execution context that acts as an intermediator to the
dialog page's execution context.

JavaScript

let dialog = null;

7. Add the following function to the end of the file (after the declaration of dialog ).
The important thing to notice about this code is what is not there: there is no call
of Excel.run . This is because the API to open a dialog is shared among all Office
applications, so it is part of the Office JavaScript Common API, not the Excel-
specific API.

JavaScript

function openDialog() {
// TODO1: Call the Office Common API that opens a dialog.
}
8. Replace TODO1 with the following code. Note:

The displayDialogAsync method opens a dialog in the center of the screen.

The first parameter is the URL of the page to open.

The second parameter passes options. height and width are percentages of
the size of the Office application's window.

JavaScript

Office.context.ui.displayDialogAsync(
'https://localhost:3000/popup.html',
{height: 45, width: 55},

// TODO2: Add callback parameter.


);

Process the message from the dialog and close the dialog
1. Within the openDialog function in the file ./src/taskpane/taskpane.js, replace
TODO2 with the following code. Note:

The callback is executed immediately after the dialog successfully opens and
before the user has taken any action in the dialog.

The result.value is the object that acts as an intermediary between the


execution contexts of the parent and dialog pages.

The processMessage function will be created in a later step. This handler will
process any values that are sent from the dialog page with calls of the
messageParent function.

JavaScript

function (result) {
dialog = result.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived,
processMessage);
}

2. Add the following function after the openDialog function.

JavaScript
function processMessage(arg) {
document.getElementById("user-name").innerHTML = arg.message;
dialog.close();
}

3. Verify that you've saved all of the changes you've made to the project.

Test the add-in


1. If the local web server is already running and your add-in is already loaded in Excel,
proceed to step 2. Otherwise, start the local web server and sideload your add-in:

To test your add-in in Excel, run the following command in the root directory
of your project. This starts the local web server (if it's not already running)
and opens Excel with your add-in loaded.

command line

npm start

To test your add-in in Excel on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of an Excel document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document
https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document


https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-
df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. If the add-in task pane isn't already open in Excel, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.

3. Choose the Open Dialog button in the task pane.

4. While the dialog is open, drag it and resize it. Note that you can interact with the
worksheet and press other buttons on the task pane, but you cannot launch a
second dialog from the same task pane page.

5. In the dialog, enter a name and choose the OK button. The name appears on the
task pane and the dialog closes.

6. Optionally, in the ./src/taskpane/taskpane.js file, comment out the line


dialog.close(); in the processMessage function. Then repeat the steps of this

section. The dialog stays open and you can change the name. You can close it
manually by pressing the X button in the upper right corner.

Next steps
In this tutorial, you've created an Excel task pane add-in that interacts with tables, charts,
worksheets, and dialogs in an Excel workbook. To learn more about building Excel add-
ins, continue to the following article.

Excel add-ins overview

See also
Office Add-ins platform overview
Develop Office Add-ins
Excel JavaScript object model in Office Add-ins
Office Add-ins code samples
Excel JavaScript API overview
Article • 05/02/2023

An Excel add-in interacts with objects in Excel by using the Office JavaScript API, which
includes two JavaScript object models:

Excel JavaScript API: These are the application-specific APIs for Excel. Introduced
with Office 2016, the Excel JavaScript API provides strongly-typed objects that you
can use to access worksheets, ranges, tables, charts, and more.

Common APIs: Introduced with Office 2013, the Common API can be used to
access features such as UI, dialogs, and client settings that are common across
multiple types of Office applications.

This section of the documentation focuses on the Excel JavaScript API, which you'll use
to develop the majority of functionality in add-ins that target Excel on the web or Excel
2016 or later. For information about the Common API, see Common JavaScript API
object model.

Learn object model concepts


See Excel JavaScript object model in Office Add-ins for information about important
object model concepts.

For hands-on experience using the Excel JavaScript API to access objects in Excel,
complete the Excel add-in tutorial.

Learn API capabilities


Each major Excel API feature has an article or set of articles exploring what that feature
can do and the relevant object model.

Charts
Comments
Conditional formatting
Custom functions
Data validation
Data types
Events
PivotTables
Ranges and Cells
RangeAreas (Multiple ranges)
Shapes
Tables
Workbooks and Application-level APIs
Worksheets

For detailed information about the Excel JavaScript API object model, see the Excel
JavaScript API reference documentation.

Try out code samples in Script Lab


Use Script Lab to get started quickly with a collection of built-in samples that show how
to complete tasks with the API. You can run the samples in Script Lab to instantly see the
result in the task pane or worksheet, examine the samples to learn how the API works,
and even use samples to prototype your own add-in.

See also
Excel add-ins documentation
Excel add-ins overview
Excel JavaScript API reference
Office client application and platform availability for Office Add-ins
Using the application-specific API model
excel package
Reference

Classes
Excel.AllowEditRange Represents an AllowEditRange object found in a worksheet. This
object works with worksheet protection properties. When
worksheet protection is enabled, an AllowEditRange object can
be used to allow editing of a specific range, while maintaining
protection on the rest of the worksheet.

Excel.AllowEditRange Represents the set of AllowEditRange objects found in a


Collection worksheet. AllowEditRange objects work with worksheet
protection properties. When worksheet protection is enabled, an
AllowEditRange object can be used to allow editing of a specific
range, while maintaining protection on the rest of the worksheet.

Excel.Application Represents the Excel application that manages the workbook.

Excel.AutoFilter Represents the AutoFilter object. AutoFilter turns the values in


Excel column into specific filters based on the cell contents.

Excel.Binding Represents an Office.js binding that is defined in the workbook.

Excel.BindingCollection Represents the collection of all the binding objects that are part
of the workbook.

Excel.CellValueConditional Represents a cell value conditional format.


Format

Excel.Chart Represents a chart object in a workbook. To learn more about


the chart object model, see Work with charts using the Excel
JavaScript API.

Excel.ChartAreaFormat Encapsulates the format properties for the overall chart area.

Excel.ChartAxes Represents the chart axes.

Excel.ChartAxis Represents a single axis in a chart.

Excel.ChartAxisFormat Encapsulates the format properties for the chart axis.

Excel.ChartAxisTitle Represents the title of a chart axis.

Excel.ChartAxisTitleFormat Represents the chart axis title formatting.

Excel.ChartBinOptions Encapsulates the bin options for histogram charts and pareto
charts.
Excel.ChartBorder Represents the border formatting of a chart element.

Excel.ChartBoxwhiskerOptions Represents the properties of a box and whisker chart.

Excel.ChartCollection A collection of all the chart objects on a worksheet.

Excel.ChartDataLabel Represents the data label of a chart point.

Excel.ChartDataLabelFormat Encapsulates the format properties for the chart data labels.

Excel.ChartDataLabels Represents a collection of all the data labels on a chart point.

Excel.ChartDataTable Represents the data table object of a chart.

Excel.ChartDataTableFormat Represents the format of a chart data table.

Excel.ChartErrorBars This object represents the attributes for a chart's error bars.

Excel.ChartErrorBarsFormat Encapsulates the format properties for chart error bars.

Excel.ChartFill Represents the fill formatting for a chart element.

Excel.ChartFont This object represents the font attributes (such as font name,
font size, and color) for a chart object.

Excel.ChartFormatString Represents the substring in chart related objects that contain


text, like a ChartTitle object or ChartAxisTitle object.

Excel.ChartGridlines Represents major or minor gridlines on a chart axis.

Excel.ChartGridlinesFormat Encapsulates the format properties for chart gridlines.

Excel.ChartLegend Represents the legend in a chart.

Excel.ChartLegendEntry Represents the legend entry in legendEntryCollection .

Excel.ChartLegendEntry Represents a collection of legend entries.


Collection

Excel.ChartLegendFormat Encapsulates the format properties of a chart legend.

Excel.ChartLineFormat Encapsulates the formatting options for line elements.

Excel.ChartMapOptions Encapsulates the properties for a region map chart.

Excel.ChartPivotOptions Encapsulates the options for the pivot chart.

Excel.ChartPlotArea This object represents the attributes for a chart plot area.

Excel.ChartPlotAreaFormat Represents the format properties for a chart plot area.

Excel.ChartPoint Represents a point of a series in a chart.


Excel.ChartPointFormat Represents the formatting object for chart points.

Excel.ChartPointsCollection A collection of all the chart points within a series inside a chart.

Excel.ChartSeries Represents a series in a chart.

Excel.ChartSeriesCollection Represents a collection of chart series.

Excel.ChartSeriesFormat Encapsulates the format properties for the chart series

Excel.ChartTitle Represents a chart title object of a chart.

Excel.ChartTitleFormat Provides access to the formatting options for a chart title.

Excel.ChartTrendline This object represents the attributes for a chart trendline object.

Excel.ChartTrendlineCollection Represents a collection of chart trendlines.

Excel.ChartTrendlineFormat Represents the format properties for the chart trendline.

Excel.ChartTrendlineLabel This object represents the attributes for a chart trendline label
object.

Excel.ChartTrendlineLabel Encapsulates the format properties for the chart trendline label.
Format

Excel.ColorScaleConditional Represents the color scale criteria for conditional formatting.


Format

Excel.Comment Represents a comment in the workbook.

Excel.CommentCollection Represents a collection of comment objects that are part of the


workbook.

Excel.CommentReply Represents a comment reply in the workbook.

Excel.CommentReply Represents a collection of comment reply objects that are part of


Collection the comment.

Excel.ConditionalDataBar Represents a conditional format for the negative side of the data
NegativeFormat bar.

Excel.ConditionalDataBar Represents a conditional format for the positive side of the data
PositiveFormat bar.

Excel.ConditionalFormat An object encapsulating a conditional format's range, format,


rule, and other properties. To learn more about the conditional
formatting object model, read Apply conditional formatting to
Excel ranges.

Excel.ConditionalFormat Represents a collection of all the conditional formats that are


Collection overlap the range.
Excel.ConditionalFormatRule Represents a rule, for all traditional rule/format pairings.

Excel.ConditionalRangeBorder Represents the border of an object.

Excel.ConditionalRangeBorder Represents the border objects that make up range border.


Collection

Excel.ConditionalRangeFill Represents the background of a conditional range object.

Excel.ConditionalRangeFont This object represents the font attributes (font style, color, etc.)
for an object.

Excel.ConditionalRangeFormat A format object encapsulating the conditional formats range's


font, fill, borders, and other properties.

Excel.CultureInfo Provides information based on current system culture settings.


This includes the culture names, number formatting, and other
culturally dependent settings.

Excel.CustomConditional Represents a custom conditional format type.


Format

Excel.CustomProperty Represents a custom property.

Excel.CustomProperty Contains the collection of custom properties.


Collection

Excel.CustomXmlPart Represents a custom XML part object in a workbook.

Excel.CustomXmlPart A collection of custom XML parts.


Collection

Excel.CustomXmlPartScoped A scoped collection of custom XML parts. A scoped collection is


Collection the result of some operation (e.g., filtering by namespace). A
scoped collection cannot be scoped any further.

Excel.DataBarConditional Represents an Excel conditional data bar type.


Format

Excel.DataConnection Represents a collection of all the data connections that are part
Collection of the workbook.

Excel.DataPivotHierarchy Represents the Excel DataPivotHierarchy.

Excel.DataPivotHierarchy Represents a collection of DataPivotHierarchy items associated


Collection with the PivotTable.

Excel.DataValidation Represents the data validation applied to the current range. To


learn more about the data validation object model, read Add
data validation to Excel ranges.
Excel.DatetimeFormatInfo Defines the culturally appropriate format of displaying numbers.
This is based on current system culture settings.

Excel.DocumentProperties Represents workbook properties.

Excel.DocumentTask Represents a task.

Excel.DocumentTaskChange Represents a recorded change to the task.

Excel.DocumentTaskChange Represents a collection of change records for a task.


Collection

Excel.DocumentTaskCollection Represents a collection of tasks.

Excel.Filter Manages the filtering of a table's column.

Excel.FilterPivotHierarchy Represents the Excel FilterPivotHierarchy.

Excel.FilterPivotHierarchy Represents a collection of FilterPivotHierarchy items associated


Collection with the PivotTable.

Excel.FormatProtection Represents the format protection of a range object.

Excel.FunctionResult An object containing the result of a function-evaluation


operation

Excel.Functions An object for evaluating Excel functions.

Excel.GeometricShape Represents a geometric shape inside a worksheet. A geometric


shape can be a rectangle, block arrow, equation symbol,
flowchart item, star, banner, callout, or any other basic shape in
Excel.

Excel.GroupShapeCollection Represents the shape collection inside a shape group.

Excel.HeaderFooter

Excel.HeaderFooterGroup

Excel.IconSetConditional Represents an icon set criteria for conditional formatting.


Format

Excel.Image Represents an image in the worksheet. To get the corresponding


Shape object, use Image.shape .

Excel.IterativeCalculation Represents the iterative calculation settings.

Excel.Line Represents a line inside a worksheet. To get the corresponding


Shape object, use Line.shape .

Excel.LinkedDataType Represents a linked data type. A linked data type is a data type
connected to an online data source.
Excel.LinkedDataType Represents a collection of linked data types.
Collection

Excel.LinkedWorkbook Contains information about a linked workbook. If a workbook


has links pointing to data in another workbook, the second
workbook is linked to the first workbook. In this scenario, the
second workbook is called the "linked workbook".

Excel.LinkedWorkbook Represents a collection of linked workbook objects.


Collection

Excel.NamedItem Represents a defined name for a range of cells or value. Names


can be primitive named objects (as seen in the type below),
range object, or a reference to a range. This object can be used
to obtain range object associated with names.

Excel.NamedItemArrayValues Represents an object containing values and types of a named


item.

Excel.NamedItemCollection A collection of all the NamedItem objects that are part of the
workbook or worksheet, depending on how it was reached.

Excel.NamedSheetView Represents a named sheet view of a worksheet. A sheet view


stores the sort and filter rules for a particular worksheet. Every
sheet view (even a temporary sheet view) has a unique,
worksheet-scoped name that is used to access the view.

Excel.NamedSheetView Represents the collection of sheet views in the worksheet.


Collection

Excel.NumberFormatInfo Defines the culturally appropriate format of displaying numbers.


This is based on current system culture settings.

Excel.PageBreak

Excel.PageBreakCollection

Excel.PageLayout Represents layout and print settings that are not dependent on
any printer-specific implementation. These settings include
margins, orientation, page numbering, title rows, and print area.

Excel.PivotField Represents the Excel PivotField.

Excel.PivotFieldCollection Represents a collection of all the PivotFields that are part of a


PivotTable's hierarchy.

Excel.PivotHierarchy Represents the Excel PivotHierarchy.

Excel.PivotHierarchyCollection Represents a collection of all the PivotHierarchies that are part of


the PivotTable.

Excel.PivotItem Represents the Excel PivotItem.


Excel.PivotItemCollection Represents a collection of all the PivotItems related to their
parent PivotField.

Excel.PivotLayout Represents the visual layout of the PivotTable.

Excel.PivotTable Represents an Excel PivotTable. To learn more about the


PivotTable object model, read Work with PivotTables using the
Excel JavaScript API.

Excel.PivotTableCollection Represents a collection of all the PivotTables that are part of the
workbook or worksheet.

Excel.PivotTableScoped Represents a scoped collection of PivotTables. The PivotTables


Collection are sorted based on the location of the PivotTable's top-left
corner. They are ordered top-to-bottom and then left-to-right.

Excel.PivotTableStyle Represents a PivotTable style, which defines style elements by


PivotTable region.

Excel.PivotTableStyleCollection Represents a collection of PivotTable styles.

Excel.PresetCriteriaConditional Represents the preset criteria conditional format such as above


Format average, below average, unique values, contains blank, nonblank,
error, and noerror.

Excel.Query Represents a Power Query query.

Excel.QueryCollection Represents the collection of queries in the workbook.

Excel.Range Range represents a set of one or more contiguous cells such as a


cell, a row, a column, or a block of cells. To learn more about
how ranges are used throughout the API, start with Ranges in
the Excel JavaScript API.

Excel.RangeAreas RangeAreas represents a collection of one or more rectangular


ranges in the same worksheet. To learn how to use
discontiguous ranges, read Work with multiple ranges
simultaneously in Excel add-ins.

Excel.RangeAreasCollection Contains the collection of cross-workbook level ranges.

Excel.RangeBorder Represents the border of an object.

Excel.RangeBorderCollection Represents the border objects that make up the range border.

Excel.RangeCollection

Excel.RangeFill Represents the background of a range object.

Excel.RangeFont This object represents the font attributes (font name, font size,
color, etc.) for an object.
Excel.RangeFormat A format object encapsulating the range's font, fill, borders,
alignment, and other properties.

Excel.RangeSort Manages sorting operations on Range objects.

Excel.RangeView RangeView represents a set of visible cells of the parent range.

Excel.RangeViewCollection Represents a collection of RangeView objects.

Excel.RemoveDuplicatesResult Represents the results from Range.removeDuplicates .

Excel.RequestContext The RequestContext object facilitates requests to the Excel


application. Since the Office add-in and the Excel application run
in two different processes, the request context is required to get
access to the Excel object model from the add-in.

Excel.RowColumnPivot Represents the Excel RowColumnPivotHierarchy.


Hierarchy

Excel.RowColumnPivot Represents a collection of RowColumnPivotHierarchy items


HierarchyCollection associated with the PivotTable.

Excel.Runtime Represents the Excel Runtime class.

Excel.Setting Setting represents a key-value pair of a setting persisted to the


document (per file, per add-in). These custom key-value pair can
be used to store state or lifecycle information needed by the
content or task-pane add-in. Note that settings are persisted in
the document and hence it is not a place to store any sensitive
or protected information such as user information and password.

Excel.SettingCollection Represents a collection of key-value pair setting objects that are


part of the workbook. The scope is limited to per file and add-in
(task-pane or content) combination.

Excel.Shape Represents a generic shape object in the worksheet. A shape


could be a geometric shape, a line, a group of shapes, etc. To
learn more about the shape object model, read Work with
shapes using the Excel JavaScript API.

Excel.ShapeCollection Represents a collection of all the shapes in the worksheet.

Excel.ShapeFill Represents the fill formatting of a shape object.

Excel.ShapeFont Represents the font attributes, such as font name, font size, and
color, for a shape's TextRange object.

Excel.ShapeGroup Represents a shape group inside a worksheet. To get the


corresponding Shape object, use ShapeGroup.shape .

Excel.ShapeLineFormat Represents the line formatting for the shape object. For images
and geometric shapes, line formatting represents the border of
the shape.

Excel.Slicer Represents a Slicer object in the workbook.

Excel.SlicerCollection Represents a collection of all the slicer objects in the workbook


or a worksheet.

Excel.SlicerItem Represents a slicer item in a slicer.

Excel.SlicerItemCollection Represents a collection of all the slicer item objects in the slicer.

Excel.SlicerStyle Represents a slicer style, which defines style elements by region


of the slicer.

Excel.SlicerStyleCollection Represents a collection of SlicerStyle objects.

Excel.Style An object encapsulating a style's format and other properties.

Excel.StyleCollection Represents a collection of all the styles.

Excel.Table Represents an Excel table. To learn more about the table object
model, read Work with tables using the Excel JavaScript API.

Excel.TableCollection Represents a collection of all the tables that are part of the
workbook or worksheet, depending on how it was reached.

Excel.TableColumn Represents a column in a table.

Excel.TableColumnCollection Represents a collection of all the columns that are part of the
table.

Excel.TableRow Represents a row in a table.

Note that unlike ranges or columns, which will adjust if new rows
or columns are added before them, a TableRow object represents
the physical location of the table row, but not the data. That is, if
the data is sorted or if new rows are added, a table row will
continue to point at the index for which it was created.

Excel.TableRowCollection Represents a collection of all the rows that are part of the table.

Note that unlike ranges or columns, which will adjust if new rows
or columns are added before them, a TableRow object represents
the physical location of the table row, but not the data. That is, if
the data is sorted or if new rows are added, a table row will
continue to point at the index for which it was created.

Excel.TableScopedCollection Represents a scoped collection of tables. For each table its top-
left corner is considered its anchor location, and the tables are
sorted top-to-bottom and then left-to-right.

Excel.TableSort Manages sorting operations on Table objects.


Excel.TableStyle Represents a table style, which defines the style elements by
region of the table.

Excel.TableStyleCollection Represents a collection of table styles.

Excel.TextConditionalFormat Represents a specific text conditional format.

Excel.TextFrame Represents the text frame of a shape object.

Excel.TextRange Contains the text that is attached to a shape, in addition to


properties and methods for manipulating the text.

Excel.TimelineStyle Represents a TimelineStyle , which defines style elements by


region in the timeline.

Excel.TimelineStyleCollection Represents a collection of timeline styles.

Excel.TopBottomConditional Represents a top/bottom conditional format.


Format

Excel.Workbook Workbook is the top level object which contains related


workbook objects such as worksheets, tables, and ranges. To
learn more about the workbook object model, read Work with
workbooks using the Excel JavaScript API.

Excel.WorkbookCreated The WorkbookCreated object is the top level object created by


Application.CreateWorkbook . A WorkbookCreated object is a
special Workbook object.

Excel.WorkbookProtection Represents the protection of a workbook object.

Excel.WorkbookRangeAreas Represents a collection of one or more rectangular ranges in


multiple worksheets.

Excel.Worksheet An Excel worksheet is a grid of cells. It can contain data, tables,


charts, etc. To learn more about the worksheet object model,
read Work with worksheets using the Excel JavaScript API.

Excel.WorksheetCollection Represents a collection of worksheet objects that are part of the


workbook.

Excel.WorksheetCustom Represents a worksheet-level custom property.


Property

Excel.WorksheetCustom Contains the collection of worksheet-level custom property.


PropertyCollection

Excel.WorksheetFreezePanes

Excel.WorksheetProtection Represents the protection of a worksheet object.


Interfaces
Excel.AllowEditRangeOptions The interface used to construct optional fields of the
AllowEditRange object.

Excel.ArrayCellValue Represents a 2D array of cell values.

Excel.Base64EncodedImage The base64 encoding type and data of an image.

Excel.BasicDataValidation Represents the basic type data validation criteria.

Excel.BindingDataChanged Provides information about the binding that raised the data
EventArgs changed event.

Excel.BindingSelection Provides information about the selection that raised the


ChangedEventArgs selection changed event.

Note*: If multiple, discontiguous cells are selected,


Binding.onSelectionChanged only reports row and column
information for one selection. Use Worksheet.onSelectionChanged
for multiple selected ranges.

Excel.BlockedErrorCellValue Represents the value of a cell containing a #BLOCKED! error.

Excel.BooleanCellValue Represents the value of a cell containing a boolean.

Excel.BusyErrorCellValue Represents the value of a cell containing a #BUSY! error.

Excel.CalcErrorCellValue Represents the value of a cell containing a #CALC! error.

Excel.CardLayoutListSection Represents a section of a card that is arranged as a list in card


view.

Excel.CardLayoutProperty Represents a reference to a property used by the card layout.


Reference

Excel.CardLayoutSection Properties of a card layout relevant to most card layouts.


StandardProperties

Excel.CardLayoutStandard Properties of a card layout relevant to most card layouts.


Properties

Excel.CardLayoutTableSection Represents a section of a card that is arranged as a table in card


view.

Excel.CellBorder Represents the properties of a single border returned by


getCellProperties , getRowProperties , and getColumnProperties ,
or the border property input parameter of setCellProperties ,
setRowProperties , and setColumnProperties .
Excel.CellBorderCollection Represents the format.borders properties of getCellProperties ,
getRowProperties , and getColumnProperties , or the
format.borders input parameter of setCellProperties ,
setRowProperties , and setColumnProperties .

Excel.CellProperties Represents the returned properties of getCellProperties.

[ API set: ExcelApi 1.9 ]

Excel.CellPropertiesBorder Specifies which properties to load on the format.borders object.


LoadOptions

Excel.CellPropertiesFill Represents the format.fill properties of getCellProperties ,


getRowProperties , and getColumnProperties or the format.fill
input parameter of setCellProperties , setRowProperties , and
setColumnProperties .

Excel.CellPropertiesFillLoad Specifies which properties to load on the format.fill object.


Options

Excel.CellPropertiesFont Represents the format.font properties of getCellProperties ,


getRowProperties , and getColumnProperties , or the format.font
input parameter of setCellProperties , setRowProperties , and
setColumnProperties .

Excel.CellPropertiesFontLoad Specifies which properties to load on the format.font object.


Options

Excel.CellPropertiesFormat Represents the returned format properties of getCellProperties


or format input parameter of setCellProperties.

[ API set: ExcelApi 1.9 ]

Excel.CellPropertiesFormat Represents which properties to load on the format object.


LoadOptions
[ API set: ExcelApi 1.9 ]

Excel.CellPropertiesLoad Represents which cell properties to load, when used as part of a


Options "range.getCellProperties" method.

[ API set: ExcelApi 1.9 ]

Excel.CellPropertiesProtection Represents the format.protection properties of


getCellProperties , getRowProperties , and getColumnProperties ,
or the format.protection input parameter of setCellProperties ,
setRowProperties , and setColumnProperties .

Excel.CellValueAttribution The attribution attributes object represents the set of details that
Attributes can be used to describe where information came from, if the
information comes from a public source.
Excel.CellValueExtraProperties These extra properties may appear on a CellValue and provide
information about that CellValue , but the extra properties are
not part of the value in the cell.

Excel.CellValueProperty Metadata about a property in EntityCellValue.properties .


Metadata

Excel.CellValueProperty Represents the exclusion of a property in


MetadataExclusions EntityCellValue.properties from features of Excel.

Excel.CellValueProvider The provider attributes object represents the set of details used
Attributes in card view to provide specified branding information for a
CellValue type that supports provider attributes.

Excel.ChangedEventDetail Provides information about the details of a


WorksheetChangedEvent or TableChangedEvent .

Excel.ChangeDirectionState Represents the direction that existing or remaining cells in a


worksheet will shift when cells are inserted into or deleted from
a worksheet.

Excel.ChartActivatedEventArgs Provides information about the chart that raised the activated
event.

Excel.ChartAddedEventArgs Provides information about the chart that raised the added
event.

Excel.ChartDeactivatedEvent Provides information about the chart that raised the deactivated
Args event.

Excel.ChartDeletedEventArgs Provides information about the chart that raised the deleted
event.

Excel.ColumnProperties Represents the returned properties of getColumnProperties.

[ API set: ExcelApi 1.9 ]

Excel.ColumnPropertiesLoad Represents which column properties to load, when used as part


Options of a "range.getColumnProperties" method.

[ API set: ExcelApi 1.9 ]

Excel.CommentAddedEvent Provides information about the comments that raised the


Args comment added event.

Excel.CommentChangedEvent Occurs when existing comments are changed.


Args

Excel.CommentDeletedEvent Provides information about the comments that raised the


Args comment deleted event.

Excel.CommentDetail A structure for the comment ID and IDs of its related replies.
Excel.CommentMention Represents the entity that is mentioned in comments.

Excel.CommentRichContent Represents the content contained within a comment or


comment reply. Rich content incudes the text string and any
other objects contained within the comment body, such as
mentions.

Excel.ConditionalCellValueRule Represents a cell value conditional format rule.

Excel.ConditionalColorScale Represents the criteria of the color scale.


Criteria

Excel.ConditionalColorScale Represents a color scale criterion which contains a type, value,


Criterion and a color.

Excel.ConditionalDataBarRule Represents a rule-type for a data bar.

Excel.ConditionalIconCriterion Represents an icon criterion which contains a type, value, an


operator, and an optional custom icon, if not using an icon set.

Excel.ConditionalPresetCriteria Represents the preset criteria conditional format rule.


Rule

Excel.ConditionalText Represents a cell value conditional format rule.


ComparisonRule

Excel.ConditionalTopBottom Represents the rule of the top/bottom conditional format.


Rule

Excel.ConnectErrorCellValue Represents the value of a cell containing a #CONNECT! error.

Excel.CustomDataValidation Represents the custom data validation criteria.

Excel.DataValidationErrorAlert Represents the error alert properties for the data validation.

Excel.DataValidationPrompt Represents the user prompt properties for the data validation.

Excel.DataValidationRule A data validation rule contains different types of data validation.


You can only use one of them at a time according the
Excel.DataValidationType .

Excel.DateTimeDataValidation Represents the date data validation criteria.

Excel.Div0ErrorCellValue Represents the value of a cell containing a #DIV/0! error.

Excel.DocumentTaskChange Represents a recorded change to the task, to be used as an input


Properties parameter.

Excel.DocumentTaskSchedule Represents information about a task's schedule.

Excel.DoubleCellValue Represents the value of a cell containing a double.


Excel.EmailIdentity Represents information about a user's identity.

Excel.EmptyCellValue Represents the value of a cell that's empty and has no formulas
or data.

Excel.EntityArrayCardLayout Represents a card layout that is best used for an array.

Excel.EntityCardLayout Represents a card layout that is best used for an array.

Excel.EntityCellValue Represents a set of properties without a schema or defined


structure.

Excel.EntityCompactLayout The compact layout properties for an entity.

Excel.EntityPropertyExtra Properties used by CellValueAndPropertyMetadata . These


Properties properties refer to the metadata and not to a CellValue .

Excel.EntityViewLayouts Represents layout information for various views of the entity.

Excel.ExternalErrorCellValue Represents the value of a cell containing an #EXTERNAL! error.

Excel.FieldErrorCellValue Represents the value of a cell containing a #FIELD! error.

Excel.FilterCriteria Represents the filtering criteria applied to a column.

Excel.FilterDatetime Represents how to filter a date when filtering on values.

Excel.FiveArrowsGraySet [ API set: ExcelApi 1.2 ]

Excel.FiveArrowsSet [ API set: ExcelApi 1.2 ]

Excel.FiveBoxesSet [ API set: ExcelApi 1.2 ]

Excel.FiveQuartersSet [ API set: ExcelApi 1.2 ]

Excel.FiveRatingSet [ API set: ExcelApi 1.2 ]

Excel.FormattedNumberCell Represents the value of a cell containing a number with a format


Value string. Number format strings must conform to Excel guidelines.
To learn more, see Review guidelines for customizing a number
format . In this scenario, the format is applied to the value and
not to the cell, so the value retains its format string throughout
calculation.

Excel.FormulaChangedEvent Provides information about a changed formula during a formula


Detail changed event.

Excel.FourArrowsGraySet [ API set: ExcelApi 1.2 ]

Excel.FourArrowsSet [ API set: ExcelApi 1.2 ]

Excel.FourRatingSet [ API set: ExcelApi 1.2 ]


Excel.FourRedToBlackSet [ API set: ExcelApi 1.2 ]

Excel.FourTrafficLightsSet [ API set: ExcelApi 1.2 ]

Excel.GettingDataErrorCell Represents the value of a cell containing a #GETTING_DATA


Value error.

Excel.Icon Represents a cell icon.

Excel.IconCollections [ API set: ExcelApi 1.2 ]

Excel.Identity Represents information about a user's identity.

Excel.InsertWorksheetOptions The options that define which worksheets to insert and where in
the workbook the new worksheets will be inserted.

Excel.Interfaces.AllowEdit An interface describing the data returned by calling


RangeCollectionData allowEditRangeCollection.toJSON() .

Excel.Interfaces.AllowEdit Represents the set of AllowEditRange objects found in a


RangeCollectionLoadOptions worksheet. AllowEditRange objects work with worksheet
protection properties. When worksheet protection is enabled, an
AllowEditRange object can be used to allow editing of a specific
range, while maintaining protection on the rest of the worksheet.

Excel.Interfaces.AllowEdit An interface for updating data on the AllowEditRangeCollection


RangeCollectionUpdateData object, for use in allowEditRangeCollection.set({ ... }) .

Excel.Interfaces.AllowEdit An interface describing the data returned by calling


RangeData allowEditRange.toJSON() .

Excel.Interfaces.AllowEdit Represents an AllowEditRange object found in a worksheet. This


RangeLoadOptions object works with worksheet protection properties. When
worksheet protection is enabled, an AllowEditRange object can
be used to allow editing of a specific range, while maintaining
protection on the rest of the worksheet.

Excel.Interfaces.AllowEdit An interface for updating data on the AllowEditRange object, for


RangeUpdateData use in allowEditRange.set({ ... }) .

Excel.Interfaces.Application An interface describing the data returned by calling


Data application.toJSON() .

Excel.Interfaces.Application Represents the Excel application that manages the workbook.


LoadOptions

Excel.Interfaces.Application An interface for updating data on the Application object, for use
UpdateData in application.set({ ... }) .

Excel.Interfaces.AutoFilterData An interface describing the data returned by calling


autoFilter.toJSON() .
Excel.Interfaces.AutoFilterLoad Represents the AutoFilter object. AutoFilter turns the values in
Options Excel column into specific filters based on the cell contents.

Excel.Interfaces.Binding An interface describing the data returned by calling


CollectionData bindingCollection.toJSON() .

Excel.Interfaces.Binding Represents the collection of all the binding objects that are part
CollectionLoadOptions of the workbook.

Excel.Interfaces.Binding An interface for updating data on the BindingCollection object,


CollectionUpdateData for use in bindingCollection.set({ ... }) .

Excel.Interfaces.BindingData An interface describing the data returned by calling


binding.toJSON() .

Excel.Interfaces.BindingLoad Represents an Office.js binding that is defined in the workbook.


Options

Excel.Interfaces.CellValue An interface describing the data returned by calling


ConditionalFormatData cellValueConditionalFormat.toJSON() .

Excel.Interfaces.CellValue Represents a cell value conditional format.


ConditionalFormatLoad
Options

Excel.Interfaces.CellValue An interface for updating data on the


ConditionalFormatUpdate CellValueConditionalFormat object, for use in
Data cellValueConditionalFormat.set({ ... }) .

Excel.Interfaces.ChartArea An interface describing the data returned by calling


FormatData chartAreaFormat.toJSON() .

Excel.Interfaces.ChartArea Encapsulates the format properties for the overall chart area.
FormatLoadOptions

Excel.Interfaces.ChartArea An interface for updating data on the ChartAreaFormat object,


FormatUpdateData for use in chartAreaFormat.set({ ... }) .

Excel.Interfaces.ChartAxesData An interface describing the data returned by calling


chartAxes.toJSON() .

Excel.Interfaces.ChartAxesLoad Represents the chart axes.


Options

Excel.Interfaces.ChartAxes An interface for updating data on the ChartAxes object, for use
UpdateData in chartAxes.set({ ... }) .

Excel.Interfaces.ChartAxisData An interface describing the data returned by calling


chartAxis.toJSON() .

Excel.Interfaces.ChartAxis An interface describing the data returned by calling


FormatData
chartAxisFormat.toJSON() .

Excel.Interfaces.ChartAxis Encapsulates the format properties for the chart axis.


FormatLoadOptions

Excel.Interfaces.ChartAxis An interface for updating data on the ChartAxisFormat object,


FormatUpdateData for use in chartAxisFormat.set({ ... }) .

Excel.Interfaces.ChartAxisLoad Represents a single axis in a chart.


Options

Excel.Interfaces.ChartAxisTitle An interface describing the data returned by calling


Data chartAxisTitle.toJSON() .

Excel.Interfaces.ChartAxisTitle An interface describing the data returned by calling


FormatData chartAxisTitleFormat.toJSON() .

Excel.Interfaces.ChartAxisTitle Represents the chart axis title formatting.


FormatLoadOptions

Excel.Interfaces.ChartAxisTitle An interface for updating data on the ChartAxisTitleFormat


FormatUpdateData object, for use in chartAxisTitleFormat.set({ ... }) .

Excel.Interfaces.ChartAxisTitle Represents the title of a chart axis.


LoadOptions

Excel.Interfaces.ChartAxisTitle An interface for updating data on the ChartAxisTitle object, for


UpdateData use in chartAxisTitle.set({ ... }) .

Excel.Interfaces.ChartAxis An interface for updating data on the ChartAxis object, for use in
UpdateData chartAxis.set({ ... }) .

Excel.Interfaces.ChartBin An interface describing the data returned by calling


OptionsData chartBinOptions.toJSON() .

Excel.Interfaces.ChartBin Encapsulates the bin options for histogram charts and pareto
OptionsLoadOptions charts.

Excel.Interfaces.ChartBin An interface for updating data on the ChartBinOptions object,


OptionsUpdateData for use in chartBinOptions.set({ ... }) .

Excel.Interfaces.ChartBorder An interface describing the data returned by calling


Data chartBorder.toJSON() .

Excel.Interfaces.ChartBorder Represents the border formatting of a chart element.


LoadOptions

Excel.Interfaces.ChartBorder An interface for updating data on the ChartBorder object, for use
UpdateData in chartBorder.set({ ... }) .

Excel.Interfaces.Chart An interface describing the data returned by calling


BoxwhiskerOptionsData chartBoxwhiskerOptions.toJSON() .
Excel.Interfaces.Chart Represents the properties of a box and whisker chart.
BoxwhiskerOptionsLoad
Options

Excel.Interfaces.Chart An interface for updating data on the ChartBoxwhiskerOptions


BoxwhiskerOptionsUpdate object, for use in chartBoxwhiskerOptions.set({ ... }) .
Data

Excel.Interfaces.Chart An interface describing the data returned by calling


CollectionData chartCollection.toJSON() .

Excel.Interfaces.Chart A collection of all the chart objects on a worksheet.


CollectionLoadOptions

Excel.Interfaces.Chart An interface for updating data on the ChartCollection object, for


CollectionUpdateData use in chartCollection.set({ ... }) .

Excel.Interfaces.ChartData An interface describing the data returned by calling


chart.toJSON() .

Excel.Interfaces.ChartData An interface describing the data returned by calling


LabelData chartDataLabel.toJSON() .

Excel.Interfaces.ChartData An interface describing the data returned by calling


LabelFormatData chartDataLabelFormat.toJSON() .

Excel.Interfaces.ChartData Encapsulates the format properties for the chart data labels.
LabelFormatLoadOptions

Excel.Interfaces.ChartData An interface for updating data on the ChartDataLabelFormat


LabelFormatUpdateData object, for use in chartDataLabelFormat.set({ ... }) .

Excel.Interfaces.ChartData Represents the data label of a chart point.


LabelLoadOptions

Excel.Interfaces.ChartData An interface describing the data returned by calling


LabelsData chartDataLabels.toJSON() .

Excel.Interfaces.ChartData Represents a collection of all the data labels on a chart point.


LabelsLoadOptions

Excel.Interfaces.ChartData An interface for updating data on the ChartDataLabels object,


LabelsUpdateData for use in chartDataLabels.set({ ... }) .

Excel.Interfaces.ChartData An interface for updating data on the ChartDataLabel object, for


LabelUpdateData use in chartDataLabel.set({ ... }) .

Excel.Interfaces.ChartData An interface describing the data returned by calling


TableData chartDataTable.toJSON() .

Excel.Interfaces.ChartData An interface describing the data returned by calling


TableFormatData
chartDataTableFormat.toJSON() .

Excel.Interfaces.ChartData Represents the format of a chart data table.


TableFormatLoadOptions

Excel.Interfaces.ChartData An interface for updating data on the ChartDataTableFormat


TableFormatUpdateData object, for use in chartDataTableFormat.set({ ... }) .

Excel.Interfaces.ChartData Represents the data table object of a chart.


TableLoadOptions

Excel.Interfaces.ChartData An interface for updating data on the ChartDataTable object, for


TableUpdateData use in chartDataTable.set({ ... }) .

Excel.Interfaces.ChartErrorBars An interface describing the data returned by calling


Data chartErrorBars.toJSON() .

Excel.Interfaces.ChartErrorBars An interface describing the data returned by calling


FormatData chartErrorBarsFormat.toJSON() .

Excel.Interfaces.ChartErrorBars Encapsulates the format properties for chart error bars.


FormatLoadOptions

Excel.Interfaces.ChartErrorBars An interface for updating data on the ChartErrorBarsFormat


FormatUpdateData object, for use in chartErrorBarsFormat.set({ ... }) .

Excel.Interfaces.ChartErrorBars This object represents the attributes for a chart's error bars.
LoadOptions

Excel.Interfaces.ChartErrorBars An interface for updating data on the ChartErrorBars object, for


UpdateData use in chartErrorBars.set({ ... }) .

Excel.Interfaces.ChartFontData An interface describing the data returned by calling


chartFont.toJSON() .

Excel.Interfaces.ChartFontLoad This object represents the font attributes (such as font name,
Options font size, and color) for a chart object.

Excel.Interfaces.ChartFont An interface for updating data on the ChartFont object, for use
UpdateData in chartFont.set({ ... }) .

Excel.Interfaces.ChartFormat An interface describing the data returned by calling


StringData chartFormatString.toJSON() .

Excel.Interfaces.ChartFormat Represents the substring in chart related objects that contain


StringLoadOptions text, like a ChartTitle object or ChartAxisTitle object.

Excel.Interfaces.ChartFormat An interface for updating data on the ChartFormatString object,


StringUpdateData for use in chartFormatString.set({ ... }) .

Excel.Interfaces.ChartGridlines An interface describing the data returned by calling


Data
chartGridlines.toJSON() .

Excel.Interfaces.ChartGridlines An interface describing the data returned by calling


FormatData chartGridlinesFormat.toJSON() .

Excel.Interfaces.ChartGridlines Encapsulates the format properties for chart gridlines.


FormatLoadOptions

Excel.Interfaces.ChartGridlines An interface for updating data on the ChartGridlinesFormat


FormatUpdateData object, for use in chartGridlinesFormat.set({ ... }) .

Excel.Interfaces.ChartGridlines Represents major or minor gridlines on a chart axis.


LoadOptions

Excel.Interfaces.ChartGridlines An interface for updating data on the ChartGridlines object, for


UpdateData use in chartGridlines.set({ ... }) .

Excel.Interfaces.ChartLegend An interface describing the data returned by calling


Data chartLegend.toJSON() .

Excel.Interfaces.ChartLegend An interface describing the data returned by calling


EntryCollectionData chartLegendEntryCollection.toJSON() .

Excel.Interfaces.ChartLegend Represents a collection of legend entries.


EntryCollectionLoadOptions

Excel.Interfaces.ChartLegend An interface for updating data on the


EntryCollectionUpdateData ChartLegendEntryCollection object, for use in
chartLegendEntryCollection.set({ ... }) .

Excel.Interfaces.ChartLegend An interface describing the data returned by calling


EntryData chartLegendEntry.toJSON() .

Excel.Interfaces.ChartLegend Represents the legend entry in legendEntryCollection .


EntryLoadOptions

Excel.Interfaces.ChartLegend An interface for updating data on the ChartLegendEntry object,


EntryUpdateData for use in chartLegendEntry.set({ ... }) .

Excel.Interfaces.ChartLegend An interface describing the data returned by calling


FormatData chartLegendFormat.toJSON() .

Excel.Interfaces.ChartLegend Encapsulates the format properties of a chart legend.


FormatLoadOptions

Excel.Interfaces.ChartLegend An interface for updating data on the ChartLegendFormat


FormatUpdateData object, for use in chartLegendFormat.set({ ... }) .

Excel.Interfaces.ChartLegend Represents the legend in a chart.


LoadOptions
Excel.Interfaces.ChartLegend An interface for updating data on the ChartLegend object, for
UpdateData use in chartLegend.set({ ... }) .

Excel.Interfaces.ChartLine An interface describing the data returned by calling


FormatData chartLineFormat.toJSON() .

Excel.Interfaces.ChartLine Encapsulates the formatting options for line elements.


FormatLoadOptions

Excel.Interfaces.ChartLine An interface for updating data on the ChartLineFormat object,


FormatUpdateData for use in chartLineFormat.set({ ... }) .

Excel.Interfaces.ChartLoad Represents a chart object in a workbook. To learn more about


Options the chart object model, see Work with charts using the Excel
JavaScript API.

Excel.Interfaces.ChartMap An interface describing the data returned by calling


OptionsData chartMapOptions.toJSON() .

Excel.Interfaces.ChartMap Encapsulates the properties for a region map chart.


OptionsLoadOptions

Excel.Interfaces.ChartMap An interface for updating data on the ChartMapOptions object,


OptionsUpdateData for use in chartMapOptions.set({ ... }) .

Excel.Interfaces.ChartPivot An interface describing the data returned by calling


OptionsData chartPivotOptions.toJSON() .

Excel.Interfaces.ChartPivot Encapsulates the options for the pivot chart.


OptionsLoadOptions

Excel.Interfaces.ChartPivot An interface for updating data on the ChartPivotOptions object,


OptionsUpdateData for use in chartPivotOptions.set({ ... }) .

Excel.Interfaces.ChartPlotArea An interface describing the data returned by calling


Data chartPlotArea.toJSON() .

Excel.Interfaces.ChartPlotArea An interface describing the data returned by calling


FormatData chartPlotAreaFormat.toJSON() .

Excel.Interfaces.ChartPlotArea Represents the format properties for a chart plot area.


FormatLoadOptions

Excel.Interfaces.ChartPlotArea An interface for updating data on the ChartPlotAreaFormat


FormatUpdateData object, for use in chartPlotAreaFormat.set({ ... }) .

Excel.Interfaces.ChartPlotArea This object represents the attributes for a chart plot area.
LoadOptions

Excel.Interfaces.ChartPlotArea An interface for updating data on the ChartPlotArea object, for


UpdateData use in chartPlotArea.set({ ... }) .
Excel.Interfaces.ChartPoint An interface describing the data returned by calling
Data chartPoint.toJSON() .

Excel.Interfaces.ChartPoint An interface describing the data returned by calling


FormatData chartPointFormat.toJSON() .

Excel.Interfaces.ChartPoint Represents the formatting object for chart points.


FormatLoadOptions

Excel.Interfaces.ChartPoint An interface for updating data on the ChartPointFormat object,


FormatUpdateData for use in chartPointFormat.set({ ... }) .

Excel.Interfaces.ChartPoint Represents a point of a series in a chart.


LoadOptions

Excel.Interfaces.ChartPoints An interface describing the data returned by calling


CollectionData chartPointsCollection.toJSON() .

Excel.Interfaces.ChartPoints A collection of all the chart points within a series inside a chart.
CollectionLoadOptions

Excel.Interfaces.ChartPoints An interface for updating data on the ChartPointsCollection


CollectionUpdateData object, for use in chartPointsCollection.set({ ... }) .

Excel.Interfaces.ChartPoint An interface for updating data on the ChartPoint object, for use
UpdateData in chartPoint.set({ ... }) .

Excel.Interfaces.ChartSeries An interface describing the data returned by calling


CollectionData chartSeriesCollection.toJSON() .

Excel.Interfaces.ChartSeries Represents a collection of chart series.


CollectionLoadOptions

Excel.Interfaces.ChartSeries An interface for updating data on the ChartSeriesCollection


CollectionUpdateData object, for use in chartSeriesCollection.set({ ... }) .

Excel.Interfaces.ChartSeries An interface describing the data returned by calling


Data chartSeries.toJSON() .

Excel.Interfaces.ChartSeries An interface describing the data returned by calling


FormatData chartSeriesFormat.toJSON() .

Excel.Interfaces.ChartSeries Encapsulates the format properties for the chart series


FormatLoadOptions

Excel.Interfaces.ChartSeries An interface for updating data on the ChartSeriesFormat object,


FormatUpdateData for use in chartSeriesFormat.set({ ... }) .

Excel.Interfaces.ChartSeries Represents a series in a chart.


LoadOptions
Excel.Interfaces.ChartSeries An interface for updating data on the ChartSeries object, for use
UpdateData in chartSeries.set({ ... }) .

Excel.Interfaces.ChartTitleData An interface describing the data returned by calling


chartTitle.toJSON() .

Excel.Interfaces.ChartTitle An interface describing the data returned by calling


FormatData chartTitleFormat.toJSON() .

Excel.Interfaces.ChartTitle Provides access to the formatting options for a chart title.


FormatLoadOptions

Excel.Interfaces.ChartTitle An interface for updating data on the ChartTitleFormat object,


FormatUpdateData for use in chartTitleFormat.set({ ... }) .

Excel.Interfaces.ChartTitleLoad Represents a chart title object of a chart.


Options

Excel.Interfaces.ChartTitle An interface for updating data on the ChartTitle object, for use in
UpdateData chartTitle.set({ ... }) .

Excel.Interfaces.ChartTrendline An interface describing the data returned by calling


CollectionData chartTrendlineCollection.toJSON() .

Excel.Interfaces.ChartTrendline Represents a collection of chart trendlines.


CollectionLoadOptions

Excel.Interfaces.ChartTrendline An interface for updating data on the ChartTrendlineCollection


CollectionUpdateData object, for use in chartTrendlineCollection.set({ ... }) .

Excel.Interfaces.ChartTrendline An interface describing the data returned by calling


Data chartTrendline.toJSON() .

Excel.Interfaces.ChartTrendline An interface describing the data returned by calling


FormatData chartTrendlineFormat.toJSON() .

Excel.Interfaces.ChartTrendline Represents the format properties for the chart trendline.


FormatLoadOptions

Excel.Interfaces.ChartTrendline An interface for updating data on the ChartTrendlineFormat


FormatUpdateData object, for use in chartTrendlineFormat.set({ ... }) .

Excel.Interfaces.ChartTrendline An interface describing the data returned by calling


LabelData chartTrendlineLabel.toJSON() .

Excel.Interfaces.ChartTrendline An interface describing the data returned by calling


LabelFormatData chartTrendlineLabelFormat.toJSON() .

Excel.Interfaces.ChartTrendline Encapsulates the format properties for the chart trendline label.
LabelFormatLoadOptions
Excel.Interfaces.ChartTrendline An interface for updating data on the ChartTrendlineLabelFormat
LabelFormatUpdateData object, for use in chartTrendlineLabelFormat.set({ ... }) .

Excel.Interfaces.ChartTrendline This object represents the attributes for a chart trendline label
LabelLoadOptions object.

Excel.Interfaces.ChartTrendline An interface for updating data on the ChartTrendlineLabel


LabelUpdateData object, for use in chartTrendlineLabel.set({ ... }) .

Excel.Interfaces.ChartTrendline This object represents the attributes for a chart trendline object.
LoadOptions

Excel.Interfaces.ChartTrendline An interface for updating data on the ChartTrendline object, for


UpdateData use in chartTrendline.set({ ... }) .

Excel.Interfaces.ChartUpdate An interface for updating data on the Chart object, for use in
Data chart.set({ ... }) .

Excel.Interfaces.CollectionLoad Provides ways to load properties of only a subset of members of


Options a collection.

Excel.Interfaces.ColorScale An interface describing the data returned by calling


ConditionalFormatData colorScaleConditionalFormat.toJSON() .

Excel.Interfaces.ColorScale Represents the color scale criteria for conditional formatting.


ConditionalFormatLoad
Options

Excel.Interfaces.ColorScale An interface for updating data on the


ConditionalFormatUpdate ColorScaleConditionalFormat object, for use in
Data colorScaleConditionalFormat.set({ ... }) .

Excel.Interfaces.Comment An interface describing the data returned by calling


CollectionData commentCollection.toJSON() .

Excel.Interfaces.Comment Represents a collection of comment objects that are part of the


CollectionLoadOptions workbook.

Excel.Interfaces.Comment An interface for updating data on the CommentCollection


CollectionUpdateData object, for use in commentCollection.set({ ... }) .

Excel.Interfaces.CommentData An interface describing the data returned by calling


comment.toJSON() .

Excel.Interfaces.CommentLoad Represents a comment in the workbook.


Options

Excel.Interfaces.Comment An interface describing the data returned by calling


ReplyCollectionData commentReplyCollection.toJSON() .

Excel.Interfaces.Comment Represents a collection of comment reply objects that are part of


ReplyCollectionLoadOptions the comment.

Excel.Interfaces.Comment An interface for updating data on the CommentReplyCollection


ReplyCollectionUpdateData object, for use in commentReplyCollection.set({ ... }) .

Excel.Interfaces.Comment An interface describing the data returned by calling


ReplyData commentReply.toJSON() .

Excel.Interfaces.Comment Represents a comment reply in the workbook.


ReplyLoadOptions

Excel.Interfaces.Comment An interface for updating data on the CommentReply object, for


ReplyUpdateData use in commentReply.set({ ... }) .

Excel.Interfaces.Comment An interface for updating data on the Comment object, for use
UpdateData in comment.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


DataBarNegativeFormatData conditionalDataBarNegativeFormat.toJSON() .

Excel.Interfaces.Conditional Represents a conditional format for the negative side of the data
DataBarNegativeFormatLoad bar.
Options

Excel.Interfaces.Conditional An interface for updating data on the


DataBarNegativeFormat ConditionalDataBarNegativeFormat object, for use in
UpdateData conditionalDataBarNegativeFormat.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


DataBarPositiveFormatData conditionalDataBarPositiveFormat.toJSON() .

Excel.Interfaces.Conditional Represents a conditional format for the positive side of the data
DataBarPositiveFormatLoad bar.
Options

Excel.Interfaces.Conditional An interface for updating data on the


DataBarPositiveFormatUpdate ConditionalDataBarPositiveFormat object, for use in
Data conditionalDataBarPositiveFormat.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


FormatCollectionData conditionalFormatCollection.toJSON() .

Excel.Interfaces.Conditional Represents a collection of all the conditional formats that are


FormatCollectionLoadOptions overlap the range.

Excel.Interfaces.Conditional An interface for updating data on the


FormatCollectionUpdateData ConditionalFormatCollection object, for use in
conditionalFormatCollection.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


FormatData conditionalFormat.toJSON() .
Excel.Interfaces.Conditional An object encapsulating a conditional format's range, format,
FormatLoadOptions rule, and other properties. To learn more about the conditional
formatting object model, read Apply conditional formatting to
Excel ranges.

Excel.Interfaces.Conditional An interface describing the data returned by calling


FormatRuleData conditionalFormatRule.toJSON() .

Excel.Interfaces.Conditional Represents a rule, for all traditional rule/format pairings.


FormatRuleLoadOptions

Excel.Interfaces.Conditional An interface for updating data on the ConditionalFormatRule


FormatRuleUpdateData object, for use in conditionalFormatRule.set({ ... }) .

Excel.Interfaces.Conditional An interface for updating data on the ConditionalFormat object,


FormatUpdateData for use in conditionalFormat.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


RangeBorderCollectionData conditionalRangeBorderCollection.toJSON() .

Excel.Interfaces.Conditional Represents the border objects that make up range border.


RangeBorderCollectionLoad
Options

Excel.Interfaces.Conditional An interface for updating data on the


RangeBorderCollectionUpdate ConditionalRangeBorderCollection object, for use in
Data conditionalRangeBorderCollection.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


RangeBorderData conditionalRangeBorder.toJSON() .

Excel.Interfaces.Conditional Represents the border of an object.


RangeBorderLoadOptions

Excel.Interfaces.Conditional An interface for updating data on the ConditionalRangeBorder


RangeBorderUpdateData object, for use in conditionalRangeBorder.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


RangeFillData conditionalRangeFill.toJSON() .

Excel.Interfaces.Conditional Represents the background of a conditional range object.


RangeFillLoadOptions

Excel.Interfaces.Conditional An interface for updating data on the ConditionalRangeFill


RangeFillUpdateData object, for use in conditionalRangeFill.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


RangeFontData conditionalRangeFont.toJSON() .

Excel.Interfaces.Conditional This object represents the font attributes (font style, color, etc.)
RangeFontLoadOptions for an object.
Excel.Interfaces.Conditional An interface for updating data on the ConditionalRangeFont
RangeFontUpdateData object, for use in conditionalRangeFont.set({ ... }) .

Excel.Interfaces.Conditional An interface describing the data returned by calling


RangeFormatData conditionalRangeFormat.toJSON() .

Excel.Interfaces.Conditional A format object encapsulating the conditional formats range's


RangeFormatLoadOptions font, fill, borders, and other properties.

Excel.Interfaces.Conditional An interface for updating data on the ConditionalRangeFormat


RangeFormatUpdateData object, for use in conditionalRangeFormat.set({ ... }) .

Excel.Interfaces.CultureInfo An interface describing the data returned by calling


Data cultureInfo.toJSON() .

Excel.Interfaces.CultureInfo Provides information based on current system culture settings.


LoadOptions This includes the culture names, number formatting, and other
culturally dependent settings.

Excel.Interfaces.Custom An interface describing the data returned by calling


ConditionalFormatData customConditionalFormat.toJSON() .

Excel.Interfaces.Custom Represents a custom conditional format type.


ConditionalFormatLoad
Options

Excel.Interfaces.Custom An interface for updating data on the CustomConditionalFormat


ConditionalFormatUpdate object, for use in customConditionalFormat.set({ ... }) .
Data

Excel.Interfaces.Custom An interface describing the data returned by calling


PropertyCollectionData customPropertyCollection.toJSON() .

Excel.Interfaces.Custom Contains the collection of custom properties.


PropertyCollectionLoad
Options

Excel.Interfaces.Custom An interface for updating data on the CustomPropertyCollection


PropertyCollectionUpdateData object, for use in customPropertyCollection.set({ ... }) .

Excel.Interfaces.Custom An interface describing the data returned by calling


PropertyData customProperty.toJSON() .

Excel.Interfaces.Custom Represents a custom property.


PropertyLoadOptions

Excel.Interfaces.Custom An interface for updating data on the CustomProperty object, for


PropertyUpdateData use in customProperty.set({ ... }) .

Excel.Interfaces.CustomXml An interface describing the data returned by calling


PartCollectionData customXmlPartCollection.toJSON() .
Excel.Interfaces.CustomXml A collection of custom XML parts.
PartCollectionLoadOptions

Excel.Interfaces.CustomXml An interface for updating data on the CustomXmlPartCollection


PartCollectionUpdateData object, for use in customXmlPartCollection.set({ ... }) .

Excel.Interfaces.CustomXml An interface describing the data returned by calling


PartData customXmlPart.toJSON() .

Excel.Interfaces.CustomXml Represents a custom XML part object in a workbook.


PartLoadOptions

Excel.Interfaces.CustomXml An interface describing the data returned by calling


PartScopedCollectionData customXmlPartScopedCollection.toJSON() .

Excel.Interfaces.CustomXml A scoped collection of custom XML parts. A scoped collection is


PartScopedCollectionLoad the result of some operation (e.g., filtering by namespace). A
Options scoped collection cannot be scoped any further.

Excel.Interfaces.CustomXml An interface for updating data on the


PartScopedCollectionUpdate CustomXmlPartScopedCollection object, for use in
Data customXmlPartScopedCollection.set({ ... }) .

Excel.Interfaces.DataBar An interface describing the data returned by calling


ConditionalFormatData dataBarConditionalFormat.toJSON() .

Excel.Interfaces.DataBar Represents an Excel conditional data bar type.


ConditionalFormatLoad
Options

Excel.Interfaces.DataBar An interface for updating data on the DataBarConditionalFormat


ConditionalFormatUpdate object, for use in dataBarConditionalFormat.set({ ... }) .
Data

Excel.Interfaces.DataPivot An interface describing the data returned by calling


HierarchyCollectionData dataPivotHierarchyCollection.toJSON() .

Excel.Interfaces.DataPivot Represents a collection of DataPivotHierarchy items associated


HierarchyCollectionLoad with the PivotTable.
Options

Excel.Interfaces.DataPivot An interface for updating data on the


HierarchyCollectionUpdate DataPivotHierarchyCollection object, for use in
Data dataPivotHierarchyCollection.set({ ... }) .

Excel.Interfaces.DataPivot An interface describing the data returned by calling


HierarchyData dataPivotHierarchy.toJSON() .

Excel.Interfaces.DataPivot Represents the Excel DataPivotHierarchy.


HierarchyLoadOptions
Excel.Interfaces.DataPivot An interface for updating data on the DataPivotHierarchy object,
HierarchyUpdateData for use in dataPivotHierarchy.set({ ... }) .

Excel.Interfaces.DataValidation An interface describing the data returned by calling


Data dataValidation.toJSON() .

Excel.Interfaces.DataValidation Represents the data validation applied to the current range. To


LoadOptions learn more about the data validation object model, read Add
data validation to Excel ranges.

Excel.Interfaces.DataValidation An interface for updating data on the DataValidation object, for


UpdateData use in dataValidation.set({ ... }) .

Excel.Interfaces.Datetime An interface describing the data returned by calling


FormatInfoData datetimeFormatInfo.toJSON() .

Excel.Interfaces.Datetime Defines the culturally appropriate format of displaying numbers.


FormatInfoLoadOptions This is based on current system culture settings.

Excel.Interfaces.Document An interface describing the data returned by calling


PropertiesData documentProperties.toJSON() .

Excel.Interfaces.Document Represents workbook properties.


PropertiesLoadOptions

Excel.Interfaces.Document An interface for updating data on the DocumentProperties


PropertiesUpdateData object, for use in documentProperties.set({ ... }) .

Excel.Interfaces.DocumentTask An interface describing the data returned by calling


ChangeCollectionData documentTaskChangeCollection.toJSON() .

Excel.Interfaces.DocumentTask Represents a collection of change records for a task.


ChangeCollectionLoadOptions

Excel.Interfaces.DocumentTask An interface for updating data on the


ChangeCollectionUpdateData DocumentTaskChangeCollection object, for use in
documentTaskChangeCollection.set({ ... }) .

Excel.Interfaces.DocumentTask An interface describing the data returned by calling


ChangeData documentTaskChange.toJSON() .

Excel.Interfaces.DocumentTask Represents a recorded change to the task.


ChangeLoadOptions

Excel.Interfaces.DocumentTask An interface describing the data returned by calling


CollectionData documentTaskCollection.toJSON() .

Excel.Interfaces.DocumentTask Represents a collection of tasks.


CollectionLoadOptions

Excel.Interfaces.DocumentTask An interface for updating data on the DocumentTaskCollection


CollectionUpdateData
object, for use in documentTaskCollection.set({ ... }) .

Excel.Interfaces.DocumentTask An interface describing the data returned by calling


Data documentTask.toJSON() .

Excel.Interfaces.DocumentTask Represents a task.


LoadOptions

Excel.Interfaces.DocumentTask An interface for updating data on the DocumentTask object, for


UpdateData use in documentTask.set({ ... }) .

Excel.Interfaces.FilterData An interface describing the data returned by calling


filter.toJSON() .

Excel.Interfaces.FilterLoad Manages the filtering of a table's column.


Options

Excel.Interfaces.FilterPivot An interface describing the data returned by calling


HierarchyCollectionData filterPivotHierarchyCollection.toJSON() .

Excel.Interfaces.FilterPivot Represents a collection of FilterPivotHierarchy items associated


HierarchyCollectionLoad with the PivotTable.
Options

Excel.Interfaces.FilterPivot An interface for updating data on the


HierarchyCollectionUpdate FilterPivotHierarchyCollection object, for use in
Data filterPivotHierarchyCollection.set({ ... }) .

Excel.Interfaces.FilterPivot An interface describing the data returned by calling


HierarchyData filterPivotHierarchy.toJSON() .

Excel.Interfaces.FilterPivot Represents the Excel FilterPivotHierarchy.


HierarchyLoadOptions

Excel.Interfaces.FilterPivot An interface for updating data on the FilterPivotHierarchy object,


HierarchyUpdateData for use in filterPivotHierarchy.set({ ... }) .

Excel.Interfaces.Format An interface describing the data returned by calling


ProtectionData formatProtection.toJSON() .

Excel.Interfaces.Format Represents the format protection of a range object.


ProtectionLoadOptions

Excel.Interfaces.Format An interface for updating data on the FormatProtection object,


ProtectionUpdateData for use in formatProtection.set({ ... }) .

Excel.Interfaces.FunctionResult An interface describing the data returned by calling


Data functionResult.toJSON() .

Excel.Interfaces.FunctionResult An object containing the result of a function-evaluation


LoadOptions operation
Excel.Interfaces.Geometric An interface describing the data returned by calling
ShapeData geometricShape.toJSON() .

Excel.Interfaces.Geometric Represents a geometric shape inside a worksheet. A geometric


ShapeLoadOptions shape can be a rectangle, block arrow, equation symbol,
flowchart item, star, banner, callout, or any other basic shape in
Excel.

Excel.Interfaces.GroupShape An interface describing the data returned by calling


CollectionData groupShapeCollection.toJSON() .

Excel.Interfaces.GroupShape Represents the shape collection inside a shape group.


CollectionLoadOptions

Excel.Interfaces.GroupShape An interface for updating data on the GroupShapeCollection


CollectionUpdateData object, for use in groupShapeCollection.set({ ... }) .

Excel.Interfaces.HeaderFooter An interface describing the data returned by calling


Data headerFooter.toJSON() .

Excel.Interfaces.HeaderFooter An interface describing the data returned by calling


GroupData headerFooterGroup.toJSON() .

Excel.Interfaces.HeaderFooterGroupLoadOptions

Excel.Interfaces.HeaderFooter An interface for updating data on the HeaderFooterGroup


GroupUpdateData object, for use in headerFooterGroup.set({ ... }) .

Excel.Interfaces.HeaderFooterLoadOptions

Excel.Interfaces.HeaderFooter An interface for updating data on the HeaderFooter object, for


UpdateData use in headerFooter.set({ ... }) .

Excel.Interfaces.IconSet An interface describing the data returned by calling


ConditionalFormatData iconSetConditionalFormat.toJSON() .

Excel.Interfaces.IconSet Represents an icon set criteria for conditional formatting.


ConditionalFormatLoad
Options

Excel.Interfaces.IconSet An interface for updating data on the IconSetConditionalFormat


ConditionalFormatUpdate object, for use in iconSetConditionalFormat.set({ ... }) .
Data

Excel.Interfaces.ImageData An interface describing the data returned by calling


image.toJSON() .

Excel.Interfaces.ImageLoad Represents an image in the worksheet. To get the corresponding


Options Shape object, use Image.shape .

Excel.Interfaces.Iterative An interface describing the data returned by calling


CalculationData
iterativeCalculation.toJSON() .

Excel.Interfaces.Iterative Represents the iterative calculation settings.


CalculationLoadOptions

Excel.Interfaces.Iterative An interface for updating data on the IterativeCalculation object,


CalculationUpdateData for use in iterativeCalculation.set({ ... }) .

Excel.Interfaces.LineData An interface describing the data returned by calling


line.toJSON() .

Excel.Interfaces.LineLoad Represents a line inside a worksheet. To get the corresponding


Options Shape object, use Line.shape .

Excel.Interfaces.LineUpdate An interface for updating data on the Line object, for use in
Data line.set({ ... }) .

Excel.Interfaces.LinkedData An interface describing the data returned by calling


TypeCollectionData linkedDataTypeCollection.toJSON() .

Excel.Interfaces.LinkedData Represents a collection of linked data types.


TypeCollectionLoadOptions

Excel.Interfaces.LinkedData An interface for updating data on the LinkedDataTypeCollection


TypeCollectionUpdateData object, for use in linkedDataTypeCollection.set({ ... }) .

Excel.Interfaces.LinkedData An interface describing the data returned by calling


TypeData linkedDataType.toJSON() .

Excel.Interfaces.LinkedData Represents a linked data type. A linked data type is a data type
TypeLoadOptions connected to an online data source.

Excel.Interfaces.Linked An interface describing the data returned by calling


WorkbookCollectionData linkedWorkbookCollection.toJSON() .

Excel.Interfaces.Linked Represents a collection of linked workbook objects.


WorkbookCollectionLoad
Options

Excel.Interfaces.Linked An interface for updating data on the LinkedWorkbookCollection


WorkbookCollectionUpdate object, for use in linkedWorkbookCollection.set({ ... }) .
Data

Excel.Interfaces.Linked An interface describing the data returned by calling


WorkbookData linkedWorkbook.toJSON() .

Excel.Interfaces.Linked Contains information about a linked workbook. If a workbook


WorkbookLoadOptions has links pointing to data in another workbook, the second
workbook is linked to the first workbook. In this scenario, the
second workbook is called the "linked workbook".
Excel.Interfaces.NamedItem An interface describing the data returned by calling
ArrayValuesData namedItemArrayValues.toJSON() .

Excel.Interfaces.NamedItem Represents an object containing values and types of a named


ArrayValuesLoadOptions item.

Excel.Interfaces.NamedItem An interface describing the data returned by calling


CollectionData namedItemCollection.toJSON() .

Excel.Interfaces.NamedItem A collection of all the NamedItem objects that are part of the
CollectionLoadOptions workbook or worksheet, depending on how it was reached.

Excel.Interfaces.NamedItem An interface for updating data on the NamedItemCollection


CollectionUpdateData object, for use in namedItemCollection.set({ ... }) .

Excel.Interfaces.NamedItem An interface describing the data returned by calling


Data namedItem.toJSON() .

Excel.Interfaces.NamedItem Represents a defined name for a range of cells or value. Names


LoadOptions can be primitive named objects (as seen in the type below),
range object, or a reference to a range. This object can be used
to obtain range object associated with names.

Excel.Interfaces.NamedItem An interface for updating data on the NamedItem object, for use
UpdateData in namedItem.set({ ... }) .

Excel.Interfaces.NamedSheet An interface describing the data returned by calling


ViewCollectionData namedSheetViewCollection.toJSON() .

Excel.Interfaces.NamedSheet Represents the collection of sheet views in the worksheet.


ViewCollectionLoadOptions

Excel.Interfaces.NamedSheet An interface for updating data on the


ViewCollectionUpdateData NamedSheetViewCollection object, for use in
namedSheetViewCollection.set({ ... }) .

Excel.Interfaces.NamedSheet An interface describing the data returned by calling


ViewData namedSheetView.toJSON() .

Excel.Interfaces.NamedSheet Represents a named sheet view of a worksheet. A sheet view


ViewLoadOptions stores the sort and filter rules for a particular worksheet. Every
sheet view (even a temporary sheet view) has a unique,
worksheet-scoped name that is used to access the view.

Excel.Interfaces.NamedSheet An interface for updating data on the NamedSheetView object,


ViewUpdateData for use in namedSheetView.set({ ... }) .

Excel.Interfaces.Number An interface describing the data returned by calling


FormatInfoData numberFormatInfo.toJSON() .
Excel.Interfaces.Number Defines the culturally appropriate format of displaying numbers.
FormatInfoLoadOptions This is based on current system culture settings.

Excel.Interfaces.PageBreak An interface describing the data returned by calling


CollectionData pageBreakCollection.toJSON() .

Excel.Interfaces.PageBreakCollectionLoadOptions

Excel.Interfaces.PageBreak An interface for updating data on the PageBreakCollection


CollectionUpdateData object, for use in pageBreakCollection.set({ ... }) .

Excel.Interfaces.PageBreak An interface describing the data returned by calling


Data pageBreak.toJSON() .

Excel.Interfaces.PageBreakLoadOptions

Excel.Interfaces.PageLayout An interface describing the data returned by calling


Data pageLayout.toJSON() .

Excel.Interfaces.PageLayout Represents layout and print settings that are not dependent on
LoadOptions any printer-specific implementation. These settings include
margins, orientation, page numbering, title rows, and print area.

Excel.Interfaces.PageLayout An interface for updating data on the PageLayout object, for use
UpdateData in pageLayout.set({ ... }) .

Excel.Interfaces.PivotField An interface describing the data returned by calling


CollectionData pivotFieldCollection.toJSON() .

Excel.Interfaces.PivotField Represents a collection of all the PivotFields that are part of a


CollectionLoadOptions PivotTable's hierarchy.

Excel.Interfaces.PivotField An interface for updating data on the PivotFieldCollection


CollectionUpdateData object, for use in pivotFieldCollection.set({ ... }) .

Excel.Interfaces.PivotFieldData An interface describing the data returned by calling


pivotField.toJSON() .

Excel.Interfaces.PivotFieldLoad Represents the Excel PivotField.


Options

Excel.Interfaces.PivotField An interface for updating data on the PivotField object, for use in
UpdateData pivotField.set({ ... }) .

Excel.Interfaces.PivotHierarchy An interface describing the data returned by calling


CollectionData pivotHierarchyCollection.toJSON() .

Excel.Interfaces.PivotHierarchy Represents a collection of all the PivotHierarchies that are part of


CollectionLoadOptions the PivotTable.

Excel.Interfaces.PivotHierarchy An interface for updating data on the PivotHierarchyCollection


CollectionUpdateData
object, for use in pivotHierarchyCollection.set({ ... }) .

Excel.Interfaces.PivotHierarchy An interface describing the data returned by calling


Data pivotHierarchy.toJSON() .

Excel.Interfaces.PivotHierarchy Represents the Excel PivotHierarchy.


LoadOptions

Excel.Interfaces.PivotHierarchy An interface for updating data on the PivotHierarchy object, for


UpdateData use in pivotHierarchy.set({ ... }) .

Excel.Interfaces.PivotItem An interface describing the data returned by calling


CollectionData pivotItemCollection.toJSON() .

Excel.Interfaces.PivotItem Represents a collection of all the PivotItems related to their


CollectionLoadOptions parent PivotField.

Excel.Interfaces.PivotItem An interface for updating data on the PivotItemCollection object,


CollectionUpdateData for use in pivotItemCollection.set({ ... }) .

Excel.Interfaces.PivotItemData An interface describing the data returned by calling


pivotItem.toJSON() .

Excel.Interfaces.PivotItemLoad Represents the Excel PivotItem.


Options

Excel.Interfaces.PivotItem An interface for updating data on the PivotItem object, for use in
UpdateData pivotItem.set({ ... }) .

Excel.Interfaces.PivotLayout An interface describing the data returned by calling


Data pivotLayout.toJSON() .

Excel.Interfaces.PivotLayout Represents the visual layout of the PivotTable.


LoadOptions

Excel.Interfaces.PivotLayout An interface for updating data on the PivotLayout object, for use
UpdateData in pivotLayout.set({ ... }) .

Excel.Interfaces.PivotTable An interface describing the data returned by calling


CollectionData pivotTableCollection.toJSON() .

Excel.Interfaces.PivotTable Represents a collection of all the PivotTables that are part of the
CollectionLoadOptions workbook or worksheet.

Excel.Interfaces.PivotTable An interface for updating data on the PivotTableCollection


CollectionUpdateData object, for use in pivotTableCollection.set({ ... }) .

Excel.Interfaces.PivotTableData An interface describing the data returned by calling


pivotTable.toJSON() .

Excel.Interfaces.PivotTable Represents an Excel PivotTable. To learn more about the


LoadOptions PivotTable object model, read Work with PivotTables using the
Excel JavaScript API.

Excel.Interfaces.PivotTable An interface describing the data returned by calling


ScopedCollectionData pivotTableScopedCollection.toJSON() .

Excel.Interfaces.PivotTable Represents a scoped collection of PivotTables. The PivotTables


ScopedCollectionLoadOptions are sorted based on the location of the PivotTable's top-left
corner. They are ordered top-to-bottom and then left-to-right.

Excel.Interfaces.PivotTable An interface for updating data on the


ScopedCollectionUpdateData PivotTableScopedCollection object, for use in
pivotTableScopedCollection.set({ ... }) .

Excel.Interfaces.PivotTableStyle An interface describing the data returned by calling


CollectionData pivotTableStyleCollection.toJSON() .

Excel.Interfaces.PivotTableStyle Represents a collection of PivotTable styles.


CollectionLoadOptions

Excel.Interfaces.PivotTableStyle An interface for updating data on the PivotTableStyleCollection


CollectionUpdateData object, for use in pivotTableStyleCollection.set({ ... }) .

Excel.Interfaces.PivotTableStyle An interface describing the data returned by calling


Data pivotTableStyle.toJSON() .

Excel.Interfaces.PivotTableStyle Represents a PivotTable style, which defines style elements by


LoadOptions PivotTable region.

Excel.Interfaces.PivotTableStyle An interface for updating data on the PivotTableStyle object, for


UpdateData use in pivotTableStyle.set({ ... }) .

Excel.Interfaces.PivotTable An interface for updating data on the PivotTable object, for use
UpdateData in pivotTable.set({ ... }) .

Excel.Interfaces.PresetCriteria An interface describing the data returned by calling


ConditionalFormatData presetCriteriaConditionalFormat.toJSON() .

Excel.Interfaces.PresetCriteria Represents the preset criteria conditional format such as above


ConditionalFormatLoad average, below average, unique values, contains blank, nonblank,
Options error, and noerror.

Excel.Interfaces.PresetCriteria An interface for updating data on the


ConditionalFormatUpdate PresetCriteriaConditionalFormat object, for use in
Data presetCriteriaConditionalFormat.set({ ... }) .

Excel.Interfaces.Query An interface describing the data returned by calling


CollectionData queryCollection.toJSON() .

Excel.Interfaces.Query Represents the collection of queries in the workbook.


CollectionLoadOptions
Excel.Interfaces.Query An interface for updating data on the QueryCollection object, for
CollectionUpdateData use in queryCollection.set({ ... }) .

Excel.Interfaces.QueryData An interface describing the data returned by calling


query.toJSON() .

Excel.Interfaces.QueryLoad Represents a Power Query query.


Options

Excel.Interfaces.RangeAreas An interface describing the data returned by calling


CollectionData rangeAreasCollection.toJSON() .

Excel.Interfaces.RangeAreas Contains the collection of cross-workbook level ranges.


CollectionLoadOptions

Excel.Interfaces.RangeAreas An interface for updating data on the RangeAreasCollection


CollectionUpdateData object, for use in rangeAreasCollection.set({ ... }) .

Excel.Interfaces.RangeAreas An interface describing the data returned by calling


Data rangeAreas.toJSON() .

Excel.Interfaces.RangeAreas RangeAreas represents a collection of one or more rectangular


LoadOptions ranges in the same worksheet. To learn how to use
discontiguous ranges, read Work with multiple ranges
simultaneously in Excel add-ins.

Excel.Interfaces.RangeAreas An interface for updating data on the RangeAreas object, for use
UpdateData in rangeAreas.set({ ... }) .

Excel.Interfaces.RangeBorder An interface describing the data returned by calling


CollectionData rangeBorderCollection.toJSON() .

Excel.Interfaces.RangeBorder Represents the border objects that make up the range border.
CollectionLoadOptions

Excel.Interfaces.RangeBorder An interface for updating data on the RangeBorderCollection


CollectionUpdateData object, for use in rangeBorderCollection.set({ ... }) .

Excel.Interfaces.RangeBorder An interface describing the data returned by calling


Data rangeBorder.toJSON() .

Excel.Interfaces.RangeBorder Represents the border of an object.


LoadOptions

Excel.Interfaces.RangeBorder An interface for updating data on the RangeBorder object, for


UpdateData use in rangeBorder.set({ ... }) .

Excel.Interfaces.Range An interface describing the data returned by calling


CollectionData rangeCollection.toJSON() .

Excel.Interfaces.RangeCollectionLoadOptions
Excel.Interfaces.Range An interface for updating data on the RangeCollection object,
CollectionUpdateData for use in rangeCollection.set({ ... }) .

Excel.Interfaces.RangeData An interface describing the data returned by calling


range.toJSON() .

Excel.Interfaces.RangeFillData An interface describing the data returned by calling


rangeFill.toJSON() .

Excel.Interfaces.RangeFillLoad Represents the background of a range object.


Options

Excel.Interfaces.RangeFill An interface for updating data on the RangeFill object, for use in
UpdateData rangeFill.set({ ... }) .

Excel.Interfaces.RangeFont An interface describing the data returned by calling


Data rangeFont.toJSON() .

Excel.Interfaces.RangeFont This object represents the font attributes (font name, font size,
LoadOptions color, etc.) for an object.

Excel.Interfaces.RangeFont An interface for updating data on the RangeFont object, for use
UpdateData in rangeFont.set({ ... }) .

Excel.Interfaces.RangeFormat An interface describing the data returned by calling


Data rangeFormat.toJSON() .

Excel.Interfaces.RangeFormat A format object encapsulating the range's font, fill, borders,


LoadOptions alignment, and other properties.

Excel.Interfaces.RangeFormat An interface for updating data on the RangeFormat object, for


UpdateData use in rangeFormat.set({ ... }) .

Excel.Interfaces.RangeLoad Range represents a set of one or more contiguous cells such as a


Options cell, a row, a column, or a block of cells. To learn more about
how ranges are used throughout the API, start with Ranges in
the Excel JavaScript API.

Excel.Interfaces.RangeUpdate An interface for updating data on the Range object, for use in
Data range.set({ ... }) .

Excel.Interfaces.RangeView An interface describing the data returned by calling


CollectionData rangeViewCollection.toJSON() .

Excel.Interfaces.RangeView Represents a collection of RangeView objects.


CollectionLoadOptions

Excel.Interfaces.RangeView An interface for updating data on the RangeViewCollection


CollectionUpdateData object, for use in rangeViewCollection.set({ ... }) .

Excel.Interfaces.RangeView An interface describing the data returned by calling


Data
rangeView.toJSON() .

Excel.Interfaces.RangeView RangeView represents a set of visible cells of the parent range.


LoadOptions

Excel.Interfaces.RangeView An interface for updating data on the RangeView object, for use
UpdateData in rangeView.set({ ... }) .

Excel.Interfaces.Remove An interface describing the data returned by calling


DuplicatesResultData removeDuplicatesResult.toJSON() .

Excel.Interfaces.Remove Represents the results from Range.removeDuplicates .


DuplicatesResultLoadOptions

Excel.Interfaces.RowColumn An interface describing the data returned by calling


PivotHierarchyCollectionData rowColumnPivotHierarchyCollection.toJSON() .

Excel.Interfaces.RowColumn Represents a collection of RowColumnPivotHierarchy items


PivotHierarchyCollectionLoad associated with the PivotTable.
Options

Excel.Interfaces.RowColumn An interface for updating data on the


PivotHierarchyCollection RowColumnPivotHierarchyCollection object, for use in
UpdateData rowColumnPivotHierarchyCollection.set({ ... }) .

Excel.Interfaces.RowColumn An interface describing the data returned by calling


PivotHierarchyData rowColumnPivotHierarchy.toJSON() .

Excel.Interfaces.RowColumn Represents the Excel RowColumnPivotHierarchy.


PivotHierarchyLoadOptions

Excel.Interfaces.RowColumn An interface for updating data on the RowColumnPivotHierarchy


PivotHierarchyUpdateData object, for use in rowColumnPivotHierarchy.set({ ... }) .

Excel.Interfaces.RuntimeData An interface describing the data returned by calling


runtime.toJSON() .

Excel.Interfaces.RuntimeLoad Represents the Excel Runtime class.


Options

Excel.Interfaces.Runtime An interface for updating data on the Runtime object, for use in
UpdateData runtime.set({ ... }) .

Excel.Interfaces.Setting An interface describing the data returned by calling


CollectionData settingCollection.toJSON() .

Excel.Interfaces.Setting Represents a collection of key-value pair setting objects that are


CollectionLoadOptions part of the workbook. The scope is limited to per file and add-in
(task-pane or content) combination.

Excel.Interfaces.Setting An interface for updating data on the SettingCollection object,


CollectionUpdateData
for use in settingCollection.set({ ... }) .

Excel.Interfaces.SettingData An interface describing the data returned by calling


setting.toJSON() .

Excel.Interfaces.SettingLoad Setting represents a key-value pair of a setting persisted to the


Options document (per file, per add-in). These custom key-value pair can
be used to store state or lifecycle information needed by the
content or task-pane add-in. Note that settings are persisted in
the document and hence it is not a place to store any sensitive
or protected information such as user information and password.

Excel.Interfaces.SettingUpdate An interface for updating data on the Setting object, for use in
Data setting.set({ ... }) .

Excel.Interfaces.Shape An interface describing the data returned by calling


CollectionData shapeCollection.toJSON() .

Excel.Interfaces.Shape Represents a collection of all the shapes in the worksheet.


CollectionLoadOptions

Excel.Interfaces.Shape An interface for updating data on the ShapeCollection object, for


CollectionUpdateData use in shapeCollection.set({ ... }) .

Excel.Interfaces.ShapeData An interface describing the data returned by calling


shape.toJSON() .

Excel.Interfaces.ShapeFillData An interface describing the data returned by calling


shapeFill.toJSON() .

Excel.Interfaces.ShapeFillLoad Represents the fill formatting of a shape object.


Options

Excel.Interfaces.ShapeFill An interface for updating data on the ShapeFill object, for use in
UpdateData shapeFill.set({ ... }) .

Excel.Interfaces.ShapeFont An interface describing the data returned by calling


Data shapeFont.toJSON() .

Excel.Interfaces.ShapeFont Represents the font attributes, such as font name, font size, and
LoadOptions color, for a shape's TextRange object.

Excel.Interfaces.ShapeFont An interface for updating data on the ShapeFont object, for use
UpdateData in shapeFont.set({ ... }) .

Excel.Interfaces.ShapeGroup An interface describing the data returned by calling


Data shapeGroup.toJSON() .

Excel.Interfaces.ShapeGroup Represents a shape group inside a worksheet. To get the


LoadOptions corresponding Shape object, use ShapeGroup.shape .
Excel.Interfaces.ShapeLine An interface describing the data returned by calling
FormatData shapeLineFormat.toJSON() .

Excel.Interfaces.ShapeLine Represents the line formatting for the shape object. For images
FormatLoadOptions and geometric shapes, line formatting represents the border of
the shape.

Excel.Interfaces.ShapeLine An interface for updating data on the ShapeLineFormat object,


FormatUpdateData for use in shapeLineFormat.set({ ... }) .

Excel.Interfaces.ShapeLoad Represents a generic shape object in the worksheet. A shape


Options could be a geometric shape, a line, a group of shapes, etc. To
learn more about the shape object model, read Work with
shapes using the Excel JavaScript API.

Excel.Interfaces.ShapeUpdate An interface for updating data on the Shape object, for use in
Data shape.set({ ... }) .

Excel.Interfaces.Slicer An interface describing the data returned by calling


CollectionData slicerCollection.toJSON() .

Excel.Interfaces.Slicer Represents a collection of all the slicer objects in the workbook


CollectionLoadOptions or a worksheet.

Excel.Interfaces.Slicer An interface for updating data on the SlicerCollection object, for


CollectionUpdateData use in slicerCollection.set({ ... }) .

Excel.Interfaces.SlicerData An interface describing the data returned by calling


slicer.toJSON() .

Excel.Interfaces.SlicerItem An interface describing the data returned by calling


CollectionData slicerItemCollection.toJSON() .

Excel.Interfaces.SlicerItem Represents a collection of all the slicer item objects in the slicer.
CollectionLoadOptions

Excel.Interfaces.SlicerItem An interface for updating data on the SlicerItemCollection


CollectionUpdateData object, for use in slicerItemCollection.set({ ... }) .

Excel.Interfaces.SlicerItemData An interface describing the data returned by calling


slicerItem.toJSON() .

Excel.Interfaces.SlicerItemLoad Represents a slicer item in a slicer.


Options

Excel.Interfaces.SlicerItem An interface for updating data on the SlicerItem object, for use
UpdateData in slicerItem.set({ ... }) .

Excel.Interfaces.SlicerLoad Represents a Slicer object in the workbook.


Options
Excel.Interfaces.SlicerStyle An interface describing the data returned by calling
CollectionData slicerStyleCollection.toJSON() .

Excel.Interfaces.SlicerStyle Represents a collection of SlicerStyle objects.


CollectionLoadOptions

Excel.Interfaces.SlicerStyle An interface for updating data on the SlicerStyleCollection


CollectionUpdateData object, for use in slicerStyleCollection.set({ ... }) .

Excel.Interfaces.SlicerStyleData An interface describing the data returned by calling


slicerStyle.toJSON() .

Excel.Interfaces.SlicerStyleLoad Represents a slicer style, which defines style elements by region


Options of the slicer.

Excel.Interfaces.SlicerStyle An interface for updating data on the SlicerStyle object, for use
UpdateData in slicerStyle.set({ ... }) .

Excel.Interfaces.SlicerUpdate An interface for updating data on the Slicer object, for use in
Data slicer.set({ ... }) .

Excel.Interfaces.StyleCollection An interface describing the data returned by calling


Data styleCollection.toJSON() .

Excel.Interfaces.StyleCollection Represents a collection of all the styles.


LoadOptions

Excel.Interfaces.StyleCollection An interface for updating data on the StyleCollection object, for


UpdateData use in styleCollection.set({ ... }) .

Excel.Interfaces.StyleData An interface describing the data returned by calling


style.toJSON() .

Excel.Interfaces.StyleLoad An object encapsulating a style's format and other properties.


Options

Excel.Interfaces.StyleUpdate An interface for updating data on the Style object, for use in
Data style.set({ ... }) .

Excel.Interfaces.Table An interface describing the data returned by calling


CollectionData tableCollection.toJSON() .

Excel.Interfaces.Table Represents a collection of all the tables that are part of the
CollectionLoadOptions workbook or worksheet, depending on how it was reached.

Excel.Interfaces.Table An interface for updating data on the TableCollection object, for


CollectionUpdateData use in tableCollection.set({ ... }) .

Excel.Interfaces.TableColumn An interface describing the data returned by calling


CollectionData tableColumnCollection.toJSON() .
Excel.Interfaces.TableColumn Represents a collection of all the columns that are part of the
CollectionLoadOptions table.

Excel.Interfaces.TableColumn An interface for updating data on the TableColumnCollection


CollectionUpdateData object, for use in tableColumnCollection.set({ ... }) .

Excel.Interfaces.TableColumn An interface describing the data returned by calling


Data tableColumn.toJSON() .

Excel.Interfaces.TableColumn Represents a column in a table.


LoadOptions

Excel.Interfaces.TableColumn An interface for updating data on the TableColumn object, for


UpdateData use in tableColumn.set({ ... }) .

Excel.Interfaces.TableData An interface describing the data returned by calling


table.toJSON() .

Excel.Interfaces.TableLoad Represents an Excel table. To learn more about the table object
Options model, read Work with tables using the Excel JavaScript API.

Excel.Interfaces.TableRow An interface describing the data returned by calling


CollectionData tableRowCollection.toJSON() .

Excel.Interfaces.TableRow Represents a collection of all the rows that are part of the table.
CollectionLoadOptions
Note that unlike ranges or columns, which will adjust if new rows
or columns are added before them, a TableRow object represents
the physical location of the table row, but not the data. That is, if
the data is sorted or if new rows are added, a table row will
continue to point at the index for which it was created.

Excel.Interfaces.TableRow An interface for updating data on the TableRowCollection object,


CollectionUpdateData for use in tableRowCollection.set({ ... }) .

Excel.Interfaces.TableRowData An interface describing the data returned by calling


tableRow.toJSON() .

Excel.Interfaces.TableRowLoad Represents a row in a table.


Options
Note that unlike ranges or columns, which will adjust if new rows
or columns are added before them, a TableRow object represents
the physical location of the table row, but not the data. That is, if
the data is sorted or if new rows are added, a table row will
continue to point at the index for which it was created.

Excel.Interfaces.TableRow An interface for updating data on the TableRow object, for use in
UpdateData tableRow.set({ ... }) .

Excel.Interfaces.TableScoped An interface describing the data returned by calling


CollectionData tableScopedCollection.toJSON() .
Excel.Interfaces.TableScoped Represents a scoped collection of tables. For each table its top-
CollectionLoadOptions left corner is considered its anchor location, and the tables are
sorted top-to-bottom and then left-to-right.

Excel.Interfaces.TableScoped An interface for updating data on the TableScopedCollection


CollectionUpdateData object, for use in tableScopedCollection.set({ ... }) .

Excel.Interfaces.TableSortData An interface describing the data returned by calling


tableSort.toJSON() .

Excel.Interfaces.TableSortLoad Manages sorting operations on Table objects.


Options

Excel.Interfaces.TableStyle An interface describing the data returned by calling


CollectionData tableStyleCollection.toJSON() .

Excel.Interfaces.TableStyle Represents a collection of table styles.


CollectionLoadOptions

Excel.Interfaces.TableStyle An interface for updating data on the TableStyleCollection


CollectionUpdateData object, for use in tableStyleCollection.set({ ... }) .

Excel.Interfaces.TableStyleData An interface describing the data returned by calling


tableStyle.toJSON() .

Excel.Interfaces.TableStyleLoad Represents a table style, which defines the style elements by


Options region of the table.

Excel.Interfaces.TableStyle An interface for updating data on the TableStyle object, for use
UpdateData in tableStyle.set({ ... }) .

Excel.Interfaces.TableUpdate An interface for updating data on the Table object, for use in
Data table.set({ ... }) .

Excel.Interfaces.Text An interface describing the data returned by calling


ConditionalFormatData textConditionalFormat.toJSON() .

Excel.Interfaces.Text Represents a specific text conditional format.


ConditionalFormatLoad
Options

Excel.Interfaces.Text An interface for updating data on the TextConditionalFormat


ConditionalFormatUpdate object, for use in textConditionalFormat.set({ ... }) .
Data

Excel.Interfaces.TextFrameData An interface describing the data returned by calling


textFrame.toJSON() .

Excel.Interfaces.TextFrame Represents the text frame of a shape object.


LoadOptions
Excel.Interfaces.TextFrame An interface for updating data on the TextFrame object, for use
UpdateData in textFrame.set({ ... }) .

Excel.Interfaces.TextRangeData An interface describing the data returned by calling


textRange.toJSON() .

Excel.Interfaces.TextRange Contains the text that is attached to a shape, in addition to


LoadOptions properties and methods for manipulating the text.

Excel.Interfaces.TextRange An interface for updating data on the TextRange object, for use
UpdateData in textRange.set({ ... }) .

Excel.Interfaces.TimelineStyle An interface describing the data returned by calling


CollectionData timelineStyleCollection.toJSON() .

Excel.Interfaces.TimelineStyle Represents a collection of timeline styles.


CollectionLoadOptions

Excel.Interfaces.TimelineStyle An interface for updating data on the TimelineStyleCollection


CollectionUpdateData object, for use in timelineStyleCollection.set({ ... }) .

Excel.Interfaces.TimelineStyle An interface describing the data returned by calling


Data timelineStyle.toJSON() .

Excel.Interfaces.TimelineStyle Represents a TimelineStyle , which defines style elements by


LoadOptions region in the timeline.

Excel.Interfaces.TimelineStyle An interface for updating data on the TimelineStyle object, for


UpdateData use in timelineStyle.set({ ... }) .

Excel.Interfaces.TopBottom An interface describing the data returned by calling


ConditionalFormatData topBottomConditionalFormat.toJSON() .

Excel.Interfaces.TopBottom Represents a top/bottom conditional format.


ConditionalFormatLoad
Options

Excel.Interfaces.TopBottom An interface for updating data on the


ConditionalFormatUpdate TopBottomConditionalFormat object, for use in
Data topBottomConditionalFormat.set({ ... }) .

Excel.Interfaces.Workbook An interface describing the data returned by calling


CreatedData workbookCreated.toJSON() .

Excel.Interfaces.Workbook An interface describing the data returned by calling


Data workbook.toJSON() .

Excel.Interfaces.Workbook Workbook is the top level object which contains related


LoadOptions workbook objects such as worksheets, tables, and ranges. To
learn more about the workbook object model, read Work with
workbooks using the Excel JavaScript API.
Excel.Interfaces.Workbook An interface describing the data returned by calling
ProtectionData workbookProtection.toJSON() .

Excel.Interfaces.Workbook Represents the protection of a workbook object.


ProtectionLoadOptions

Excel.Interfaces.Workbook An interface describing the data returned by calling


RangeAreasData workbookRangeAreas.toJSON() .

Excel.Interfaces.Workbook Represents a collection of one or more rectangular ranges in


RangeAreasLoadOptions multiple worksheets.

Excel.Interfaces.Workbook An interface for updating data on the Workbook object, for use
UpdateData in workbook.set({ ... }) .

Excel.Interfaces.Worksheet An interface describing the data returned by calling


CollectionData worksheetCollection.toJSON() .

Excel.Interfaces.Worksheet Represents a collection of worksheet objects that are part of the


CollectionLoadOptions workbook.

Excel.Interfaces.Worksheet An interface for updating data on the WorksheetCollection


CollectionUpdateData object, for use in worksheetCollection.set({ ... }) .

Excel.Interfaces.Worksheet An interface describing the data returned by calling


CustomPropertyCollection worksheetCustomPropertyCollection.toJSON() .
Data

Excel.Interfaces.Worksheet Contains the collection of worksheet-level custom property.


CustomPropertyCollection
LoadOptions

Excel.Interfaces.Worksheet An interface for updating data on the


CustomPropertyCollection WorksheetCustomPropertyCollection object, for use in
UpdateData worksheetCustomPropertyCollection.set({ ... }) .

Excel.Interfaces.Worksheet An interface describing the data returned by calling


CustomPropertyData worksheetCustomProperty.toJSON() .

Excel.Interfaces.Worksheet Represents a worksheet-level custom property.


CustomPropertyLoadOptions

Excel.Interfaces.Worksheet An interface for updating data on the WorksheetCustomProperty


CustomPropertyUpdateData object, for use in worksheetCustomProperty.set({ ... }) .

Excel.Interfaces.Worksheet An interface describing the data returned by calling


Data worksheet.toJSON() .

Excel.Interfaces.Worksheet An Excel worksheet is a grid of cells. It can contain data, tables,


LoadOptions charts, etc. To learn more about the worksheet object model,
read Work with worksheets using the Excel JavaScript API.
Excel.Interfaces.Worksheet An interface describing the data returned by calling
ProtectionData worksheetProtection.toJSON() .

Excel.Interfaces.Worksheet Represents the protection of a worksheet object.


ProtectionLoadOptions

Excel.Interfaces.Worksheet An interface for updating data on the Worksheet object, for use
UpdateData in worksheet.set({ ... }) .

Excel.LinkedDataTypeAdded The argument that is passed to the event handler after a new
EventArgs linked data type is added to the workbook.

Excel.LinkedEntityCellValue Represents a value whose properties derive from a service.

Excel.LinkedEntityId The linked entity ID object represents a set of properties that


describes a service and culture for locating this service defined
value.

Excel.ListDataValidation Represents the List data validation criteria.

Excel.LocalImageCellValue Represents the value of a cell containing a locally stored or


generated image.

Excel.LocalImageCellValue The UID of a previously cached image.


CacheId

Excel.NameErrorCellValue Represents the value of a cell containing a #NAME? error.

Excel.NotAvailableErrorCell Represents the value of a cell containing a #N/A! error.


Value

Excel.NullErrorCellValue Represents the value of a cell containing a #NULL! error.

Excel.NumErrorCellValue Represents the value of a cell containing a #NUM! error.

Excel.PageLayoutMargin Represents the options in page layout margins.


Options

Excel.PageLayoutZoom Represents page zoom properties.


Options

Excel.PivotDateFilter Configurable template for a date filter to apply to a PivotField.


The condition defines what criteria need to be set in order for
the filter to operate.

Excel.PivotFilters An interface representing all PivotFilters currently applied to a


given PivotField.

Excel.PivotLabelFilter Configurable template for a label filter to apply to a PivotField.


The condition defines what criteria need to be set in order for
the filter to operate.
Excel.PivotManualFilter Configurable template for a manual filter to apply to a
PivotField. The condition defines what criteria need to be set in
order for the filter to operate.

Excel.PivotValueFilter Configurable template for a value filter to apply to a PivotField.


The condition defines what criteria need to be set in order for
the filter to operate.

Excel.PlaceholderErrorCell Represents the value of a cell containing a #BUSY! error. This


Value type of error is used as a placeholder while the value of a cell is
downloaded.

Excel.RangeHyperlink Represents the necessary strings to get/set a hyperlink (XHL)


object.

Excel.RangeReference Represents a string reference of the form "SheetName!A1:B5", or


a global or local named range.

Excel.ReferenceCellValue Represents a reference into referencedValues . One scenario for


using this reference is to avoid duplicating cell value objects
(such as an EntityCellValue ). Define a cell value object once in
referencedValues , and then refer to that cell value from many
places by using a ReferenceCellValue where the duplicated
value would have appeared.

Excel.RefErrorCellValue Represents the value of a cell containing a #REF! error.

Excel.RefreshModeChanged Represents information about a newly added linked data type,


EventArgs such as source and ID.

Excel.RefreshRequest The argument that is passed to the event handler upon


CompletedEventArgs completion of refresh request to an external service or link.

Excel.ReplaceCriteria Represents the replace criteria to be used.

Excel.RootReferenceCellValue Represents a reference to the value which contains


referencedValues .

Excel.RowProperties Represents the returned properties of getRowProperties.

[ API set: ExcelApi 1.9 ]

Excel.RowPropertiesLoad Represents which row properties to load, when used as part of a


Options "range.getRowProperties" method.

[ API set: ExcelApi 1.9 ]

Excel.RunOptions

Excel.SearchCriteria Represents the search criteria to be used.


Excel.SelectionChangedEvent Provides information about the document that raised the
Args selection changed event.

Excel.Session

Excel.SettableCellProperties Represents the input parameter of setCellProperties.

[ API set: ExcelApi 1.9 ]

Excel.SettableColumn Represents the input parameter of setColumnProperties.


Properties
[ API set: ExcelApi 1.9 ]

Excel.SettableRowProperties Represents the input parameter of setRowProperties.

[ API set: ExcelApi 1.9 ]

Excel.SettingsChangedEvent Provides information about the setting that raised the settings
Args changed event

Excel.ShapeActivatedEvent Provides information about the shape that raised the activated
Args event.

Excel.ShapeDeactivatedEvent Provides information about the shape that raised the


Args deactivated event.

Excel.ShowAsRule

Excel.SortField Represents a condition in a sorting operation.

Excel.SpillErrorCellValue Represents the value of a cell containing a #SPILL! error.

Excel.StringCellValue Represents the value of a cell containing a string.

Excel.Subtotals Subtotals for the Pivot Field.

Excel.TableAddedEventArgs Provides information about the table that raised the added
event.

Excel.TableChangedEventArgs Provides information about the table that raised the changed
event.

Excel.TableDeletedEventArgs Provides information about the table that raised the deleted
event.

Excel.TableFilteredEventArgs Provides information about the table that raised the filter
applied event.

Excel.TableSelectionChanged Provides information about the table that raised the selection
EventArgs changed event.

Excel.ThreeArrowsGraySet [ API set: ExcelApi 1.2 ]


Excel.ThreeArrowsSet [ API set: ExcelApi 1.2 ]

Excel.ThreeFlagsSet [ API set: ExcelApi 1.2 ]

Excel.ThreeSignsSet [ API set: ExcelApi 1.2 ]

Excel.ThreeStarsSet [ API set: ExcelApi 1.2 ]

Excel.ThreeSymbols2Set [ API set: ExcelApi 1.2 ]

Excel.ThreeSymbolsSet [ API set: ExcelApi 1.2 ]

Excel.ThreeTrafficLights1Set [ API set: ExcelApi 1.2 ]

Excel.ThreeTrafficLights2Set [ API set: ExcelApi 1.2 ]

Excel.ThreeTrianglesSet [ API set: ExcelApi 1.2 ]

Excel.ValueErrorCellValue Represents the value of a cell containing a #VALUE! error.

Excel.ValueTypeNotAvailable Represents the value of a cell containing a type of value which


CellValue cannot be serialized. For example, an #UNKNOWN! error which
represents a type of rich value not known to this version of Excel.

Excel.WebImageCellValue Represents the value of a cell containing an image downloaded


from the internet.

Excel.WorkbookActivated Provides information about the workbook that raised the


EventArgs activated event.

Excel.WorkbookAutoSave Provides information about the workbook's


SettingChangedEventArgs onAutoSaveSettingChanged event.

Excel.WorksheetActivated Provides information about the worksheet that raised the


EventArgs activated event.

Excel.WorksheetAddedEvent Provides information about the worksheet that raised the added
Args event.

Excel.WorksheetCalculated Provides information about the worksheet that raised the


EventArgs calculated event.

Excel.WorksheetChangedEvent Provides information about the worksheet that raised the


Args changed event.

Excel.WorksheetColumnSorted Provides information about the column-sorted event and its


EventArgs related worksheet.

Excel.WorksheetDeactivated Provides information about the worksheet that raised the


EventArgs deactivated event.

Excel.WorksheetDeletedEvent Provides information about the worksheet that raised the


Args deleted event.

Excel.WorksheetFilteredEvent Provides information about the worksheet that raised the filter
Args applied event.

Excel.WorksheetFormat Provides information about the worksheet format change event.


ChangedEventArgs

Excel.WorksheetFormula Provides information about the worksheet and formulas that


ChangedEventArgs raised the formula changed event.

Excel.WorksheetMovedEvent Notifies when a worksheet is moved within a workbook.


Args
If a worksheet is moved from one position within the workbook
to another via the Excel UI, then this API will trigger an event.
Note that if the position of a worksheet changes as a result of
moving a different worksheet, then this event won't trigger for
both position changes. This event only triggers for the primary
worksheet move, and not any worksheet position changes that
occur as a result of that primary move.

Excel.WorksheetName Provides information about the worksheet whose name has


ChangedEventArgs changed.

Excel.WorksheetProtection Provides information about the worksheet that raised the


ChangedEventArgs protection status changed event, which fires when the protection
status is updated in a worksheet.

Excel.WorksheetProtection Represents the options in sheet protection.


Options

Excel.WorksheetRowHidden Provides information about the worksheet's row hidden change


ChangedEventArgs event.

Excel.WorksheetRowSorted Provides information about the row-sorted event and its related
EventArgs worksheet.

Excel.WorksheetSearchCriteria Represents the worksheet search criteria to be used.

Excel.WorksheetSelection Provides information about the worksheet that raised the


ChangedEventArgs selection changed event.

Excel.WorksheetSingleClicked Provides information about the left-clicked/tapped event and its


EventArgs related worksheet.

Excel.WorksheetVisibility Provides information about the worksheet whose visibility has


ChangedEventArgs changed.

Type Aliases
Excel.CardLayout Represents the layout of a card in card view.

Excel.CardLayoutSection Represents the layout of a section of a card in card view.

Excel.CellValue Represents the value in a cell.

Excel.CellValueAndProperty Represents the value and metadata of a property. The metadata


Metadata applies to the property (and not the value), but it is combined
with the value in this type.

Excel.CompactLayout Represents the layout used when there is limited space to


represent the entity.

Excel.EntityPropertyType Represents the value of a property.

Excel.ErrorCellValue Represents a cell value which contains an error.

Excel.ReferencedValue Represents the value in a cell.

Enums
Excel.AggregationFunction Aggregation function for the DataPivotHierarchy .

Excel.ArrowheadLength

Excel.ArrowheadStyle

Excel.ArrowheadWidth

Excel.AutoFillType The behavior types when AutoFill is used on a range in the


workbook.

Excel.Base64EncodingType The file type represented by the base64 encoding.

Excel.BindingType

Excel.BlockedErrorCellValue Represents types of #BLOCKED! errors.


SubType

Excel.BorderIndex

Excel.BorderLineStyle

Excel.BorderWeight

Excel.BuiltInPivotTableStyle Represents a built-in PivotTable style

Excel.BuiltInSlicerStyle Represents a built-in slicer style.

Excel.BuiltInStyle
Excel.BuiltInTableStyle Represents a built-in table style.

Excel.BusyErrorCellValueSub Represents types of #BUSY! errors.


Type

Excel.CalcErrorCellValueSub Represents types of #CALC! errors.


Type

Excel.CalculationMode

Excel.CalculationState Represents the state of calculation across the entire Excel


application.

Excel.CalculationType

Excel.CellValueType Represents the types of the CellValue object.

Excel.ChartAxisCategoryType Specifies the type of the category axis.

Excel.ChartAxisDisplayUnit

Excel.ChartAxisGroup

Excel.ChartAxisPosition

Excel.ChartAxisScaleType

Excel.ChartAxisTickLabelPosition

Excel.ChartAxisTickMark

Excel.ChartAxisTimeUnit Specifies the unit of time for chart axes and data series.

Excel.ChartAxisType

Excel.ChartBinType Specifies the bin type of a histogram chart or pareto chart series.

Excel.ChartBoxQuartile Represents the quartile calculation type of chart series layout.


Calculation Only applies to a box and whisker chart.

Excel.ChartColorScheme

Excel.ChartDataLabelPosition

Excel.ChartDataSourceType Specifies the data source type of the chart series.

Excel.ChartDisplayBlanksAs

Excel.ChartErrorBarsInclude Represents which parts of the error bar to include.

Excel.ChartErrorBarsType Represents the range type for error bars.

Excel.ChartGradientStyle Represents the gradient style of a chart series. This is only


applicable for region map charts.

Excel.ChartGradientStyleType Represents the gradient style type of a chart series. This is only
applicable for region map charts.

Excel.ChartLegendPosition

Excel.ChartLineStyle

Excel.ChartMapAreaLevel Represents the mapping level of a chart series. This only applies
to region map charts.

Excel.ChartMapLabelStrategy Represents the region level of a chart series layout. This only
applies to region map charts.

Excel.ChartMapProjectionType Represents the region projection type of a chart series layout.


This only applies to region map charts.

Excel.ChartMarkerStyle

Excel.ChartParentLabel Represents the parent label strategy of the chart series layout.
Strategy This only applies to treemap charts

Excel.ChartPlotAreaPosition

Excel.ChartPlotBy

Excel.ChartSeriesBy Specifies whether the series are by rows or by columns. In Excel


on desktop, the "auto" option will inspect the source data shape
to automatically guess whether the data is by rows or columns.
In Excel on the web, "auto" will simply default to "columns".

Excel.ChartSeriesDimension Represents the dimensions when getting values from chart


series.

Excel.ChartSplitType

Excel.ChartTextHorizontal Represents the horizontal alignment for the specified object.


Alignment

Excel.ChartTextVertical Represents the vertical alignment for the specified object.


Alignment

Excel.ChartTickLabelAlignment

Excel.ChartTitlePosition Represents the position of the chart title.

Excel.ChartTrendlineType

Excel.ChartType

Excel.ChartUnderlineStyle
Excel.ClearApplyTo

Excel.CloseBehavior Specifies the close behavior for Workbook.close .

Excel.CommentChangeType Represents how the comments in the event were changed.

Excel.ConditionalCellValue Represents the operator of the text conditional format type.


Operator

Excel.ConditionalDataBarAxis Represents the format options for a data bar axis.


Format

Excel.ConditionalDataBar Represents the data bar direction within a cell.


Direction

Excel.ConditionalFormatColor Represents the types of color criterion for conditional


CriterionType formatting.

Excel.ConditionalFormat Represents the direction for a selection.


Direction

Excel.ConditionalFormatIcon Represents the types of icon conditional format.


RuleType

Excel.ConditionalFormatPreset Represents the criteria of the preset criteria conditional format


Criterion type.

Excel.ConditionalFormatRule Represents the types of conditional format values.


Type

Excel.ConditionalFormatType

Excel.ConditionalIconCriterion Represents the operator for each icon criteria.


Operator

Excel.ConditionalRangeBorderIndex

Excel.ConditionalRangeBorderLineStyle

Excel.ConditionalRangeFontUnderlineStyle

Excel.ConditionalTextOperator Represents the operator of the text conditional format type.

Excel.ConditionalTopBottom Represents the criteria for the above/below average conditional


CriterionType format type.

Excel.ConnectErrorCellValue Represents types of #CONNECT! errors.


SubType

Excel.ConnectorType

Excel.ContentType
Excel.DataChangeType

Excel.DataSourceType Represents a command type of DataConnection .

Excel.DataValidationAlertStyle Represents the data validation error alert style. The default is
Stop .

Excel.DataValidationOperator Represents the data validation operator enum.

Excel.DataValidationType Represents the data validation type enum.

Excel.DateFilterCondition Enum representing all accepted conditions by which a date filter


can be applied. Used to configure the type of PivotFilter that is
applied to the field.

Excel.DeleteShiftDirection

Excel.DocumentPropertyItem

Excel.DocumentPropertyType

Excel.DocumentTaskChange Represents the type of change recorded in the task change


Action record.

Excel.DynamicFilterCriteria

Excel.EntityCardLayoutType Types of entity card layouts.

Excel.EntityCompactLayout The list of icons available for EntityCompactLayout . An icon


Icons displays in the Excel UI, either to the left of the title in a cell that
contains an entity card, or to the left of the title of a referenced
entity inside an entity card. Selecting the icon opens the entity
card.

Excel.ErrorCellValueType Represents the types of the ErrorCellValue object.

Excel.ErrorCodes

Excel.EventSource

Excel.EventTriggerSource

Excel.EventType

Excel.ExternalErrorCellValue Represents types of #EXTERNAL! errors.


SubType

Excel.FieldErrorCellValueSub Represents types of #FIELD! errors.


Type

Excel.FillPattern
Excel.FilterDatetimeSpecificity

Excel.FilterOn

Excel.FilterOperator

Excel.GeometricShapeType Specifies the shape type for a GeometricShape object.

Excel.GroupOption

Excel.HeaderFooterState

Excel.HorizontalAlignment

Excel.IconSet

Excel.ImageFittingMode

Excel.InsertShiftDirection

Excel.KeyboardDirection

Excel.LabelFilterCondition Enum representing all accepted conditions by which a label filter


can be applied. Used to configure the type of PivotFilter that is
applied to the field. PivotFilter.criteria.exclusive can be set
to true to invert many of these conditions.

Excel.LinkedDataTypeRefresh Representation of a refresh mode.


Mode

Excel.LinkedDataTypeState

Excel.LoadToType An enum that specifies the query load to destination.

Excel.NamedItemScope

Excel.NamedItemType

Excel.NumberFormatCategory Represents a category of number formats.

Excel.NumErrorCellValueSub Represents types of #NUM! errors.


Type

Excel.PageOrientation

Excel.PaperType

Excel.PictureFormat The format of the image.

Excel.PivotAxis Represents the axis from which to get the PivotItems.

Excel.PivotFilterTopBottom Represents the criteria for the top/bottom values filter.


Criterion
Excel.PivotFilterType A simple enum that represents a type of filter for a PivotField.

Excel.PivotLayoutType

Excel.PivotTableDateGroupBy Represents the DateTime Grouping condition.

Excel.Placement Specifies the way that an object is attached to its underlying


cells.

Excel.PrintComments

Excel.PrintErrorType

Excel.PrintMarginUnit

Excel.PrintOrder

Excel.ProtectionSelectionMode

Excel.QueryError An enum that specifies the query load error message.

Excel.RangeCopyType

Excel.RangeUnderlineStyle

Excel.RangeValueType

Excel.ReadingOrder

Excel.ReferenceValueType Represents the types of the ReferenceValue object.

Excel.RefErrorCellValueSub Represents types of #REF! errors.


Type

Excel.RibbonTab

Excel.RowHiddenChangeType

Excel.SaveBehavior Specifies the save behavior for Workbook.save .

Excel.SearchDirection Specifies the search direction.

Excel.ShapeAutoSize Determines the type of automatic sizing allowed.

Excel.ShapeFillType Specifies a shape's fill type.

Excel.ShapeFontUnderlineStyle The type of underline applied to a font.

Excel.ShapeLineDashStyle The dash style for a line.

Excel.ShapeLineStyle The style for a line.

Excel.ShapeScaleFrom Specifies which part of the shape retains its position when the
shape is scaled.

Excel.ShapeScaleType Specifies whether the shape is scaled relative to its original or


current size.

Excel.ShapeTextHorizontal Specifies the horizontal alignment for the text frame in a shape.
Alignment

Excel.ShapeTextHorizontal Specifies the horizontal overflow for the text frame in a shape.
Overflow

Excel.ShapeTextOrientation Specifies the orientation for the text frame in a shape.

Excel.ShapeTextReadingOrder Specifies the reading order for the text frame in a shape.

Excel.ShapeTextVertical Specifies the vertical alignment for the text frame in a shape.
Alignment

Excel.ShapeTextVertical Specifies the vertical overflow for the text frame in a shape.
Overflow

Excel.ShapeType Specifies the type of a shape.

Excel.ShapeZOrder Specifies where in the z-order a shape should be moved relative


to other shapes.

Excel.SheetVisibility

Excel.ShowAsCalculation The ShowAs calculation function for the DataPivotField.

Excel.SlicerSortType Specifies the slicer sort behavior for Slicer.sortBy .

Excel.SortBy Represents the sort direction.

Excel.SortDataOption

Excel.SortMethod

Excel.SortOn

Excel.SortOrientation

Excel.SpecialCellType

Excel.SpecialCellValueType

Excel.SpillErrorCellValueSub Represents types of #SPILL! errors.


Type

Excel.SubtotalLocationType

Excel.TopBottomSelectionType A simple enum for top/bottom filters to select whether to filter


by the top N or bottom N percent, number, or sum of values.
Excel.ValueErrorCellValueSub Represents types of #VALUE! errors.
Type

Excel.ValueFilterCondition Enum representing all accepted conditions by which a value filter


can be applied. Used to configure the type of PivotFilter that is
applied to the field. PivotFilter.exclusive can be set to true
to invert many of these conditions.

Excel.VerticalAlignment

Excel.WorkbookLinksRefresh Represents the refresh mode of the workbook links.


Mode

Excel.WorksheetPositionType

Functions
Excel.createWorkbook(base64) Creates and opens a new workbook. Optionally, the workbook
can be pre-populated with a base64-encoded .xlsx file. Note:
Macros can be a security risk. If this API is used to create a
workbook that includes a macro, the add-in user will be
prompted with a "Trust this add-in?" dialog in the Excel UI. The
user must select the "Trust add-in" button to proceed.

[ API set: ExcelApi 1.8 ]

Excel.getDataCommon
Postprocess(response, call
Args)

Excel.postprocessBinding
Descriptor(response)

Excel.run(batch) Executes a batch script that performs actions on the Excel object
model, using a new RequestContext. When the promise is
resolved, any tracked objects that were automatically allocated
during execution will be released.

Excel.run(object, batch) Executes a batch script that performs actions on the Excel object
model, using the RequestContext of a previously-created API
object. When the promise is resolved, any tracked objects that
were automatically allocated during execution will be released.

Excel.run(objects, batch) Executes a batch script that performs actions on the Excel object
model, using the RequestContext of previously-created API
objects.

Excel.run(options, batch) Executes a batch script that performs actions on the Excel object
model, using the RequestContext of a previously-created API
object. When the promise is resolved, any tracked objects that
were automatically allocated during execution will be released.

Excel.run(context, batch) Executes a batch script that performs actions on the Excel object
model, using the RequestContext of a previously-created object.
When the promise is resolved, any tracked objects that were
automatically allocated during execution will be released.

Function Details

Excel.createWorkbook(base64)
Creates and opens a new workbook. Optionally, the workbook can be pre-populated
with a base64-encoded .xlsx file. Note: Macros can be a security risk. If this API is
used to create a workbook that includes a macro, the add-in user will be prompted
with a "Trust this add-in?" dialog in the Excel UI. The user must select the "Trust add-
in" button to proceed.

[ API set: ExcelApi 1.8 ]

TypeScript

export function createWorkbook(base64?: string): Promise<void>;

Parameters
base64 string

Returns
Promise<void>

Examples

TypeScript

const myFile = <HTMLInputElement>document.getElementById("file");


const reader = new FileReader();

reader.onload = (event) => {


Excel.run((context) => {
// Remove the metadata before the base64-encoded string.
const startIndex = reader.result.toString().indexOf("base64,");
const mybase64 = reader.result.toString().substr(startIndex + 7);
Excel.createWorkbook(mybase64);
return context.sync();
});
};

// Read in the file as a data URL so we can parse the base64-encoded


string.
reader.readAsDataURL(myFile.files[0]);

Excel.getDataCommonPostprocess(response, callArgs)
TypeScript

export function getDataCommonPostprocess(response: any, callArgs: any):


any;

Parameters
response any

callArgs any

Returns
any

Excel.postprocessBindingDescriptor(response)
TypeScript

export function postprocessBindingDescriptor(response: any): any;

Parameters
response any

Returns
any

Excel.run(batch)
Executes a batch script that performs actions on the Excel object model, using a new
RequestContext. When the promise is resolved, any tracked objects that were
automatically allocated during execution will be released.

TypeScript

export function run<T>(batch: (context: Excel.RequestContext) =>


Promise<T>): Promise<T>;

Parameters
batch (context: Excel.RequestContext) => Promise<T>
A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Excel
application. Since the Office add-in and the Excel application run in two different
processes, the RequestContext is required to get access to the Excel object model
from the add-in.

Returns
Promise<T>

Excel.run(object, batch)
Executes a batch script that performs actions on the Excel object model, using the
RequestContext of a previously-created API object. When the promise is resolved,
any tracked objects that were automatically allocated during execution will be
released.

TypeScript

export function run<T>(object: OfficeExtension.ClientObject, batch:


(context: Excel.RequestContext) => Promise<T>): Promise<T>;

Parameters
object OfficeExtension.ClientObject
A previously-created API object. The batch will use the same RequestContext as the
passed-in object, which means that any changes applied to the object will be picked
up by "context.sync()".

batch (context: Excel.RequestContext) => Promise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Excel
application. Since the Office add-in and the Excel application run in two different
processes, the RequestContext is required to get access to the Excel object model
from the add-in.

Returns
Promise<T>

Excel.run(objects, batch)
Executes a batch script that performs actions on the Excel object model, using the
RequestContext of previously-created API objects.

TypeScript

export function run<T>(objects: OfficeExtension.ClientObject[], batch:


(context: Excel.RequestContext) => Promise<T>): Promise<T>;

Parameters
objects OfficeExtension.ClientObject[]
An array of previously-created API objects. The array will be validated to make sure
that all of the objects share the same context. The batch will use this shared
RequestContext, which means that any changes applied to these objects will be
picked up by "context.sync()".

batch (context: Excel.RequestContext) => Promise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Excel
application. Since the Office add-in and the Excel application run in two different
processes, the RequestContext is required to get access to the Excel object model
from the add-in.

Returns
Promise<T>

Excel.run(options, batch)
Executes a batch script that performs actions on the Excel object model, using the
RequestContext of a previously-created API object. When the promise is resolved,
any tracked objects that were automatically allocated during execution will be
released.

TypeScript

export function run<T>(options: Excel.RunOptions, batch: (context:


Excel.RequestContext) => Promise<T>): Promise<T>;

Parameters
options Excel.RunOptions
The additional options for this Excel.run which specify previous objects, whether to
delay the request for cell edit, session info, etc.

batch (context: Excel.RequestContext) => Promise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Excel
application. Since the Office add-in and the Excel application run in two different
processes, the RequestContext is required to get access to the Excel object model
from the add-in.

Returns
Promise<T>

Excel.run(context, batch)
Executes a batch script that performs actions on the Excel object model, using the
RequestContext of a previously-created object. When the promise is resolved, any
tracked objects that were automatically allocated during execution will be released.

TypeScript

export function run<T>(context: OfficeExtension.ClientRequestContext,


batch: (context: Excel.RequestContext) => Promise<T>): Promise<T>;

Parameters
context OfficeExtension.ClientRequestContext
A previously-created object. The batch will use the same RequestContext as the
passed-in object, which means that any changes applied to the object will be picked
up by "context.sync()".

batch (context: Excel.RequestContext) => Promise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Excel
application. Since the Office add-in and the Excel application run in two different
processes, the RequestContext is required to get access to the Excel object model
from the add-in.

Returns
Promise<T>
Excel JavaScript object model in Office
Add-ins
Article • 03/21/2023

This article describes how to use the Excel JavaScript API to build add-ins for Excel 2016
or later. It introduces core concepts that are fundamental to using the API and provides
guidance for performing specific tasks such as reading or writing to a large range,
updating all cells in range, and more.

) Important

See Using the application-specific API model to learn about the asynchronous
nature of the Excel APIs and how they work with the workbook.

Office.js APIs for Excel


An Excel add-in interacts with objects in Excel by using the Office JavaScript API, which
includes two JavaScript object models:

Excel JavaScript API: Introduced with Office 2016, the Excel JavaScript API provides
strongly-typed Excel objects that you can use to access worksheets, ranges, tables,
charts, and more.

Common API: Introduced with Office 2013, the Common API enables you to access
features such as UI, dialogs, and client settings that are common across multiple
types of Office applications. The limited functionality for Excel interaction in the
Common API has been replaced by the Excel JavaScript API.

While you'll likely use the Excel JavaScript API to develop the majority of functionality,
you'll also use objects in the Common API. For example:

Context: The Context object represents the runtime environment of the add-in and
provides access to key objects of the API. It consists of workbook configuration
details such as contentLanguage and officeTheme and also provides information
about the add-in's runtime environment such as host and platform . Additionally,
it provides the requirements.isSetSupported() method, which you can use to
check whether the specified requirement set is supported by the Excel application
where the add-in is running.
Document: The Document object provides the getFileAsync() method, which you
can use to download the Excel file where the add-in is running.

The following image illustrates when you might use the Excel JavaScript API or the
Common APIs.

Excel-specific object model


To understand the Excel APIs, you must understand how the components of a workbook
are related to one another.

A Workbook contains one or more Worksheets.


A Worksheet contains collections of those data objects that are present in the
individual sheet, and gives access to cells through Range objects.
A Range represents a group of contiguous cells.
Ranges are used to create and place Tables, Charts, Shapes, and other data
visualization or organization objects.
Workbooks contain collections of some of those data objects (such as Tables) for
the entire Workbook.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using
the Excel JavaScript API to learn more.
Ranges
A range is a group of contiguous cells in the workbook. Add-ins typically use A1-style
notation (e.g. B3 for the single cell in column B and row 3 or C2:F4 for the cells from
columns C through F and rows 2 through 4) to define ranges.

Ranges have three core properties: values , formulas , and format . These properties get
or set the cell values, formulas to be evaluated, and the visual formatting of the cells.

Range sample
The following sample shows how to create sales records. This function uses Range
objects to set the values, formulas, and formats.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();

// Create the headers and format them to stand out.


let headers = [
["Product", "Quantity", "Unit Price", "Totals"]
];
let headerRange = sheet.getRange("B2:E2");
headerRange.values = headers;
headerRange.format.fill.color = "#4472C4";
headerRange.format.font.color = "white";

// Create the product data rows.


let productData = [
["Almonds", 6, 7.5],
["Coffee", 20, 34.5],
["Chocolate", 10, 9.56],
];
let dataRange = sheet.getRange("B3:D5");
dataRange.values = productData;

// Create the formulas to total the amounts sold.


let totalFormulas = [
["=C3 * D3"],
["=C4 * D4"],
["=C5 * D5"],
["=SUM(E3:E5)"]
];
let totalRange = sheet.getRange("E3:E6");
totalRange.formulas = totalFormulas;
totalRange.format.font.bold = true;

// Display the totals as US dollar amounts.


totalRange.numberFormat = [["$0.00"]];
await context.sync();
});

This sample creates the following data in the current worksheet.

For more information, see Set and get range values, text, or formulas using the Excel
JavaScript API.

Charts, tables, and other data objects


The Excel JavaScript APIs can create and manipulate the data structures and
visualizations within Excel. Tables and charts are two of the more commonly used
objects, but the APIs support PivotTables, shapes, images, and more.

Creating a table
Create tables by using data-filled ranges. Formatting and table controls (such as filters)
are automatically applied to the range.

The following sample creates a table using the ranges from the previous sample.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.tables.add("B2:E5", true);
await context.sync();
});

Using this sample code on the worksheet with the previous data creates the following
table.
For more information, see Work with tables using the Excel JavaScript API.

Creating a chart

Create charts to visualize the data in a range. The APIs support dozens of chart varieties,
each of which can be customized to suit your needs.

The following sample creates a simple column chart for three items and places it 100
pixels below the top of the worksheet.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let chart = sheet.charts.add(Excel.ChartType.columnStacked,
sheet.getRange("B3:C5"));
chart.top = 100;
await context.sync();
});

Running this sample on the worksheet with the previous table creates the following
chart.

For more information, see Work with charts using the Excel JavaScript API.
See also
Build your first Excel add-in
Excel add-ins code samples
Excel JavaScript API performance optimization
Excel JavaScript API reference
Blank and null values in Excel add-ins
Article • 03/22/2022

null and empty strings have special implications in the Excel JavaScript APIs. They're
used to represent empty cells, no formatting, or default values. This section details the
use of null and empty string when getting and setting properties.

null input in 2-D Array


In Excel, a range is represented by a 2-D array, where the first dimension is rows and the
second dimension is columns. To set values, number format, or formula for only specific
cells within a range, specify the values, number format, or formula for those cells in the
2-D array, and specify null for all other cells in the 2-D array.

For example, to update the number format for only one cell within a range, and retain
the existing number format for all other cells in the range, specify the new number
format for the cell to update, and specify null for all other cells. The following code
snippet sets a new number format for the fourth cell in the range, and leaves the
number format unchanged for the first three cells in the range.

JavaScript

range.values = [['Eurasia', '29.96', '0.25', '15-Feb' ]];


range.numberFormat = [[null, null, null, 'm/d/yyyy;@']];

null input for a property


null is not a valid input for single property. For example, the following code snippet is
not valid, as the values property of the range cannot be set to null .

JavaScript

range.values = null; // This is not a valid snippet.

Likewise, the following code snippet is not valid, as null is not a valid value for the
color property.

JavaScript

range.format.fill.color = null; // This is not a valid snippet.


null property values in the response
Formatting properties such as size and color will contain null values in the response
when different values exist in the specified range. For example, if you retrieve a range
and load its format.font.color property:

If all cells in the range have the same font color, range.format.font.color specifies
that color.
If multiple font colors are present within the range, range.format.font.color is
null .

Blank input for a property


When you specify a blank value for a property (i.e., two quotation marks with no space
in-between '' ), it will be interpreted as an instruction to clear or reset the property. For
example:

If you specify a blank value for the values property of a range, the content of the
range is cleared.
If you specify a blank value for the numberFormat property, the number format is
reset to General .
If you specify a blank value for the formula property and formulaLocale property,
the formula values are cleared.

Blank property values in the response


For read operations, a blank property value in the response (i.e., two quotation marks
with no space in-between '' ) indicates that cell contains no data or value. In the first
example below, the first and last cell in the range contain no data. In the second
example, the first two cells in the range do not contain a formula.

JavaScript

range.values = [['', 'some', 'data', 'in', 'other', 'cells', '']];

JavaScript

range.formula = [['', '', '=Rand()']];


Call built-in Excel worksheet functions
Article • 03/22/2022

This article explains how to call built-in Excel worksheet functions such as VLOOKUP and
SUM using the Excel JavaScript API. It also provides the full list of built-in Excel worksheet

functions that can be called using the Excel JavaScript API.

7 Note

For information about how to create custom functions in Excel using the Excel
JavaScript API, see Create custom functions in Excel.

Calling a worksheet function


The following code snippet shows how to call a worksheet function, where
sampleFunction() is a placeholder that should be replaced with the name of the

function to call and the input parameters that the function requires. The value property
of the FunctionResult object that's returned by a worksheet function contains the result
of the specified function. As this example shows, you must load the value property of
the FunctionResult object before you can read it. In this example, the result of the
function is simply being written to the console.

JavaScript

await Excel.run(async (context) => {


let functionResult = context.workbook.functions.sampleFunction();
functionResult.load('value');

await context.sync();
console.log('Result of the function: ' + functionResult.value);
});

 Tip

See the Supported worksheet functions section of this article for a list of functions
that can be called using the Excel JavaScript API.

Sample data
The following image shows a table in an Excel worksheet that contains sales data for
various types of tools over a three month period. Each number in the table represents
the number of units sold for a specific tool in a specific month. The examples that follow
will show how to apply built-in worksheet functions to this data.

Example 1: Single function


The following code sample applies the VLOOKUP function to the sample data described
previously to identify the number of wrenches sold in November.

JavaScript

await Excel.run(async (context) => {


let range =
context.workbook.worksheets.getItem("Sheet1").getRange("A1:D4");
let unitSoldInNov = context.workbook.functions.vlookup("Wrench", range,
2, false);
unitSoldInNov.load('value');

await context.sync();
console.log(' Number of wrenches sold in November = ' +
unitSoldInNov.value);
});

Example 2: Nested functions


The following code sample applies the VLOOKUP function to the sample data described
previously to identify the number of wrenches sold in November and the number of
wrenches sold in December, and then applies the SUM function to calculate the total
number of wrenches sold during those two months.

As this example shows, when one or more function calls are nested within another
function call, you only need to load the final result that you subsequently want to read
(in this example, sumOfTwoLookups ). Any intermediate results (in this example, the result
of each VLOOKUP function) will be calculated and used to calculate the final result.
JavaScript

await Excel.run(async (context) => {


let range =
context.workbook.worksheets.getItem("Sheet1").getRange("A1:D4");
let sumOfTwoLookups = context.workbook.functions.sum(
context.workbook.functions.vlookup("Wrench", range, 2, false),
context.workbook.functions.vlookup("Wrench", range, 3, false)
);
sumOfTwoLookups.load('value');

await context.sync();
console.log(' Number of wrenches sold in November and December = ' +
sumOfTwoLookups.value);
});

Supported worksheet functions


The following built-in Excel worksheet functions can be called using the Excel JavaScript
API.

Function Description

ABS function Returns the absolute value of a number

ACCRINT function Returns the accrued interest for a security that pays periodic interest

ACCRINTM Returns the accrued interest for a security that pays interest at maturity
function

ACOS function Returns the arccosine of a number

ACOSH function Returns the inverse hyperbolic cosine of a number

ACOT function Returns the arccotangent of a number

ACOTH function Returns the hyperbolic arccotangent of a number

AMORDEGRC Returns the depreciation for each accounting period by using a


function depreciation coefficient

AMORLINC Returns the depreciation for each accounting period


function

AND function Returns TRUE if all of its arguments are true

ARABIC function Converts a Roman number to Arabic, as a number

AREAS function Returns the number of areas in a reference


Function Description

ASC function Changes full-width (double-byte) English letters or katakana within a


character string to half-width (single-byte) characters

ASIN function Returns the arcsine of a number

ASINH function Returns the inverse hyperbolic sine of a number

ATAN function Returns the arctangent of a number

ATAN2 function Returns the arctangent from x- and y-coordinates

ATANH function Returns the inverse hyperbolic tangent of a number

AVEDEV function Returns the average of the absolute deviations of data points from their
mean

AVERAGE function Returns the average of its arguments

AVERAGEA Returns the average of its arguments, including numbers, text, and logical
function values

AVERAGEIF Returns the average (arithmetic mean) of all the cells in a range that meet
function a given criteria

AVERAGEIFS Returns the average (arithmetic mean) of all cells that meet multiple
function criteria

BAHTTEXT Converts a number to text, using the ß (baht) currency format


function

BASE function Converts a number into a text representation with the given radix (base)

BESSELI function Returns the modified Bessel function In(x)

BESSELJ function Returns the Bessel function Jn(x)

BESSELK function Returns the modified Bessel function Kn(x)

BESSELY function Returns the Bessel function Yn(x)

BETA.DIST Returns the beta cumulative distribution function


function

BETA.INV function Returns the inverse of the cumulative distribution function for a specified
beta distribution

BIN2DEC function Converts a binary number to decimal

BIN2HEX function Converts a binary number to hexadecimal

BIN2OCT function Converts a binary number to octal


Function Description

BINOM.DIST Returns the individual term binomial distribution probability


function

BINOM.DIST.RANGE Returns the probability of a trial result using a binomial distribution


function

BINOM.INV Returns the smallest value for which the cumulative binomial distribution
function is less than or equal to a criterion value

BITAND function Returns a 'Bitwise And' of two numbers

BITLSHIFT Returns a value number shifted left by shift_amount bits


function

BITOR function Returns a bitwise OR of 2 numbers

BITRSHIFT Returns a value number shifted right by shift_amount bits


function

BITXOR function Returns a bitwise 'Exclusive Or' of two numbers

CEILING.MATH, Rounds a number up, to the nearest integer or to the nearest multiple of
ECMA_CEILING significance
functions

CEILING.PRECISE Rounds a number the nearest integer or to the nearest multiple of


function significance. Regardless of the sign of the number, the number is rounded
up.

CHAR function Returns the character specified by the code number

CHISQ.DIST Returns the cumulative beta probability density function


function

CHISQ.DIST.RT Returns the one-tailed probability of the chi-squared distribution


function

CHISQ.INV Returns the cumulative beta probability density function


function

CHISQ.INV.RT Returns the inverse of the one-tailed probability of the chi-squared


function distribution

CHOOSE function Chooses a value from a list of values

CLEAN function Removes all nonprintable characters from text

CODE function Returns a numeric code for the first character in a text string
Function Description

COLUMNS Returns the number of columns in a reference


function

COMBIN function Returns the number of combinations for a given number of objects

COMBINA Returns the number of combinations with repetitions for a given number
function of items

COMPLEX Converts real and imaginary coefficients into a complex number


function

CONCATENATE Joins several text items into one text item


function

CONFIDENCE.NORM Returns the confidence interval for a population mean


function

CONFIDENCE.T Returns the confidence interval for a population mean, using a Student's t
function distribution

CONVERT function Converts a number from one measurement system to another

COS function Returns the cosine of a number

COSH function Returns the hyperbolic cosine of a number

COT function Returns the cotangent of an angle

COTH function Returns the hyperbolic cotangent of a number

COUNT function Counts how many numbers are in the list of arguments

COUNTA function Counts how many values are in the list of arguments

COUNTBLANK Counts the number of blank cells within a range


function

COUNTIF function Counts the number of cells within a range that meet the given criteria

COUNTIFS Counts the number of cells within a range that meet multiple criteria
function

COUPDAYBS Returns the number of days from the beginning of the coupon period to
function the settlement date

COUPDAYS Returns the number of days in the coupon period that contains the
function settlement date

COUPDAYSNC Returns the number of days from the settlement date to the next coupon
function date
Function Description

COUPNCD Returns the next coupon date after the settlement date
function

COUPNUM Returns the number of coupons payable between the settlement date and
function maturity date

COUPPCD Returns the previous coupon date before the settlement date
function

CSC function Returns the cosecant of an angle

CSCH function Returns the hyperbolic cosecant of an angle

CUMIPMT Returns the cumulative interest paid between two periods


function

CUMPRINC Returns the cumulative principal paid on a loan between two periods
function

DATE function Returns the serial number of a particular date

DATEVALUE Converts a date in the form of text to a serial number


function

DAVERAGE Returns the average of selected database entries


function

DAY function Converts a serial number to a day of the month

DAYS function Returns the number of days between two dates

DAYS360 function Calculates the number of days between two dates based on a 360-day
year

DB function Returns the depreciation of an asset for a specified period by using the
fixed-declining balance method

DBCS function Changes half-width (single-byte) English letters or katakana within a


character string to full-width (double-byte) characters

DCOUNT function Counts the cells that contain numbers in a database

DCOUNTA Counts nonblank cells in a database


function

DDB function Returns the depreciation of an asset for a specified period by using the
double-declining balance method or some other method that you specify

DEC2BIN function Converts a decimal number to binary


Function Description

DEC2HEX function Converts a decimal number to hexadecimal

DEC2OCT function Converts a decimal number to octal

DECIMAL function Converts a text representation of a number in a given base into a decimal
number

DEGREES function Converts radians to degrees

DELTA function Tests whether two values are equal

DEVSQ function Returns the sum of squares of deviations

DGET function Extracts from a database a single record that matches the specified criteria

DISC function Returns the discount rate for a security

DMAX function Returns the maximum value from selected database entries

DMIN function Returns the minimum value from selected database entries

DOLLAR, USDOLLAR Converts a number to text, using the $ (dollar) currency format
functions

DOLLARDE Converts a dollar price, expressed as a fraction, into a dollar price,


function expressed as a decimal number

DOLLARFR Converts a dollar price, expressed as a decimal number, into a dollar price,
function expressed as a fraction

DPRODUCT Multiplies the values in a particular field of records that match the criteria
function in a database

DSTDEV function Estimates the standard deviation based on a sample of selected database
entries

DSTDEVP function Calculates the standard deviation based on the entire population of
selected database entries

DSUM function Adds the numbers in the field column of records in the database that
match the criteria

DURATION Returns the annual duration of a security with periodic interest payments
function

Dlet function Estimates variance based on a sample from selected database entries

DVARP function Calculates variance based on the entire population of selected database
entries
Function Description

EDATE function Returns the serial number of the date that is the indicated number of
months before or after the start date

EFFECT function Returns the effective annual interest rate

EOMONTH Returns the serial number of the last day of the month before or after a
function specified number of months

ERF function Returns the error function

ERF.PRECISE Returns the error function


function

ERFC function Returns the complementary error function

ERFC.PRECISE Returns the complementary ERF function integrated between x and infinity
function

ERROR.TYPE Returns a number corresponding to an error type


function

EVEN function Rounds a number up to the nearest even integer

EXACT function Checks to see if two text values are identical

EXP function Returns e raised to the power of a given number

EXPON.DIST Returns the exponential distribution


function

F.DIST function Returns the F probability distribution

F.DIST.RT function Returns the F probability distribution

F.INV function Returns the inverse of the F probability distribution

F.INV.RT function Returns the inverse of the F probability distribution

FACT function Returns the factorial of a number

FACTDOUBLE Returns the double factorial of a number


function

FALSE function Returns the logical value FALSE

FIND, FINDB Finds one text value within another (case-sensitive)


functions

FISHER function Returns the Fisher transformation


Function Description

FISHERINV Returns the inverse of the Fisher transformation


function

FIXED function Formats a number as text with a fixed number of decimals

FLOOR.MATH Rounds a number down, to the nearest integer or to the nearest multiple
function of significance

FLOOR.PRECISE Rounds a number down to the nearest integer or to the nearest multiple
function of significance. Regardless of the sign of the number, the number is
rounded down.

FV function Returns the future value of an investment

FVSCHEDULE Returns the future value of an initial principal after applying a series of
function compound interest rates

GAMMA function Returns the Gamma function value

GAMMA.DIST Returns the gamma distribution


function

GAMMA.INV Returns the inverse of the gamma cumulative distribution


function

GAMMALN Returns the natural logarithm of the gamma function, Γ(x)


function

GAMMALN.PRECISE Returns the natural logarithm of the gamma function, Γ(x)


function

GAUSS function Returns 0.5 less than the standard normal cumulative distribution

GCD function Returns the greatest common divisor

GEOMEAN Returns the geometric mean


function

GESTEP function Tests whether a number is greater than a threshold value

HARMEAN Returns the harmonic mean


function

HEX2BIN function Converts a hexadecimal number to binary

HEX2DEC function Converts a hexadecimal number to decimal

HEX2OCT function Converts a hexadecimal number to octal


Function Description

HLOOKUP Looks in the top row of an array and returns the value of the indicated cell
function

HOUR function Converts a serial number to an hour

HYPERLINK Creates a shortcut or jump that opens a document stored on a network


function server, an intranet, or the Internet

HYPGEOM.DIST Returns the hypergeometric distribution


function

IF function Specifies a logical test to perform

IMABS function Returns the absolute value (modulus) of a complex number

IMAGINARY Returns the imaginary coefficient of a complex number


function

IMARGUMENT Returns the argument theta, an angle expressed in radians


function

IMCONJUGATE Returns the complex conjugate of a complex number


function

IMCOS function Returns the cosine of a complex number

IMCOSH function Returns the hyperbolic cosine of a complex number

IMCOT function Returns the cotangent of a complex number

IMCSC function Returns the cosecant of a complex number

IMCSCH function Returns the hyperbolic cosecant of a complex number

IMDIV function Returns the quotient of two complex numbers

IMEXP function Returns the exponential of a complex number

IMLN function Returns the natural logarithm of a complex number

IMLOG10 function Returns the base-10 logarithm of a complex number

IMLOG2 function Returns the base-2 logarithm of a complex number

IMPOWER Returns a complex number raised to an integer power


function

IMPRODUCT Returns the product of from 2 to 255 complex numbers


function

IMREAL function Returns the real coefficient of a complex number


Function Description

IMSEC function Returns the secant of a complex number

IMSECH function Returns the hyperbolic secant of a complex number

IMSIN function Returns the sine of a complex number

IMSINH function Returns the hyperbolic sine of a complex number

IMSQRT function Returns the square root of a complex number

IMSUB function Returns the difference between two complex numbers

IMSUM function Returns the sum of complex numbers

IMTAN function Returns the tangent of a complex number

INT function Rounds a number down to the nearest integer

INTRATE function Returns the interest rate for a fully invested security

IPMT function Returns the interest payment for an investment for a given period

IRR function Returns the internal rate of return for a series of cash flows

ISERR function Returns TRUE if the value is any error value except #N/A

ISERROR function Returns TRUE if the value is any error value

ISEVEN function Returns TRUE if the number is even

ISFORMULA Returns TRUE if there is a reference to a cell that contains a formula


function

ISLOGICAL Returns TRUE if the value is a logical value


function

ISNA function Returns TRUE if the value is the #N/A error value

ISNONTEXT Returns TRUE if the value is not text


function

ISNUMBER Returns TRUE if the value is a number


function

ISO.CEILING Returns a number that is rounded up to the nearest integer or to the


function nearest multiple of significance

ISODD function Returns TRUE if the number is odd

ISOWEEKNUM Returns the number of the ISO week number of the year for a given date
function
Function Description

ISPMT function Calculates the interest paid during a specific period of an investment

ISREF function Returns TRUE if the value is a reference

ISTEXT function Returns TRUE if the value is text

KURT function Returns the kurtosis of a data set

LARGE function Returns the k-th largest value in a data set

LCM function Returns the least common multiple

LEFT, LEFTB Returns the leftmost characters from a text value


functions

LEN, LENB Returns the number of characters in a text string


functions

LN function Returns the natural logarithm of a number

LOG function Returns the logarithm of a number to a specified base

LOG10 function Returns the base-10 logarithm of a number

LOGNORM.DIST Returns the cumulative lognormal distribution


function

LOGNORM.INV Returns the inverse of the lognormal cumulative distribution


function

LOOKUP function Looks up values in a vector or array

LOWER function Converts text to lowercase

MATCH function Looks up values in a reference or array

MAX function Returns the maximum value in a list of arguments

MAXA function Returns the maximum value in a list of arguments, including numbers,
text, and logical values

MDURATION Returns the Macauley modified duration for a security with an assumed
function par value of $100

MEDIAN function Returns the median of the given numbers

MID, MIDB Returns a specific number of characters from a text string starting at the
functions position you specify

MIN function Returns the minimum value in a list of arguments


Function Description

MINA function Returns the smallest value in a list of arguments, including numbers, text,
and logical values

MINUTE function Converts a serial number to a minute

MIRR function Returns the internal rate of return where positive and negative cash flows
are financed at different rates

MOD function Returns the remainder from division

MONTH function Converts a serial number to a month

MROUND Returns a number rounded to the desired multiple


function

MULTINOMIAL Returns the multinomial of a set of numbers


function

N function Returns a value converted to a number

NA function Returns the error value #N/A

NEGBINOM.DIST Returns the negative binomial distribution


function

NETWORKDAYS Returns the number of whole workdays between two dates


function

NETWORKDAYS.INTL Returns the number of whole workdays between two dates using
function parameters to indicate which and how many days are weekend days

NOMINAL Returns the annual nominal interest rate


function

NORM.DIST Returns the normal cumulative distribution


function

NORM.INV Returns the inverse of the normal cumulative distribution


function

NORM.S.DIST Returns the standard normal cumulative distribution


function

NORM.S.INV Returns the inverse of the standard normal cumulative distribution


function

NOT function Reverses the logic of its argument

NOW function Returns the serial number of the current date and time
Function Description

NPER function Returns the number of periods for an investment

NPV function Returns the net present value of an investment based on a series of
periodic cash flows and a discount rate

NUMBERVALUE Converts text to number in a locale-independent manner


function

OCT2BIN function Converts an octal number to binary

OCT2DEC function Converts an octal number to decimal

OCT2HEX function Converts an octal number to hexadecimal

ODD function Rounds a number up to the nearest odd integer

ODDFPRICE Returns the price per $100 face value of a security with an odd first period
function

ODDFYIELD Returns the yield of a security with an odd first period


function

ODDLPRICE Returns the price per $100 face value of a security with an odd last period
function

ODDLYIELD Returns the yield of a security with an odd last period


function

OR function Returns TRUE if any argument is true

PDURATION Returns the number of periods required by an investment to reach a


function specified value

PERCENTILE.EXC Returns the k-th percentile of values in a range, where k is in the range
function 0..1, exclusive

PERCENTILE.INC Returns the k-th percentile of values in a range


function

PERCENTRANK.EXC Returns the rank of a value in a data set as a percentage (0..1, exclusive) of
function the data set

PERCENTRANK.INC Returns the percentage rank of a value in a data set


function

PERMUT function Returns the number of permutations for a given number of objects

PERMUTATIONA Returns the number of permutations for a given number of objects (with
function repetitions) that can be selected from the total objects
Function Description

PHI function Returns the value of the density function for a standard normal
distribution

PI function Returns the value of pi

PMT function Returns the periodic payment for an annuity

POISSON.DIST Returns the Poisson distribution


function

POWER function Returns the result of a number raised to a power

PPMT function Returns the payment on the principal for an investment for a given period

PRICE function Returns the price per $100 face value of a security that pays periodic
interest

PRICEDISC Returns the price per $100 face value of a discounted security
function

PRICEMAT Returns the price per $100 face value of a security that pays interest at
function maturity

PRODUCT Multiplies its arguments


function

PROPER function Capitalizes the first letter in each word of a text value

PV function Returns the present value of an investment

QUARTILE.EXC Returns the quartile of the data set, based on percentile values from 0..1,
function exclusive

QUARTILE.INC Returns the quartile of a data set


function

QUOTIENT Returns the integer portion of a division


function

RADIANS function Converts degrees to radians

RAND function Returns a random number between 0 and 1

RANDBETWEEN Returns a random number between the numbers you specify


function

RANK.AVG Returns the rank of a number in a list of numbers


function

RANK.EQ function Returns the rank of a number in a list of numbers


Function Description

RATE function Returns the interest rate per period of an annuity

RECEIVED function Returns the amount received at maturity for a fully invested security

REPLACE, REPLACEB Replaces characters within text


functions

REPT function Repeats text a given number of times

RIGHT, RIGHTB Returns the rightmost characters from a text value


functions

ROMAN function Converts an Arabic numeral to Roman, as text

ROUND function Rounds a number to a specified number of digits

ROUNDDOWN Rounds a number down, toward zero


function

ROUNDUP Rounds a number up, away from zero


function

ROWS function Returns the number of rows in a reference

RRI function Returns an equivalent interest rate for the growth of an investment

SEC function Returns the secant of an angle

SECH function Returns the hyperbolic secant of an angle

SECOND function Converts a serial number to a second

SERIESSUM Returns the sum of a power series based on the formula


function

SHEET function Returns the sheet number of the referenced sheet

SHEETS function Returns the number of sheets in a reference

SIGN function Returns the sign of a number

SIN function Returns the sine of the given angle

SINH function Returns the hyperbolic sine of a number

SKEW function Returns the skewness of a distribution

SKEW.P function Returns the skewness of a distribution based on a population: a


characterization of the degree of asymmetry of a distribution around its
mean
Function Description

SLN function Returns the straight-line depreciation of an asset for one period

SMALL function Returns the k-th smallest value in a data set

SQRT function Returns a positive square root

SQRTPI function Returns the square root of (number * pi)

STANDARDIZE Returns a normalized value


function

STDEV.P function Calculates standard deviation based on the entire population

STDEV.S function Estimates standard deviation based on a sample

STDEVA function Estimates standard deviation based on a sample, including numbers, text,
and logical values

STDEVPA function Calculates standard deviation based on the entire population, including
numbers, text, and logical values

SUBSTITUTE Substitutes new text for old text in a text string


function

SUBTOTAL Returns a subtotal in a list or database


function

SUM function Adds its arguments

SUMIF function Adds the cells specified by a given criteria

SUMIFS function Adds the cells in a range that meet multiple criteria

SUMSQ function Returns the sum of the squares of the arguments

SYD function Returns the sum-of-years' digits depreciation of an asset for a specified
period

T function Converts its arguments to text

T.DIST function Returns the Percentage Points (probability) for the Student t-distribution

T.DIST.2T function Returns the Percentage Points (probability) for the Student t-distribution

T.DIST.RT function Returns the Student's t-distribution

T.INV function Returns the t-value of the Student's t-distribution as a function of the
probability and the degrees of freedom

T.INV.2T function Returns the inverse of the Student's t-distribution


Function Description

TAN function Returns the tangent of a number

TANH function Returns the hyperbolic tangent of a number

TBILLEQ function Returns the bond-equivalent yield for a Treasury bill

TBILLPRICE Returns the price per $100 face value for a Treasury bill
function

TBILLYIELD Returns the yield for a Treasury bill


function

TEXT function Formats a number and converts it to text

TIME function Returns the serial number of a particular time

TIMEVALUE Converts a time in the form of text to a serial number


function

TODAY function Returns the serial number of today's date

TRIM function Removes spaces from text

TRIMMEAN Returns the mean of the interior of a data set


function

TRUE function Returns the logical value TRUE

TRUNC function Truncates a number to an integer

TYPE function Returns a number indicating the data type of a value

UNICHAR function Returns the Unicode character that is references by the given numeric
value

UNICODE function Returns the number (code point) that corresponds to the first character of
the text

UPPER function Converts text to uppercase

VALUE function Converts a text argument to a number

VAR.P function Calculates variance based on the entire population

VAR.S function Estimates variance based on a sample

VARA function Estimates variance based on a sample, including numbers, text, and logical
values

VARPA function Calculates variance based on the entire population, including numbers,
text, and logical values
Function Description

VDB function Returns the depreciation of an asset for a specified or partial period by
using a declining balance method

VLOOKUP Looks in the first column of an array and moves across the row to return
function the value of a cell

WEEKDAY function Converts a serial number to a day of the week

WEEKNUM Converts a serial number to a number representing where the week falls
function numerically with a year

WEIBULL.DIST Returns the Weibull distribution


function

WORKDAY Returns the serial number of the date before or after a specified number
function of workdays

WORKDAY.INTL Returns the serial number of the date before or after a specified number
function of workdays using parameters to indicate which and how many days are
weekend days

XIRR function Returns the internal rate of return for a schedule of cash flows that is not
necessarily periodic

XNPV function Returns the net present value for a schedule of cash flows that is not
necessarily periodic

XOR function Returns a logical exclusive OR of all arguments

YEAR function Converts a serial number to a year

YEARFRAC Returns the year fraction representing the number of whole days between
function start_date and end_date

YIELD function Returns the yield on a security that pays periodic interest

YIELDDISC Returns the annual yield for a discounted security; for example, a Treasury
function bill

YIELDMAT Returns the annual yield of a security that pays interest at maturity
function

Z.TEST function Returns the one-tailed probability-value of a z-test

See also
Excel JavaScript object model in Office Add-ins
Functions Class (JavaScript API for Excel)
Workbook Functions Object (JavaScript API for Excel)
Work with cells using the Excel
JavaScript API
Article • 05/02/2023

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, all Excel cells are
Range objects. An individual cell in the Excel UI translates to a Range object with one cell

in the Excel JavaScript API.

A Range object can also contain multiple, contiguous cells. Contiguous cells form an
unbroken rectangle (including single rows or columns). To learn about working with cells
that are not contiguous, see Work with discontiguous cells using the RangeAreas object.

For the complete list of properties and methods that the Range object supports, see
Range Object (JavaScript API for Excel).

Work with discontiguous cells using the


RangeAreas object
The RangeAreas object lets your add-in perform operations on multiple ranges at once.
These ranges may be contiguous, but they don't have to be. RangeAreas are further
discussed in the article Work with multiple ranges simultaneously in Excel add-ins.

See also
Excel JavaScript object model in Office Add-ins
Get a range using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Work with charts using the Excel
JavaScript API
Article • 03/22/2022

This article provides code samples that show how to perform common tasks with charts
using the Excel JavaScript API. For the complete list of properties and methods that the
Chart and ChartCollection objects support, see Chart Object (JavaScript API for Excel)

and Chart Collection Object (JavaScript API for Excel).

Create a chart
The following code sample creates a chart in the worksheet named Sample. The chart is
a Line chart that is based upon data in the range A1:B13.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let dataRange = sheet.getRange("A1:B13");
let chart = sheet.charts.add(
Excel.ChartType.line,
dataRange,
Excel.ChartSeriesBy.auto);

chart.title.text = "Sales Data";


chart.legend.position = Excel.ChartLegendPosition.right;
chart.legend.format.fill.setSolidColor("white");
chart.dataLabels.format.font.size = 15;
chart.dataLabels.format.font.color = "black";

await context.sync();
});

New line chart


Add a data series to a chart
The following code sample adds a data series to the first chart in the worksheet. The
new data series corresponds to the column named 2016 and is based upon data in the
range D2:D5.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let chart = sheet.charts.getItemAt(0);
let dataRange = sheet.getRange("D2:D5");

let newSeries = chart.series.add("2016");


newSeries.setValues(dataRange);

await context.sync();
});

Chart before the 2016 data series is added

Chart after the 2016 data series is added


Set chart title
The following code sample sets the title of the first chart in the worksheet to Sales Data
by Year.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let chart = sheet.charts.getItemAt(0);


chart.title.text = "Sales Data by Year";

await context.sync();
});

Chart after title is set


Set properties of an axis in a chart
Charts that use the Cartesian coordinate system such as column charts, bar charts,
and scatter charts contain a category axis and a value axis. These examples show how to
set the title and display unit of an axis in a chart.

Set axis title


The following code sample sets the title of the category axis for the first chart in the
worksheet to Product.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let chart = sheet.charts.getItemAt(0);


chart.axes.categoryAxis.title.text = "Product";

await context.sync();
});

Chart after title of category axis is set


Set axis display unit
The following code sample sets the display unit of the value axis for the first chart in the
worksheet to Hundreds.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let chart = sheet.charts.getItemAt(0);


chart.axes.valueAxis.displayUnit = "Hundreds";

await context.sync();
});

Chart after display unit of value axis is set


Set visibility of gridlines in a chart
The following code sample hides the major gridlines for the value axis of the first chart
in the worksheet. You can show the major gridlines for the value axis of the chart, by
setting chart.axes.valueAxis.majorGridlines.visible to true .

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let chart = sheet.charts.getItemAt(0);


chart.axes.valueAxis.majorGridlines.visible = false;

await context.sync();
});

Chart with gridlines hidden


Chart trendlines

Add a trendline
The following code sample adds a moving average trendline to the first series in the first
chart in the worksheet named Sample. The trendline shows a moving average over 5
periods.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let chart = sheet.charts.getItemAt(0);


let seriesCollection = chart.series;

seriesCollection.getItemAt(0).trendlines.add("MovingAverage").movingAverageP
eriod = 5;

await context.sync();
});

Chart with moving average trendline


Update a trendline
The following code sample sets the trendline to type Linear for the first series in the
first chart in the worksheet named Sample.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let chart = sheet.charts.getItemAt(0);


let seriesCollection = chart.series;
let series = seriesCollection.getItemAt(0);
series.trendlines.getItem(0).type = "Linear";

await context.sync();
});

Chart with linear trendline


Add and format a chart data table
You can access the data table element of a chart with the
Chart.getDataTableOrNullObject method. This method returns the ChartDataTable
object. The ChartDataTable object has boolean formatting properties such as visible ,
showLegendKey , and showHorizontalBorder .

The ChartDataTable.format property returns the ChartDataTableFormat object, which


allows you to further format and style the data table. The ChartDataTableFormat object
offers border , fill , and font properties.

The following code sample shows how to add a data table to a chart and then format
that data table using the ChartDataTable and ChartDataTableFormat objects.

JavaScript

// This code sample adds a data table to a chart that already exists on the
worksheet,
// and then adjusts the display and format of that data table.
await Excel.run(async (context) => {
// Retrieve the chart on the "Sample" worksheet.
let chart =
context.workbook.worksheets.getItem("Sample").charts.getItemAt(0);

// Get the chart data table object and load its properties.
let chartDataTable = chart.getDataTableOrNullObject();
chartDataTable.load();
// Set the display properties of the chart data table.
chartDataTable.visible = true;
chartDataTable.showLegendKey = true;
chartDataTable.showHorizontalBorder = false;
chartDataTable.showVerticalBorder = true;
chartDataTable.showOutlineBorder = true;

// Retrieve the chart data table format object and set font and border
properties.
let chartDataTableFormat = chartDataTable.format;
chartDataTableFormat.font.color = "#B76E79";
chartDataTableFormat.font.name = "Comic Sans";
chartDataTableFormat.border.color = "blue";

await context.sync();
});

The following screenshot shows the data table that the preceding code sample creates.

Export a chart as an image


Charts can be rendered as images outside of Excel. Chart.getImage returns the chart as
a base64-encoded string representing the chart as a JPEG image. The following code
shows how to get the image string and log it to the console.

JavaScript

await Excel.run(async (context) => {


let chart =
context.workbook.worksheets.getItem("Sheet1").charts.getItem("Chart1");
let imageAsString = chart.getImage();
await context.sync();

console.log(imageAsString.value);
// Instead of logging, your add-in may use the base64-encoded string to
save the image as a file or insert it in HTML.
});

Chart.getImage takes three optional parameters: width, height, and the fitting mode.
TypeScript

getImage(width?: number, height?: number, fittingMode?:


Excel.ImageFittingMode): OfficeExtension.ClientResult<string>;

These parameters determine the size of the image. Images are always proportionally
scaled. The width and height parameters put upper or lower bounds on the scaled
image. ImageFittingMode has three values with the following behaviors.

Fill : The image's minimum height or width is the specified height or width
(whichever is reached first when scaling the image). This is the default behavior
when no fitting mode is specified.
Fit : The image's maximum height or width is the specified height or width
(whichever is reached first when scaling the image).
FitAndCenter : The image's maximum height or width is the specified height or
width (whichever is reached first when scaling the image). The resulting image is
centered relative to the other dimension.

See also
Excel JavaScript object model in Office Add-ins
Coauthoring in Excel add-ins
Article • 03/22/2022

With coauthoring , multiple people can work together and edit the same Excel
workbook simultaneously. All coauthors of a workbook can see another coauthor's
changes as soon as that coauthor saves the workbook. To coauthor an Excel workbook,
the workbook must be stored in OneDrive, OneDrive for Business, or SharePoint Online.

) Important

In Excel for Microsoft 365, you will notice AutoSave in the upper-left corner. When
AutoSave is turned on, coauthors see your changes in real time. Consider the
impact of this behavior on the design of your Excel add-in. Users can turn off
AutoSave via the switch in the upper left of the Excel window.

Coauthoring overview
When you change a workbook's content, Excel automatically synchronizes those
changes across all coauthors. Coauthors can change the content of a workbook, but so
can code running within an Excel add-in. For example, when the following JavaScript
code runs in an Office Add-in, the value of a range is set to Contoso.

JavaScript

range.values = [['Contoso']];

After 'Contoso' synchronizes across all coauthors, any user or add-in running in the
same workbook will see the new value of the range.

Coauthoring only synchronizes the content within the shared workbook. Values copied
from the workbook to JavaScript variables in an Excel add-in are not synchronized. For
example, if your add-in stores the value of a cell (such as 'Contoso') in a JavaScript
variable, and then a coauthor changes the value of the cell to 'Example', after
synchronization all coauthors see 'Example' in the cell. However, the value of the
JavaScript variable is still set to 'Contoso'. Furthermore, when multiple coauthors use the
same add-in, each coauthor has their own copy of the variable, which is not
synchronized. When you use variables that use workbook content, be sure you check for
updated values in the workbook before you use the variable.
Use events to manage the in-memory state of
your add-in
Excel add-ins can read workbook content (from hidden worksheets and a setting object),
and then store it in data structures such as variables. After the original values are copied
into any of these data structures, coauthors can update the original workbook content.
This means that the copied values in the data structures are now out of sync with the
workbook content. When you build your add-ins, be sure to account for this separation
of workbook content and values stored in data structures.

For example, you might build a content add-in that displays custom visualizations. The
state of your custom visualizations might be saved in a hidden worksheet. When
coauthors use the same workbook, the following scenario can occur.

User A opens the document and the custom visualizations are shown in the
workbook. The custom visualizations read data from a hidden worksheet (for
example, the color of the visualizations is set to blue).
User B opens the same document, and starts modifying the custom visualizations.
User B sets the color of the custom visualizations to orange. Orange is saved to the
hidden worksheet.
User A's hidden worksheet is updated with the new value of orange.
User A's custom visualizations are still blue.

If you want User A's custom visualizations to respond to changes made by coauthors on
the hidden worksheet, use the BindingDataChanged event. This ensures that changes to
workbook content made by coauthors is reflected in the state of your add-in.

Caveats to using events with coauthoring


As described earlier, in some scenarios, triggering events for all coauthors provides an
improved user experience. However, be aware that in some scenarios this behavior can
produce poor user experiences.

For example, in data validation scenarios, it is common to display UI in response to


events. The BindingDataChanged event described in the previous section runs when
either a local user or coauthor (remote) changes the workbook content within the
binding. If the event handler of the BindingDataChanged event displays UI, users will see
UI that is unrelated to changes they were working on in the workbook, leading to a poor
user experience. Avoid displaying UI when using events in your add-in.
Avoid table row coauthoring conflicts
It is a known issue that calls to the TableRowCollection.add API can cause coauthoring
conflicts. We do not recommend using that API if you anticipate your add-in will be run
while other users are editing the add-in's workbook (specifically, if they are editing the
table or any range under the table). The following guidance should help you avoid
issues with the TableRowCollection.add method (and avoid triggering the yellow bar
Excel shows that asks users to refresh).

1. Use Range.values instead of TableRowCollection.add. Setting the Range values


directly below the table automatically expands the table. Otherwise, adding table
rows through the Table APIs results in merge conflicts for coauth users.
2. There should be no data validation rules applied to cells below the table, unless
the data validation is applied to the entire column.
3. If there is data under the table, the add-in needs to handle that before setting the
range value. Using Range.insert to insert an empty row will move the data and
make space for the expanding table. Otherwise, you risk overwriting cells below
the table.
4. You cannot add an empty row to a table with Range.values . The table only
automatically expands if data is present in the cells directly below the table. Use
either temporary data or hidden columns as a workaround to add an empty table
row.

See also
About coauthoring in Excel (VBA)
How AutoSave impacts add-ins and macros (VBA)
Work with comments using the Excel
JavaScript API
Article • 08/30/2022

This article describes how to add, read, modify, and remove comments in a workbook
with the Excel JavaScript API. You can learn more about the comment feature from the
Insert comments and notes in Excel article.

In the Excel JavaScript API, a comment includes both the single initial comment and the
connected threaded discussion. It is tied to an individual cell. Anyone viewing the
workbook with sufficient permissions can reply to a comment. A Comment object stores
those replies as CommentReply objects. You should consider a comment to be a thread
and that a thread must have a special entry as the starting point.

Comments within a workbook are tracked by the Workbook.comments property. This


includes comments created by users and also comments created by your add-in. The
Workbook.comments property is a CommentCollection object that contains a collection of

Comment objects. Comments are also accessible at the Worksheet level. The samples in
this article work with comments at the workbook level, but they can be easily modified
to use the Worksheet.comments property.

Add comments
Use the CommentCollection.add method to add comments to a workbook. This method
takes up to three parameters:

cellAddress : The cell where the comment is added. This can either be a string or

Range object. The range must be a single cell.


content : The comment's content. Use a string for plain text comments. Use a

CommentRichContent object for comments with mentions.


contentType : A ContentType enum specifying type of content. The default value is
ContentType.plain .

The following code sample adds a comment to cell A2.

JavaScript

await Excel.run(async (context) => {


// Add a comment to A2 on the "MyWorksheet" worksheet.
let comments = context.workbook.comments;

// Note that an InvalidArgument error will be thrown if multiple cells


passed to `Comment.add`.
comments.add("MyWorksheet!A2", "TODO: add data.");
await context.sync();
});

7 Note

Comments added by an add-in are attributed to the current user of that add-in.

Add comment replies


A Comment object is a comment thread that contains zero or more replies. Comment
objects have a replies property, which is a CommentReplyCollection that contains
CommentReply objects. To add a reply to a comment, use the
CommentReplyCollection.add method, passing in the text of the reply. Replies are

displayed in the order they are added. They are also attributed to the current user of the
add-in.

The following code sample adds a reply to the first comment in the workbook.

JavaScript

await Excel.run(async (context) => {


// Get the first comment added to the workbook.
let comment = context.workbook.comments.getItemAt(0);
comment.replies.add("Thanks for the reminder!");
await context.sync();
});

Edit comments
To edit a comment or comment reply, set its Comment.content property or
CommentReply.content property.

JavaScript

await Excel.run(async (context) => {


// Edit the first comment in the workbook.
let comment = context.workbook.comments.getItemAt(0);
comment.content = "PLEASE add headers here.";
await context.sync();
});

Edit comment replies


To edit a comment reply, set its CommentReply.content property.

JavaScript

await Excel.run(async (context) => {


// Edit the first comment reply on the first comment in the workbook.
let comment = context.workbook.comments.getItemAt(0);
let reply = comment.replies.getItemAt(0);
reply.content = "Never mind";
await context.sync();
});

Delete comments
To delete a comment use the Comment.delete method. Deleting a comment also deletes
the replies associated with that comment.

JavaScript

await Excel.run(async (context) => {


// Delete the comment thread at A2 on the "MyWorksheet" worksheet.
context.workbook.comments.getItemByCell("MyWorksheet!A2").delete();
await context.sync();
});
Delete comment replies
To delete a comment reply, use the CommentReply.delete method.

JavaScript

await Excel.run(async (context) => {


// Delete the first comment reply from this worksheet's first comment.
let comment = context.workbook.comments.getItemAt(0);
comment.replies.getItemAt(0).delete();
await context.sync();
});

Resolve comment threads


A comment thread has a configurable boolean value, resolved , to indicate if it is
resolved. A value of true means the comment thread is resolved. A value of false
means the comment thread is either new or reopened.

JavaScript

await Excel.run(async (context) => {


// Resolve the first comment thread in the workbook.
context.workbook.comments.getItemAt(0).resolved = true;
await context.sync();
});

Comment replies have a read-only resolved property. Its value is always equal to that of
the rest of the thread.

Comment metadata
Each comment contains metadata about its creation, such as the author and creation
date. Comments created by your add-in are considered to be authored by the current
user.

The following sample shows how to display the author's email, author's name, and
creation date of a comment at A2.

JavaScript
await Excel.run(async (context) => {
// Get the comment at cell A2 in the "MyWorksheet" worksheet.
let comment = context.workbook.comments.getItemByCell("MyWorksheet!A2");

// Load and print the following values.


comment.load(["authorEmail", "authorName", "creationDate"]);
await context.sync();

console.log(`${comment.creationDate.toDateString()}:
${comment.authorName} (${comment.authorEmail})`);
});

Comment reply metadata


Comment replies store the same types of metadata as the initial comment.

The following sample shows how to display the author's email, author's name, and
creation date of the latest comment reply at A2.

JavaScript

await Excel.run(async (context) => {


// Get the comment at cell A2 in the "MyWorksheet" worksheet.
let comment = context.workbook.comments.getItemByCell("MyWorksheet!A2");
let replyCount = comment.replies.getCount();
// Sync to get the current number of comment replies.
await context.sync();

// Get the last comment reply in the comment thread.


let reply = comment.replies.getItemAt(replyCount.value - 1);
reply.load(["authorEmail", "authorName", "creationDate"]);

// Sync to load the reply metadata to print.


await context.sync();

console.log(`Latest reply: ${reply.creationDate.toDateString()}:


${reply.authorName} ${reply.authorEmail})`);
await context.sync();
});

Mentions
Mentions are used to tag colleagues in a comment. This sends them notifications with
your comment's content. Your add-in can create these mentions on your behalf.

Comments with mentions need to be created with CommentRichContent objects. Call


CommentCollection.add with a CommentRichContent containing one or more mentions
and specify ContentType.mention as the contentType parameter. The content string also
needs to be formatted to insert the mention into the text. The format for a mention is:
<at id="{replyIndex}">{mentionName}</at> .

7 Note

Currently, only the mention's exact name can be used as the text of the mention
link. Support for shortened versions of a name will be added later.

The following example shows a comment with a single mention.

JavaScript

await Excel.run(async (context) => {


// Add an "@mention" for "Kate Kristensen" to cell A1 in the
"MyWorksheet" worksheet.
let mention = {
email: "kakri@contoso.com",
id: 0,
name: "Kate Kristensen"
};

// This will tag the mention's name using the '@' syntax.
// They will be notified via email.
let commentBody = {
mentions: [mention],
richContent: '<at id="0">' + mention.name + "</at> - Can you take a
look?"
};

// Note that an InvalidArgument error will be thrown if multiple cells


passed to `comment.add`.
context.workbook.comments.add("MyWorksheet!A1", commentBody,
Excel.ContentType.mention);
await context.sync();
});

Comment events
Your add-in can listen for comment additions, changes, and deletions. Comment events
occur on the CommentCollection object. To listen for comment events, register the
onAdded , onChanged , or onDeleted comment event handler. When a comment event is

detected, use this event handler to retrieve data about the added, changed, or deleted
comment. The onChanged event also handles comment reply additions, changes, and
deletions.
Each comment event only triggers once when multiple additions, changes, or deletions
are performed at the same time. All the CommentAddedEventArgs,
CommentChangedEventArgs, and CommentDeletedEventArgs objects contain arrays of
comment IDs to map the event actions back to the comment collections.

See the Work with Events using the Excel JavaScript API article for additional information
about registering event handlers, handling events, and removing event handlers.

Comment addition events


The onAdded event is triggered when one or more new comments are added to the
comment collection. This event is not triggered when replies are added to a comment
thread (see Comment change events to learn about comment reply events).

The following sample shows how to register the onAdded event handler and then use the
CommentAddedEventArgs object to retrieve the commentDetails array of the added
comment.

7 Note

This sample only works when a single comment is added.

JavaScript

await Excel.run(async (context) => {


let comments =
context.workbook.worksheets.getActiveWorksheet().comments;

// Register the onAdded comment event handler.


comments.onAdded.add(commentAdded);

await context.sync();
});

async function commentAdded() {


await Excel.run(async (context) => {
// Retrieve the added comment using the comment ID.
// Note: This method assumes only a single comment is added at a
time.
let addedComment =
context.workbook.comments.getItem(event.commentDetails[0].commentId);

// Load the added comment's data.


addedComment.load(["content", "authorName"]);

await context.sync();
// Print out the added comment's data.
console.log(`A comment was added. ID:
${event.commentDetails[0].commentId}. Comment
content:${addedComment.content}. Comment
author:${addedComment.authorName}`);
await context.sync();
});
}

Comment change events


The onChanged comment event is triggered in the following scenarios.

A comment's content is updated.


A comment thread is resolved.
A comment thread is reopened.
A reply is added to a comment thread.
A reply is updated in a comment thread.
A reply is deleted in a comment thread.

The following sample shows how to register the onChanged event handler and then use
the CommentChangedEventArgs object to retrieve the commentDetails array of the changed
comment.

7 Note

This sample only works when a single comment is changed.

JavaScript

await Excel.run(async (context) => {


let comments =
context.workbook.worksheets.getActiveWorksheet().comments;

// Register the onChanged comment event handler.


comments.onChanged.add(commentChanged);

await context.sync();
});

async function commentChanged() {


await Excel.run(async (context) => {
// Retrieve the changed comment using the comment ID.
// Note: This method assumes only a single comment is changed at a
time.
let changedComment =
context.workbook.comments.getItem(event.commentDetails[0].commentId);
// Load the changed comment's data.
changedComment.load(["content", "authorName"]);

await context.sync();

// Print out the changed comment's data.


console.log(`A comment was changed. ID:
${event.commentDetails[0].commentId}. Updated comment content:
${changedComment.content}. Comment author: ${changedComment.authorName}`);
await context.sync();
});
}

Comment deletion events


The onDeleted event is triggered when a comment is deleted from the comment
collection. Once a comment has been deleted, its metadata is no longer available. The
CommentDeletedEventArgs object provides comment IDs, in case your add-in is
managing individual comments.

The following sample shows how to register the onDeleted event handler and then use
the CommentDeletedEventArgs object to retrieve the commentDetails array of the deleted
comment.

7 Note

This sample only works when a single comment is deleted.

JavaScript

await Excel.run(async (context) => {


let comments =
context.workbook.worksheets.getActiveWorksheet().comments;

// Register the onDeleted comment event handler.


comments.onDeleted.add(commentDeleted);

await context.sync();
});

async function commentDeleted() {


await Excel.run(async (context) => {
// Print out the deleted comment's ID.
// Note: This method assumes only a single comment is deleted at a
time.
console.log(`A comment was deleted. ID:
${event.commentDetails[0].commentId}`);
});
}

See also
Excel JavaScript object model in Office Add-ins
Work with workbooks using the Excel JavaScript API
Work with Events using the Excel JavaScript API
Insert comments and notes in Excel
Overview of data types in Excel add-ins
Article • 03/09/2023

Data types organize complex data structures as objects. This includes formatted number
values, web images, and entities as entity cards.

The following screenshot highlights one of the primary features of data types: an entity
card. In this case, the entity card shows expanded information about the Tofu product
from a list of grocery store products.

7 Note

To start experimenting with data types right away, install Script Lab in Excel and
check out the Data types section in our Samples library. You can also explore the
Script Lab samples in our OfficeDev/office-js-snippets repository.
Prior to the data types addition, the Excel JavaScript API supported string, number,
boolean, and error data types. The Excel UI formatting layer is capable of adding
currency, date, and other types of formatting to cells that contain the four original data
types, but this formatting layer only controls the display of the original data types in the
Excel UI. The underlying number value is not changed, even when a cell in the Excel UI is
formatted as currency or a date. This gap between an underlying value and the
formatted display in the Excel UI can result in confusion and errors during add-in
calculations. The data types APIs are a solution to this gap.

Data types expand Excel JavaScript API support beyond the four original data types
(string, number, boolean, and error) to include web images, formatted number values,
entities, arrays within entities, and improved error data types as flexible data structures.
These types, which power many linked data types experiences, allow for precision and
simplicity during add-in calculations and extend the potential of Excel add-ins beyond a
2-dimensional grid.

 Tip

Check out examples of linked data types, which are available to all Excel users, in
the following sample workbook: linked-data-types-sample-workbook.xlsx.

Resources
Explore data types with the following resources.

1. Learn data types basics in the Excel data types core concepts article.
2. Install Script Lab in Excel and explore the Data types section in our Samples library.
3. Learn how to extend Excel beyond a 2-dimensional grid with entity data types in
the Use cards with entity value data types article.
4. Try the Create and explore data types in Excel sample in our OfficeDev/Office-
Add-in-samples repository.

Data types and custom functions


Data types enhance the power of custom functions. Custom functions accept data types
as both inputs to custom functions and outputs of custom functions, and custom
functions use the same JSON schema for data types as the Excel JavaScript API. This
data types JSON schema is maintained as custom functions calculate and evaluate. To
learn more about integrating data types with your custom functions, see Custom
functions and data types.
Excel data types core concepts
Article • 03/09/2023

This article describes how to use the Excel JavaScript API to work with data types. It
introduces core concepts that are fundamental to data type development.

The valuesAsJson property


The valuesAsJson property (or the singular valueAsJson for NamedItem) is integral to
creating data types in Excel. This property is an expansion of values properties, such as
Range.values. Both the values and valuesAsJson properties are used to access the value
in a cell, but the values property only returns one of the four basic types: string,
number, boolean, or error (as a string). In contrast, valuesAsJson returns expanded
information about the four basic types, and this property can return data types such as
formatted number values, entities, and web images.

The following objects offer the valuesAsJson property.

NamedItem (as valueAsJson )


NamedItemArrayValues
Range
RangeView
TableColumn
TableRow

7 Note

Some cell values change based on a user's locale. The valuesAsJsonLocal property
offers localization support and is available on all the same objects as valuesAsJson .

Cell values
The valuesAsJson property returns a CellValue type alias, which is a union of the
following data types.

ArrayCellValue
BooleanCellValue
DoubleCellValue
EmptyCellValue
EntityCellValue
ErrorCellValue
FormattedNumberCellValue
LinkedEntityCellValue
ReferenceCellValue
StringCellValue
ValueTypeNotAvailableCellValue
WebImageCellValue

The CellValue type alias also returns the CellValueExtraProperties object, which is an
intersection with the rest of the *CellValue types. It's not a data type itself. The
properties of the CellValueExtraProperties object are used with all data types to
specify details related to overwriting cell values.

JSON schema
Each cell value type returned by valuesAsJson uses a JSON metadata schema designed
for that type. Along with additional properties unique to each data type, these JSON
metadata schemas all have the type , basicType , and basicValue properties in common.

The type defines the CellValueType of the data. The basicType is always read-only and
is used as a fallback when the data type isn't supported or is formatted incorrectly. The
basicValue matches the value that would be returned by the values property. The

basicValue is used as a fallback when calculations encounter incompatible scenarios,


such as an older version of Excel that doesn't support the data types feature. The
basicValue is read-only for ArrayCellValue , EntityCellValue , LinkedEntityCellValue ,
and WebImageCellValue data types.

In addition to the three fields that all data types share, the JSON metadata schema for
each *CellValue has properties available according to that type. For example, the
WebImageCellValue type includes the altText and attribution properties, while the
EntityCellValue type offers the properties and text fields.

The following sections show JSON code samples for the formatted number value, entity
value, and web image data types.

Formatted number values


The FormattedNumberCellValue object enables Excel add-ins to define a numberFormat
property for a value. Once assigned, this number format travels through calculations
with the value and can be returned by functions.

The following JSON code sample shows the complete schema of a formatted number
value. The myDate formatted number value in the code sample displays as 1/16/1990 in
the Excel UI. If the minimum compatibility requirements for the data types feature aren't
met, calculations use the basicValue in place of the formatted number.

TypeScript

// This is an example of the complete JSON of a formatted number value.


// In this case, the number is formatted as a date.
const myDate: Excel.FormattedNumberCellValue = {
type: Excel.CellValueType.formattedNumber,
basicValue: 32889.0,
basicType: Excel.RangeValueType.double, // A read-only property. Used as
a fallback in incompatible scenarios.
numberFormat: "m/d/yyyy"
};

Begin experimenting with formatted number values by opening Script Lab and checking
out the Data types: Formatted numbers snippet in our Samples library.

Entity values
An entity value is a container for data types, similar to an object in object-oriented
programming. Entities also support arrays as properties of an entity value. The
EntityCellValue object allows add-ins to define properties such as type , text , and
properties . The properties property enables the entity value to define and contain

additional data types.

The basicType and basicValue properties define how calculations read this entity data
type if the minimum compatibility requirements to use data types aren't met. In that
scenario, this entity data type displays as a #VALUE! error in the Excel UI.

The following JSON code sample shows the complete schema of an entity value that
contains text, an image, a date, and an additional text value.

TypeScript

// This is an example of the complete JSON for an entity value.


// The entity contains text and properties which contain an image, a date,
and another text value.
const myEntity: Excel.EntityCellValue = {
type: Excel.CellValueType.entity,
text: "A llama",
properties: {
image: myImage,
"start date": myDate,
"quote": {
type: Excel.CellValueType.string,
basicValue: "I love llamas."
}
},
basicType: Excel.RangeValueType.error, // A read-only property. Used as
a fallback in incompatible scenarios.
basicValue: "#VALUE!" // A read-only property. Used as a fallback in
incompatible scenarios.
};

Entity values also offer a layouts property that creates a card for the entity. The card
displays as a modal window in the Excel UI and can display additional information
contained within the entity value, beyond what's visible in the cell. To learn more, see
Use cards with entity value data types.

To explore entity data types, start by going to Script Lab in Excel and opening the Data
types: Create entity cards from data in a table snippet in our Samples library. The Data
types: Entity values with references and Data types: Entity value attribution
properties snippets offer a deeper look at entity features.

Linked entities
Linked entity values, or LinkedEntityCellValue objects, are a type of entity value. These
objects integrate data provided by an external service and can display this data as an
entity card, like regular entity values. The Stocks and Geography data types available
via the Excel UI are linked entity values.

Web image values


The WebImageCellValue object creates the ability to store an image as part of an entity
or as an independent value in a range. This object offers many properties, including
address , altText , and relatedImagesAddress .

The basicType and basicValue properties define how calculations read the web image
data type if the minimum compatibility requirements to use the data types feature
aren't met. In that scenario, this web image data type displays as a #VALUE! error in the
Excel UI.

The following JSON code sample shows the complete schema of a web image.

TypeScript
// This is an example of the complete JSON for a web image.
const myImage: Excel.WebImageCellValue = {
type: Excel.CellValueType.webImage,
address: "https://bit.ly/2YGOwtw",
basicType: Excel.RangeValueType.error, // A read-only property. Used as
a fallback in incompatible scenarios.
basicValue: "#VALUE!" // A read-only property. Used as a fallback in
incompatible scenarios.
};

Try out web image data types by opening Script Lab and selecting the Data types: Web
images snippet in our Samples library.

Improved error support


The data types APIs expose existing Excel UI errors as objects. Now that these errors are
accessible as objects, add-ins can define or retrieve properties such as type , errorType ,
and errorSubType .

The following is a list of all the error objects with expanded support through data types.

BlockedErrorCellValue
BusyErrorCellValue
CalcErrorCellValue
ConnectErrorCellValue
Div0ErrorCellValue
FieldErrorCellValue
GettingDataErrorCellValue
NotAvailableErrorCellValue
NameErrorCellValue
NullErrorCellValue
NumErrorCellValue
RefErrorCellValue
SpillErrorCellValue
ValueErrorCellValue

Each of the error objects can access an enum through the errorSubType property, and
this enum contains additional data about the error. For example, the
BlockedErrorCellValue error object can access the BlockedErrorCellValueSubType enum.

The BlockedErrorCellValueSubType enum offers additional data about what caused the
error.
Learn more about the data types error objects by checking out the Data types: Set error
values snippet in our Script Lab Samples library.

Next steps
Learn how entity data types extend the potential of Excel add-ins beyond a 2-
dimensional grid with the Use cards with entity value data types article.

Use the Create and explore data types in Excel sample in our OfficeDev/Office-Add-
in-samples repository to experiment more deeply with data types by building and
sideloading an add-in that creates and edits data types in a workbook.

See also
Overview of data types in Excel add-ins
Use cards with entity value data types
Create and explore data types in Excel
Custom functions and data types
Excel JavaScript API reference
Use cards with entity value data types
Article • 03/09/2023

This article describes how to use the Excel JavaScript API to create card modal windows
in the Excel UI with entity value data types. These cards can display additional
information contained within an entity value, beyond what's already visible in a cell, such
as related images, product category information, and data attributions.

7 Note

This article expands on information described in the Excel data types core
concepts article. We recommend reading that article before learning about entity
cards.

An entity value, or EntityCellValue, is a container for data types and similar to an object
in object-oriented programming. This article shows how to use entity value card
properties, layout options, and data attribution functionality to create entity values that
display as cards.

The following screenshot shows an example of an open entity value card, in this case for
the Chef Anton's Gumbo Mix product from a list of grocery store products.
Card properties
The entity value properties property allows you to set customized information about
your data types. The properties key accepts nested data types. Each nested property, or
data type, must have a type and basicValue setting.

) Important

The nested properties data types are used in combination with the Card layout
values described in the subsequent article section. After defining a nested data type
in properties , it must be assigned in the layouts property to display on the card.

The following code snippet shows the JSON for an entity value with multiple data types
nested within properties .
7 Note

To experiment with this code snippet in a complete sample, open Script Lab in
Excel and select Data types: Create entity cards from data in a table in our
Samples library.

TypeScript

const entity: Excel.EntityCellValue = {


type: Excel.CellValueType.entity,
text: productName,
properties: {
"Product ID": {
type: Excel.CellValueType.string,
basicValue: productID.toString() || ""
},
"Product Name": {
type: Excel.CellValueType.string,
basicValue: productName || ""
},
"Image": {
type: Excel.CellValueType.webImage,
address: product.productImage || ""
},
"Quantity Per Unit": {
type: Excel.CellValueType.string,
basicValue: product.quantityPerUnit || ""
},
"Unit Price": {
type: Excel.CellValueType.formattedNumber,
basicValue: product.unitPrice,
numberFormat: "$* #,##0.00"
},
Discontinued: {
type: Excel.CellValueType.boolean,
basicValue: product.discontinued || false
}
},
layouts: {
// Enter layout settings here.
}
};

The following screenshot shows an entity value card that uses the preceding code
snippet. The screenshot shows the Product ID, Product Name, Image, Quantity Per
Unit, and Unit Price information from the preceding code snippet.
Property metadata
Entity properties have an optional propertyMetadata field that uses the
CellValuePropertyMetadata object and offers the properties attribution , excludeFrom ,
and sublabel . The following code snippet shows how to add a sublabel to the "Unit
Price" property from the preceding code snippet. In this case, the sublabel identifies
the currency type.

7 Note

The propertyMetadata field is only available on data types that are nested within
entity properties.

TypeScript
// This code snippet is an excerpt from the `properties` field of the
// preceding `EntityCellValue` snippet. "Unit Price" is a property of
// an entity value.
"Unit Price": {
type: Excel.CellValueType.formattedNumber,
basicValue: product.unitPrice,
numberFormat: "$* #,##0.00",
propertyMetadata: {
sublabel: "USD"
}
},

The following screenshot shows an entity value card that uses the preceding code
snippet, displaying the property metadata sublabel of USD next to the Unit Price
property.

Card layout
The entity value layouts property defines the appearance of the entity. Use layouts to
specify attributes such as an entity icon, card title, image for a card, and the number of
sections to display.

) Important

The nested layouts values are used in combination with the Card properties data
types described in the preceding article section. A nested data type must be
defined in properties before it can be assigned in layouts to display on the card.

The layouts property contains two direct subproperties, compact and card . The card
property specifies the appearance of a card when the entity card is open. The compact
property only defines the icon for an entity, and this icon only displays when the card is
in its compact, or unopened state. See the EntityCompactLayoutIcons enum for a full list
of available icons. The next code snippet shows how to display the shoppingBag icon.

Within the card property, use the CardLayoutStandardProperties object to define the
components of the card like title , subTitle , and sections .

The entity value JSON in the next code snippet shows a card layout with nested title
and mainImage objects, as well as three sections within the card. Note that the title
property "Product Name" has a corresponding data type in the preceding Card
properties article section. The mainImage property also has a corresponding "Image"
data type in the preceding section. The sections property takes a nested array and uses
the CardLayoutSectionStandardProperties object to define the appearance of each
section.

Within each card section you can specify elements like layout , title , and properties .
The layout key uses the CardLayoutListSection object and accepts the value "List" .
The properties key accepts an array of strings. Note that the properties values, such as
"Product ID" , have corresponding data types in the preceding Card properties article

section. Sections can also be collapsible and can be defined with boolean values as
collapsed or not collapsed when the entity card is opened in the Excel UI.

7 Note

To experiment with this code snippet in a complete sample, open Script Lab in
Excel and select Data types: Create entity cards from data in a table in our
Samples library.
TypeScript

const entity: Excel.EntityCellValue = {


type: Excel.CellValueType.entity,
text: productName,
properties: {
// Enter property settings here.
},
layouts: {
compact: {
icon: Excel.EntityCompactLayoutIcons.shoppingBag
},
card: {
title: {
property: "Product Name"
},
mainImage: {
property: "Image"
},
sections: [
{
layout: "List",
properties: ["Product ID"]
},
{
layout: "List",
title: "Quantity and price",
collapsible: true,
collapsed: false, // This section will not be collapsed
when the card is opened.
properties: ["Quantity Per Unit", "Unit Price"]
},
{
layout: "List",
title: "Additional information",
collapsible: true,
collapsed: true, // This section will be collapsed when
the card is opened.
properties: ["Discontinued"]
}
]
}
}
};

The following screenshot shows an entity value card that uses the preceding code
snippets. In the screenshot, the shoppingBag icon displays alongside the product names
in the spreadsheet. In the entity card, the mainImage object displays at the top, followed
by the title object which uses the Product Name and is set to Chef Anton's Gumbo
Mix. The screenshot also shows sections . The Quantity and price section is collapsible
and contains Quantity Per Unit and Unit Price. The Additional information field is
collapsible and is collapsed when the card is opened.

7 Note

In the preceding screenshot, the branch icon displays alongside Condiments in the
Category section. See the Data types: Create entity cards from data in a table
sample to learn how to set nested icons like the Category section icon.

There is a known issue with nested icons in Excel on Mac. In that environment,
nested icons will always display as the generic icon, regardless of which icon is
selected with the EntityCompactLayoutIcons enum.

Card data attribution


Entity value cards can display a data attribution to give credit to the provider of the
information in the entity card. The entity value provider property uses the
CellValueProviderAttributes object, which defines the description , logoSourceAddress ,
and logoTargetAddress values.

The data provider property displays an image in the lower left corner of the entity card.
It uses the logoSourceAddress to specify a source URL for the image. The
logoTargetAddress value defines the URL destination if the logo image is selected. The
description value displays as a tooltip when hovering over the logo. The description

value also displays as a plain text fallback if the logoSourceAddress is not defined or if
the source address for the image is broken.

The JSON in the following code snippet shows an entity value that uses the provider
property to specify a data provider attribution for the entity.

7 Note

To experiment with this code snippet in a complete sample, open Script Lab in
Excel and select Data types: Entity value attribution properties in our Samples
library.

TypeScript

const entity: Excel.EntityCellValue = {


type: Excel.CellValueType.entity,
text: productName,
properties: {
// Enter property settings here.
},
layouts: {
// Enter layout settings here.
},
provider: {
description: product.providerName, // Name of the data provider.
Displays as a tooltip when hovering over the logo. Also displays as a
fallback if the source address for the image is broken.
logoSourceAddress: product.sourceAddress, // Source URL of the logo
to display.
logoTargetAddress: product.targetAddress // Destination URL that the
logo navigates to when selected.
}
};

The following screenshot shows an entity value card that uses the preceding code
snippet. The screenshot shows the data provider attribution in the lower left corner. In
this instance, the data provider is Microsoft and the Microsoft logo is displayed.

Next steps
Try out the Create and explore data types in Excel sample in our OfficeDev/Office-
Add-in-samples repository. This sample guides you through building and then
sideloading an add-in that creates and edits data types in a workbook.

See also
Overview of data types in Excel add-ins
Excel data types core concepts
Create and explore data types in Excel
Excel JavaScript API reference
Add data validation to Excel ranges
Article • 03/22/2022

The Excel JavaScript Library provides APIs to enable your add-in to add automatic data
validation to tables, columns, rows, and other ranges in a workbook. To understand the
concepts and the terminology of data validation, please see the following articles about
how users add data validation through the Excel UI.

Apply data validation to cells


More on data validation
Description and examples of data validation in Excel

Programmatic control of data validation


The Range.dataValidation property, which takes a DataValidation object, is the entry
point for programmatic control of data validation in Excel. There are five properties to
the DataValidation object:

rule — Defines what constitutes valid data for the range. See DataValidationRule.

errorAlert — Specifies whether an error pops up if the user enters invalid data,
and defines the alert text, title, and style; for example, information , warning , and
stop . See DataValidationErrorAlert.

prompt — Specifies whether a prompt appears when the user hovers over the
range and defines the prompt message. See DataValidationPrompt.
ignoreBlanks — Specifies whether the data validation rule applies to blank cells in
the range. Defaults to true .
type — A read-only identification of the validation type, such as WholeNumber,
Date, TextLength, etc. It is set indirectly when you set the rule property.

7 Note

Data validation added programmatically behaves just like manually added data
validation. In particular, note that data validation is triggered only if the user
directly enters a value into a cell or copies and pastes a cell from elsewhere in the
workbook and chooses the Values paste option. If the user copies a cell and does a
plain paste into a range with data validation, validation is not triggered.

Creating validation rules


To add data validation to a range, your code must set the rule property of the
DataValidation object in Range.dataValidation . This takes a DataValidationRule object
which has seven optional properties. No more than one of these properties may be
present in any DataValidationRule object. The property that you include determines the
type of validation.

Basic and DateTime validation rule types


The first three DataValidationRule properties (i.e., validation rule types) take a
BasicDataValidation object as their value.

wholeNumber — Requires a whole number in addition to any other validation

specified by the BasicDataValidation object.


decimal — Requires a decimal number in addition to any other validation specified

by the BasicDataValidation object.


textLength — Applies the validation details in the BasicDataValidation object to

the length of the cell's value.

Here is an example of creating a validation rule. Note the following about this code.

The operator is the binary operator greaterThan . Whenever you use a binary
operator, the value that the user tries to enter in the cell is the left-hand operand
and the value specified in formula1 is the right-hand operand. So this rule says
that only whole numbers that are greater than 0 are valid.
The formula1 is a hard-coded number. If you don't know at coding time what the
value should be, you can also use an Excel formula (as a string) for the value. For
example, "=A3" and "=SUM(A4,B5)" could also be values of formula1 .

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let range = sheet.getRange("B2:C5");

range.dataValidation.rule = {
wholeNumber: {
formula1: 0,
operator: Excel.DataValidationOperator.greaterThan
}
};

await context.sync();
});
See BasicDataValidation for a list of the other binary operators.

There are also two ternary operators: between and notBetween . To use these, you must
specify the optional formula2 property. The formula1 and formula2 values are the
bounding operands. The value that the user tries to enter in the cell is the third
(evaluated) operand. The following is an example of using the "Between" operator.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let range = sheet.getRange("B2:C5");

range.dataValidation.rule = {
decimal: {
formula1: 0,
formula2: 100,
operator: Excel.DataValidationOperator.between
}
};

await context.sync();
});

The next two rule properties take a DateTimeDataValidation object as their value.

date

time

The DateTimeDataValidation object is structured similarly to the BasicDataValidation : it


has the properties formula1 , formula2 , and operator , and is used in the same way. The
difference is that you cannot use a number in the formula properties, but you can enter
a ISO 8606 datetime string (or an Excel formula). The following is an example that
defines valid values as dates in the first week of April, 2022.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let range = sheet.getRange("B2:C5");

range.dataValidation.rule = {
date: {
formula1: "2022-04-01",
formula2: "2022-04-08",
operator: Excel.DataValidationOperator.between
}
};
await context.sync();
});

List validation rule type


Use the list property in the DataValidationRule object to specify that the only valid
values are those from a finite list. The following is an example. Note the following about
this code.

It assumes that there is a worksheet named "Names" and that the values in the
range "A1:A3" are names.
The source property specifies the list of valid values. The string argument refers to
a range containing the names. You can also assign a comma-delimited list; for
example: "Sue, Ricky, Liz".
The inCellDropDown property specifies whether a drop-down control will appear in
the cell when the user selects it. If set to true , then the drop-down appears with
the list of values from the source .

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let range = sheet.getRange("B2:C5");
let nameSourceRange =
context.workbook.worksheets.getItem("Names").getRange("A1:A3");

range.dataValidation.rule = {
list: {
inCellDropDown: true,
source: "=Names!$A$1:$A$3"
}
};

await context.sync();
})

Custom validation rule type


Use the custom property in the DataValidationRule object to specify a custom
validation formula. The following is an example. Note the following about this code.

It assumes there is a two-column table with columns Athlete Name and


Comments in the A and B columns of the worksheet.
To reduce verbosity in the Comments column, it makes data that includes the
athlete's name invalid.
SEARCH(A2,B2) returns the starting position, in string in B2, of the string in A2. If A2

is not contained in B2, it does not return a number. ISNUMBER() returns a boolean.
So the formula property says that valid data for the Comment column is data that
does not include the string in the Athlete Name column.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let commentsRange =
sheet.tables.getItem("AthletesTable").columns.getItem("Comments").getDataBod
yRange();

commentsRange.dataValidation.rule = {
custom: {
formula: "=NOT(ISNUMBER(SEARCH(A2,B2)))"
}
};

await context.sync();
});

Create validation error alerts


You can a create custom error alert that appears when a user tries to enter invalid data
in a cell. The following is a simple example. Note the following about this code.

The style property determines whether the user gets an informational alert, a
warning, or a "stop" alert. Only stop actually prevents the user from adding invalid
data. The pop-ups for warning and information have options that allow the user
enter the invalid data anyway.
The showAlert property defaults to true . This means that Excel will pop-up a
generic alert (of type stop ) unless you create a custom alert which either sets
showAlert to false or sets a custom message, title, and style. This code sets a
custom message and title.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let range = sheet.getRange("B2:C5");

range.dataValidation.errorAlert = {
message: "Sorry, only positive whole numbers are allowed",
showAlert: true, // The default is 'true'.
style: Excel.DataValidationAlertStyle.stop,
title: "Negative or Decimal Number Entered"
};

// Set range.dataValidation.rule and optionally .prompt here.

await context.sync();
});

For more information, see DataValidationErrorAlert.

Create validation prompts


You can create an instructional prompt that appears when a user hovers over, or selects,
a cell to which data validation has been applied. The following is an example.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let range = sheet.getRange("B2:C5");

range.dataValidation.prompt = {
message: "Please enter a positive whole number.",
showPrompt: true, // The default is 'false'.
title: "Positive Whole Numbers Only."
};

// Set range.dataValidation.rule and optionally .errorAlert here.

await context.sync();
});

For more information, see DataValidationPrompt.

Remove data validation from a range


To remove data validation from a range, call the Range.dataValidation.clear() method.

JavaScript

myrange.dataValidation.clear()
It isn't necessary that the range you clear is exactly the same range as a range on which
you added data validation. If it isn't, only the overlapping cells, if any, of the two ranges
are cleared.

7 Note

Clearing data validation from a range will also clear any data validation that a user
has added manually to the range.

See also
Excel JavaScript object model in Office Add-ins
DataValidation Object (JavaScript API for Excel)
Range Object (JavaScript API for Excel)
Work with Events using the Excel
JavaScript API
Article • 05/19/2023

This article describes important concepts related to working with events in Excel and
provides code samples that show how to register event handlers, handle events, and
remove event handlers using the Excel JavaScript API.

Events in Excel
Each time certain types of changes occur in an Excel workbook, an event notification
fires. By using the Excel JavaScript API, you can register event handlers that allow your
add-in to automatically run a designated function when a specific event occurs. The
following events are currently supported.

Event Description Supported objects

onActivated Occurs when an object is activated. Chart, ChartCollection,


Shape, Worksheet,
WorksheetCollection

onActivated Occurs when a workbook is activated. Workbook

onAdded Occurs when an object is added to the ChartCollection,


collection. CommentCollection,
TableCollection,
WorksheetCollection

onAutoSaveSettingChanged Occurs when the autoSave setting is Workbook


changed on the workbook.

onCalculated Occurs when a worksheet has finished Worksheet,


calculation (or all the worksheets of the WorksheetCollection
collection have finished).

onChanged Occurs when the data of individual CommentCollection,


cells or comments has changed. Table, TableCollection,
Worksheet,
WorksheetCollection

onColumnSorted Occurs when one or more columns Worksheet,


have been sorted. This happens as the WorksheetCollection
result of a left-to-right sort operation.
Event Description Supported objects

onDataChanged Occurs when data or formatting within Binding


the binding is changed.

onDeactivated Occurs when an object is deactivated. Chart, ChartCollection,


Shape, Worksheet,
WorksheetCollection

onDeleted Occurs when an object is deleted from ChartCollection,


the collection. CommentCollection,
TableCollection,
WorksheetCollection

onFormatChanged Occurs when the format is changed on Worksheet,


a worksheet. WorksheetCollection

onFormulaChanged Occurs when a formula is changed. Worksheet,


WorksheetCollection

onMoved Occurs when a worksheet is moved WorksheetCollection


within a workbook.

onNameChanged Occurs when the worksheet name is Worksheet,


changed. WorksheetCollection

onProtectionChanged Occurs when the worksheet protection Worksheet,


state is changed. WorksheetCollection

onRowHiddenChanged Occurs when the row-hidden state Worksheet,


changes on a specific worksheet. WorksheetCollection

onRowSorted Occurs when one or more rows have Worksheet,


been sorted. This happens as the result WorksheetCollection
of a top-to-bottom sort operation.

onSelectionChanged Occurs when the active cell or selected Binding, Table, Workbook,
range is changed. Worksheet,
WorksheetCollection

onSettingsChanged Occurs when the Settings in the SettingCollection


document are changed.

onSingleClicked Occurs when left-clicked/tapped action Worksheet,


occurs in the worksheet. WorksheetCollection

onVisibilityChanged Occurs when the worksheet visibility is Worksheet,


changed. WorksheetCollection

Events in preview
7 Note

The following events are currently available only in public preview. To use this
feature, you must use the preview version of the Office JavaScript API library from
the Office.js content delivery network (CDN) . The type definition file for
TypeScript compilation and IntelliSense is found at the CDN and DefinitelyTyped .
You can install these types with npm install --save-dev @types/office-js-preview .
For more information on our upcoming APIs, please visit Excel JavaScript API
requirement sets.

Event Description Supported objects

onFiltered Occurs when a filter is applied to Table, TableCollection, Worksheet,


an object. WorksheetCollection

Event triggers
Events within an Excel workbook can be triggered by:

User interaction via the Excel user interface (UI) that changes the workbook
Office Add-in (JavaScript) code that changes the workbook
VBA add-in (macro) code that changes the workbook

Any change that complies with default behavior of Excel will trigger the corresponding
event(s) in a workbook.

Lifecycle of an event handler


An event handler is created when an add-in registers the event handler. It is destroyed
when the add-in unregisters the event handler or when the add-in is refreshed,
reloaded, or closed. Event handlers do not persist as part of the Excel file, or across
sessions with Excel on the web.

U Caution

When an object to which events are registered is deleted (e.g., a table with an
onChanged event registered), the event handler no longer triggers but remains in
memory until the add-in or Excel session refreshes or closes.

Events and coauthoring


With coauthoring, multiple people can work together and edit the same Excel workbook
simultaneously. For events that can be triggered by a coauthor, such as onChanged , the
corresponding Event object will contain a source property that indicates whether the
event was triggered locally by the current user ( event.source = Local ) or was triggered
by the remote coauthor ( event.source = Remote ).

Register an event handler


The following code sample registers an event handler for the onChanged event in the
worksheet named Sample. The code specifies that when data changes in that worksheet,
the handleChange function should run.

JavaScript

await Excel.run(async (context) => {


const worksheet = context.workbook.worksheets.getItem("Sample");
worksheet.onChanged.add(handleChange);

await context.sync();
console.log("Event handler successfully registered for onChanged event
in the worksheet.");
}).catch(errorHandlerFunction);

Handle an event
As shown in the previous example, when you register an event handler, you indicate the
function that should run when the specified event occurs. You can design that function
to perform whatever actions your scenario requires. The following code sample shows
an event handler function that simply writes information about the event to the console.

JavaScript

async function handleChange(event) {


await Excel.run(async (context) => {
await context.sync();
console.log("Change type of event: " + event.changeType);
console.log("Address of event: " + event.address);
console.log("Source of event: " + event.source);
}).catch(errorHandlerFunction);
}

Remove an event handler


The following code sample registers an event handler for the onSelectionChanged event
in the worksheet named Sample and defines the handleSelectionChange function that
will run when the event occurs. It also defines the remove() function that can
subsequently be called to remove that event handler. Note that the RequestContext
used to create the event handler is needed to remove it.

JavaScript

let eventResult;

async function run() {


await Excel.run(async (context) => {
const worksheet = context.workbook.worksheets.getItem("Sample");
eventResult = worksheet.onSelectionChanged.add(handleSelectionChange);

await context.sync();
console.log("Event handler successfully registered for
onSelectionChanged event in the worksheet.");
});
}

async function handleSelectionChange(event) {


await Excel.run(async (context) => {
await context.sync();
console.log("Address of current selection: " + event.address);
});
}

async function remove() {


await Excel.run(eventResult.context, async (context) => {
eventResult.remove();
await context.sync();

eventResult = null;
console.log("Event handler successfully removed.");
});
}

Enable and disable events


The performance of an add-in may be improved by disabling events. For example, your
app might never need to receive events, or it could ignore events while performing
batch-edits of multiple entities.

Events are enabled and disabled at the runtime level. The enableEvents property
determines if events are fired and their handlers are activated.

The following code sample shows how to toggle events on and off.
JavaScript

await Excel.run(async (context) => {


context.runtime.load("enableEvents");
await context.sync();

let eventBoolean = !context.runtime.enableEvents;


context.runtime.enableEvents = eventBoolean;
if (eventBoolean) {
console.log("Events are currently on.");
} else {
console.log("Events are currently off.");
}

await context.sync();
});

See also
Excel JavaScript object model in Office Add-ins
Performance optimization using the
Excel JavaScript API
Article • 03/09/2023

There are multiple ways that you can perform common tasks with the Excel JavaScript
API. You'll find significant performance differences between various approaches. This
article provides guidance and code samples to show you how to perform common tasks
efficiently using Excel JavaScript API.

) Important

Many performance issues can be addressed through recommended usage of load


and sync calls. See the "Performance improvements with the application-specific
APIs" section of Resource limits and performance optimization for Office Add-ins
for advice on working with the application-specific APIs in an efficient way.

Suspend Excel processes temporarily


Excel has a number of background tasks reacting to input from both users and your
add-in. Some of these Excel processes can be controlled to yield a performance benefit.
This is especially helpful when your add-in deals with large data sets.

Suspend calculation temporarily


If you are trying to perform an operation on a large number of cells (for example,
setting the value of a huge range object) and you don't mind suspending the calculation
in Excel temporarily while your operation finishes, we recommend that you suspend
calculation until the next context.sync() is called.

See the Application Object reference documentation for information about how to use
the suspendApiCalculationUntilNextSync() API to suspend and reactivate calculations in
a very convenient way. The following code demonstrates how to suspend calculation
temporarily.

JavaScript

await Excel.run(async (context) => {


let app = context.workbook.application;
let sheet = context.workbook.worksheets.getItem("sheet1");
let rangeToSet: Excel.Range;
let rangeToGet: Excel.Range;
app.load("calculationMode");
await context.sync();
// Calculation mode should be "Automatic" by default
console.log(app.calculationMode);

rangeToSet = sheet.getRange("A1:C1");
rangeToSet.values = [[1, 2, "=SUM(A1:B1)"]];
rangeToGet = sheet.getRange("A1:C1");
rangeToGet.load("values");
await context.sync();
// Range value should be [1, 2, 3] now
console.log(rangeToGet.values);

// Suspending recalculation
app.suspendApiCalculationUntilNextSync();
rangeToSet = sheet.getRange("A1:B1");
rangeToSet.values = [[10, 20]];
rangeToGet = sheet.getRange("A1:C1");
rangeToGet.load("values");
app.load("calculationMode");
await context.sync();
// Range value should be [10, 20, 3] when we load the property, because
calculation is suspended at that point
console.log(rangeToGet.values);
// Calculation mode should still be "Automatic" even with suspend
recalculation
console.log(app.calculationMode);

rangeToGet.load("values");
await context.sync();
// Range value should be [10, 20, 30] when we load the property, because
calculation is resumed after last sync
console.log(rangeToGet.values);
});

Please note that only formula calculations are suspended. Any altered references are still
rebuilt. For example, renaming a worksheet still updates any references in formulas to
that worksheet.

Suspend screen updating


Excel displays changes your add-in makes approximately as they happen in the code.
For large, iterative data sets, you may not need to see this progress on the screen in
real-time. Application.suspendScreenUpdatingUntilNextSync() pauses visual updates to
Excel until the add-in calls context.sync() , or until Excel.run ends (implicitly calling
context.sync ). Be aware, Excel will not show any signs of activity until the next sync.
Your add-in should either give users guidance to prepare them for this delay or provide
a status bar to demonstrate activity.

7 Note

Don't call suspendScreenUpdatingUntilNextSync repeatedly (such as in a loop).


Repeated calls will cause the Excel window to flicker.

Enable and disable events


Performance of an add-in may be improved by disabling events. A code sample showing
how to enable and disable events is in the Work with Events article.

Importing data into tables


When trying to import a huge amount of data directly into a Table object directly (for
example, by using TableRowCollection.add() ), you might experience slow performance.
If you are trying to add a new table, you should fill in the data first by setting
range.values , and then call worksheet.tables.add() to create a table over the range. If

you are trying to write data into an existing table, write the data into a range object via
table.getDataBodyRange() , and the table will expand automatically.

Here is an example of this approach:

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sheet1");
// Write the data into the range first.
let range = sheet.getRange("A1:B3");
range.values = [["Key", "Value"], ["A", 1], ["B", 2]];

// Create the table over the range


let table = sheet.tables.add('A1:B3', true);
table.name = "Example";
await context.sync();

// Insert a new row to the table


table.getDataBodyRange().getRowsBelow(1).values = [["C", 3]];
// Change a existing row value
table.getDataBodyRange().getRow(1).values = [["D", 4]];
await context.sync();
});
7 Note

You can conveniently convert a Table object to a Range object by using the
Table.convertToRange() method.

Payload size limit best practices


The Excel JavaScript API has size limitations for API calls. Excel on the web has a payload
size limit for requests and responses of 5MB, and an API returns a RichAPI.Error error if
this limit is exceeded. On all platforms, a range is limited to five million cells for get
operations. Large ranges typically exceed both of these limitations.

The payload size of a request is a combination of the following three components.

The number of API calls


The number of objects, such as Range objects
The length of the value to set or get

If an API returns the RequestPayloadSizeLimitExceeded error, use the best practice


strategies documented in this article to optimize your script and avoid the error.

Strategy 1: Move unchanged values out of loops


Limit the number of processes that occur within loops to improve performance. In the
following code sample, context.workbook.worksheets.getActiveWorksheet() can be
moved out of the for loop, because it doesn't change within that loop.

JavaScript

// DO NOT USE THIS CODE SAMPLE. This sample shows a poor performance
strategy.
async function run() {
await Excel.run(async (context) => {
let ranges = [];

// This sample retrieves the worksheet every time the loop runs, which
is bad for performance.
for (let i = 0; i < 7500; i++) {
let rangeByIndex =
context.workbook.worksheets.getActiveWorksheet().getRangeByIndexes(i, 1, 1,
1);
}
await context.sync();
});
}

The following code sample shows logic similar to the preceding code sample, but with
an improved performance strategy. The value
context.workbook.worksheets.getActiveWorksheet() is retrieved before the for loop,

because this value doesn't need to be retrieved each time the for loop runs. Only
values that change within the context of a loop should be retrieved within that loop.

JavaScript

// This code sample shows a good performance strategy.


async function run() {
await Excel.run(async (context) => {
let ranges = [];
// Retrieve the worksheet outside the loop.
let worksheet = context.workbook.worksheets.getActiveWorksheet();

// Only process the necessary values inside the loop.


for (let i = 0; i < 7500; i++) {
let rangeByIndex = worksheet.getRangeByIndexes(i, 1, 1, 1);
}
await context.sync();
});
}

Strategy 2: Create fewer range objects


Create fewer range objects to improve performance and minimize payload size. Two
approaches for creating fewer range objects are described in the following article
sections and code samples.

Split each range array into multiple arrays

One way to create fewer range objects is to split each range array into multiple arrays,
and then process each new array with a loop and a new context.sync() call.

) Important

Only use this strategy if you've first determined that you're exceeding the payload
request size limit. Using multiple loops can reduce the size of each payload request
to avoid exceeding the 5MB limit, but using multiple loops and multiple
context.sync() calls also negatively impacts performance.
The following code sample attempts to process a large array of ranges in a single loop
and then a single context.sync() call. Processing too many range values in one
context.sync() call causes the payload request size to exceed the 5MB limit.

JavaScript

// This code sample does not show a recommended strategy.


// Calling 10,000 rows would likely exceed the 5MB payload size limit in a
real-world situation.
async function run() {
await Excel.run(async (context) => {
let worksheet = context.workbook.worksheets.getActiveWorksheet();

// This sample attempts to process too many ranges at once.


for (let row = 1; row < 10000; row++) {
let range = sheet.getRangeByIndexes(row, 1, 1, 1);
range.values = [["1"]];
}
await context.sync();
});
}

The following code sample shows logic similar to the preceding code sample, but with a
strategy that avoids exceeding the 5MB payload request size limit. In the following code
sample, the ranges are processed in two separate loops, and each loop is followed by a
context.sync() call.

JavaScript

// This code sample shows a strategy for reducing payload request size.
// However, using multiple loops and `context.sync()` calls negatively
impacts performance.
// Only use this strategy if you've determined that you're exceeding the
payload request limit.
async function run() {
await Excel.run(async (context) => {
let worksheet = context.workbook.worksheets.getActiveWorksheet();

// Split the ranges into two loops, rows 1-5000 and then 5001-10000.
for (let row = 1; row < 5000; row++) {
let range = worksheet.getRangeByIndexes(row, 1, 1, 1);
range.values = [["1"]];
}
// Sync after each loop.
await context.sync();

for (let row = 5001; row < 10000; row++) {


let range = worksheet.getRangeByIndexes(row, 1, 1, 1);
range.values = [["1"]];
}
await context.sync();
});
}

Set range values in an array


Another way to create fewer range objects is to create an array, use a loop to set all the
data in that array, and then pass the array values to a range. This benefits both
performance and payload size. Instead of calling range.values for each range in a loop,
range.values is a called once outside the loop.

The following code sample shows how to create an array, set the values of that array in a
for loop, and then pass the array values to a range outside the loop.

JavaScript

// This code sample shows a good performance strategy.


async function run() {
await Excel.run(async (context) => {
const worksheet = context.workbook.worksheets.getActiveWorksheet();
// Create an array.
const array = new Array(10000);

// Set the values of the array inside the loop.


for (let i = 0; i < 10000; i++) {
array[i] = [1];
}

// Pass the array values to a range outside the loop.


let range = worksheet.getRange("A1:A10000");
range.values = array;
await context.sync();
});
}

See also
Excel JavaScript object model in Office Add-ins
Error handling with the application-specific JavaScript APIs
Resource limits and performance optimization for Office Add-ins
Worksheet Functions Object (JavaScript API for Excel)
Work with PivotTables using the Excel
JavaScript API
Article • 03/22/2022

PivotTables streamline larger data sets. They allow the quick manipulation of grouped
data. The Excel JavaScript API lets your add-in create PivotTables and interact with their
components. This article describes how PivotTables are represented by the Office
JavaScript API and provides code samples for key scenarios.

If you are unfamiliar with the functionality of PivotTables, consider exploring them as an
end user. See Create a PivotTable to analyze worksheet data for a good primer on
these tools.

) Important

PivotTables created with OLAP are not currently supported. There is also no support
for Power Pivot.

Object model
The PivotTable is the central object for PivotTables in the Office JavaScript API.

Workbook.pivotTables and Worksheet.pivotTables are PivotTableCollections that

contain the PivotTables in the workbook and worksheet, respectively.


A PivotTable contains a PivotHierarchyCollection that has multiple PivotHierarchies.
These PivotHierarchies can be added to specific hierarchy collections to define how
the PivotTable pivots data (as explained in the following section).
A PivotHierarchy contains a PivotFieldCollection that has exactly one PivotField. If
the design expands to include OLAP PivotTables, this may change.
A PivotField can have one or more PivotFilters applied, as long as the field's
PivotHierarchy is assigned to a hierarchy category.
A PivotField contains a PivotItemCollection that has multiple PivotItems.
A PivotTable contains a PivotLayout that defines where the PivotFields and
PivotItems are displayed in the worksheet. The layout also controls some display
settings for the PivotTable.

Let's look at how these relationships apply to some example data. The following data
describes fruit sales from various farms. It will be the example throughout this article.

This fruit farm sales data will be used to make a PivotTable. Each column, such as Types,
is a PivotHierarchy . The Types hierarchy contains the Types field. The Types field
contains the items Apple, Kiwi, Lemon, Lime, and Orange.

Hierarchies
PivotTables are organized based on four hierarchy categories: row, column, data, and
filter.

The farm data shown earlier has five hierarchies: Farms, Type, Classification, Crates Sold
at Farm, and Crates Sold Wholesale. Each hierarchy can only exist in one of the four
categories. If Type is added to column hierarchies, it cannot also be in the row, data, or
filter hierarchies. If Type is subsequently added to row hierarchies, it is removed from
the column hierarchies. This behavior is the same whether hierarchy assignment is done
through the Excel UI or the Excel JavaScript APIs.
Row and column hierarchies define how data will be grouped. For example, a row
hierarchy of Farms will group together all the data sets from the same farm. The choice
between row and column hierarchy defines the orientation of the PivotTable.

Data hierarchies are the values to be aggregated based on the row and column
hierarchies. A PivotTable with a row hierarchy of Farms and a data hierarchy of Crates
Sold Wholesale shows the sum total (by default) of all the different fruits for each farm.

Filter hierarchies include or exclude data from the pivot based on values within that
filtered type. A filter hierarchy of Classification with the type Organic selected only
shows data for organic fruit.

Here is the farm data again, alongside a PivotTable. The PivotTable is using Farm and
Type as the row hierarchies, Crates Sold at Farm and Crates Sold Wholesale as the data
hierarchies (with the default aggregation function of sum), and Classification as a filter
hierarchy (with Organic selected).

This PivotTable could be generated through the JavaScript API or through the Excel UI.
Both options allow for further manipulation through add-ins.

Create a PivotTable
PivotTables need a name, source, and destination. The source can be a range address or
table name (passed as a Range , string , or Table type). The destination is a range
address (given as either a Range or string ). The following samples show various
PivotTable creation techniques.

Create a PivotTable with range addresses


JavaScript

await Excel.run(async (context) => {


// Create a PivotTable named "Farm Sales" on the current worksheet at
cell
// A22 with data from the range A1:E21.
context.workbook.worksheets.getActiveWorksheet().pivotTables.add(
"Farm Sales", "A1:E21", "A22");

await context.sync();
});

Create a PivotTable with Range objects


JavaScript

await Excel.run(async (context) => {


// Create a PivotTable named "Farm Sales" on a worksheet called
"PivotWorksheet" at cell A2
// the data comes from the worksheet "DataWorksheet" across the range
A1:E21.
let rangeToAnalyze =
context.workbook.worksheets.getItem("DataWorksheet").getRange("A1:E21");
let rangeToPlacePivot =
context.workbook.worksheets.getItem("PivotWorksheet").getRange("A2");
context.workbook.worksheets.getItem("PivotWorksheet").pivotTables.add(
"Farm Sales", rangeToAnalyze, rangeToPlacePivot);

await context.sync();
});

Create a PivotTable at the workbook level


JavaScript

await Excel.run(async (context) => {


// Create a PivotTable named "Farm Sales" on a worksheet called
"PivotWorksheet" at cell A2
// the data is from the worksheet "DataWorksheet" across the range
A1:E21.
context.workbook.pivotTables.add(
"Farm Sales", "DataWorksheet!A1:E21", "PivotWorksheet!A2");

await context.sync();
});

Use an existing PivotTable


Manually created PivotTables are also accessible through the PivotTable collection of the
workbook or of individual worksheets. The following code gets a PivotTable named My
Pivot from the workbook.

JavaScript

await Excel.run(async (context) => {


let pivotTable = context.workbook.pivotTables.getItem("My Pivot");
await context.sync();
});

Add rows and columns to a PivotTable


Rows and columns pivot the data around those fields' values.

Adding the Farm column pivots all the sales around each farm. Adding the Type and
Classification rows further breaks down the data based on what fruit was sold and
whether it was organic or not.
JavaScript

await Excel.run(async (context) => {


let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");

pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Type"));

pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Classification
"));

pivotTable.columnHierarchies.add(pivotTable.hierarchies.getItem("Farm"));

await context.sync();
});

You can also have a PivotTable with only rows or columns.

JavaScript

await Excel.run(async (context) => {


let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");
pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Farm"));
pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Type"));

pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Classification
"));

await context.sync();
});

Add data hierarchies to the PivotTable


Data hierarchies fill the PivotTable with information to combine based on the rows and
columns. Adding the data hierarchies of Crates Sold at Farm and Crates Sold Wholesale
gives sums of those figures for each row and column.

In the example, both Farm and Type are rows, with the crate sales as the data.

JavaScript

await Excel.run(async (context) => {


let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");

// "Farm" and "Type" are the hierarchies on which the aggregation is


based.
pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Farm"));
pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Type"));

// "Crates Sold at Farm" and "Crates Sold Wholesale" are the hierarchies
// that will have their data aggregated (summed in this case).
pivotTable.dataHierarchies.add(pivotTable.hierarchies.getItem("Crates
Sold at Farm"));
pivotTable.dataHierarchies.add(pivotTable.hierarchies.getItem("Crates
Sold Wholesale"));

await context.sync();
});

PivotTable layouts and getting pivoted data


A PivotLayout defines the placement of hierarchies and their data. You access the layout
to determine the ranges where data is stored.

The following diagram shows which layout function calls correspond to which ranges of
the PivotTable.

Get data from the PivotTable


The layout defines how the PivotTable is displayed in the worksheet. This means the
PivotLayout object controls the ranges used for PivotTable elements. Use the ranges

provided by the layout to get data collected and aggregated by the PivotTable. In
particular, use PivotLayout.getDataBodyRange to access the data produced by the
PivotTable.

The following code demonstrates how to get the last row of the PivotTable data by
going through the layout (the Grand Total of both the Sum of Crates Sold at Farm and
Sum of Crates Sold Wholesale columns in the earlier example). Those values are then
summed together for a final total, which is displayed in cell E30 (outside of the
PivotTable).

JavaScript

await Excel.run(async (context) => {


let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");

// Get the totals for each data hierarchy from the layout.
let range = pivotTable.layout.getDataBodyRange();
let grandTotalRange = range.getLastRow();
grandTotalRange.load("address");
await context.sync();

// Sum the totals from the PivotTable data hierarchies and place them in
a new range, outside of the PivotTable.
let masterTotalRange =
context.workbook.worksheets.getActiveWorksheet().getRange("E30");
masterTotalRange.formulas = [["=SUM(" + grandTotalRange.address + ")"]];
await context.sync();
});

Layout types
PivotTables have three layout styles: Compact, Outline, and Tabular. We've seen the
compact style in the previous examples.

The following examples use the outline and tabular styles, respectively. The code sample
shows how to cycle between the different layouts.

Outline layout
Tabular layout
PivotLayout type switch code sample

JavaScript

await Excel.run(async (context) => {


// Change the PivotLayout.type to a new type.
let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");
pivotTable.layout.load("layoutType");
await context.sync();

// Cycle between the three layout types.


if (pivotTable.layout.layoutType === "Compact") {
pivotTable.layout.layoutType = "Outline";
} else if (pivotTable.layout.layoutType === "Outline") {
pivotTable.layout.layoutType = "Tabular";
} else {
pivotTable.layout.layoutType = "Compact";
}

await context.sync();
});
Other PivotLayout functions
By default, PivotTables adjust row and column sizes as needed. This is done when the
PivotTable is refreshed. PivotLayout.autoFormat specifies that behavior. Any row or
column size changes made by your add-in persist when autoFormat is false .
Additionally, the default settings of a PivotTable keep any custom formatting in the
PivotTable (such as fills and font changes). Set PivotLayout.preserveFormatting to false
to apply the default format when refreshed.

A PivotLayout also controls header and total row settings, how empty data cells are
displayed, and alt text options. The PivotLayout reference provides a complete list of
these features.

The following code sample makes empty data cells display the string "--" , formats the
body range to a consistent horizontal alignment, and ensures that the formatting
changes remain even after the PivotTable is refreshed.

JavaScript

await Excel.run(async (context) => {


let pivotTable = context.workbook.pivotTables.getItem("Farm Sales");
let pivotLayout = pivotTable.layout;

// Set a default value for an empty cell in the PivotTable. This doesn't
include cells left blank by the layout.
pivotLayout.emptyCellText = "--";

// Set the text alignment to match the rest of the PivotTable.


pivotLayout.getDataBodyRange().format.horizontalAlignment =
Excel.HorizontalAlignment.right;

// Ensure empty cells are filled with a default value.


pivotLayout.fillEmptyCells = true;

// Ensure that the format settings persist, even after the PivotTable is
refreshed and recalculated.
pivotLayout.preserveFormatting = true;
await context.sync();
});

Delete a PivotTable
PivotTables are deleted by using their name.

JavaScript
await Excel.run(async (context) => {
context.workbook.worksheets.getItem("Pivot").pivotTables.getItem("Farm
Sales").delete();
await context.sync();
});

Filter a PivotTable
The primary method for filtering PivotTable data is with PivotFilters. Slicers offer an
alternate, less flexible filtering method.

PivotFilters filter data based on a PivotTable's four hierarchy categories (filters, columns,
rows, and values). There are four types of PivotFilters, allowing calendar date-based
filtering, string parsing, number comparison, and filtering based on a custom input.

Slicers can be applied to both PivotTables and regular Excel tables. When applied to a
PivotTable, slicers function like a PivotManualFilter and allow filtering based on a custom
input. Unlike PivotFilters, slicers have an Excel UI component . With the Slicer class,
you create this UI component, manage filtering, and control its visual appearance.

Filter with PivotFilters


PivotFilters allow you to filter PivotTable data based on the four hierarchy categories
(filters, columns, rows, and values). In the PivotTable object model, PivotFilters are
applied to a PivotField, and each PivotField can have one or more assigned
PivotFilters . To apply PivotFilters to a PivotField, the field's corresponding

PivotHierarchy must be assigned to a hierarchy category.

Types of PivotFilters

Filter type Filter purpose Excel JavaScript API reference

DateFilter Calendar date-based filtering. PivotDateFilter

LabelFilter Text comparison filtering. PivotLabelFilter

ManualFilter Custom input filtering. PivotManualFilter

ValueFilter Number comparison filtering. PivotValueFilter

Create a PivotFilter
To filter PivotTable data with a Pivot*Filter (such as a PivotDateFilter ), apply the filter
to a PivotField. The following four code samples show how to use each of the four types
of PivotFilters.

PivotDateFilter

The first code sample applies a PivotDateFilter to the Date Updated PivotField, hiding
any data prior to 2020-08-01.

) Important

A Pivot*Filter can't be applied to a PivotField unless that field's PivotHierarchy is


assigned to a hierarchy category. In the following code sample, the dateHierarchy
must be added to the PivotTable's rowHierarchies category before it can be used
for filtering.

JavaScript

await Excel.run(async (context) => {


// Get the PivotTable and the date hierarchy.
let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");
let dateHierarchy = pivotTable.rowHierarchies.getItemOrNullObject("Date
Updated");
await context.sync();

// PivotFilters can only be applied to PivotHierarchies that are being


used for pivoting.
// If it's not already there, add "Date Updated" to the hierarchies.
if (dateHierarchy.isNullObject) {
dateHierarchy =
pivotTable.rowHierarchies.add(pivotTable.hierarchies.getItem("Date
Updated"));
}

// Apply a date filter to filter out anything logged before August.


let filterField = dateHierarchy.fields.getItem("Date Updated");
let dateFilter = {
condition: Excel.DateFilterCondition.afterOrEqualTo,
comparator: {
date: "2020-08-01",
specificity: Excel.FilterDatetimeSpecificity.month
}
};
filterField.applyFilter({ dateFilter: dateFilter });
await context.sync();
});

7 Note

The following three code snippets only display filter-specific excerpts, instead of full
Excel.run calls.

PivotLabelFilter

The second code snippet demonstrates how to apply a PivotLabelFilter to the Type
PivotField, using the LabelFilterCondition.beginsWith property to exclude labels that
start with the letter L.

JavaScript

// Get the "Type" field.


let filterField =
pivotTable.hierarchies.getItem("Type").fields.getItem("Type");

// Filter out any types that start with "L" ("Lemons" and "Limes" in
this case).
let filter: Excel.PivotLabelFilter = {
condition: Excel.LabelFilterCondition.beginsWith,
substring: "L",
exclusive: true
};

// Apply the label filter to the field.


filterField.applyFilter({ labelFilter: filter });

PivotManualFilter

The third code snippet applies a manual filter with PivotManualFilter to the the
Classification field, filtering out data that doesn't include the classification Organic.

JavaScript

// Apply a manual filter to include only a specific PivotItem (the


string "Organic").
let filterField = classHierarchy.fields.getItem("Classification");
let manualFilter = { selectedItems: ["Organic"] };
filterField.applyFilter({ manualFilter: manualFilter });
PivotValueFilter

To compare numbers, use a value filter with PivotValueFilter, as shown in the final code
snippet. The PivotValueFilter compares the data in the Farm PivotField to the data in
the Crates Sold Wholesale PivotField, including only farms whose sum of crates sold
exceeds the value 500.

JavaScript

// Get the "Farm" field.


let filterField =
pivotTable.hierarchies.getItem("Farm").fields.getItem("Farm");

// Filter to only include rows with more than 500 wholesale crates sold.
let filter: Excel.PivotValueFilter = {
condition: Excel.ValueFilterCondition.greaterThan,
comparator: 500,
value: "Sum of Crates Sold Wholesale"
};

// Apply the value filter to the field.


filterField.applyFilter({ valueFilter: filter });

Remove PivotFilters

To remove all PivotFilters, apply the clearAllFilters method to each PivotField, as


shown in the following code sample.

JavaScript

await Excel.run(async (context) => {


// Get the PivotTable.
let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");
pivotTable.hierarchies.load("name");
await context.sync();

// Clear the filters on each PivotField.


pivotTable.hierarchies.items.forEach(function (hierarchy) {
hierarchy.fields.getItem(hierarchy.name).clearAllFilters();
});
await context.sync();
});

Filter with slicers


Slicers allow data to be filtered from an Excel PivotTable or table. A slicer uses values
from a specified column or PivotField to filter corresponding rows. These values are
stored as SlicerItem objects in the Slicer . Your add-in can adjust these filters, as can
users (through the Excel UI ). The slicer sits on top of the worksheet in the drawing
layer, as shown in the following screenshot.

7 Note

The techniques described in this section focus on how to use slicers connected to
PivotTables. The same techniques also apply to using slicers connected to tables.

Create a slicer
You can create a slicer in a workbook or worksheet by using the Workbook.slicers.add
method or Worksheet.slicers.add method. Doing so adds a slicer to the SlicerCollection
of the specified Workbook or Worksheet object. The SlicerCollection.add method has
three parameters:

slicerSource : The data source on which the new slicer is based. It can be a

PivotTable , Table , or string representing the name or ID of a PivotTable or

Table .
sourceField : The field in the data source by which to filter. It can be a PivotField ,

TableColumn , or string representing the name or ID of a PivotField or


TableColumn .

slicerDestination : The worksheet where the new slicer will be created. It can be a

Worksheet object or the name or ID of a Worksheet . This parameter is unnecessary


when the SlicerCollection is accessed through Worksheet.slicers . In this case,
the collection's worksheet is used as the destination.

The following code sample adds a new slicer to the Pivot worksheet. The slicer's source
is the Farm Sales PivotTable and filters using the Type data. The slicer is also named
Fruit Slicer for future reference.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Pivot");
let slicer = sheet.slicers.add(
"Farm Sales" /* The slicer data source. For PivotTables, this can be
the PivotTable object reference or name. */,
"Type" /* The field in the data to filter by. For PivotTables, this
can be a PivotField object reference or ID. */
);
slicer.name = "Fruit Slicer";
await context.sync();
});

Filter items with a slicer

The slicer filters the PivotTable with items from the sourceField . The
Slicer.selectItems method sets the items that remain in the slicer. These items are

passed to the method as a string[] , representing the keys of the items. Any rows
containing those items remain in the PivotTable's aggregation. Subsequent calls to
selectItems set the list to the keys specified in those calls.

7 Note

If Slicer.selectItems is passed an item that's not in the data source, an


InvalidArgument error is thrown. The contents can be verified through the

Slicer.slicerItems property, which is a SlicerItemCollection.

The following code sample shows three items being selected for the slicer: Lemon, Lime,
and Orange.

JavaScript

await Excel.run(async (context) => {


let slicer = context.workbook.slicers.getItem("Fruit Slicer");
// Anything other than the following three values will be filtered out
of the PivotTable for display and aggregation.
slicer.selectItems(["Lemon", "Lime", "Orange"]);
await context.sync();
});
To remove all filters from the slicer, use the Slicer.clearFilters method, as shown in
the following sample.

JavaScript

await Excel.run(async (context) => {


let slicer = context.workbook.slicers.getItem("Fruit Slicer");
slicer.clearFilters();
await context.sync();
});

Style and format a slicer

You add-in can adjust a slicer's display settings through Slicer properties. The
following code sample sets the style to SlicerStyleLight6, sets the text at the top of the
slicer to Fruit Types, places the slicer at the position (395, 15) on the drawing layer, and
sets the slicer's size to 135x150 pixels.

JavaScript

await Excel.run(async (context) => {


let slicer = context.workbook.slicers.getItem("Fruit Slicer");
slicer.caption = "Fruit Types";
slicer.left = 395;
slicer.top = 15;
slicer.height = 135;
slicer.width = 150;
slicer.style = "SlicerStyleLight6";
await context.sync();
});

Delete a slicer
To delete a slicer, call the Slicer.delete method. The following code sample deletes the
first slicer from the current worksheet.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.slicers.getItemAt(0).delete();
await context.sync();
});
Change aggregation function
Data hierarchies have their values aggregated. For datasets of numbers, this is a sum by
default. The summarizeBy property defines this behavior based on an
AggregationFunction type.

The currently supported aggregation function types are Sum , Count , Average , Max , Min ,
Product , CountNumbers , StandardDeviation , StandardDeviationP , Variance , VarianceP ,
and Automatic (the default).

The following code samples changes the aggregation to be averages of the data.

JavaScript

await Excel.run(async (context) => {


let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");
pivotTable.dataHierarchies.load("no-properties-needed");
await context.sync();

// Change the aggregation from the default sum to an average of all the
values in the hierarchy.
pivotTable.dataHierarchies.items[0].summarizeBy =
Excel.AggregationFunction.average;
pivotTable.dataHierarchies.items[1].summarizeBy =
Excel.AggregationFunction.average;
await context.sync();
});

Change calculations with a ShowAsRule


PivotTables, by default, aggregate the data of their row and column hierarchies
independently. A ShowAsRule changes the data hierarchy to output values based on
other items in the PivotTable.

The ShowAsRule object has three properties:

calculation : The type of relative calculation to apply to the data hierarchy (the

default is none ).
baseField : The PivotField in the hierarchy containing the base data before the

calculation is applied. Since Excel PivotTables have a one-to-one mapping of


hierarchy to field, you'll use the same name to access both the hierarchy and the
field.
baseItem : The individual PivotItem compared against the values of the base fields

based on the calculation type. Not all calculations require this field.

The following example sets the calculation on the Sum of Crates Sold at Farm data
hierarchy to be a percentage of the column total. We still want the granularity to extend
to the fruit type level, so we'll use the Type row hierarchy and its underlying field. The
example also has Farm as the first row hierarchy, so the farm total entries display the
percentage each farm is responsible for producing as well.

JavaScript

await Excel.run(async (context) => {


let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");
let farmDataHierarchy = pivotTable.dataHierarchies.getItem("Sum of
Crates Sold at Farm");

farmDataHierarchy.load("showAs");
await context.sync();

// Show the crates of each fruit type sold at the farm as a percentage
of the column's total.
let farmShowAs = farmDataHierarchy.showAs;
farmShowAs.calculation = Excel.ShowAsCalculation.percentOfColumnTotal;
farmShowAs.baseField =
pivotTable.rowHierarchies.getItem("Type").fields.getItem("Type");
farmDataHierarchy.showAs = farmShowAs;
farmDataHierarchy.name = "Percentage of Total Farm Sales";
});

The previous example set the calculation to the column, relative to the field of an
individual row hierarchy. When the calculation relates to an individual item, use the
baseItem property.

The following example shows the differenceFrom calculation. It displays the difference
of the farm crate sales data hierarchy entries relative to those of A Farms. The baseField
is Farm, so we see the differences between the other farms, as well as breakdowns for
each type of like fruit (Type is also a row hierarchy in this example).

JavaScript

await Excel.run(async (context) => {


let pivotTable =
context.workbook.worksheets.getActiveWorksheet().pivotTables.getItem("Farm
Sales");
let farmDataHierarchy = pivotTable.dataHierarchies.getItem("Sum of
Crates Sold at Farm");

farmDataHierarchy.load("showAs");
await context.sync();

// Show the difference between crate sales of the "A Farms" and the
other farms.
// This difference is both aggregated and shown for individual fruit
types (where applicable).
let farmShowAs = farmDataHierarchy.showAs;
farmShowAs.calculation = Excel.ShowAsCalculation.differenceFrom;
farmShowAs.baseField =
pivotTable.rowHierarchies.getItem("Farm").fields.getItem("Farm");
farmShowAs.baseItem =
pivotTable.rowHierarchies.getItem("Farm").fields.getItem("Farm").items.getIt
em("A Farms");
farmDataHierarchy.showAs = farmShowAs;
farmDataHierarchy.name = "Difference from A Farms";
});

Change hierarchy names


Hierarchy fields are editable. The following code demonstrates how to change the
displayed names of two data hierarchies.

JavaScript

await Excel.run(async (context) => {


let dataHierarchies = context.workbook.worksheets.getActiveWorksheet()
.pivotTables.getItem("Farm Sales").dataHierarchies;
dataHierarchies.load("no-properties-needed");
await context.sync();

// Changing the displayed names of these entries.


dataHierarchies.items[0].name = "Farm Sales";
dataHierarchies.items[1].name = "Wholesale";
});

See also
Excel JavaScript object model in Office Add-ins
Excel JavaScript API Reference
Get a range using the Excel JavaScript
API
Article • 05/02/2023

This article provides examples that show different ways to get a range within a
worksheet using the Excel JavaScript API. For the complete list of properties and
methods that the Range object supports, see Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Get range by address


The following code sample gets the range with address B2:C5 from the worksheet
named Sample, loads its address property, and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("B2:C5");


range.load("address");
await context.sync();

console.log(`The address of the range B2:C5 is "${range.address}"`);


});

Get range by name


The following code sample gets the range named MyRange from the worksheet named
Sample, loads its address property, and writes a message to the console.

JavaScript
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("MyRange");


range.load("address");
await context.sync();

console.log(`The address of the range "MyRange" is "${range.address}"`);


});

Get used range


The following code sample gets the used range from the worksheet named Sample,
loads its address property, and writes a message to the console. The used range is the
smallest range that encompasses any cells in the worksheet that have a value or
formatting assigned to them. If the entire worksheet is blank, the getUsedRange()
method returns a range that consists of only the top-left cell.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getUsedRange();


range.load("address");
await context.sync();

console.log(`The address of the used range in the worksheet is


"${range.address}"`);
});

Get entire range


The following code sample gets the entire worksheet range from the worksheet named
Sample, loads its address property, and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange();


range.load("address");
await context.sync();
console.log(`The address of the entire worksheet range is
"${range.address}"`);
});

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Insert a range using the Excel JavaScript API
Insert a range of cells using the Excel
JavaScript API
Article • 05/02/2023

This article provides a code sample that inserts a range of cells with the Excel JavaScript
API. For the complete list of properties and methods that the Range object supports, see
the Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Insert a range of cells


The following code sample inserts a range of cells in location B4:E4 and shifts other cells
down to provide space for the new cells.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let range = sheet.getRange("B4:E4");

range.insert(Excel.InsertShiftDirection.down);

await context.sync();
});

Data before range is inserted


Data after range is inserted

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Clear or delete a ranges using the Excel JavaScript API
Clear or delete ranges using the Excel
JavaScript API
Article • 05/02/2023

This article provides code samples that clear and delete ranges with the Excel JavaScript
API. For the complete list of properties and methods supported by the Range object, see
Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Clear a range of cells


The following code sample clears all contents and formatting of cells in the range E2:E5.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let range = sheet.getRange("E2:E5");

range.clear();

await context.sync();
});

Data before range is cleared


Data after range is cleared

Delete a range of cells


The following code sample deletes the cells in the range B4:E4 and shifts other cells up
to fill the space that was vacated by the deleted cells.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let range = sheet.getRange("B4:E4");

range.delete(Excel.DeleteShiftDirection.up);

await context.sync();
});

Data before range is deleted

Data after range is deleted


See also
Work with cells using the Excel JavaScript API
Set and get ranges using the Excel JavaScript API
Excel JavaScript object model in Office Add-ins
Set and get the selected range using the
Excel JavaScript API
Article • 05/02/2023

This article provides code samples that set and get the selected range with the Excel
JavaScript API. For the complete list of properties and methods that the Range object
supports, see Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Set the selected range


The following code sample selects the range B2:E6 in the active worksheet.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let range = sheet.getRange("B2:E6");

range.select();

await context.sync();
});

Selected range B2:E6


Get the selected range
The following code sample gets the selected range, loads its address property, and
writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let range = context.workbook.getSelectedRange();
range.load("address");

await context.sync();

console.log(`The address of the selected range is "${range.address}"`);


});

Select the edge of a used range


The Range.getRangeEdge and Range.getExtendedRange methods let your add-in
replicate the behavior of the keyboard selection shortcuts, selecting the edge of the
used range based on the currently selected range. To learn more about used ranges, see
Get used range.

In the following screenshot, the used range is the table with values in each cell, C5:F12.
The empty cells outside this table are outside the used range.
Select the cell at the edge of the current used range
The following code sample shows how use the Range.getRangeEdge method to select the
cell at the furthest edge of the current used range, in the direction up. This action
matches the result of using the Ctrl+Up arrow key keyboard shortcut while a range is
selected.

JavaScript

await Excel.run(async (context) => {


// Get the selected range.
let range = context.workbook.getSelectedRange();

// Specify the direction with the `KeyboardDirection` enum.


let direction = Excel.KeyboardDirection.up;

// Get the active cell in the workbook.


let activeCell = context.workbook.getActiveCell();

// Get the top-most cell of the current used range.


// This method acts like the Ctrl+Up arrow key keyboard shortcut while a
range is selected.
let rangeEdge = range.getRangeEdge(
direction,
activeCell
);
rangeEdge.select();

await context.sync();
});

Before selecting the cell at the edge of the used range


The following screenshot shows a used range and a selected range within the used
range. The used range is a table with data at C5:F12. Inside this table, the range D8:E9 is
selected. This selection is the before state, prior to running the Range.getRangeEdge
method.

After selecting the cell at the edge of the used range

The following screenshot shows the same table as the preceding screenshot, with data
in the range C5:F12. Inside this table, the range D5 is selected. This selection is after
state, after running the Range.getRangeEdge method to select the cell at the edge of the
used range in the up direction.

Select all cells from current range to furthest edge of


used range
The following code sample shows how use the Range.getExtendedRange method to to
select all the cells from the currently selected range to the furthest edge of the used
range, in the direction down. This action matches the result of using the
Ctrl+Shift+Down arrow key keyboard shortcut while a range is selected.

JavaScript

await Excel.run(async (context) => {


// Get the selected range.
let range = context.workbook.getSelectedRange();

// Specify the direction with the `KeyboardDirection` enum.


let direction = Excel.KeyboardDirection.down;

// Get the active cell in the workbook.


let activeCell = context.workbook.getActiveCell();

// Get all the cells from the currently selected range to the bottom-
most edge of the used range.
// This method acts like the Ctrl+Shift+Down arrow key keyboard shortcut
while a range is selected.
let extendedRange = range.getExtendedRange(
direction,
activeCell
);
extendedRange.select();

await context.sync();
});

Before selecting all the cells from the current range to the edge of
the used range

The following screenshot shows a used range and a selected range within the used
range. The used range is a table with data at C5:F12. Inside this table, the range D8:E9 is
selected. This selection is the before state, prior to running the Range.getExtendedRange
method.
After selecting all the cells from the current range to the edge of
the used range

The following screenshot shows the same table as the preceding screenshot, with data
in the range C5:F12. Inside this table, the range D8:E12 is selected. This selection is after
state, after running the Range.getExtendedRange method to select all the cells from the
current range to the edge of the used range in the down direction.

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Set and get range values, text, or formulas using the Excel JavaScript API
Set range format using the Excel JavaScript API
Set and get range values, text, or
formulas using the Excel JavaScript API
Article • 05/02/2023

This article provides code samples that set and get range values, text, or formulas with
the Excel JavaScript API. For the complete list of properties and methods that the Range
object supports, see Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Set values or formulas


The following code samples set values and formulas for a single cell or a range of cells.

Set value for a single cell


The following code sample sets the value of cell C3 to "5" and then sets the width of the
columns to best fit the data.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("C3");


range.values = [[ 5 ]];
range.format.autofitColumns();

await context.sync();
});

Data before cell value is updated


Data after cell value is updated

Set values for a range of cells


The following code sample sets values for the cells in the range B5:D5 and then sets the
width of the columns to best fit the data.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let data = [
["Potato Chips", 10, 1.80],
];

let range = sheet.getRange("B5:D5");


range.values = data;
range.format.autofitColumns();

await context.sync();
});

Data before cell values are updated


Data after cell values are updated

Set formula for a single cell


The following code sample sets a formula for cell E3 and then sets the width of the
columns to best fit the data.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("E3");


range.formulas = [[ "=C3 * D3" ]];
range.format.autofitColumns();

await context.sync();
});

Data before cell formula is set


Data after cell formula is set

Set formulas for a range of cells


The following code sample sets formulas for cells in the range E2:E6 and then sets the
width of the columns to best fit the data.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let data = [
["=C3 * D3"],
["=C4 * D4"],
["=C5 * D5"],
["=SUM(E3:E5)"]
];

let range = sheet.getRange("E3:E6");


range.formulas = data;
range.format.autofitColumns();

await context.sync();
});

Data before cell formulas are set

Data after cell formulas are set


Get values, text, or formulas
These code samples get values, text, and formulas from a range of cells.

Get values from a range of cells


The following code sample gets the range B2:E6, loads its values property, and writes
the values to the console. The values property of a range specifies the raw values that
the cells contain. Even if some cells in a range contain formulas, the values property of
the range specifies the raw values for those cells, not any of the formulas.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("B2:E6");


range.load("values");
await context.sync();

console.log(JSON.stringify(range.values, null, 4));


});

Data in range (values in column E are a result of formulas)

range.values (as logged to the console by the code sample above)


JSON

[
[
"Product",
"Qty",
"Unit Price",
"Total Price"
],
[
"Almonds",
2,
7.5,
15
],
[
"Coffee",
1,
34.5,
34.5
],
[
"Chocolate",
5,
9.56,
47.8
],
[
"",
"",
"",
97.3
]
]

Get text from a range of cells


The following code sample gets the range B2:E6, loads its text property, and writes it
to the console. The text property of a range specifies the display values for cells in the
range. Even if some cells in a range contain formulas, the text property of the range
specifies the display values for those cells, not any of the formulas.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("B2:E6");


range.load("text");
await context.sync();
console.log(JSON.stringify(range.text, null, 4));
});

Data in range (values in column E are a result of formulas)

range.text (as logged to the console by the code sample above)

JSON

[
[
"Product",
"Qty",
"Unit Price",
"Total Price"
],
[
"Almonds",
"2",
"7.5",
"15"
],
[
"Coffee",
"1",
"34.5",
"34.5"
],
[
"Chocolate",
"5",
"9.56",
"47.8"
],
[
"",
"",
"",
"97.3"
]
]

Get formulas from a range of cells


The following code sample gets the range B2:E6, loads its formulas property, and writes
it to the console. The formulas property of a range specifies the formulas for cells in the
range that contain formulas and the raw values for cells in the range that do not contain
formulas.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("B2:E6");


range.load("formulas");
await context.sync();

console.log(JSON.stringify(range.formulas, null, 4));


});

Data in range (values in column E are a result of formulas)

range.formulas (as logged to the console by the code sample


above)

JSON

[
[
"Product",
"Qty",
"Unit Price",
"Total Price"
],
[
"Almonds",
2,
7.5,
"=C3 * D3"
],
[
"Coffee",
1,
34.5,
"=C4 * D4"
],
[
"Chocolate",
5,
9.56,
"=C5 * D5"
],
[
"",
"",
"",
"=SUM(E3:E5)"
]
]

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Set and get ranges using the Excel JavaScript API
Set range format using the Excel JavaScript API
Set range format using the Excel
JavaScript API
Article • 05/02/2023

This article provides code samples that set font color, fill color, and number format for
cells in a range with the Excel JavaScript API. For the complete list of properties and
methods that the Range object supports, see Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Set font color and fill color


The following code sample sets the font color and fill color for cells in range B2:E2.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let range = sheet.getRange("B2:E2");


range.format.fill.color = "#4472C4";
range.format.font.color = "white";

await context.sync();
});

Data in range before font color and fill color are set
Data in range after font color and fill color are set

Set number format


The following code sample sets the number format for the cells in range D3:E5.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let formats = [
["0.00", "0.00"],
["0.00", "0.00"],
["0.00", "0.00"]
];

let range = sheet.getRange("D3:E5");


range.numberFormat = formats;

await context.sync();
});

Data in range before number format is set

Data in range after number format is set


See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Set and get ranges using the Excel JavaScript API
Set and get range values, text, or formulas using the Excel JavaScript API
Apply conditional formatting to Excel
ranges
Article • 05/22/2023

The Excel JavaScript Library provides APIs to apply conditional formatting to data ranges
in your worksheets. This functionality makes large sets of data easy to visually parse. The
formatting also dynamically updates based on changes within the range.

7 Note

This article covers conditional formatting in the context of Excel JavaScript add-ins.
The following articles provide detailed information about the full conditional
formatting capabilities within Excel.

Add, change, or clear conditional formats


Use formulas with conditional formatting

Programmatic control of conditional


formatting
The Range.conditionalFormats property is a collection of ConditionalFormat objects that
apply to the range. The ConditionalFormat object contains several properties that define
the format to be applied based on the ConditionalFormatType.

cellValue

colorScale

custom
dataBar

iconSet
preset

textComparison
topBottom

7 Note

Each of these formatting properties has a corresponding *OrNullObject variant.


Learn more about that pattern in the *OrNullObject methods section.
Only one format type can be set for the ConditionalFormat object. This is determined by
the type property, which is a ConditionalFormatType enum value. type is set when
adding a conditional format to a range.

Create conditional formatting rules


Conditional formats are added to a range by using conditionalFormats.add . Once
added, the properties specific to the conditional format can be set. The following
examples show the creation of different formatting types.

Cell value
Cell value conditional formatting applies a user-defined format based on the results of
one or two formulas in the ConditionalCellValueRule. The operator property is a
ConditionalCellValueOperator defining how the resulting expressions relate to the
formatting.

The following example shows red font coloring applied to any value in the range less
than zero.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B21:E23");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.cellValue
);

// Set the font of negative numbers to red.


conditionalFormat.cellValue.format.font.color = "red";
conditionalFormat.cellValue.rule = { formula1: "=0", operator:
"LessThan" };

await context.sync();
});

Color scale
Color scale conditional formatting applies a color gradient across the data range. The
criteria property on the ColorScaleConditionalFormat defines three
ConditionalColorScaleCriterion: minimum , maximum , and, optionally, midpoint . Each of the
criterion scale points have three properties:

color - The HTML color code for the endpoint.

formula - A number or formula representing the endpoint. This will be null if

type is lowestValue or highestValue .


type - How the formula should be evaluated. highestValue and lowestValue refer

to values in the range being formatted.

The following example shows a range being colored blue to yellow to red. Note that
minimum and maximum are the lowest and highest values respectively and use null

formulas. midpoint is using the percentage type with a formula of "=50" so the
yellowest cell is the mean value.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B2:M5");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.colorScale
);

// Color the backgrounds of the cells from blue to yellow to red based
on value.
const criteria = {
minimum: {
formula: null,
type: Excel.ConditionalFormatColorCriterionType.lowestValue,
color: "blue"
},
midpoint: {
formula: "50",
type: Excel.ConditionalFormatColorCriterionType.percent,
color: "yellow"
},
maximum: {
formula: null,
type: Excel.ConditionalFormatColorCriterionType.highestValue,
color: "red"
}
};
conditionalFormat.colorScale.criteria = criteria;

await context.sync();
});

Custom
Custom conditional formatting applies a user-defined format to the cells based on a
formula of arbitrary complexity. The ConditionalFormatRule object lets you define the
formula in different notations:

formula - Standard notation.


formulaLocal - Localized based on the user's language.

formulaR1C1 - R1C1-style notation.

The following example colors the fonts green of cells with higher values than the cell to
their left.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B8:E13");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.custom
);

// If a cell has a higher value than the one to its left, set that
cell's font to green.
conditionalFormat.custom.rule.formula =
'=IF(B8>INDIRECT("RC[-1]",0),TRUE)';
conditionalFormat.custom.format.font.color = "green";

await context.sync();
});

Data bar
Data bar conditional formatting adds data bars to the cells. By default, the minimum and
maximum values in the Range form the bounds and proportional sizes of the data bars.
The DataBarConditionalFormat object has several properties to control the bar's
appearance.

The following example formats the range with data bars filling left-to-right.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B8:E13");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.dataBar
);

// Give left-to-right, default-appearance data bars to all the cells.


conditionalFormat.dataBar.barDirection =
Excel.ConditionalDataBarDirection.leftToRight;
await context.sync();
});

Icon set
Icon set conditional formatting uses Excel Icons to highlight cells. The criteria property
is an array of ConditionalIconCriterion, which define the symbol to be inserted and the
condition under which it is inserted. This array is automatically prepopulated with
criterion elements with default properties. Individual properties cannot be overwritten.
Instead, the whole criteria object must be replaced.

The following example shows a three-triangle icon set applied across the range.
JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B8:E13");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.iconSet
);

const iconSetCF = conditionalFormat.iconSet;


iconSetCF.style = Excel.IconSet.threeTriangles;

/*
With a "three*" icon set style, such as "threeTriangles", the third
element in the criteria array (criteria[2]) defines the "top" icon;
e.g., a green triangle. The second (criteria[1]) defines the
"middle"
icon, The first (criteria[0]) defines the "low" icon, but it can
often
be left empty as this method does below, because every cell that
does not match the other two criteria always gets the low icon.
*/
iconSetCF.criteria = [
{},
{
type: Excel.ConditionalFormatIconRuleType.number,
operator:
Excel.ConditionalIconCriterionOperator.greaterThanOrEqual,
formula: "=700"
},
{
type: Excel.ConditionalFormatIconRuleType.number,
operator:
Excel.ConditionalIconCriterionOperator.greaterThanOrEqual,
formula: "=1000"
}
];

await context.sync();
});

Preset criteria
Preset conditional formatting applies a user-defined format to the range based on a
selected standard rule. These rules are defined by the ConditionalFormatPresetCriterion
in the ConditionalPresetCriteriaRule.

The following example colors the font white wherever a cell's value is at least one
standard deviation above the range's average.
JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B2:M5");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.presetCriteria
);

// Color every cell's font white that is one standard deviation above
average relative to the range.
conditionalFormat.preset.format.font.color = "white";
conditionalFormat.preset.rule = {
criterion:
Excel.ConditionalFormatPresetCriterion.oneStdDevAboveAverage
};

await context.sync();
});

Text comparison
Text comparison conditional formatting uses string comparisons as the condition. The
rule property is a ConditionalTextComparisonRule defining a string to compare with
the cell and an operator to specify the type of comparison.

The following example formats the font color red when a cell's text contains "Delayed".

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B16:D18");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.containsText
);

// Color the font of every cell containing "Delayed".


conditionalFormat.textComparison.format.font.color = "red";
conditionalFormat.textComparison.rule = {
operator: Excel.ConditionalTextOperator.contains,
text: "Delayed"
};

await context.sync();
});

Top/bottom
Top/bottom conditional formatting applies a format to the highest or lowest values in a
range. The rule property, which is of type ConditionalTopBottomRule, sets whether the
condition is based on the highest or lowest, as well as whether the evaluation is ranked
or percentage-based.

The following example applies a green highlight to the highest value cell in the range.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B21:E23");
const conditionalFormat = range.conditionalFormats.add(
Excel.ConditionalFormatType.topBottom
);

// For the highest valued cell in the range, make the background green.
conditionalFormat.topBottom.format.fill.color = "green"
conditionalFormat.topBottom.rule = { rank: 1, type: "TopItems"}

await context.sync();
});

Change conditional formatting rules


The ConditionalFormat object offers multiple methods to change conditional formatting
rules after they've been set.

changeRuleToCellValue
changeRuleToColorScale
changeRuleToContainsText
changeRuleToCustom
changeRuleToDataBar
changeRuleToIconSet
changeRuleToPresetCriteria
changeRuleToTopBottom

The following example shows how to use the changeRuleToPresetCriteria method from
the preceding list to change an existing conditional format rule to the preset criteria rule
type.

7 Note

The specified range must have an existing conditional format rule to use the
change methods. If the specified range has no conditional format rule, the change
methods don't apply a new rule.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange("B2:M5");

// Retrieve the first existing `ConditionalFormat` rule on this range.


// Note: The specified range must have an existing conditional format
rule.
const conditionalFormat =
range.conditionalFormats.getItemOrNullObject("0");

// Change the conditional format rule to preset criteria.


conditionalFormat.changeRuleToPresetCriteria({
criterion:
Excel.ConditionalFormatPresetCriterion.oneStdDevAboveAverage,
});
conditionalFormat.preset.format.font.color = "red";

await context.sync();
});

Multiple formats and priority


You can apply multiple conditional formats to a range. If the formats have conflicting
elements, such as differing font colors, only one format applies that particular element.
Precedence is defined by the ConditionalFormat.priority property. Priority is a number
(equal to the index in the ConditionalFormatCollection ) and can be set when creating
the format. The lower the priority value, the higher the priority of the format is.

The following example shows a conflicting font color choice between the two formats.
Negative numbers will get a bold font, but NOT a red font, because priority goes to the
format that gives them a blue font.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const temperatureDataRange =
sheet.tables.getItem("TemperatureTable").getDataBodyRange();

// Set low numbers to bold, dark red font and assign priority 1.
const presetFormat = temperatureDataRange.conditionalFormats
.add(Excel.ConditionalFormatType.presetCriteria);
presetFormat.preset.format.font.color = "red";
presetFormat.preset.format.font.bold = true;
presetFormat.preset.rule = { criterion:
Excel.ConditionalFormatPresetCriterion.oneStdDevBelowAverage };
presetFormat.priority = 1;

// Set negative numbers to blue font with green background and set
priority 0.
const cellValueFormat = temperatureDataRange.conditionalFormats
.add(Excel.ConditionalFormatType.cellValue);
cellValueFormat.cellValue.format.font.color = "blue";
cellValueFormat.cellValue.format.fill.color = "lightgreen";
cellValueFormat.cellValue.rule = { formula1: "=0", operator: "LessThan"
};
cellValueFormat.priority = 0;

await context.sync();
});

Mutually exclusive conditional formats


The stopIfTrue property of ConditionalFormat prevents lower priority conditional
formats from being applied to the range. When a range matching the conditional
format with stopIfTrue === true is applied, no subsequent conditional formats are
applied, even if their formatting details are not contradictory.

The following example shows two conditional formats being added to a range. Negative
numbers will have a blue font with a light green background, regardless of whether the
other format condition is true.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const temperatureDataRange =
sheet.tables.getItem("TemperatureTable").getDataBodyRange();

// Set low numbers to bold, dark red font and assign priority 1.
const presetFormat = temperatureDataRange.conditionalFormats
.add(Excel.ConditionalFormatType.presetCriteria);
presetFormat.preset.format.font.color = "red";
presetFormat.preset.format.font.bold = true;
presetFormat.preset.rule = { criterion:
Excel.ConditionalFormatPresetCriterion.oneStdDevBelowAverage };
presetFormat.priority = 1;

// Set negative numbers to blue font with green background and


// set priority 0, but set stopIfTrue to true, so none of the
// formatting of the conditional format with the higher priority
// value will apply, not even the bolding of the font.
const cellValueFormat = temperatureDataRange.conditionalFormats
.add(Excel.ConditionalFormatType.cellValue);
cellValueFormat.cellValue.format.font.color = "blue";
cellValueFormat.cellValue.format.fill.color = "lightgreen";
cellValueFormat.cellValue.rule = { formula1: "=0", operator: "LessThan"
};
cellValueFormat.priority = 0;
cellValueFormat.stopIfTrue = true;

await context.sync();
});

Clear conditional formatting rules


To remove format properties from a specific conditional format rule, use the clearFormat
method of the ConditionalRangeFormat object. The clearFormat method creates a
formatting rule without format settings.

To remove all the conditional formatting rules from a specific range, or an entire
worksheet, use the clearAll method of the ConditionalFormatCollection object.

The following sample shows how to remove all conditional formatting from a worksheet
with the clearAll method.

JavaScript

await Excel.run(async (context) => {


const sheet = context.workbook.worksheets.getItem("Sample");
const range = sheet.getRange();
range.conditionalFormats.clearAll();

await context.sync();
});

See also
Excel JavaScript object model in Office Add-ins
ConditionalFormat Object (JavaScript API for Excel)
Add, change, or clear conditional formats
Use formulas with conditional formatting
Read or write to an unbounded range
using the Excel JavaScript API
Article • 05/02/2023

This article describes how to read and write to an unbounded range with the Excel
JavaScript API. For the complete list of properties and methods that the Range object
supports, see Excel.Range class.

An unbounded range address is a range address that specifies either entire columns or
entire rows. For example:

Range addresses comprised of entire columns.


C:C

A:F

Range addresses comprised of entire rows.


2:2

1:4

Read an unbounded range


When the API makes a request to retrieve an unbounded range (for example,
getRange('C:C') ), the response will contain null values for cell-level properties such as

values , text , numberFormat , and formula . Other properties of the range, such as

address and cellCount , will contain valid values for the unbounded range.

Write to an unbounded range


You cannot set cell-level properties such as values , numberFormat , and formula on an
unbounded range because the input request is too large. For example, the following
code example is not valid because it attempts to specify values for an unbounded
range. The API returns an error if you attempt to set cell-level properties for an
unbounded range.

JavaScript

// Note: This code sample attempts to specify `values` for an unbounded


range, which is not a valid request. The sample will return an error.
let range =
context.workbook.worksheets.getActiveWorksheet().getRange('A:B');
range.values = 'Due Date';
See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Read or write to a large range using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Read or write to a large range using the
Excel JavaScript API
Article • 03/09/2023

This article describes how to handle reading and writing to large ranges with the Excel
JavaScript API.

Run separate read or write operations for large


ranges
If a range contains a large number of cells, values, number formats, or formulas, it may
not be possible to run API operations on that range. The API will always make a best
attempt to run the requested operation on a range (i.e., to retrieve or write the specified
data), but attempting to perform read or write operations for a large range may result in
an API error due to excessive resource utilization. To avoid such errors, we recommend
that you run separate read or write operations for smaller subsets of a large range,
instead of attempting to run a single read or write operation on a large range.

For details on the system limitations, see the "Excel add-ins" section of Resource limits
and performance optimization for Office Add-ins.

Conditional formatting of ranges


Ranges can have formats applied to individual cells based on conditions. For more
information about this, see Apply conditional formatting to Excel ranges.

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Read or write to an unbounded range using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Find a string within a range using the
Excel JavaScript API
Article • 05/02/2023

This article provides a code sample that finds a string within a range using the Excel
JavaScript API. For the complete list of properties and methods that the Range object
supports, see Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Match a string within a range


The Range object has a find method to search for a specified string within the range. It
returns the range of the first cell with matching text.

The following code sample finds the first cell with a value equal to the string Food and
logs its address to the console. Note that find throws an ItemNotFound error if the
specified string doesn't exist in the range. If you expect that the specified string may not
exist in the range, use the findOrNullObject method instead, so your code gracefully
handles that scenario.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let table = sheet.tables.getItem("ExpensesTable");
let searchRange = table.getRange();
let foundRange = searchRange.find("Food", {
completeMatch: true, // Match the whole cell value.
matchCase: false, // Don't match case.
searchDirection: Excel.SearchDirection.forward // Start search at
the beginning of the range.
});

foundRange.load("address");
await context.sync();
console.log(foundRange.address);
});

When the find method is called on a range representing a single cell, the entire
worksheet is searched. The search begins at that cell and goes in the direction specified
by SearchCriteria.searchDirection , wrapping around the ends of the worksheet if
needed.

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Find special cells within a range using the Excel JavaScript API
Work with dates using the Excel
JavaScript API and the Moment-MSDate
plug-in
Article • 05/02/2023

This article provides code samples that show how to work with dates using the Excel
JavaScript API and the Moment-MSDate plug-in . For the complete list of properties
and methods that the Range object supports, see the Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Use the Moment-MSDate plug-in to work with


dates
The Moment JavaScript library provides a convenient way to use dates and
timestamps. The Moment-MSDate plug-in converts the format of moments into one
preferable for Excel. This is the same format the NOW function returns.

The following code shows how to set the range at B4 to a moment's timestamp.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let now = Date.now();


let nowMoment = moment(now);
let nowMS = nowMoment.toOADate();

let dateRange = sheet.getRange("B4");


dateRange.values = [[nowMS]];

dateRange.numberFormat = [["[$-409]m/d/yy h:mm AM/PM;@"]];


await context.sync();
});

The following code sample demonstrates a similar technique to get the date back out of
the cell and convert it to a Moment or other format.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let dateRange = sheet.getRange("B4");


dateRange.load("values");

await context.sync();

let nowMS = dateRange.values[0][0];

// Log the date as a moment.


let nowMoment = moment.fromOADate(nowMS);
console.log(`get (moment): ${JSON.stringify(nowMoment)}`);

// Log the date as a UNIX-style timestamp.


let now = nowMoment.unix();
console.log(`get (timestamp): ${now}`);
});

Your add-in has to format the ranges to display the dates in a more human-readable
form. For example, "[$-409]m/d/yy h:mm AM/PM;@" displays "12/3/18 3:57 PM". For more
information about date and time number formats, see "Guidelines for date and time
formats" in the Review guidelines for customizing a number format article.

See also
Work with cells using the Excel JavaScript API
Excel JavaScript object model in Office Add-ins
Work with multiple ranges simultaneously in Excel add-ins
Find special cells within a range using
the Excel JavaScript API
Article • 05/02/2023

This article provides code samples that find special cells within a range using the Excel
JavaScript API. For the complete list of properties and methods that the Range object
supports, see Excel.Range class.

Find ranges with special cells


The Range.getSpecialCells and Range.getSpecialCellsOrNullObject methods find ranges
based on the characteristics of their cells and the types of values of their cells. Both of
these methods return RangeAreas objects. Here are the signatures of the methods from
the TypeScript data types file:

TypeScript

getSpecialCells(cellType: Excel.SpecialCellType, cellValueType?:


Excel.SpecialCellValueType): Excel.RangeAreas;

TypeScript

getSpecialCellsOrNullObject(cellType: Excel.SpecialCellType, cellValueType?:


Excel.SpecialCellValueType): Excel.RangeAreas;

The following code sample uses the getSpecialCells method to find all the cells with
formulas. About this code, note:

It limits the part of the sheet that needs to be searched by first calling
Worksheet.getUsedRange and calling getSpecialCells for only that range.

The getSpecialCells method returns a RangeAreas object, so all of the cells with
formulas will be colored pink even if they are not all contiguous.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let usedRange = sheet.getUsedRange();
let formulaRanges =
usedRange.getSpecialCells(Excel.SpecialCellType.formulas);
formulaRanges.format.fill.color = "pink";
await context.sync();
});

If no cells with the targeted characteristic exist in the range, getSpecialCells throws an
ItemNotFound error. This diverts the flow of control to a catch block, if there is one. If
there isn't a catch block, the error halts the method.

If you expect that cells with the targeted characteristic should always exist, you'll likely
want your code to throw an error if those cells aren't there. If it's a valid scenario that
there aren't any matching cells, your code should check for this possibility and handle it
gracefully without throwing an error. You can achieve this behavior with the
getSpecialCellsOrNullObject method and its returned isNullObject property. The

following code sample uses this pattern. About this code, note:

The getSpecialCellsOrNullObject method always returns a proxy object, so it's


never null in the ordinary JavaScript sense. But if no matching cells are found, the
isNullObject property of the object is set to true .
It calls context.sync before it tests the isNullObject property. This is a
requirement with all *OrNullObject methods and properties, because you always
have to load and sync a property in order to read it. However, it's not necessary to
explicitly load the isNullObject property. It's automatically loaded by the
context.sync even if load is not called on the object. For more information, see

*OrNullObject methods and properties.


You can test this code by first selecting a range that has no formula cells and
running it. Then select a range that has at least one cell with a formula and run it
again.

JavaScript

await Excel.run(async (context) => {


let range = context.workbook.getSelectedRange();
let formulaRanges =
range.getSpecialCellsOrNullObject(Excel.SpecialCellType.formulas);
await context.sync();

if (formulaRanges.isNullObject) {
console.log("No cells have formulas");
}
else {
formulaRanges.format.fill.color = "pink";
}

await context.sync();
});
For simplicity, all other code samples in this article use the getSpecialCells method
instead of getSpecialCellsOrNullObject .

Narrow the target cells with cell value types


The Range.getSpecialCells() and Range.getSpecialCellsOrNullObject() methods
accept an optional second parameter used to further narrow down the targeted cells.
This second parameter is an Excel.SpecialCellValueType you use to specify that you
only want cells that contain certain types of values.

7 Note

The Excel.SpecialCellValueType parameter can only be used if the


Excel.SpecialCellType is Excel.SpecialCellType.formulas or

Excel.SpecialCellType.constants .

Test for a single cell value type


The Excel.SpecialCellValueType enum has these four basic types (in addition to the
other combined values described later in this section):

Excel.SpecialCellValueType.errors

Excel.SpecialCellValueType.logical (which means boolean)

Excel.SpecialCellValueType.numbers
Excel.SpecialCellValueType.text

The following code sample finds special cells that are numerical constants and colors
those cells pink. About this code, note:

It only highlights cells that have a literal number value. It won't highlight cells that
have a formula (even if the result is a number) or a boolean, text, or error state
cells.
To test the code, be sure the worksheet has some cells with literal number values,
some with other kinds of literal values, and some with formulas.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let usedRange = sheet.getUsedRange();
let constantNumberRanges = usedRange.getSpecialCells(
Excel.SpecialCellType.constants,
Excel.SpecialCellValueType.numbers);
constantNumberRanges.format.fill.color = "pink";

await context.sync();
});

Test for multiple cell value types


Sometimes you need to operate on more than one cell value type, such as all text-
valued and all boolean-valued ( Excel.SpecialCellValueType.logical ) cells. The
Excel.SpecialCellValueType enum has values with combined types. For example,

Excel.SpecialCellValueType.logicalText targets all boolean and all text-valued cells.


Excel.SpecialCellValueType.all is the default value, which does not limit the cell value

types returned. The following code sample colors all cells with formulas that produce
number or boolean value.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let usedRange = sheet.getUsedRange();
let formulaLogicalNumberRanges = usedRange.getSpecialCells(
Excel.SpecialCellType.formulas,
Excel.SpecialCellValueType.logicalNumbers);
formulaLogicalNumberRanges.format.fill.color = "pink";

await context.sync();
});

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Find a string using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Cut, copy, and paste ranges using the
Excel JavaScript API
Article • 05/02/2023

This article provides code samples that cut, copy, and paste ranges using the Excel
JavaScript API. For the complete list of properties and methods that the Range object
supports, see Excel.Range class.

7 Note

The Excel JavaScript API doesn't have a "Cell" object or class. Instead, the Excel
JavaScript API defines all Excel cells as Range objects. An individual cell in the Excel
UI translates to a Range object with one cell in the Excel JavaScript API. A single
Range object can also contain multiple contiguous cells. See Work with cells using

the Excel JavaScript API to learn more.

Copy and paste


The Range.copyFrom method replicates the Copy and Paste actions of the Excel UI. The
destination is the Range object that copyFrom is called on. The source to be copied is
passed as a range or a string address representing a range.

The following code sample copies the data from A1:E1 into the range starting at G1
(which ends up pasting into G1:K1).

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
// Copy everything from "A1:E1" into "G1" and the cells afterwards
("G1:K1").
sheet.getRange("G1").copyFrom("A1:E1");
await context.sync();
});

Range.copyFrom has three optional parameters.

TypeScript

copyFrom(sourceRange: Range | RangeAreas | string, copyType?:


Excel.RangeCopyType, skipBlanks?: boolean, transpose?: boolean): void;
copyType specifies what data gets copied from the source to the destination.

Excel.RangeCopyType.formulas transfers the formulas in the source cells and


preserves the relative positioning of those formulas' ranges. Any non-formula
entries are copied as-is.
Excel.RangeCopyType.values copies the data values and, in the case of formulas,

the result of the formula.


Excel.RangeCopyType.formats copies the formatting of the range, including font,
color, and other format settings, but no values.
Excel.RangeCopyType.all (the default option) copies both data and formatting,
preserving cells' formulas if found.

skipBlanks sets whether blank cells are copied into the destination. When true,

copyFrom skips blank cells in the source range. Skipped cells will not overwrite the
existing data of their corresponding cells in the destination range. The default is false.

transpose determines whether or not the data is transposed, meaning its rows and
columns are switched, into the source location. A transposed range is flipped along the
main diagonal, so rows 1, 2, and 3 will become columns A, B, and C.

The following code sample and images demonstrate this behavior in a simple scenario.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
// Copy a range, omitting the blank cells so existing data is not
overwritten in those cells.
sheet.getRange("D1").copyFrom("A1:C1",
Excel.RangeCopyType.all,
true, // skipBlanks
false); // transpose
// Copy a range, including the blank cells which will overwrite existing
data in the target cells.
sheet.getRange("D2").copyFrom("A2:C2",
Excel.RangeCopyType.all,
false, // skipBlanks
false); // transpose
await context.sync();
});

Data before range is copied and pasted


Data after range is copied and pasted

Cut and paste (move) cells


The Range.moveTo method moves cells to a new location in the workbook. This cell
movement behavior works the same as when cells are moved by dragging the range
border or when taking the Cut and Paste actions. Both the formatting and values of
the range are moved to the location specified as the destinationRange parameter.

The following code sample moves a range with the Range.moveTo method. Note that if
the destination range is smaller than the source, it will be expanded to encompass the
source content.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.getRange("F1").values = [["Moved Range"]];

// Move the cells "A1:E1" to "G1" (which fills the range "G1:K1").
sheet.getRange("A1:E1").moveTo("G1");
await context.sync();
});

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Remove duplicates using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Remove duplicates using the Excel
JavaScript API
Article • 05/02/2023

This article provides a code sample that removes duplicate entries in a range using the
Excel JavaScript API. For the complete list of properties and methods that the Range
object supports, see Excel.Range class.

Remove rows with duplicate entries


The Range.removeDuplicates method removes rows with duplicate entries in the
specified columns. The method goes through each row in the range from the lowest-
valued index to the highest-valued index in the range (from top to bottom). A row is
deleted if a value in its specified column or columns appeared earlier in the range. Rows
in the range below the deleted row are shifted up. removeDuplicates does not affect the
position of cells outside of the range.

removeDuplicates takes in a number[] representing the column indices which are


checked for duplicates. This array is zero-based and relative to the range, not the
worksheet. The method also takes in a boolean parameter that specifies whether the
first row is a header. When true , the top row is ignored when considering duplicates.
The removeDuplicates method returns a RemoveDuplicatesResult object that specifies
the number of rows removed and the number of unique rows remaining.

When using a range's removeDuplicates method, keep the following in mind.

removeDuplicates considers cell values, not function results. If two different


functions evaluate to the same result, the cell values are not considered duplicates.
Empty cells are not ignored by removeDuplicates . The value of an empty cell is
treated like any other value. This means empty rows contained within in the range
will be included in the RemoveDuplicatesResult .

The following code sample shows the removal of entries with duplicate values in the first
column.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let range = sheet.getRange("B2:D11");
let deleteResult = range.removeDuplicates([0],true);
deleteResult.load();

await context.sync();

console.log(deleteResult.removed + " entries with duplicate names


removed.");
console.log(deleteResult.uniqueRemaining + " entries with unique names
remain in the range.");
});

Data before duplicate entries are removed

Data after duplicate entries are removed

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Cut, copy, and paste ranges using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Group ranges for an outline using the
Excel JavaScript API
Article • 05/02/2023

This article provides a code sample that shows how to group ranges for an outline using
the Excel JavaScript API. For the complete list of properties and methods that the Range
object supports, see Excel.Range class.

Group rows or columns of a range for an


outline
Rows or columns of a range can be grouped together to create an outline . These
groups can be collapsed and expanded to hide and show the corresponding cells. This
makes quick analysis of top-line data easier. Use Range.group to make these outline
groups.

An outline can have a hierarchy, where smaller groups are nested under larger groups.
This allows the outline to be viewed at different levels. Changing the visible outline level
can be done programmatically through the Worksheet.showOutlineLevels method. Note
that Excel only supports eight levels of outline groups.

The following code sample creates an outline with two levels of groups for both the
rows and columns. The subsequent image shows the groupings of that outline. In the
code sample, the ranges being grouped do not include the row or column of the outline
control (the "Totals" for this example). A group defines what will be collapsed, not the
row or column with the control.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

// Group the larger, main level. Note that the outline controls
// will be on row 10, meaning 4-9 will collapse and expand.
sheet.getRange("4:9").group(Excel.GroupOption.byRows);

// Group the smaller, sublevels. Note that the outline controls


// will be on rows 6 and 9, meaning 4-5 and 7-8 will collapse and
expand.
sheet.getRange("4:5").group(Excel.GroupOption.byRows);
sheet.getRange("7:8").group(Excel.GroupOption.byRows);

// Group the larger, main level. Note that the outline controls
// will be on column R, meaning C-Q will collapse and expand.
sheet.getRange("C:Q").group(Excel.GroupOption.byColumns);

// Group the smaller, sublevels. Note that the outline controls


// will be on columns G, L, and R, meaning C-F, H-K, and M-P will
collapse and expand.
sheet.getRange("C:F").group(Excel.GroupOption.byColumns);
sheet.getRange("H:K").group(Excel.GroupOption.byColumns);
sheet.getRange("M:P").group(Excel.GroupOption.byColumns);
await context.sync();
});

Remove grouping from rows or columns of a


range
To ungroup a row or column group, use the Range.ungroup method. This removes the
outermost level from the outline. If multiple groups of the same row or column type are
at the same level within the specified range, all of those groups are ungrouped.

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Handle dynamic arrays and spilling
using the Excel JavaScript API
Article • 05/02/2023

This article provides a code sample that handles dynamic arrays and range spilling using
the Excel JavaScript API. For the complete list of properties and methods that the Range
object supports, see Excel.Range class.

Dynamic arrays
Some Excel formulas return Dynamic arrays . These fill the values of multiple cells
outside of the formula's original cell. This value overflow is referred to as a "spill". Your
add-in can find the range used for a spill with the Range.getSpillingToRange method.
There is also a *OrNullObject version, Range.getSpillingToRangeOrNullObject .

The following sample shows a basic formula that copies the contents of a range into a
cell, which spills into neighboring cells. The add-in then logs the range that contains the
spill.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

// Set G4 to a formula that returns a dynamic array.


let targetCell = sheet.getRange("G4");
targetCell.formulas = [["=A4:D4"]];

// Get the address of the cells that the dynamic array spilled into.
let spillRange = targetCell.getSpillingToRange();
spillRange.load("address");

// Sync and log the spilled-to range.


await context.sync();

// This will log the range as "G4:J4".


console.log(`Copying the table headers spilled into
${spillRange.address}.`);
});

Range spilling
Find the cell responsible for spilling into a given cell by using the Range.getSpillParent
method. Note that getSpillParent only works when the range object is a single cell.
Calling getSpillParent on a range with multiple cells will result in an error being thrown
(or a null range being returned for Range.getSpillParentOrNullObject ).

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Get formula precedents and dependents
using the Excel JavaScript API
Article • 05/02/2023

Excel formulas often refer to other cells. These cross-cell references are known as
"precedents" and "dependents". A precedent is a cell that provides data to a formula. A
dependent is a cell that contains a formula that refers to other cells. To learn more about
Excel features related to relationships between cells, see Display the relationships
between formulas and cells .

A precedent cell may have its own precedent cells. Every precedent cell in this chain of
precedents is still a precedent of the original cell. The same relationship exists for
dependents. Any cell affected by another cell is a dependent of that cell. A "direct
precedent" is the first preceding group of cells in this sequence, similar to the concept
of parents in a parent-child relationship. A "direct dependent" is the first dependent
group of cells in a sequence, similar to children in a parent-child relationship.

This article provides code samples that retrieve precedents and dependents of formulas
using the Excel JavaScript API. For the complete list of properties and methods that the
Range object supports, see Range Object (JavaScript API for Excel).

Get the precedents of a formula


Locate a formula's precedent cells with Range.getPrecedents. Range.getPrecedents
returns a WorkbookRangeAreas object. This object contains the addresses of all the
precedents in the workbook. It has a separate RangeAreas object for each worksheet
containing at least one formula precedent. To learn more about the RangeAreas object,
see Work with multiple ranges simultaneously in Excel add-ins.

To locate only the direct precedent cells of a formula, use Range.getDirectPrecedents.


Range.getDirectPrecedents works like Range.getPrecedents and returns a

WorkbookRangeAreas object containing the addresses of direct precedents.

The following screenshot shows the result of selecting the Trace Precedents button in
the Excel UI. This button draws an arrow from precedent cells to the selected cell. The
selected cell, E3, contains the formula "=C3 * D3", so both C3 and D3 are precedent
cells. Unlike the Excel UI button, the getPrecedents and getDirectPrecedents methods
don't draw arrows.
) Important

The getPrecedents and getDirectPrecedents methods don't retrieve precedent


cells across workbooks.

The following code sample shows how to work with the Range.getPrecedents and
Range.getDirectPrecedents methods. The sample gets the precedents for the active
range and then changes the background color of those precedent cells. The background
color of the direct precedent cells is set to yellow and the background color of the other
precedent cells is set to orange.

JavaScript

// This code sample shows how to find and highlight the precedents
// and direct precedents of the currently selected cell.
await Excel.run(async (context) => {
let range = context.workbook.getActiveCell();
// Precedents are all cells that provide data to the selected formula.
let precedents = range.getPrecedents();
// Direct precedents are the parent cells, or the first preceding group of
cells that provide data to the selected formula.
let directPrecedents = range.getDirectPrecedents();

range.load("address");
precedents.areas.load("address");
directPrecedents.areas.load("address");

await context.sync();

console.log(`All precedent cells of ${range.address}:`);

// Use the precedents API to loop through all precedents of the active
cell.
for (let i = 0; i < precedents.areas.items.length; i++) {
// Highlight and print out the address of all precedent cells.
precedents.areas.items[i].format.fill.color = "Orange";
console.log(` ${precedents.areas.items[i].address}`);
}

console.log(`Direct precedent cells of ${range.address}:`);

// Use the direct precedents API to loop through direct precedents of the
active cell.
for (let i = 0; i < directPrecedents.areas.items.length; i++) {
// Highlight and print out the address of each direct precedent cell.
directPrecedents.areas.items[i].format.fill.color = "Yellow";
console.log(` ${directPrecedents.areas.items[i].address}`);
}
});

Get the dependents of a formula


Locate a formula's dependent cells with Range.getDependents. Like
Range.getPrecedents , Range.getDependents also returns a WorkbookRangeAreas object.
This object contains the addresses of all the dependents in the workbook. It has a
separate RangeAreas object for each worksheet containing at least one formula
dependent. For more information on working with the RangeAreas object, see Work with
multiple ranges simultaneously in Excel add-ins.

To locate only the direct dependent cells of a formula, use Range.getDirectDependents.


Range.getDirectDependents works like Range.getDependents and returns a

WorkbookRangeAreas object containing the addresses of direct dependents.

The following screenshot shows the result of selecting the Trace Dependents button in
the Excel UI. This button draws an arrow from the selected cell to dependent cells. The
selected cell, D3, has cell E3 as a dependent. E3 contains the formula "=C3 * D3". Unlike
the Excel UI button, the getDependents and getDirectDependents methods don't draw
arrows.
) Important

The getDependents and getDirectDependents methods don't retrieve dependent


cells across workbooks.

The following code sample gets the direct dependents for the active range and then
changes the background color of those dependent cells to yellow.

The following code sample shows how to work with the Range.getDependents and
Range.getDirectDependents methods. The sample gets the dependents for the active
range and then changes the background color of those dependent cells. The
background color of the direct dependent cells is set to yellow and the background
color of the other dependent cells is set to orange.

JavaScript

// This code sample shows how to find and highlight the dependents
// and direct dependents of the currently selected cell.
await Excel.run(async (context) => {
let range = context.workbook.getActiveCell();
// Dependents are all cells that contain formulas that refer to other
cells.
let dependents = range.getDependents();
// Direct dependents are the child cells, or the first succeeding group
of cells in a sequence of cells that refer to other cells.
let directDependents = range.getDirectDependents();

range.load("address");
dependents.areas.load("address");
directDependents.areas.load("address");

await context.sync();

console.log(`All dependent cells of ${range.address}:`);

// Use the dependents API to loop through all dependents of the active
cell.
for (let i = 0; i < dependents.areas.items.length; i++) {
// Highlight and print out the addresses of all dependent cells.
dependents.areas.items[i].format.fill.color = "Orange";
console.log(` ${dependents.areas.items[i].address}`);
}

console.log(`Direct dependent cells of ${range.address}:`);

// Use the direct dependents API to loop through direct dependents of


the active cell.
for (let i = 0; i < directDependents.areas.items.length; i++) {
// Highlight and print the address of each dependent cell.
directDependents.areas.items[i].format.fill.color = "Yellow";
console.log(` ${directDependents.areas.items[i].address}`);
}
});

See also
Excel JavaScript object model in Office Add-ins
Work with cells using the Excel JavaScript API
Work with multiple ranges simultaneously in Excel add-ins
Delay execution while cell is being
edited
Article • 07/21/2022

Excel.run has an overload that takes in a Excel.RunOptions object. This contains a set of
properties that affect platform behavior when the function runs. The following property
is currently supported.

delayForCellEdit : Determines whether Excel delays the batch request until the
user exits cell edit mode. When true , the batch request is delayed and runs when
the user exits cell edit mode. When false , the batch request automatically fails if
the user is in cell edit mode (causing an error to reach the user). The default
behavior with no delayForCellEdit property specified is equivalent to when it is
false .

JavaScript

await Excel.run({ delayForCellEdit: true }, async (context) => { ... });


Work with multiple ranges
simultaneously in Excel add-ins
Article • 03/22/2022

The Excel JavaScript library enables your add-in to perform operations, and set
properties, on multiple ranges simultaneously. The ranges do not have to be
contiguous. In addition to making your code simpler, this way of setting a property runs
much faster than setting the same property individually for each of the ranges.

RangeAreas
A set of (possibly discontiguous) ranges is represented by a RangeAreas object. It has
properties and methods similar to the Range type (many with the same, or similar,
names), but adjustments have been made to:

The data types for properties and the behavior of the setters and getters.
The data types of method parameters and the method behaviors.
The data types of method return values.

Some examples:

RangeAreas has an address property that returns a comma-delimited string of


range addresses, instead of just one address as with the Range.address property.
RangeAreas has a dataValidation property that returns a DataValidation object

that represents the data validation of all the ranges in the RangeAreas , if it is
consistent. The property is null if identical DataValidation objects are not applied
to all the all the ranges in the RangeAreas . This is a general, but not universal,
principle with the RangeAreas object: If a property does not have consistent values
on all the all the ranges in the RangeAreas , then it is null . See Read properties of
RangeAreas for more information and some exceptions.
RangeAreas.cellCount gets the total number of cells in all the ranges in the

RangeAreas .
RangeAreas.calculate recalculates the cells of all the ranges in the RangeAreas .

RangeAreas.getEntireColumn and RangeAreas.getEntireRow return another

RangeAreas object that represents all of the columns (or rows) in all the ranges in
the RangeAreas . For example, if the RangeAreas represents "A1:C4" and "F14:L15",
then RangeAreas.getEntireColumn returns a RangeAreas object that represents
"A:C" and "F:L".
RangeAreas.copyFrom can take either a Range or a RangeAreas parameter

representing the source range(s) of the copy operation.

Complete list of Range members that are also available


on RangeAreas

Properties
Be familiar with Read properties of RangeAreas before you write code that reads any
properties listed. There are subtleties to what gets returned.

address
addressLocal

cellCount
conditionalFormats

context

dataValidation
format

isEntireColumn
isEntireRow

style

worksheet

Methods

calculate()
clear()

convertDataTypeToText()
convertToLinkedDataType()

copyFrom()

getEntireColumn()
getEntireRow()

getIntersection()
getIntersectionOrNullObject()

getOffsetRange() (named getOffsetRangeAreas on the RangeAreas object)

getSpecialCells()
getSpecialCellsOrNullObject()

getTables()
getUsedRange() (named getUsedRangeAreas on the RangeAreas object)

getUsedRangeOrNullObject() (named getUsedRangeAreasOrNullObject on the


RangeAreas object)

load()
set()

setDirty()

toJSON()
track()

untrack()

RangeArea-specific properties and methods


The RangeAreas type has some properties and methods that are not on the Range
object. The following is a selection of them.

areas : A RangeCollection object that contains all of the ranges represented by the

RangeAreas object. The RangeCollection object is also new and is similar to other

Excel collection objects. It has an items property which is an array of Range objects
representing the ranges.
areaCount : The total number of ranges in the RangeAreas .
getOffsetRangeAreas : Works just like Range.getOffsetRange, except that a

RangeAreas is returned and it contains ranges that are each offset from one of the

ranges in the original RangeAreas .

Create RangeAreas
You can create RangeAreas object in two basic ways:

Call Worksheet.getRanges() and pass it a string with comma-delimited range


addresses. If any range you want to include has been made into a NamedItem, you
can include the name, instead of the address, in the string.
Call Workbook.getSelectedRanges() . This method returns a RangeAreas representing
all the ranges that are selected on the currently active worksheet.

Once you have a RangeAreas object, you can create others using the methods on the
object that return RangeAreas such as getOffsetRangeAreas and getIntersection .

7 Note
You cannot directly add additional ranges to a RangeAreas object. For example, the
collection in RangeAreas.areas does not have an add method.

2 Warning

Do not attempt to directly add or delete members of the the


RangeAreas.areas.items array. This will lead to undesirable behavior in your code.

For example, it is possible to push an additional Range object onto the array, but
doing so will cause errors because RangeAreas properties and methods behave as if
the new item isn't there. For example, the areaCount property does not include
ranges pushed in this way, and the RangeAreas.getItemAt(index) throws an error if
index is larger than areasCount-1 . Similarly, deleting a Range object in the

RangeAreas.areas.items array by getting a reference to it and calling its

Range.delete method causes bugs: although the Range object is deleted, the
properties and methods of the parent RangeAreas object behave, or try to, as if it is
still in existence. For example, if your code calls RangeAreas.calculate , Office will
try to calculate the range, but will error because the range object is gone.

Set properties on multiple ranges


Setting a property on a RangeAreas object sets the corresponding property on all the
ranges in the RangeAreas.areas collection.

The following is an example of setting a property on multiple ranges. The function


highlights the ranges F3:F5 and H3:H5.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let rangeAreas = sheet.getRanges("F3:F5, H3:H5");
rangeAreas.format.fill.color = "pink";

await context.sync();
});

This example applies to scenarios in which you can hard code the range addresses that
you pass to getRanges or easily calculate them at runtime. Some of the scenarios in
which this would be true include:
The code runs in the context of a known template.
The code runs in the context of imported data where the schema of the data is
known.

Get special cells from multiple ranges


The getSpecialCells and getSpecialCellsOrNullObject methods on the RangeAreas
object work analogously to methods of the same name on the Range object. These
methods return the cells with the specified characteristic from all of the ranges in the
RangeAreas.areas collection. For more details on special cells, see Find special cells

within a range.

When calling the getSpecialCells or getSpecialCellsOrNullObject method on a


RangeAreas object:

If you pass Excel.SpecialCellType.sameConditionalFormat as the first parameter,


the method returns all cells with the same conditional formatting as the upper-
leftmost cell in the first range in the RangeAreas.areas collection.
If you pass Excel.SpecialCellType.sameDataValidation as the first parameter, the
method returns all cells with the same data validation rule as the upper-leftmost
cell in the first range in the RangeAreas.areas collection.

Read properties of RangeAreas


Reading property values of RangeAreas requires care, because a given property may
have different values for different ranges within the RangeAreas . The general rule is that
if a consistent value can be returned it will be returned. For example, in the following
code, the RGB code for pink ( #FFC0CB ) and true will be logged to the console because
both the ranges in the RangeAreas object have a pink fill and both are entire columns.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();

// The ranges are the F column and the H column.


let rangeAreas = sheet.getRanges("F:F, H:H");
rangeAreas.format.fill.color = "pink";

rangeAreas.load("format/fill/color, isEntireColumn");
await context.sync();

console.log(rangeAreas.format.fill.color); // #FFC0CB
console.log(rangeAreas.isEntireColumn); // true
});

Things get more complicated when consistency isn't possible. The behavior of
RangeAreas properties follows these three principles:

A boolean property of a RangeAreas object returns false unless the property is


true for all the member ranges.
Non-boolean properties, with the exception of the address property, return null
unless the corresponding property on all the member ranges has the same value.
The address property returns a comma-delimited string of the addresses of the
member ranges.

For example, the following code creates a RangeAreas in which only one range is an
entire column and only one is filled with pink. The console will show null for the fill
color, false for the isEntireRow property, and "Sheet1!F3:F5, Sheet1!H:H" (assuming
the sheet name is "Sheet1") for the address property.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
let rangeAreas = sheet.getRanges("F3:F5, H:H");

let pinkColumnRange = sheet.getRange("H:H");


pinkColumnRange.format.fill.color = "pink";

rangeAreas.load("format/fill/color, isEntireColumn, address");


await context.sync();

console.log(rangeAreas.format.fill.color); // null
console.log(rangeAreas.isEntireColumn); // false
console.log(rangeAreas.address); // "Sheet1!F3:F5, Sheet1!H:H"
});

See also
Fundamental programming concepts with the Excel JavaScript API
Read or write to a large range using the Excel JavaScript API
Work with shapes using the Excel
JavaScript API
Article • 07/19/2022

Excel defines shapes as any object that sits on the drawing layer of Excel. That means
anything outside of a cell is a shape. This article describes how to use geometric shapes,
lines, and images in conjunction with the Shape and ShapeCollection APIs. Charts are
covered in their own article, Work with charts using the Excel JavaScript API.

The following image shows shapes which form a thermometer.

Create shapes
Shapes are created through and stored in a worksheet's shape collection
( Worksheet.shapes ). ShapeCollection has several .add* methods for this purpose. All
shapes have names and IDs generated for them when they are added to the collection.
These are the name and id properties, respectively. name can be set by your add-in for
easy retrieval with the ShapeCollection.getItem(name) method.

The following types of shapes are added using the associated method.

Shape Add Method Signature

Geometric addGeometricShape addGeometricShape(geometricShapeType:


Shape Excel.GeometricShapeType): Excel.Shape

Image addImage addImage(base64ImageString: string): Excel.Shape


(either JPEG
or PNG)

Line addLine addLine(startLeft: number, startTop: number, endLeft:


number, endTop: number, connectorType?:
Excel.ConnectorType): Excel.Shape
Shape Add Method Signature

SVG addSvg addSvg(xml: string): Excel.Shape

Text Box addTextBox addTextBox(text?: string): Excel.Shape

Geometric shapes
A geometric shape is created with ShapeCollection.addGeometricShape . That method
takes a GeometricShapeType enum as an argument.

The following code sample creates a 150x150-pixel rectangle named "Square" that is
positioned 100 pixels from the top and left sides of the worksheet.

JavaScript

// This sample creates a rectangle positioned 100 pixels from the top and
left sides
// of the worksheet and is 150x150 pixels.
await Excel.run(async (context) => {
let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;

let rectangle =
shapes.addGeometricShape(Excel.GeometricShapeType.rectangle);
rectangle.left = 100;
rectangle.top = 100;
rectangle.height = 150;
rectangle.width = 150;
rectangle.name = "Square";

await context.sync();
});

Images
JPEG, PNG, and SVG images can be inserted into a worksheet as shapes. The
ShapeCollection.addImage method takes a base64-encoded string as an argument. This
is either a JPEG or PNG image in string form. ShapeCollection.addSvg also takes in a
string, though this argument is XML that defines the graphic.

The following code sample shows an image file being loaded by a FileReader as a
string. The string has the metadata "base64," removed before the shape is created.

JavaScript
// This sample creates an image as a Shape object in the worksheet.
let myFile = document.getElementById("selectedFile");
let reader = new FileReader();

reader.onload = (event) => {


Excel.run(function (context) {
let startIndex = reader.result.toString().indexOf("base64,");
let myBase64 = reader.result.toString().substr(startIndex + 7);
let sheet = context.workbook.worksheets.getItem("MyWorksheet");
let image = sheet.shapes.addImage(myBase64);
image.name = "Image";
return context.sync();
}).catch(errorHandlerFunction);
};

// Read in the image file as a data URL.


reader.readAsDataURL(myFile.files[0]);

Lines
A line is created with ShapeCollection.addLine . That method needs the left and top
margins of the line's start and end points. It also takes a ConnectorType enum to specify
how the line contorts between endpoints. The following code sample creates a straight
line on the worksheet.

JavaScript

// This sample creates a straight line from [200,50] to [300,150] on the


worksheet.
await Excel.run(async (context) => {
let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
let line = shapes.addLine(200, 50, 300, 150,
Excel.ConnectorType.straight);
line.name = "StraightLine";
await context.sync();
});

Lines can be connected to other Shape objects. The connectBeginShape and


connectEndShape methods attach the beginning and ending of a line to shapes at the

specified connection points. The locations of these points vary by shape, but the
Shape.connectionSiteCount can be used to ensure your add-in does not connect to a
point that's out-of-bounds. A line is disconnected from any attached shapes using the
disconnectBeginShape and disconnectEndShape methods.

The following code sample connects the "MyLine" line to two shapes named
"LeftShape" and "RightShape".
JavaScript

// This sample connects a line between two shapes at connection points '0'
and '3'.
await Excel.run(async (context) => {
let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
let line = shapes.getItem("MyLine").line;
line.connectBeginShape(shapes.getItem("LeftShape"), 0);
line.connectEndShape(shapes.getItem("RightShape"), 3);
await context.sync();
});

Move and resize shapes


Shapes sit on top of the worksheet. Their placement is defined by the left and top
property. These act as margins from worksheet's respective edges, with [0, 0] being the
upper-left corner. These can either be set directly or adjusted from their current position
with the incrementLeft and incrementTop methods. How much a shape is rotated from
the default position is also established in this manner, with the rotation property being
the absolute amount and the incrementRotation method adjusting the existing rotation.

A shape's depth relative to other shapes is defined by the zorderPosition property. This
is set using the setZOrder method, which takes a ShapeZOrder. setZOrder adjusts the
ordering of the current shape relative to the other shapes.

Your add-in has a couple options for changing the height and width of shapes. Setting
either the height or width property changes the specified dimension without changing
the other dimension. The scaleHeight and scaleWidth adjust the shape's respective
dimensions relative to either the current or original size (based on the value of the
provided ShapeScaleType). An optional ShapeScaleFrom parameter specifies from where
the shape scales (top-left corner, middle, or bottom-right corner). If the
lockAspectRatio property is true , the scale methods maintain the shape's current

aspect ratio by also adjusting the other dimension.

7 Note

Direct changes to the height and width properties only affect that property,
regardless of the lockAspectRatio property's value.

The following code sample shows a shape being scaled to 1.25 times its original size and
rotated 30 degrees.
JavaScript

// In this sample, the shape "Octagon" is rotated 30 degrees clockwise


// and scaled 25% larger, with the upper-left corner remaining in place.
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getItem("MyWorksheet");

let shape = sheet.shapes.getItem("Octagon");


shape.incrementRotation(30);
shape.lockAspectRatio = true;
shape.scaleWidth(
1.25,
Excel.ShapeScaleType.currentSize,
Excel.ShapeScaleFrom.scaleFromTopLeft);

await context.sync();
});

Text in shapes
Geometric Shapes can contain text. Shapes have a textFrame property of type
TextFrame. The TextFrame object manages the text display options (such as margins and
text overflow). TextFrame.textRange is a TextRange object with the text content and font
settings.

The following code sample creates a geometric shape named "Wave" with the text
"Shape Text". It also adjusts the shape and text colors, as well as sets the text's
horizontal alignment to the center.

JavaScript

// This sample creates a light-blue wave shape and adds the purple text
"Shape text" to the center.
await Excel.run(async (context) => {
let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
let wave = shapes.addGeometricShape(Excel.GeometricShapeType.wave);
wave.left = 100;
wave.top = 400;
wave.height = 50;
wave.width = 150;

wave.name = "Wave";
wave.fill.setSolidColor("lightblue");

wave.textFrame.textRange.text = "Shape text";


wave.textFrame.textRange.font.color = "purple";
wave.textFrame.horizontalAlignment =
Excel.ShapeTextHorizontalAlignment.center;
await context.sync();
});

The addTextBox method of ShapeCollection creates a GeometricShape of type Rectangle


with a white background and black text. This is the same as what is created by Excel's
Text Box button on the Insert tab. addTextBox takes a string argument to set the text of
the TextRange .

The following code sample shows the creation of a text box with the text "Hello!".

JavaScript

// This sample creates a text box with the text "Hello!" and sizes it
appropriately.
await Excel.run(async (context) => {
let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
let textbox = shapes.addTextBox("Hello!");
textbox.left = 100;
textbox.top = 100;
textbox.height = 20;
textbox.width = 45;
textbox.name = "Textbox";
await context.sync();
});

Shape groups
Shapes can be grouped together. This allows a user to treat them as a single entity for
positioning, sizing, and other related tasks. A ShapeGroup is a type of Shape , so your
add-in treats the group as a single shape.

The following code sample shows three shapes being grouped together. The
subsequent code sample shows that shape group being moved to the right 50 pixels.

JavaScript

// This sample takes three previously-created shapes ("Square", "Pentagon",


and "Octagon")
// and groups them into a single ShapeGroup.
await Excel.run(async (context) => {
let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
let square = shapes.getItem("Square");
let pentagon = shapes.getItem("Pentagon");
let octagon = shapes.getItem("Octagon");

let shapeGroup = shapes.addGroup([square, pentagon, octagon]);


shapeGroup.name = "Group";
console.log("Shapes grouped");

await context.sync();
});

// This sample moves the previously created shape group to the right by 50
pixels.
await Excel.run(async (context) => {
let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
let shapeGroup = shapes.getItem("Group");
shapeGroup.incrementLeft(50);
await context.sync();
});

) Important

Individual shapes within the group are referenced through the ShapeGroup.shapes
property, which is of type GroupShapeCollection. They are no longer accessible
through the worksheet's shape collection after being grouped. As an example, if
your worksheet had three shapes and they were all grouped together, the
worksheet's shapes.getCount method would return a count of 1.

Export shapes as images


Any Shape object can be converted to an image. Shape.getAsImage returns base64-
encoded string. The image's format is specified as a PictureFormat enum passed to
getAsImage .

JavaScript

await Excel.run(async (context) => {


let shapes = context.workbook.worksheets.getItem("MyWorksheet").shapes;
let shape = shapes.getItem("Image");
let stringResult = shape.getAsImage(Excel.PictureFormat.png);

await context.sync();

console.log(stringResult.value);
// Instead of logging, your add-in may use the base64-encoded string to
save the image as a file or insert it in HTML.
});

Delete shapes
Shapes are removed from the worksheet with the Shape object's delete method. No
other metadata is needed.

The following code sample deletes all the shapes from MyWorksheet.

JavaScript

// This deletes all the shapes from "MyWorksheet".


await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getItem("MyWorksheet");
let shapes = sheet.shapes;

// We'll load all the shapes in the collection without loading their
properties.
shapes.load("items/$none");
await context.sync();

shapes.items.forEach(function (shape) {
shape.delete();
});

await context.sync();
});

See also
Fundamental programming concepts with the Excel JavaScript API
Work with charts using the Excel JavaScript API
Work with tables using the Excel
JavaScript API
Article • 05/20/2022

This article provides code samples that show how to perform common tasks with tables
using the Excel JavaScript API. For the complete list of properties and methods that the
Table and TableCollection objects support, see Table Object (JavaScript API for Excel)

and TableCollection Object (JavaScript API for Excel).

Create a table
The following code sample creates a table in the worksheet named Sample. The table
has headers and contains four columns and seven rows of data. If the Excel application
where the code is running supports requirement set ExcelApi 1.2, the width of the
columns and height of the rows are set to best fit the current data in the table.

7 Note

To specify a name for a table, you must first create the table and then set its name
property, as shown in the following example.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.add("A1:D1", true /*hasHeaders*/);
expensesTable.name = "ExpensesTable";

expensesTable.getHeaderRowRange().values = [["Date", "Merchant",


"Category", "Amount"]];

expensesTable.rows.add(null /*add rows to the end of the table*/, [


["1/1/2017", "The Phone Company", "Communications", "$120"],
["1/2/2017", "Northwind Electric Cars", "Transportation", "$142"],
["1/5/2017", "Best For You Organics Company", "Groceries", "$27"],
["1/10/2017", "Coho Vineyard", "Restaurant", "$33"],
["1/11/2017", "Bellows College", "Education", "$350"],
["1/15/2017", "Trey Research", "Other", "$135"],
["1/15/2017", "Best For You Organics Company", "Groceries", "$97"]
]);

if (Office.context.requirements.isSetSupported("ExcelApi", "1.2")) {
sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();
}

sheet.activate();

await context.sync();
});

New table

Add rows to a table


The following code sample adds seven new rows to the table named ExpensesTable
within the worksheet named Sample. The index parameter of the add method is set to
null , which specifies that the rows be added after the existing rows in the table. The

alwaysInsert parameter is set to true , which indicates that the new rows be inserted
into the table, not below the table. The width of the columns and height of the rows are
then set to best fit the current data in the table.

7 Note

The index property of a TableRow object indicates the index number of the row
within the rows collection of the table. A TableRow object does not contain an id
property that can be used as a unique key to identify the row.

JavaScript

// This code sample shows how to add rows to a table that already exists
// on a worksheet named Sample.
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

expensesTable.rows.add(
null, // index, Adds rows to the end of the table.
[
["1/16/2017", "THE PHONE COMPANY", "Communications", "$120"],
["1/20/2017", "NORTHWIND ELECTRIC CARS", "Transportation",
"$142"],
["1/20/2017", "BEST FOR YOU ORGANICS COMPANY", "Groceries",
"$27"],
["1/21/2017", "COHO VINEYARD", "Restaurant", "$33"],
["1/25/2017", "BELLOWS COLLEGE", "Education", "$350"],
["1/28/2017", "TREY RESEARCH", "Other", "$135"],
["1/31/2017", "BEST FOR YOU ORGANICS COMPANY", "Groceries",
"$97"]
],
true, // alwaysInsert, Specifies that the new rows be inserted into
the table.
);

sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();

await context.sync();
});

Table with new rows

Add a column to a table


These examples show how to add a column to a table. The first example populates the
new column with static values; the second example populates the new column with
formulas.
7 Note

The index property of a TableColumn object indicates the index number of the
column within the columns collection of the table. The id property of a
TableColumn object contains a unique key that identifies the column.

Add a column that contains static values


The following code sample adds a new column to the table named ExpensesTable
within the worksheet named Sample. The new column is added after all existing
columns in the table and contains a header ("Day of the Week") as well as data to
populate the cells in the column. The width of the columns and height of the rows are
then set to best fit the current data in the table.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

expensesTable.columns.add(null /*add columns to the end of the table*/,


[
["Day of the Week"],
["Saturday"],
["Friday"],
["Monday"],
["Thursday"],
["Sunday"],
["Saturday"],
["Monday"]
]);

sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();

await context.sync();
});

Table with new column


Add a column that contains formulas
The following code sample adds a new column to the table named ExpensesTable
within the worksheet named Sample. The new column is added to the end of the table,
contains a header ("Type of the Day"), and uses a formula to populate each data cell in
the column. The width of the columns and height of the rows are then set to best fit the
current data in the table.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

expensesTable.columns.add(null /*add columns to the end of the table*/,


[
["Type of the Day"],
['=IF(OR((TEXT([DATE], "dddd") = "Saturday"), (TEXT([DATE], "dddd")
= "Sunday")), "Weekend", "Weekday")'],
['=IF(OR((TEXT([DATE], "dddd") = "Saturday"), (TEXT([DATE], "dddd")
= "Sunday")), "Weekend", "Weekday")'],
['=IF(OR((TEXT([DATE], "dddd") = "Saturday"), (TEXT([DATE], "dddd")
= "Sunday")), "Weekend", "Weekday")'],
['=IF(OR((TEXT([DATE], "dddd") = "Saturday"), (TEXT([DATE], "dddd")
= "Sunday")), "Weekend", "Weekday")'],
['=IF(OR((TEXT([DATE], "dddd") = "Saturday"), (TEXT([DATE], "dddd")
= "Sunday")), "Weekend", "Weekday")'],
['=IF(OR((TEXT([DATE], "dddd") = "Saturday"), (TEXT([DATE], "dddd")
= "Sunday")), "Weekend", "Weekday")'],
['=IF(OR((TEXT([DATE], "dddd") = "Saturday"), (TEXT([DATE], "dddd")
= "Sunday")), "Weekend", "Weekday")']
]);

sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();

await context.sync();
});
Table with new calculated column

Resize a table
Your add-in can resize a table without adding data to the table or changing cell values.
To resize a table, use the Table.resize method. The following code sample shows how to
resize a table. This code sample uses the ExpensesTable from the Create a table section
earlier in this article and sets the new range of the table to A1:D20.

JavaScript

await Excel.run(async (context) => {


// Retrieve the worksheet and a table on that worksheet.
let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

// Resize the table.


expensesTable.resize("A1:D20");

await context.sync();
});

) Important

The new range of the table must overlap with the original range, and the headers
(or the top of the table) must be in the same row.

Table after resize


Update column name
The following code sample updates the name of the first column in the table to
Purchase date. The width of the columns and height of the rows are then set to best fit
the current data in the table.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let expensesTable = sheet.tables.getItem("ExpensesTable");


expensesTable.columns.load("items");

await context.sync();

expensesTable.columns.items[0].name = "Purchase date";

sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();
await context.sync();
});

Table with new column name

Get data from a table


The following code sample reads data from a table named ExpensesTable in the
worksheet named Sample and then outputs that data below the table in the same
worksheet.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

// Get data from the header row.


let headerRange = expensesTable.getHeaderRowRange().load("values");

// Get data from the table.


let bodyRange = expensesTable.getDataBodyRange().load("values");

// Get data from a single column.


let columnRange =
expensesTable.columns.getItem("Merchant").getDataBodyRange().load("values");

// Get data from a single row.


let rowRange = expensesTable.rows.getItemAt(1).load("values");

// Sync to populate proxy objects with data from Excel.


await context.sync();

let headerValues = headerRange.values;


let bodyValues = bodyRange.values;
let merchantColumnValues = columnRange.values;
let secondRowValues = rowRange.values;
// Write data from table back to the sheet
sheet.getRange("A11:A11").values = [["Results"]];
sheet.getRange("A13:D13").values = headerValues;
sheet.getRange("A14:D20").values = bodyValues;
sheet.getRange("B23:B29").values = merchantColumnValues;
sheet.getRange("A32:D32").values = secondRowValues;

// Sync to update the sheet in Excel.


await context.sync();
});

Table and data output

Detect data changes


Your add-in may need to react to users changing the data in a table. To detect these
changes, you can register an event handler for the onChanged event of a table. Event
handlers for the onChanged event receive a TableChangedEventArgs object when the
event fires.

The TableChangedEventArgs object provides information about the changes and the
source. Since onChanged fires when either the format or value of the data changes, it can
be useful to have your add-in check if the values have actually changed. The details
property encapsulates this information as a ChangedEventDetail. The following code
sample shows how to display the before and after values and types of a cell that has
been changed.

JavaScript

// This function would be used as an event handler for the Table.onChanged


event.
async function onTableChanged(eventArgs) {
await Excel.run(async (context) => {
let details = eventArgs.details;
let address = eventArgs.address;

// Print the before and after types and values to the console.
console.log(`Change at ${address}: was ${details.valueBefore}
(${details.valueTypeBefore}),`
+ ` now is ${details.valueAfter}(${details.valueTypeAfter})`);
await context.sync();
});
}

Sort data in a table


The following code sample sorts table data in descending order according to the values
in the fourth column of the table.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

// Queue a command to sort data by the fourth column of the table


(descending).
let sortRange = expensesTable.getDataBodyRange();
sortRange.sort.apply([
{
key: 3,
ascending: false,
},
]);

// Sync to run the queued command in Excel.


await context.sync();
});

Table data sorted by Amount (descending)

When data is sorted in a worksheet, an event notification fires. To learn more about sort-
related events and how your add-in can register event handlers to respond to such
events, see Handle sorting events.

Apply filters to a table


The following code sample applies filters to the Amount column and the Category
column within a table. As a result of the filters, only rows where Category is one of the
specified values and Amount is below the average value for all rows is shown.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

// Queue a command to apply a filter on the Category column.


let categoryFilter = expensesTable.columns.getItem("Category").filter;
categoryFilter.apply({
filterOn: Excel.FilterOn.values,
values: ["Restaurant", "Groceries"]
});

// Queue a command to apply a filter on the Amount column.


let amountFilter = expensesTable.columns.getItem("Amount").filter;
amountFilter.apply({
filterOn: Excel.FilterOn.dynamic,
dynamicCriteria: Excel.DynamicFilterCriteria.belowAverage
});
// Sync to run the queued commands in Excel.
await context.sync();
});

Table data with filters applied for Category and Amount

Clear table filters


The following code sample clears any filters currently applied on the table.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

expensesTable.clearFilters();

await context.sync();
});

Table data with no filters applied

Get the visible range from a filtered table


The following code sample gets a range that contains data only for cells that are
currently visible within the specified table, and then writes the values of that range to
the console. You can use the getVisibleView() method as shown below to get the
visible contents of a table whenever column filters have been applied.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

let visibleRange = expensesTable.getDataBodyRange().getVisibleView();


visibleRange.load("values");

await context.sync();
console.log(visibleRange.values);
});

AutoFilter
An add-in can use the table's AutoFilter object to filter data. An AutoFilter object is the
entire filter structure of a table or range. All of the filter operations discussed earlier in
this article are compatible with the auto-filter. The single access point does make it
easier to access and manage multiple filters.

The following code sample shows the same data filtering as the earlier code sample, but
done entirely through the auto-filter.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

expensesTable.autoFilter.apply(expensesTable.getRange(), 2, {
filterOn: Excel.FilterOn.values,
values: ["Restaurant", "Groceries"]
});
expensesTable.autoFilter.apply(expensesTable.getRange(), 3, {
filterOn: Excel.FilterOn.dynamic,
dynamicCriteria: Excel.DynamicFilterCriteria.belowAverage
});

await context.sync();
});

An AutoFilter can also be applied to a range at the worksheet level. See Work with
worksheets using the Excel JavaScript API for more information.
Format a table
The following code sample applies formatting to a table. It specifies different fill colors
for the header row of the table, the body of the table, the second row of the table, and
the first column of the table. For information about the properties you can use to specify
format, see RangeFormat Object (JavaScript API for Excel).

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let expensesTable = sheet.tables.getItem("ExpensesTable");

expensesTable.getHeaderRowRange().format.fill.color = "#C70039";
expensesTable.getDataBodyRange().format.fill.color = "#DAF7A6";
expensesTable.rows.getItemAt(1).getRange().format.fill.color =
"#FFC300";
expensesTable.columns.getItemAt(0).getDataBodyRange().format.fill.color
= "#FFA07A";

await context.sync();
});

Table after formatting is applied

Convert a range to a table


The following code sample creates a range of data and then converts that range to a
table. The width of the columns and height of the rows are then set to best fit the
current data in the table.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
// Define values for the range.
let values = [["Product", "Qtr1", "Qtr2", "Qtr3", "Qtr4"],
["Frames", 5000, 7000, 6544, 4377],
["Saddles", 400, 323, 276, 651],
["Brake levers", 12000, 8766, 8456, 9812],
["Chains", 1550, 1088, 692, 853],
["Mirrors", 225, 600, 923, 544],
["Spokes", 6005, 7634, 4589, 8765]];

// Create the range.


let range = sheet.getRange("A1:E7");
range.values = values;

sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();

sheet.activate();

// Convert the range to a table.


let expensesTable = sheet.tables.add('A1:E7', true);
expensesTable.name = "ExpensesTable";

await context.sync();
});

Data in the range (before the range is converted to a


table)

Data in the table (after the range is converted to a table)


Import JSON data into a table
The following code sample creates a table in the worksheet named Sample and then
populates the table by using a JSON object that defines two rows of data. The width of
the columns and height of the rows are then set to best fit the current data in the table.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");

let expensesTable = sheet.tables.add("A1:D1", true /*hasHeaders*/);


expensesTable.name = "ExpensesTable";
expensesTable.getHeaderRowRange().values = [["Date", "Merchant",
"Category", "Amount"]];

let transactions = [
{
"DATE": "1/1/2017",
"MERCHANT": "The Phone Company",
"CATEGORY": "Communications",
"AMOUNT": "$120"
},
{
"DATE": "1/1/2017",
"MERCHANT": "Southridge Video",
"CATEGORY": "Entertainment",
"AMOUNT": "$40"
}
];

let newData = transactions.map(item =>


[item.DATE, item.MERCHANT, item.CATEGORY, item.AMOUNT]);

expensesTable.rows.add(null, newData);

sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();

sheet.activate();

await context.sync();
});

New table
See also
Excel JavaScript object model in Office Add-ins
Work with workbooks using the Excel
JavaScript API
Article • 05/02/2023

This article provides code samples that show how to perform common tasks with
workbooks using the Excel JavaScript API. For the complete list of properties and
methods that the Workbook object supports, see Workbook Object (JavaScript API for
Excel). This article also covers workbook-level actions performed through the
Application object.

The Workbook object is the entry point for your add-in to interact with Excel. It
maintains collections of worksheets, tables, PivotTables, and more, through which Excel
data is accessed and changed. The WorksheetCollection object gives your add-in access
to all the workbook's data through individual worksheets. Specifically, it lets your add-in
add worksheets, navigate among them, and assign handlers to worksheet events. The
article Work with worksheets using the Excel JavaScript API describes how to access and
edit worksheets.

Get the active cell or selected range


The Workbook object contains two methods that get a range of cells the user or add-in
has selected: getActiveCell() and getSelectedRange() . getActiveCell() gets the active
cell from the workbook as a Range object. The following example shows a call to
getActiveCell() , followed by the cell's address being printed to the console.

JavaScript

await Excel.run(async (context) => {


let activeCell = context.workbook.getActiveCell();
activeCell.load("address");
await context.sync();

console.log("The active cell is " + activeCell.address);


});

The getSelectedRange() method returns the currently selected single range. If multiple
ranges are selected, an InvalidSelection error is thrown. The following example shows a
call to getSelectedRange() that then sets the range's fill color to yellow.

JavaScript
await Excel.run(async (context) => {
let range = context.workbook.getSelectedRange();
range.format.fill.color = "yellow";
await context.sync();
});

Create a workbook
Your add-in can create a new workbook, separate from the Excel instance in which the
add-in is currently running. The Excel object has the createWorkbook method for this
purpose. When this method is called, the new workbook is immediately opened and
displayed in a new instance of Excel. Your add-in remains open and running with the
previous workbook.

JavaScript

Excel.createWorkbook();

The createWorkbook method can also create a copy of an existing workbook. The
method accepts a base64-encoded string representation of an .xlsx file as an optional
parameter. The resulting workbook will be a copy of that file, assuming the string
argument is a valid .xlsx file.

You can get your add-in's current workbook as a base64-encoded string by using file
slicing. The FileReader class can be used to convert a file into the required base64-
encoded string, as demonstrated in the following example.

JavaScript

// Retrieve the external workbook file and set up a `FileReader` object.


let myFile = document.getElementById("file");
let reader = new FileReader();

reader.onload = (function (event) {


Excel.run(function (context) {
// Remove the metadata before the base64-encoded string.
let startIndex = reader.result.toString().indexOf("base64,");
let externalWorkbook = reader.result.toString().substr(startIndex +
7);

Excel.createWorkbook(externalWorkbook);
return context.sync();
});
});
// Read the file as a data URL so we can parse the base64-encoded string.
reader.readAsDataURL(myFile.files[0]);

Insert a copy of an existing workbook into the current


one
The previous example shows a new workbook being created from an existing workbook.
You can also copy some or all of an existing workbook into the one currently associated
with your add-in. A Workbook has the insertWorksheetsFromBase64 method to insert
copies of the target workbook's worksheets into itself. The other workbook's file is
passed as a base64-encoded string, just like the Excel.createWorkbook call.

TypeScript

insertWorksheetsFromBase64(base64File: string, options?:


Excel.InsertWorksheetOptions): OfficeExtension.ClientResult<string[]>;

) Important

The insertWorksheetsFromBase64 method is supported for Excel on Windows, Mac,


and the web. It's not supported for iOS. Additionally, in Excel on the web this
method doesn't support source worksheets with PivotTable, Chart, Comment, or
Slicer elements. If those objects are present, the insertWorksheetsFromBase64
method returns the UnsupportedFeature error in Excel on the web.

The following code sample shows how to insert worksheets from another workbook into
the current workbook. This code sample first processes a workbook file with a
FileReader object and extracts a base64-encoded string, and then it inserts this
base64-encoded string into the current workbook. The new worksheets are inserted
after the worksheet named Sheet1. Note that [] is passed as the parameter for the
InsertWorksheetOptions.sheetNamesToInsert property. This means that all the
worksheets from the target workbook are inserted into the current workbook.

JavaScript

// Retrieve the external workbook file and set up a `FileReader` object.


let myFile = document.getElementById("file");
let reader = new FileReader();

reader.onload = (event) => {


Excel.run((context) => {
// Remove the metadata before the base64-encoded string.
let startIndex = reader.result.toString().indexOf("base64,");
let externalWorkbook = reader.result.toString().substr(startIndex +
7);

// Retrieve the current workbook.


let workbook = context.workbook;

// Set up the insert options.


let options = {
sheetNamesToInsert: [], // Insert all the worksheets from the
source workbook.
positionType: Excel.WorksheetPositionType.after, // Insert after
the `relativeTo` sheet.
relativeTo: "Sheet1" // The sheet relative to which the other
worksheets will be inserted. Used with `positionType`.
};

// Insert the new worksheets into the current workbook.


workbook.insertWorksheetsFromBase64(externalWorkbook, options);
return context.sync();
});
};

// Read the file as a data URL so we can parse the base64-encoded string.
reader.readAsDataURL(myFile.files[0]);

Protect the workbook's structure


Your add-in can control a user's ability to edit the workbook's structure. The Workbook
object's protection property is a WorkbookProtection object with a protect() method.
The following example shows a basic scenario toggling the protection of the workbook's
structure.

JavaScript

await Excel.run(async (context) => {


let workbook = context.workbook;
workbook.load("protection/protected");
await context.sync();

if (!workbook.protection.protected) {
workbook.protection.protect();
}
});

The protect method accepts an optional string parameter. This string represents the
password needed for a user to bypass protection and change the workbook's structure.
Protection can also be set at the worksheet level to prevent unwanted data editing. For
more information, see the Data protection section of the Work with worksheets using
the Excel JavaScript API article.

7 Note

For more information about workbook protection in Excel, see the Protect a
workbook article.

Access document properties


Workbook objects have access to the Office file metadata, which is known as the
document properties . The Workbook object's properties property is a
DocumentProperties object containing these metadata values. The following example
shows how to set the author property.

JavaScript

await Excel.run(async (context) => {


let docProperties = context.workbook.properties;
docProperties.author = "Alex";
await context.sync();
});

Custom properties
You can also define custom properties. The DocumentProperties object contains a
custom property that represents a collection of key-value pairs for user-defined
properties. The following example shows how to create a custom property named
Introduction with the value "Hello", then retrieve it.

JavaScript

await Excel.run(async (context) => {


let customDocProperties = context.workbook.properties.custom;
customDocProperties.add("Introduction", "Hello");
await context.sync();
});

[...]

await Excel.run(async (context) => {


let customDocProperties = context.workbook.properties.custom;
let customProperty = customDocProperties.getItem("Introduction");
customProperty.load(["key, value"]);
await context.sync();

console.log("Custom key : " + customProperty.key); // "Introduction"


console.log("Custom value : " + customProperty.value); // "Hello"
});

Worksheet-level custom properties


Custom properties can also be set at the worksheet level. These are similar to
document-level custom properties, except that the same key can be repeated across
different worksheets. The following example shows how to create a custom property
named WorksheetGroup with the value "Alpha" on the current worksheet, then retrieve
it.

JavaScript

await Excel.run(async (context) => {


// Add the custom property.
let customWorksheetProperties =
context.workbook.worksheets.getActiveWorksheet().customProperties;
customWorksheetProperties.add("WorksheetGroup", "Alpha");

await context.sync();
});

[...]

await Excel.run(async (context) => {


// Load the keys and values of all custom properties in the current
worksheet.
let worksheet = context.workbook.worksheets.getActiveWorksheet();
worksheet.load("name");

let customWorksheetProperties = worksheet.customProperties;


let customWorksheetProperty =
customWorksheetProperties.getItem("WorksheetGroup");
customWorksheetProperty.load(["key", "value"]);

await context.sync();

// Log the WorksheetGroup custom property to the console.


console.log(worksheet.name + ": " + customWorksheetProperty.key); //
"WorksheetGroup"
console.log(" Custom value : " + customWorksheetProperty.value); //
"Alpha"
});
Access document settings
A workbook's settings are similar to the collection of custom properties. The difference
is settings are unique to a single Excel file and add-in pairing, whereas properties are
solely connected to the file. The following example shows how to create and access a
setting.

JavaScript

await Excel.run(async (context) => {


let settings = context.workbook.settings;
settings.add("NeedsReview", true);
let needsReview = settings.getItem("NeedsReview");
needsReview.load("value");

await context.sync();
console.log("Workbook needs review : " + needsReview.value);
});

Access application culture settings


A workbook has language and culture settings that affect how certain data is displayed.
These settings can help localize data when your add-in's users are sharing workbooks
across different languages and cultures. Your add-in can use string parsing to localize
the format of numbers, dates, and times based on the system culture settings so that
each user sees data in their own culture's format.

Application.cultureInfo defines the system culture settings as a CultureInfo object.

This contains settings like the numerical decimal separator or the date format.

Some culture settings can be changed through the Excel UI . The system settings are
preserved in the CultureInfo object. Any local changes are kept as Application-level
properties, such as Application.decimalSeparator .

The following sample changes the decimal separator character of a numerical string
from a ',' to the character used by the system settings.

JavaScript

// This will convert a number like "14,37" to "14.37"


// (assuming the system decimal separator is ".").
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getItem("Sample");
let decimalSource = sheet.getRange("B2");
decimalSource.load("values");

context.application.cultureInfo.numberFormat.load("numberDecimalSeparator");
await context.sync();

let systemDecimalSeparator =
context.application.cultureInfo.numberFormat.numberDecimalSeparator;
let oldDecimalString = decimalSource.values[0][0];

// This assumes the input column is standardized to use "," as the


decimal separator.
let newDecimalString = oldDecimalString.replace(",",
systemDecimalSeparator);

let resultRange = sheet.getRange("C2");


resultRange.values = [[newDecimalString]];
resultRange.format.autofitColumns();
await context.sync();
});

Add custom XML data to the workbook


Excel's Open XML .xlsx file format lets your add-in embed custom XML data in the
workbook. This data persists with the workbook, independent of the add-in.

A workbook contains a CustomXmlPartCollection, which is a list of CustomXmlParts.


These give access to the XML strings and a corresponding unique ID. By storing these
IDs as settings, your add-in can maintain the keys to its XML parts between sessions.

The following samples show how to use custom XML parts. The first code block
demonstrates how to embed XML data in the document. It stores a list of reviewers,
then uses the workbook's settings to save the XML's id for future retrieval. The second
block shows how to access that XML later. The "ContosoReviewXmlPartId" setting is
loaded and passed to the workbook's customXmlParts . The XML data is then printed to
the console.

JavaScript

await Excel.run(async (context) => {


// Add reviewer data to the document as XML
let originalXml = "<Reviewers
xmlns='http://schemas.contoso.com/review/1.0'><Reviewer>Juan</Reviewer>
<Reviewer>Hong</Reviewer><Reviewer>Sally</Reviewer></Reviewers>";
let customXmlPart = context.workbook.customXmlParts.add(originalXml);
customXmlPart.load("id");
await context.sync();

// Store the XML part's ID in a setting


let settings = context.workbook.settings;
settings.add("ContosoReviewXmlPartId", customXmlPart.id);
});

JavaScript

await Excel.run(async (context) => {


// Retrieve the XML part's id from the setting
let settings = context.workbook.settings;
let xmlPartIDSetting =
settings.getItemOrNullObject("ContosoReviewXmlPartId").load("value");

await context.sync();

if (xmlPartIDSetting.value) {
let customXmlPart =
context.workbook.customXmlParts.getItem(xmlPartIDSetting.value);
let xmlBlob = customXmlPart.getXml();

await context.sync();

// Add spaces to make it more human-readable in the console.


let readableXML = xmlBlob.value.replace(/></g, "> <");
console.log(readableXML);
}
});

7 Note

CustomXMLPart.namespaceUri is only populated if the top-level custom XML element

contains the xmlns attribute.

Control calculation behavior

Set calculation mode


By default, Excel recalculates formula results whenever a referenced cell is changed. Your
add-in's performance may benefit from adjusting this calculation behavior. The
Application object has a calculationMode property of type CalculationMode . It can be
set to the following values.

automatic : The default recalculation behavior where Excel calculates new formula

results every time the relevant data is changed.


automaticExceptTables : Same as automatic , except any changes made to values in

tables are ignored.


manual : Calculations only occur when the user or add-in requests them.

Set calculation type


The Application object provides a method to force an immediate recalculation.
Application.calculate(calculationType) starts a manual recalculation based on the

specified calculationType . The following values can be specified.

full : Recalculate all formulas in all open workbooks, regardless of whether they

have changed since the last recalculation.


fullRebuild : Check dependent formulas, and then recalculate all formulas in all
open workbooks, regardless of whether they have changed since the last
recalculation.
recalculate : Recalculate formulas that have changed (or been programmatically

marked for recalculation) since the last calculation, and formulas dependent on
them, in all active workbooks.

7 Note

For more information about recalculation, see the Change formula recalculation,
iteration, or precision article.

Temporarily suspend calculations


The Excel API also lets add-ins turn off calculations until RequestContext.sync() is called.
This is done with suspendApiCalculationUntilNextSync() . Use this method when your
add-in is editing large ranges without needing to access the data between edits.

JavaScript

context.application.suspendApiCalculationUntilNextSync();

Detect workbook activation


Your add-in can detect when a workbook is activated. A workbook becomes inactive
when the user switches focus to another workbook, to another application, or (in Excel
on the web) to another tab of the web browser. A workbook is activated when the user
returns focus to the workbook. The workbook activation can trigger callback functions in
your add-in, such as refreshing workbook data.

To detect when a workbook is activated, register an event handler for the onActivated
event of a workbook. Event handlers for the onActivated event receive a
WorkbookActivatedEventArgs object when the event fires.

) Important

The onActivated event doesn't detect when a workbook is opened. This event only
detects when a user switches focus back to an already open workbook.

The following code sample shows how to register the onActivated event handler and
set up a callback function.

JavaScript

async function run() {


await Excel.run(async (context) => {
// Retrieve the workbook.
let workbook = context.workbook;

// Register the workbook activated event handler.


workbook.onActivated.add(workbookActivated);
await context.sync();
});
}

async function workbookActivated(event) {


await Excel.run(async (context) => {
// Retrieve the workbook and load the name.
let workbook = context.workbook;
workbook.load("name");
await context.sync();

// Callback function for when the workbook is activated.


console.log(`The workbook ${workbook.name} was activated.`);
});
}

Save the workbook


Workbook.save saves the workbook to persistent storage. The save method takes a

single, optional saveBehavior parameter that can be one of the following values.
Excel.SaveBehavior.save (default): The file is saved without prompting the user to

specify file name and save location. If the file has not been saved previously, it's
saved to the default location. If the file has been saved previously, it's saved to the
same location.
Excel.SaveBehavior.prompt : If file has not been saved previously, the user will be

prompted to specify file name and save location. If the file has been saved
previously, it will be saved to the same location and the user will not be prompted.

U Caution

If the user is prompted to save and cancels the operation, save throws an
exception.

JavaScript

context.workbook.save(Excel.SaveBehavior.prompt);

Close the workbook


Workbook.close closes the workbook, along with add-ins that are associated with the

workbook (the Excel application remains open). The close method takes a single,
optional closeBehavior parameter that can be one of the following values.

Excel.CloseBehavior.save (default): The file is saved before closing. If the file has

not been saved previously, the user will be prompted to specify file name and save
location.
Excel.CloseBehavior.skipSave : The file is immediately closed, without saving. Any

unsaved changes will be lost.

JavaScript

context.workbook.close(Excel.CloseBehavior.save);

See also
Excel JavaScript object model in Office Add-ins
Work with worksheets using the Excel JavaScript API
Work with worksheets using the Excel
JavaScript API
Article • 07/21/2022

This article provides code samples that show how to perform common tasks with
worksheets using the Excel JavaScript API. For the complete list of properties and
methods that the Worksheet and WorksheetCollection objects support, see Worksheet
Object (JavaScript API for Excel) and WorksheetCollection Object (JavaScript API for
Excel).

7 Note

The information in this article applies only to regular worksheets; it does not apply
to "chart" sheets or "macro" sheets.

Get worksheets
The following code sample gets the collection of worksheets, loads the name property of
each worksheet, and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let sheets = context.workbook.worksheets;
sheets.load("items/name");

await context.sync();

if (sheets.items.length > 1) {
console.log(`There are ${sheets.items.length} worksheets in the
workbook:`);
} else {
console.log(`There is one worksheet in the workbook:`);
}

sheets.items.forEach(function (sheet) {
console.log(sheet.name);
});
});

7 Note
The id property of a worksheet uniquely identifies the worksheet in a given
workbook and its value will remain the same even when the worksheet is renamed
or moved. When a worksheet is deleted from a workbook in Excel on Mac, the id
of the deleted worksheet may be reassigned to a new worksheet that is
subsequently created.

Get the active worksheet


The following code sample gets the active worksheet, loads its name property, and
writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.load("name");

await context.sync();
console.log(`The active worksheet is "${sheet.name}"`);
});

Set the active worksheet


The following code sample sets the active worksheet to the worksheet named Sample,
loads its name property, and writes a message to the console. If there is no worksheet
with that name, the activate() method throws an ItemNotFound error.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
sheet.activate();
sheet.load("name");

await context.sync();
console.log(`The active worksheet is "${sheet.name}"`);
});

Reference worksheets by relative position


These examples show how to reference a worksheet by its relative position.
Get the first worksheet
The following code sample gets the first worksheet in the workbook, loads its name
property, and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let firstSheet = context.workbook.worksheets.getFirst();
firstSheet.load("name");

await context.sync();
console.log(`The name of the first worksheet is "${firstSheet.name}"`);
});

Get the last worksheet


The following code sample gets the last worksheet in the workbook, loads its name
property, and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let lastSheet = context.workbook.worksheets.getLast();
lastSheet.load("name");

await context.sync();
console.log(`The name of the last worksheet is "${lastSheet.name}"`);
});

Get the next worksheet


The following code sample gets the worksheet that follows the active worksheet in the
workbook, loads its name property, and writes a message to the console. If there is no
worksheet after the active worksheet, the getNext() method throws an ItemNotFound
error.

JavaScript

await Excel.run(async (context) => {


let currentSheet = context.workbook.worksheets.getActiveWorksheet();
let nextSheet = currentSheet.getNext();
nextSheet.load("name");

await context.sync();
console.log(`The name of the sheet that follows the active worksheet is
"${nextSheet.name}"`);
});

Get the previous worksheet


The following code sample gets the worksheet that precedes the active worksheet in the
workbook, loads its name property, and writes a message to the console. If there is no
worksheet before the active worksheet, the getPrevious() method throws an
ItemNotFound error.

JavaScript

await Excel.run(async (context) => {


let currentSheet = context.workbook.worksheets.getActiveWorksheet();
let previousSheet = currentSheet.getPrevious();
previousSheet.load("name");

await context.sync();
console.log(`The name of the sheet that precedes the active worksheet is
"${previousSheet.name}"`);
});

Add a worksheet
The following code sample adds a new worksheet named Sample to the workbook,
loads its name and position properties, and writes a message to the console. The new
worksheet is added after all existing worksheets.

JavaScript

await Excel.run(async (context) => {


let sheets = context.workbook.worksheets;

let sheet = sheets.add("Sample");


sheet.load("name, position");

await context.sync();
console.log(`Added worksheet named "${sheet.name}" in position
${sheet.position}`);
});

Copy an existing worksheet


Worksheet.copy adds a new worksheet that is a copy of an existing worksheet. The new

worksheet's name will have a number appended to the end, in a manner consistent with
copying a worksheet through the Excel UI (for example, MySheet (2)). Worksheet.copy
can take two parameters, both of which are optional:

positionType - A WorksheetPositionType enum specifying where in the workbook

the new worksheet is to be added.


relativeTo - If the positionType is Before or After , you need to specify a
worksheet relative to which the new sheet is to be added (this parameter answers
the question "Before or after what?").

The following code sample copies the current worksheet and inserts the new sheet
directly after the current worksheet.

JavaScript

await Excel.run(async (context) => {


let myWorkbook = context.workbook;
let sampleSheet = myWorkbook.worksheets.getActiveWorksheet();
let copiedSheet = sampleSheet.copy(Excel.WorksheetPositionType.after,
sampleSheet);
await context.sync();
});

Delete a worksheet
The following code sample deletes the final worksheet in the workbook (as long as it's
not the only sheet in the workbook) and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let sheets = context.workbook.worksheets;
sheets.load("items/name");

await context.sync();
if (sheets.items.length === 1) {
console.log("Unable to delete the only worksheet in the workbook");
} else {
let lastSheet = sheets.items[sheets.items.length - 1];

console.log(`Deleting worksheet named "${lastSheet.name}"`);


lastSheet.delete();

await context.sync();
}
});
7 Note

A worksheet with a visibility of "Very Hidden" cannot be deleted with the delete
method. If you wish to delete the worksheet anyway, you must first change the
visibility.

Rename a worksheet
The following code sample changes the name of the active worksheet to New Name.

JavaScript

await Excel.run(async (context) => {


let currentSheet = context.workbook.worksheets.getActiveWorksheet();
currentSheet.name = "New Name";

await context.sync();
});

Move a worksheet
The following code sample moves a worksheet from the last position in the workbook to
the first position in the workbook.

JavaScript

await Excel.run(async (context) => {


let sheets = context.workbook.worksheets;
sheets.load("items");
await context.sync();

let lastSheet = sheets.items[sheets.items.length - 1];


lastSheet.position = 0;
await context.sync();
});

Set worksheet visibility


These examples show how to set the visibility of a worksheet.
Hide a worksheet
The following code sample sets the visibility of worksheet named Sample to hidden,
loads its name property, and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
sheet.visibility = Excel.SheetVisibility.hidden;
sheet.load("name");

await context.sync();
console.log(`Worksheet with name "${sheet.name}" is hidden`);
});

Unhide a worksheet
The following code sample sets the visibility of worksheet named Sample to visible,
loads its name property, and writes a message to the console.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
sheet.visibility = Excel.SheetVisibility.visible;
sheet.load("name");

await context.sync();
console.log(`Worksheet with name "${sheet.name}" is visible`);
});

Get a single cell within a worksheet


The following code sample gets the cell that is located in row 2, column 5 of the
worksheet named Sample, loads its address and values properties, and writes a
message to the console. The values that are passed into the getCell(row: number,
column:number) method are the zero-indexed row number and column number for the

cell that is being retrieved.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let cell = sheet.getCell(1, 4);
cell.load("address, values");

await context.sync();
console.log(`The value of the cell in row 2, column 5 is
"${cell.values[0][0]}" and the address of that cell is "${cell.address}"`);
});

Detect data changes


Your add-in may need to react to users changing the data in a worksheet. To detect
these changes, you can register an event handler for the onChanged event of a
worksheet. Event handlers for the onChanged event receive a
WorksheetChangedEventArgs object when the event fires.

The WorksheetChangedEventArgs object provides information about the changes and the
source. Since onChanged fires when either the format or value of the data changes, it can
be useful to have your add-in check if the values have actually changed. The details
property encapsulates this information as a ChangedEventDetail. The following code
sample shows how to display the before and after values and types of a cell that has
been changed.

JavaScript

// This function would be used as an event handler for the


Worksheet.onChanged event.
function onWorksheetChanged(eventArgs) {
Excel.run(function (context) {
let details = eventArgs.details;
let address = eventArgs.address;

// Print the before and after types and values to the console.
console.log(`Change at ${address}: was ${details.valueBefore}
(${details.valueTypeBefore}),`
+ ` now is ${details.valueAfter}(${details.valueTypeAfter})`);
return context.sync();
});
}

Detect formula changes


Your add-in can track changes to formulas in a worksheet. This is useful when a
worksheet is connected to an external database. When the formula changes in the
worksheet, the event in this scenario triggers corresponding updates in the external
database.
To detect changes to formulas, register an event handler for the onFormulaChanged
event of a worksheet. Event handlers for the onFormulaChanged event receive a
WorksheetFormulaChangedEventArgs object when the event fires.

) Important

The onFormulaChanged event detects when a formula itself changes, not the data
value resulting from the formula's calculation.

The following code sample shows how to register the onFormulaChanged event handler,
use the WorksheetFormulaChangedEventArgs object to retrieve the formulaDetails array of
the changed formula, and then print out details about the changed formula with the
FormulaChangedEventDetail properties.

7 Note

This code sample only works when a single formula is changed.

JavaScript

async function run() {


await Excel.run(async (context) => {
// Retrieve the worksheet named "Sample".
let sheet = context.workbook.worksheets.getItem("Sample");

// Register the formula changed event handler for this worksheet.


sheet.onFormulaChanged.add(formulaChangeHandler);

await context.sync();
});
}

async function formulaChangeHandler(event) {


await Excel.run(async (context) => {
// Retrieve details about the formula change event.
// Note: This method assumes only a single formula is changed at a
time.
let cellAddress = event.formulaDetails[0].cellAddress;
let previousFormula = event.formulaDetails[0].previousFormula;
let source = event.source;

// Print out the change event details.


console.log(
`The formula in cell ${cellAddress} changed.
The previous formula was: ${previousFormula}.
The source of the change was: ${source}.`
);
});
}

Handle sorting events


The onColumnSorted and onRowSorted events indicate when any worksheet data is
sorted. These events are connected to individual Worksheet objects and to the
workbook's WorkbookCollection . They fire whether the sorting is done programmatically
or manually through the Excel user interface.

7 Note

onColumnSorted fires when columns are sorted as the result of a left-to-right sort

operation. onRowSorted fires when rows are sorted as the result of a top-to-bottom
sort operation. Sorting a table using the drop-down menu on a column header
results in an onRowSorted event. The event corresponds with what is moving, not
what is being considered as the sorting criteria.

The onColumnSorted and onRowSorted events provide their callbacks with


WorksheetColumnSortedEventArgs or WorksheetRowSortedEventArgs, respectively.
These give more details about the event. In particular, both EventArgs have an address
property that represents the rows or columns moved as a result of the sort operation.
Any cell with sorted content is included, even if that cell's value was not part of the
sorting criteria.

The following images show the ranges returned by the address property for sort events.
First, here is the sample data before sorting:

If a top-to-bottom sort is performed on "Q1" (the values in "B"), the following


highlighted rows are returned by WorksheetRowSortedEventArgs.address .
If a left-to-right sort is performed on "Quinces" (the values in "4") on the original data,
the following highlighted columns are returned by
WorksheetColumnsSortedEventArgs.address .

The following code sample shows how to register an event handler for the
Worksheet.onRowSorted event. The handler's callback clears the fill color for the range,
then fills the cells of the moved rows.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();

// This will fire whenever a row has been moved as the result of a sort
action.
sheet.onRowSorted.add(async (event) => {
await Excel.run(async (context) => {
console.log("Row sorted: " + event.address);
let sheet = context.workbook.worksheets.getActiveWorksheet();

// Clear formatting for section, then highlight the sorted area.


sheet.getRange("A1:E5").format.fill.clear();
if (event.address !== "") {
sheet.getRanges(event.address).format.fill.color = "yellow";
}

await context.sync();
});
});

await context.sync();
});
Find all cells with matching text
The Worksheet object has a findAll method to search for a specified string within the
worksheet. It returns a RangeAreas object, which is a collection of Range objects that can
be edited all at once.

The following code sample finds all cells with values equal to the string Complete and
colors them green. Note that findAll throws an ItemNotFound error if the specified
string doesn't exist in the worksheet. If you're uncertain whether the specified string
exists in the worksheet, use the findAllOrNullObject method to gracefully handle that
scenario.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getItem("Sample");
let foundRanges = sheet.findAll("Complete", {
completeMatch: true, /* Match the whole cell value, not any part of
the text. */
matchCase: false /* Make the search case-insensitive. */
});

await context.sync();
foundRanges.format.fill.color = "green";
});

7 Note

This section describes how to find cells and ranges using the Worksheet object's
methods. More range retrieval information can be found in object-specific articles.

For examples that show how to get a range within a worksheet using the
Range object, see Get a range using the Excel JavaScript API.

For examples that show how to get ranges from a Table object, see Work
with tables using the Excel JavaScript API.
For examples that show how to search a large range for multiple sub-ranges
based on cell characteristics, see Work with multiple ranges simultaneously
in Excel add-ins.

Filter data
An AutoFilter applies data filters across a range within the worksheet. This is created
with Worksheet.autoFilter.apply , which has the following parameters.

range : The range to which the filter is applied, specified as either a Range object or

a string.
columnIndex : The zero-based column index against which the filter criteria is

evaluated.
criteria : A FilterCriteria object determining which rows should be filtered based
on the column's cell.

The first code sample shows how to add a filter to the worksheet's used range. This filter
will hide entries that are not in the top 25%, based on the values in column 3.

JavaScript

// This method adds a custom AutoFilter to the active worksheet


// and applies the filter to a column of the used range.
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getActiveWorksheet();
let farmData = sheet.getUsedRange();

// This filter will only show the rows with the top 25% of values in
column 3.
sheet.autoFilter.apply(farmData, 3, { criterion1: "25", filterOn:
Excel.FilterOn.topPercent });
await context.sync();
});

The next code sample shows how to refresh the auto-filter using the reapply method.
This should be done when the data in the range changes.

JavaScript

// This method refreshes the AutoFilter to ensure that changes are captured.
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.autoFilter.reapply();
await context.sync();
});

The following code sample shows how to use the clearColumnCriteria method to clear
the auto-filter from only one column, while leaving the filter active on other columns.

JavaScript

// This method clears the AutoFilter setting from one column.


await Excel.run(async (context) => {
// Retrieve the active worksheet.
let sheet = context.workbook.worksheets.getActiveWorksheet();

// Clear the filter from only column 3.


sheet.autoFilter.clearColumnCriteria(3);
await context.sync();
});

The final auto-filter code sample shows how to remove the auto-filter from the
worksheet with the remove method.

JavaScript

// This method removes all AutoFilters from the active worksheet.


await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.autoFilter.remove();
await context.sync();
});

An AutoFilter can also be applied to individual tables. See Work with tables using the
Excel JavaScript API for more information.

Data protection
Your add-in can control a user's ability to edit data in a worksheet. The worksheet's
protection property is a WorksheetProtection object with a protect() method. The

following example shows a basic scenario toggling the complete protection of the active
worksheet.

JavaScript

await Excel.run(async (context) => {


let activeSheet = context.workbook.worksheets.getActiveWorksheet();
activeSheet.load("protection/protected");
await context.sync();

if (!activeSheet.protection.protected) {
activeSheet.protection.protect();
}
});

The protect method has two optional parameters:

options : A WorksheetProtectionOptions object defining specific editing

restrictions.
password : A string representing the password needed for a user to bypass

protection and edit the worksheet.

The article Protect a worksheet has more information about worksheet protection and
how to change it through the Excel UI.

Detect changes to the worksheet protection state


The protection state of a worksheet can be changed by an add-in or through the Excel
UI. To detect changes to the protection state, register an event handler for the
onProtectionChanged event of a worksheet. Event handlers for the onProtectionChanged
event receive a WorksheetProtectionChangedEventArgs object when the event fires.

The following code sample shows how to register the onProtectionChanged event
handler and use the WorksheetProtectionChangedEventArgs object to retrieve the
isProtected , worksheetId , and source properties of the event.

JavaScript

// This function registers an event handler for the onProtectionChanged


event of a worksheet.
async function run() {
await Excel.run(async (context) => {
// Retrieve the worksheet named "Sample".
let sheet = context.workbook.worksheets.getItem("Sample");

// Register the onProtectionChanged event handler.


sheet.onProtectionChanged.add(checkProtection);
await context.sync();
});
}

// This function is an event handler that returns the protection state of a


worksheet
// and information about the changed worksheet.
async function checkProtection(event) {
await Excel.run(async (context) => {
// Retrieve the protection, worksheet ID, and source properties of
the event.
let protectionStatus = event.isProtected;
let worksheetId = event.worksheetId;
let source = event.source;

// Print the event properties to the console.


console.log("Protection status changed. Protection status is now: "
+ protectionStatus);
console.log(" ID of changed worksheet: " + worksheetId);
console.log(" Source of change event: " + source);
});
}

Page layout and print settings


Add-ins have access to page layout settings at a worksheet level. These control how the
sheet is printed. A Worksheet object has three layout-related properties:
horizontalPageBreaks , verticalPageBreaks , pageLayout .

Worksheet.horizontalPageBreaks and Worksheet.verticalPageBreaks are

PageBreakCollections. These are collections of PageBreaks, which specify ranges where


manual page breaks are inserted. The following code sample adds a horizontal page
break above row 21.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();
sheet.horizontalPageBreaks.add("A21:E21"); // The page break is added
above this range.
await context.sync();
});

Worksheet.pageLayout is a PageLayout object. This object contains layout and print


settings that are not dependent any printer-specific implementation. These settings
include margins, orientation, page numbering, title rows, and print area.

The following code sample centers the page (both vertically and horizontally), sets a title
row that will be printed at the top of every page, and sets the printed area to a
subsection of the worksheet.

JavaScript

await Excel.run(async (context) => {


let sheet = context.workbook.worksheets.getActiveWorksheet();

// Center the page in both directions.


sheet.pageLayout.centerHorizontally = true;
sheet.pageLayout.centerVertically = true;

// Set the first row as the title row for every page.
sheet.pageLayout.setPrintTitleRows("$1:$1");

// Limit the area to be printed to the range "A1:D100".


sheet.pageLayout.setPrintArea("A1:D100");
await context.sync();
});

See also
Excel JavaScript object model in Office Add-ins
Troubleshooting Excel add-ins
Article • 03/09/2023

This article discusses troubleshooting issues that are unique to Excel. Please use the
feedback tool at the bottom of the page to suggest other issues that can be added to
the article.

API limitations when the active workbook


switches
Add-ins for Excel are intended to operate on a single workbook at a time. Errors can
arise when a workbook that is separate from the one running the add-in gains focus.
This only happens when particular methods are in the process of being called when the
focus changes.

The following APIs are affected by this workbook switch.

Excel JavaScript API Error thrown

Chart.activate GeneralException

Range.select GeneralException

Table.clearFilters GeneralException

Workbook.getActiveCell InvalidSelection

Workbook.getSelectedRange InvalidSelection

Workbook.getSelectedRanges InvalidSelection

Worksheet.activate GeneralException

Worksheet.delete InvalidSelection

Worksheet.gridlines GeneralException

Worksheet.showHeadings GeneralException

WorksheetCollection.add GeneralException

WorksheetFreezePanes.freezeAt GeneralException

WorksheetFreezePanes.freezeColumns GeneralException

WorksheetFreezePanes.freezeRows GeneralException
Excel JavaScript API Error thrown

WorksheetFreezePanes.getLocationOrNullObject GeneralException

WorksheetFreezePanes.unfreeze GeneralException

7 Note

This only applies to multiple Excel workbooks open on Windows or Mac.

Coauthoring
See Coauthoring in Excel add-ins for patterns to use with events in a coauthoring
environment. The article also discusses potential merge conflicts when using certain
APIs, such as TableRowCollection.add.

Known Issues

Binding events return temporary Binding objects


Both BindingDataChangedEventArgs.binding and
BindingSelectionChangedEventArgs.binding return a temporary Binding object that
contains the ID of the Binding object that raised the event. Use this ID with
BindingCollection.getItem(id) to retrieve the Binding object that raised the event.

The following code sample shows how to use this temporary binding ID to retrieve the
related Binding object. In the sample, an event listener is assigned to a binding. The
listener calls the getBindingId method when the onDataChanged event is triggered. The
getBindingId method uses the ID of the temporary Binding object to retrieve the
Binding object that raised the event.

JavaScript

async function run() {


await Excel.run(async (context) => {
// Retrieve your binding.
let binding = context.workbook.bindings.getItemAt(0);

await context.sync();

// Register an event listener to detect changes to your binding


// and then trigger the `getBindingId` method when the data changes.
binding.onDataChanged.add(getBindingId);
await context.sync();
});
}

async function getBindingId(eventArgs) {


await Excel.run(async (context) => {
// Get the temporary binding object and load its ID.
let tempBindingObject = eventArgs.binding;
tempBindingObject.load("id");

// Use the temporary binding object's ID to retrieve the original


binding object.
let originalBindingObject =
context.workbook.bindings.getItem(tempBindingObject.id);

// You now have the binding object that raised the event:
`originalBindingObject`.
});
}

Cell format useStandardHeight and useStandardWidth


issues
The useStandardHeight property of CellPropertiesFormat doesn't work properly in
Excel on the web. Due to an issue in the Excel on the web UI, setting the
useStandardHeight property to true calculates height imprecisely on this platform. For

example, a standard height of 14 is modified to 14.25 in Excel on the web.

On all platforms, the useStandardHeight and useStandardWidth properties of


CellPropertiesFormat are only intended to be set to true . Setting these properties to
false has no effect.

Range getImage method unsupported on Excel for Mac


The Range getImage method isn't currently supported in Excel for Mac. See
OfficeDev/office-js Issue #235 for the current status.

Range return character limit


The Worksheet.getRange(address) and Worksheet.getRanges(address) methods have an
address string limit of 8192 characters. When this limit is exceeded, the address string is
truncated to 8192 characters.
See also
Troubleshoot development errors with Office Add-ins
Troubleshoot user errors with Office Add-ins
Create custom functions in Excel
Article • 03/28/2023

Custom functions enable developers to add new functions to Excel by defining those
functions in JavaScript as part of an add-in. Users within Excel can access custom
functions just as they would any native function in Excel, such as SUM() .

7 Note

Custom function is a general term that is interchangeable with user-defined


function. Both terms apply to VBA, COM, and Office.js add-ins. The Office Add-ins
documentation uses the term custom function when referring to custom functions
that use Office JavaScript APIs.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

The following animated image shows your workbook calling a function you've created
with JavaScript or TypeScript. In this example, the custom function
=MYFUNCTION.SPHEREVOLUME calculates the volume of a sphere.
The following code defines the custom function =MYFUNCTION.SPHEREVOLUME .

JavaScript

/**
* Returns the volume of a sphere.
* @customfunction
* @param {number} radius
*/
function sphereVolume(radius) {
return Math.pow(radius, 3) * 4 * Math.PI / 3;
}

How a custom function is defined in code


If you use the Yeoman generator for Office Add-ins to create an Excel custom functions
add-in project, it creates files which control your functions and task pane. We'll
concentrate on the files that are important to custom functions.

File File Description


format

./src/functions/functions.js JavaScript Contains the code that defines custom functions.


or or
./src/functions/functions.ts TypeScript

./src/functions/functions.html HTML Provides a <script> reference to the JavaScript file


that defines custom functions.

./manifest.xml XML Specifies the location of multiple files that your


custom function use, such as the custom functions
JavaScript, JSON, and HTML files. It also lists the
locations of task pane files, command files, and
specifies which runtime your custom functions
should use.
 Tip

The Yeoman generator for Office Add-ins offers multiple Excel Custom Functions
projects. We recommend selecting the project type Excel Custom Functions using
a Shared Runtime and the script type JavaScript.

Script file
The script file (./src/functions/functions.js or ./src/functions/functions.ts) contains the
code that defines custom functions and comments which define the function.

The following code defines the custom function add . The code comments are used to
generate a JSON metadata file that describes the custom function to Excel. The required
@customfunction comment is declared first, to indicate that this is a custom function.
Next, two parameters are declared, first and second , followed by their description
properties. Finally, a returns description is given. For more information about what
comments are required for your custom function, see Autogenerate JSON metadata for
custom functions.

JavaScript

/**
* Adds two numbers.
* @customfunction
* @param first First number.
* @param second Second number.
* @returns The sum of the two numbers.
*/

function add(first, second){


return first + second;
}

Manifest file
The XML manifest file for an add-in that defines custom functions (./manifest.xml in the
project that the Yeoman generator for Office Add-ins creates) does several things.

Defines the namespace for your custom functions. A namespace prepends itself to
your custom functions to help customers identify your functions as part of your
add-in.
Uses <ExtensionPoint> and <Resources> elements that are unique to a custom
functions manifest. These elements contain the information about the locations of
the JavaScript, JSON, and HTML files.
Specifies which runtime to use for your custom function. We recommend always
using a shared runtime unless you have a specific need for another runtime,
because a shared runtime allows for the sharing of data between functions and the
task pane.

To see a full working manifest from a sample add-in, see the manifest in the one of our
Office Add-in samples Github repositories .

 Tip

If you'll be testing your add-in across multiple environments (for example, in


development, staging, demo, etc.), we recommend that you maintain a different
XML manifest file for each environment. In each manifest file, you can:

Specify the URLs that correspond to the environment.


Customize metadata values like DisplayName and labels within Resources to
indicate the environment, so that end users will be able to identify a
sideloaded add-in's corresponding environment.
Customize the custom functions namespace to indicate the environment, if
your add-in defines custom functions.

By following this guidance, you'll streamline the testing process and avoid issues
that would otherwise occur when an add-in is simultaneously sideloaded for
multiple environments.

Coauthoring
Excel on the web and on Windows connected to a Microsoft 365 subscription allow end
users to coauthor in Excel. If an end user's workbook uses a custom function, that end
user's coauthoring colleague is prompted to load the corresponding custom functions
add-in. Once both users have loaded the add-in, the custom function shares results
through coauthoring.

For more information on coauthoring, see About coauthoring in Excel.

Next steps
Want to try out custom functions? Check out the simple custom functions quick start or
the more in-depth custom functions tutorial if you haven't already.

Another easy way to try out custom functions is to use Script Lab , an add-in that
allows you to experiment with custom functions right in Excel. You can try out creating
your own custom function or play with the provided samples.

See also
Learn about the Microsoft 365 Developer Program
Custom functions requirement sets
Custom functions naming guidelines
Make your custom functions compatible with XLL user-defined functions
Configure your Office Add-in to use a shared runtime
Runtimes in Office Add-ins
Get started developing Excel custom
functions
Article • 03/28/2023

With custom functions, developers can add new functions to Excel by defining them in
JavaScript or TypeScript as part of an add-in. Excel users can access custom functions
just as they would any native function in Excel, such as SUM() .

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Office connected to a Microsoft 365 subscription (including Office on the web).

7 Note

If you don't already have Office, you can join the Microsoft 365 developer
program to get a free, 90-day renewable Microsoft 365 subscription to use
during development.
Build your first custom functions project
To start, you'll use the Yeoman generator to create the custom functions project. This
will set up your project with the correct folder structure, source files, and dependencies
to begin coding your custom functions.

1. Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Excel Custom Functions using a Shared Runtime


Choose a script type: JavaScript
What do you want to name your add-in? My custom functions add-in

The Yeoman generator will create the project files and install supporting Node
components.

2. The Yeoman generator will give you some instructions in your command line about
what to do with the project, but ignore them and continue to follow our
instructions. Navigate to the root folder of the project.
command line

cd "My custom functions add-in"

3. Build the project.

command line

npm run build

4. Start the local web server, which runs in Node.js. You can try out the custom
function add-in in Excel. You may be prompted to open the add-in's task pane,
although this is optional. You can still run your custom functions without opening
your add-in's task pane.

Excel on Windows or Mac

To test your add-in in Excel on Windows or Mac, run the following command. When
you run this command, the local web server will start and Excel will open with your
add-in loaded.

command line

npm run start:desktop

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

Try out a prebuilt custom function


The custom functions project that you created by using the Yeoman generator contains
some prebuilt custom functions, defined within the ./src/functions/functions.js file. The
./manifest.xml file in the root directory of the project specifies that all custom functions
belong to the CONTOSO namespace.
In your Excel workbook, try out the ADD custom function by completing the following
steps.

1. Select a cell and type =CONTOSO . Notice that the autocomplete menu shows the list
of all functions in the CONTOSO namespace.

2. Run the CONTOSO.ADD function, using numbers 10 and 200 as input parameters, by
typing the value =CONTOSO.ADD(10,200) in the cell and pressing enter.

The ADD custom function computes the sum of the two numbers that you specify as
input parameters. Typing =CONTOSO.ADD(10,200) should produce the result 210 in the cell
after you press enter.

If the CONTOSO namespace isn't available in the autocomplete menu, take the following
steps to register the add-in in Excel.

Excel on Windows or Mac

1. In Excel, choose the Insert tab and then choose the down-arrow located to the
right of My Add-ins.

2. In the list of available add-ins, find the Developer Add-ins section and select
My custom functions add-in to register it.
Next steps
Congratulations, you've successfully created a custom function in an Excel add-in! Next,
build a more complex add-in with streaming data capability. The following link takes
you through the next steps in the Excel add-in with custom functions tutorial.

Excel custom functions add-in tutorial

Troubleshooting
You may encounter issues if you run the quick start multiple times. If the Office cache
already has an instance of a function with the same name, your add-in gets an error
when it sideloads. You can prevent this by clearing the Office cache before running npm
run start .

See also
Custom functions overview
Custom functions metadata
Runtime for Excel custom functions
Using Visual Studio Code to publish
Tutorial: Create custom functions in
Excel
Article • 03/28/2023

Custom functions enable you to add new functions to Excel by defining those functions
in JavaScript as part of an add-in. Users within Excel can access custom functions as they
would any native function in Excel, such as SUM() . You can create custom functions that
perform simple tasks like calculations or more complex tasks such as streaming real-
time data from the web into a worksheet.

In this tutorial, you will:

" Create a custom function add-in using the Yeoman generator for Office Add-ins.
" Use a prebuilt custom function to perform a simple calculation.
" Create a custom function that gets data from the web.
" Create a custom function that streams real-time data from the web.

Prerequisites
Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Office connected to a Microsoft 365 subscription (including Office on the web).

7 Note

If you don't already have Office, you can join the Microsoft 365 developer
program to get a free, 90-day renewable Microsoft 365 subscription to use
during development.

Create a custom functions project


To start, create the code project to build your custom function add-in. The Yeoman
generator for Office Add-ins will set up your project with some prebuilt custom
functions that you can try out. If you've already run the custom functions quick start and
generated a project, continue to use that project and skip to this step instead.

7 Note

If you recreate the yo office project, you may get an error because the Office cache
already has an instance of a function with the same name. You can prevent this by
clearing the Office cache before running npm run start .

1. Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Excel Custom Functions using a Shared Runtime


Choose a script type: JavaScript
What do you want to name your add-in? My custom functions add-in
The Yeoman generator will create the project files and install supporting Node
components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

2. Navigate to the root folder of the project.

command line

cd "My custom functions add-in"

3. Build the project.

command line

npm run build

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run npm run build , accept
the prompt to install the certificate that the Yeoman generator provides.

4. Start the local web server, which runs in Node.js. You can try out the custom
function add-in in Excel.
Excel on Windows or Mac

To test your add-in in Excel on Windows or Mac, run the following command. When
you run this command, the local web server will start and Excel will open with your
add-in loaded.

command line

npm run start:desktop

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

Try out a prebuilt custom function


The custom functions project that you created contains some prebuilt custom functions,
defined within the ./src/functions/functions.js file. The ./manifest.xml file specifies that
all custom functions belong to the CONTOSO namespace. You'll use the CONTOSO
namespace to access the custom functions in Excel.

Next, try out the ADD custom function by completing the following steps.

1. In Excel, go to any cell and enter =CONTOSO . Notice that the autocomplete menu
shows the list of all functions in the CONTOSO namespace.

2. Run the CONTOSO.ADD function, with numbers 10 and 200 as input parameters, by
typing the value =CONTOSO.ADD(10,200) in the cell and pressing enter.

The ADD custom function computes the sum of the two numbers that you provided and
returns the result of 210.

If the CONTOSO namespace isn't available in the autocomplete menu, take the following
steps to register the add-in in Excel.

Excel on Windows or Mac


1. In Excel, choose the Insert tab and then choose the down-arrow located to the
right of My Add-ins.

2. In the list of available add-ins, find the Developer Add-ins section and select
My custom functions add-in to register it.

7 Note

See the Troubleshooting section of this article if you encounter errors when
sideloading the add-in.

Create a custom function that requests data


from the web
Integrating data from the Web is a great way to extend Excel through custom functions.
Next you'll create a custom function named getStarCount that shows how many stars a
given Github repository possesses.

1. In the My custom functions add-in project, find the file


./src/functions/functions.js and open it in your code editor.
2. In function.js, add the following code.

JS

/**
* Gets the star count for a given Github repository.
* @customfunction
* @param {string} userName string name of Github user or
organization.
* @param {string} repoName string name of the Github repository.
* @return {number} number of stars given to a Github repository.
*/
async function getStarCount(userName, repoName) {
try {
//You can change this URL to any web request you want to work
with.
const url = "https://api.github.com/repos/" + userName + "/" +
repoName;
const response = await fetch(url);
//Expect that status code is in 200-299 range
if (!response.ok) {
throw new Error(response.statusText)
}
const jsonResponse = await response.json();
return jsonResponse.watchers_count;
}
catch (error) {
return error;
}
}

3. Run the following command to rebuild the project.

command line

npm run build

4. Complete the following steps (for Excel on the web, Windows, or Mac) to re-
register the add-in in Excel. You must complete these steps before the new
function will be available.

Excel on Windows or Mac

1. Close Excel and then reopen Excel.

2. In Excel, choose the Insert tab and then choose the down-arrow located to the
right of My Add-ins.
3. In the list of available add-ins, find the Developer Add-ins section and select
My custom functions add-in to register it.

4. Try out the new function. In cell B1, type the text
=CONTOSO.GETSTARCOUNT("OfficeDev", "Office-Add-in-Samples") and
press Enter. You should see that the result in cell B1 is the current number of
stars given to the Office-Add-in-Samples repository .

7 Note

See the Troubleshooting section of this article if you encounter errors when
sideloading the add-in.

Create a streaming asynchronous custom


function
The getStarCount function returns the number of stars a repository has at a specific
moment in time. Custom functions also return data that is continuously changing. These
functions are called streaming functions. They must include an invocation parameter
which refers to the cell that called the function. The invocation parameter is used to
update the contents of the cell at any time.

In the following code sample, notice that there are two functions, currentTime and
clock . The currentTime function is a static function that doesn't use streaming. It
returns the date as a string. The clock function uses the currentTime function to
provide the new time every second to a cell in Excel. It uses invocation.setResult to
deliver the time to the Excel cell and invocation.onCanceled to handle function
cancellation.

The My custom functions add-in project already contains the following two functions in
the ./src/functions/functions.js file.

JS

/**
* Returns the current time
* @returns {string} String with the current time formatted for the current
locale.
*/
function currentTime() {
return new Date().toLocaleTimeString();
}

/**
* Displays the current time once a second
* @customfunction
* @param {CustomFunctions.StreamingInvocation<string>} invocation Custom
function invocation
*/
function clock(invocation) {
const timer = setInterval(() => {
const time = currentTime();
invocation.setResult(time);
}, 1000);

invocation.onCanceled = () => {
clearInterval(timer);
};
}

To try out the functions, type the text =CONTOSO.CLOCK() in cell C1 and press enter.
You should see the current date, which streams an update every second. While this clock
is just a timer on a loop, you can use the same idea of setting a timer on more complex
functions that make web requests for real-time data.

Troubleshooting
You may encounter issues if you run the tutorial multiple times. If the Office cache
already has an instance of a function with the same name, your add-in gets an error
when it sideloads.

You can prevent this conflict by clearing the Office cache before running npm run start .
If your npm process is already running, enter npm stop , clear the Office cache, and then
restart npm.

Next steps
Congratulations! You've created a new custom functions project, tried out a prebuilt
function, created a custom function that requests data from the web, and created a
custom function that streams data. Next, learn how to Share custom function data with
the task pane.
Tutorial: Share data and events between
Excel custom functions and the task
pane
Article • 03/28/2023

Share global data and send events between the task pane and custom functions of your
Excel add-in with a shared runtime.

Share a state between custom function and


task pane code
The following instructions show how to share a global variable between custom function
and task pane code. This tutorial assumes that you've completed the Excel custom
functions tutorial, with a Excel Custom Functions using a Shared Runtime project using
the script type JavaScript. Use the add-in you created in that tutorial to complete the
following instructions.

Create custom functions to get or store shared state


1. In Visual Studio Code open the file src/functions/functions.js.

2. On line 1, insert the following code at the very top. This will initialize a global
variable named sharedState.

JavaScript

window.sharedState = "empty";

3. Add the following code to create a custom function that stores values to the
sharedState variable.

JavaScript

/**
* Saves a string value to shared state with the task pane
* @customfunction STOREVALUE
* @param {string} value String to write to shared state with task
pane.
* @return {string} A success value
*/
function storeValue(sharedValue) {
window.sharedState = sharedValue;
return "value stored";
}

4. Add the following code to create a custom function that gets the current value of
the sharedState variable.

JavaScript

/**
* Gets a string value from shared state with the task pane
* @customfunction GETVALUE
* @returns {string} String value of the shared state with task pane.
*/
function getValue() {
return window.sharedState;
}

5. Save the file.

Create task pane controls to work with global data


1. Open the file src/taskpane/taskpane.html.

2. After the closing </main> element, add the following HTML. The HTML creates two
text boxes and buttons used to get or store global data.

HTML

<ol>
<li>
Enter a value to send to the custom function and select
<strong>Store</strong>.
</li>
<li>
Enter <strong>=CONTOSO.GETVALUE()</strong> into a cell to retrieve
it.
</li>
<li>
To send data to the task pane, in a cell, enter
<strong>=CONTOSO.STOREVALUE("new value")</strong>
</li>
<li>Select <strong>Get</strong> to display the value in the task
pane.</li>
</ol>

<p>Store new value to shared state</p>


<div>
<input type="text" id="storeBox" />
<button onclick="storeSharedValue()">Store</button>
</div>

<p>Get shared state value</p>


<div>
<input type="text" id="getBox" />
<button onclick="getSharedValue()">Get</button>
</div>

3. Before the closing </body> element, add the following script. This code will handle
the button click events when the user wants to store or get global data.

HTML

<script>
function storeSharedValue() {
let sharedValue = document.getElementById('storeBox').value;
window.sharedState = sharedValue;
}

function getSharedValue() {
document.getElementById('getBox').value = window.sharedState;
}
</script>

4. Save the file.

5. Build the project.

command line

npm run build

Try sharing data between the custom functions and task


pane
Start the project by using the following command.

command line

npm run start

Once Excel starts, you can use the task pane buttons to store or get shared data. Enter
=CONTOSO.GETVALUE() into a cell for the custom function to retrieve the same shared
data. Or use =CONTOSO.STOREVALUE("new value") to change the shared data to a new
value.

7 Note

Calling some Office APIs from custom functions using a shared runtime is possible.
See Call Microsoft Excel APIs from a custom function for more details.

See also
Excel custom functions tutorial
Configure your Office Add-in to use a shared runtime
Custom functions naming guidelines
Article • 03/22/2022

A custom function is identified by an id and name property in the JSON metadata file.

The function id is used to uniquely identify custom functions in your JavaScript


code.
The function name is used as the display name that appears to a user in Excel.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

A function name can differ from the function id , such as for localization purposes. In
general, a function's name should stay the same as the id if there is no reason for them
to differ.

A function's name and id share some common requirements.

A function's id may only use characters A through Z, numbers zero through nine,
underscores, and periods.

A function's name may use any Unicode alphabetic characters, underscores, and
periods.

Both function name and id must start with a letter and have a minimum limit of
three characters.

Excel uses uppercase letters for built-in function names (such as SUM ). Use uppercase
letters for your custom function's name and id as a best practice.
A function's name shouldn't be the same as:

Any cells between A1 to XFD1048576 or any cells between R1C1 to


R1048576C16384.

Any Excel 4.0 Macro Function (such as RUN , ECHO ). For a full list of these functions,
see this Excel Macro Functions Reference document .

Naming conflicts
If your function name is the same as a function name in an add-in that already exists, the
#REF! error will appear in your workbook.

To fix a naming conflict, change the name in your add-in and try the function again. You
can also uninstall the add-in with the conflicting name. Or, if you're testing your add-in
in different environments, try using a different namespace to differentiate your function
(such as NAMESPACE_NAMEOFFUNCTION ).

Best practices
Consider adding multiple arguments to a function rather than creating multiple
functions with the same or similar names.
Avoid ambiguous abbreviations in function names. Clarity is more important than
brevity. Choose a name like =INCREASETIME rather than =INC .
Function names should indicate the action of the function, such as =GETZIPCODE
instead of ZIPCODE.
Consistently use the same verbs for functions which perform similar actions. For
example, use =DELETEZIPCODE and =DELETEADDRESS , rather than =DELETEZIPCODE and
=REMOVEADDRESS .

When naming a streaming function, consider adding a note to that effect in the
description of the function or adding STREAM to the end of the function's name.

 Tip

If you'll be testing your add-in across multiple environments (for example, in


development, staging, demo, etc.), we recommend that you maintain a different
XML manifest file for each environment. In each manifest file, you can:

Specify the URLs that correspond to the environment.


Customize metadata values like DisplayName and labels within Resources to
indicate the environment, so that end users will be able to identify a
sideloaded add-in's corresponding environment.
Customize the custom functions namespace to indicate the environment, if
your add-in defines custom functions.

By following this guidance, you'll streamline the testing process and avoid issues
that would otherwise occur when an add-in is simultaneously sideloaded for
multiple environments.

Localizing function names


You can localize your function names for different languages using separate JSON files
and override values in your add-in's manifest file. Avoid giving your functions an id or
name that is a built-in Excel function in another language as this could conflict with

localized functions.

For full information on localizing, see Localize custom functions

Next steps
Learn about error handling best practices.

See also
Manually create JSON metadata for custom functions
Excel custom functions tutorial
Custom functions parameter options
Article • 07/05/2023

Custom functions are configurable with many different parameter options.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

Optional parameters
When a user invokes a function in Excel, optional parameters appear in brackets. In the
following sample, the add function can optionally add a third number. This function
appears as =CONTOSO.ADD(first, second, [third]) in Excel.

JavaScript

JavaScript

/**
* Calculates the sum of the specified numbers
* @customfunction
* @param {number} first First number.
* @param {number} second Second number.
* @param {number} [third] Third number to add. If omitted, third = 0.
* @returns {number} The sum of the numbers.
*/
function add(first, second, third) {
if (third === null) {
third = 0;
}
return first + second + third;
}

7 Note

When no value is specified for an optional parameter, Excel assigns it the value
null . This means default-initialized parameters in TypeScript will not work as

expected. Don't use the syntax function add(first:number, second:number,


third=0):number because it will not initialize third to 0. Instead use the TypeScript

syntax as shown in the previous example.

When you define a function that contains one or more optional parameters, specify
what happens when the optional parameters are null. In the following example, zipCode
and dayOfWeek are both optional parameters for the getWeatherReport function. If the
zipCode parameter is null, the default value is set to 98052 . If the dayOfWeek parameter

is null, it's set to Wednesday.

JavaScript

JavaScript

/**
* Gets a weather report for a specified zipCode and dayOfWeek
* @customfunction
* @param {number} [zipCode] Zip code. If omitted, zipCode = 98052.
* @param {string} [dayOfWeek] Day of the week. If omitted, dayOfWeek =
Wednesday.
* @returns {string} Weather report for the day of the week in that zip
code.
*/
function getWeatherReport(zipCode, dayOfWeek) {
if (zipCode === null) {
zipCode = 98052;
}

if (dayOfWeek === null) {


dayOfWeek = "Wednesday";
}

// Get weather report for specified zipCode and dayOfWeek.


// ...
}
Range parameters
Your custom function may accept a range of cell data as an input parameter. A function
can also return a range of data. Excel will pass a range of cell data as a two-dimensional
array.

For example, suppose that your function returns the second highest value from a range
of numbers stored in Excel. The following function accepts the parameter values , and
the JSDOC syntax number[][] sets the parameter's dimensionality property to matrix
in the JSON metadata for this function.

JavaScript

/**
* Returns the second highest value in a matrixed range of values.
* @customfunction
* @param {number[][]} values Multiple ranges of values.
*/
function secondHighest(values) {
let highest = values[0][0],
secondHighest = values[0][0];
for (let i = 0; i < values.length; i++) {
for (let j = 0; j < values[i].length; j++) {
if (values[i][j] >= highest) {
secondHighest = highest;
highest = values[i][j];
} else if (values[i][j] >= secondHighest) {
secondHighest = values[i][j];
}
}
}
return secondHighest;
}

Repeating parameters
A repeating parameter allows a user to enter a series of optional arguments to a
function. When the function is called, the values are provided in an array for the
parameter. If the parameter name ends with a number, each argument's number will
increase incrementally, such as ADD(number1, [number2], [number3],…) . This matches the
convention used for built-in Excel functions.

The following function sums the total of numbers, cell addresses, as well as ranges, if
entered.

TS
/**
* The sum of all of the numbers.
* @customfunction
* @param operands A number (such as 1 or 3.1415), a cell address (such as A1
or $E$11), or a range of cell addresses (such as B3:F12)
*/

function ADD(operands: number[][][]): number {


let total: number = 0;

operands.forEach(range => {
range.forEach(row => {
row.forEach(num => {
total += num;
});
});
});

return total;
}

This function shows =CONTOSO.ADD([operands], [operands]...) in the Excel workbook.

Repeating single value parameter


A repeating single value parameter allows multiple single values to be passed. For
example, the user could enter ADD(1,B2,3). The following sample shows how to declare
a single value parameter.

JS

/**
* @customfunction
* @param {number[]} singleValue An array of numbers that are repeating
parameters.
*/
function addSingleValue(singleValue) {
let total = 0;
singleValue.forEach(value => {
total += value;
})

return total;
}
Single range parameter
A single range parameter isn't technically a repeating parameter, but is included here
because the declaration is very similar to repeating parameters. It would appear to the
user as ADD(A2:B3) where a single range is passed from Excel. The following sample
shows how to declare a single range parameter.

JS

/**
* @customfunction
* @param {number[][]} singleRange
*/
function addSingleRange(singleRange) {
let total = 0;
singleRange.forEach(setOfSingleValues => {
setOfSingleValues.forEach(value => {
total += value;
})
})
return total;
}

Repeating range parameter


A repeating range parameter allows multiple ranges or numbers to be passed. For
example, the user could enter ADD(5,B2,C3,8,E5:E8). Repeating ranges are usually
specified with the type number[][][] as they are three-dimensional matrices. For a
sample, see the main sample listed for repeating parameters.

Declaring repeating parameters


In Typescript, indicate that the parameter is multi-dimensional. For example, ADD(values:
number[]) would indicate a one-dimensional array, ADD(values:number[][]) would
indicate a two-dimensional array, and so on.

In JavaScript, use @param values {number[]} for one-dimensional arrays, @param <name>
{number[][]} for two-dimensional arrays, and so on for more dimensions.

For hand-authored JSON, ensure your parameter is specified as "repeating": true in


your JSON file, as well as check that your parameters are marked as "dimensionality":
matrix .
Invocation parameter
Every custom function is automatically passed an invocation argument as the last input
parameter, even if it's not explicitly declared. This invocation parameter corresponds to
the Invocation object. The Invocation object can be used to retrieve additional context,
such as the address of the cell that invoked your custom function. To access the
Invocation object, you must declare invocation as the last parameter in your custom
function.

7 Note

The invocation parameter doesn't appear as a custom function argument for users
in Excel.

The following sample shows how to use the invocation parameter to return the address
of the cell that invoked your custom function. This sample uses the address property of
the Invocation object. To access the Invocation object, first declare
CustomFunctions.Invocation as a parameter in your JSDoc. Next, declare
@requiresAddress in your JSDoc to access the address property of the Invocation

object. Finally, within the function, retrieve and then return the address property.

JavaScript

/**
* Return the address of the cell that invoked the custom function.
* @customfunction
* @param {number} first First parameter.
* @param {number} second Second parameter.
* @param {CustomFunctions.Invocation} invocation Invocation object.
* @requiresAddress
*/
function getAddress(first, second, invocation) {
const address = invocation.address;
return address;
}

In Excel, a custom function calling the address property of the Invocation object will
return the absolute address following the format SheetName!RelativeCellAddress in the
cell that invoked the function. For example, if the input parameter is located on a sheet
called Prices in cell F6, the returned parameter address value will be Prices!F6 .

7 Note
If a blank space or any of the following characters is in a worksheet name: ~ ` ! @ #
$ % ^ & ( ) - _ = + { } | ; : , ' < . >, then the worksheet name in the returned address
is enclosed in single quotation marks, so the format is
'SheetName'!RelativeCellAddress ; for example, 'Latest Prices'!F6 . If the single
quotation mark (apostrophe) character, ', is in the name, the returned address has
two such characters in a row; for example, 'Bob''s Region'!F6 .

The invocation parameter can also be used to send information to Excel. See Make a
streaming function to learn more.

Detect the address of a parameter


In combination with the invocation parameter, you can use the Invocation object to
retrieve the address of a custom function input parameter. When invoked, the
parameterAddresses property of the Invocation object allows a function to return the
addresses of all input parameters.

This is useful in scenarios where input data types may vary. The address of an input
parameter can be used to check the number format of the input value. The number
format can then be adjusted prior to input, if necessary. The address of an input
parameter can also be used to detect whether the input value has any related properties
that may be relevant to subsequent calculations.

7 Note

If you're working with manually-created JSON metadata to return parameter


addresses instead of the Yeoman generator for Office Add-ins, the options object
must have the requiresParameterAddresses property set to true , and the result
object must have the dimensionality property set to matrix .

The following custom function takes in three input parameters, retrieves the
parameterAddresses property of the Invocation object for each parameter, and then

returns the addresses.

JavaScript

/**
* Return the addresses of three parameters.
* @customfunction
* @param {string} firstParameter First parameter.
* @param {string} secondParameter Second parameter.
* @param {string} thirdParameter Third parameter.
* @param {CustomFunctions.Invocation} invocation Invocation object.
* @returns {string[][]} The addresses of the parameters, as a 2-dimensional
array.
* @requiresParameterAddresses
*/
function getParameterAddresses(firstParameter, secondParameter,
thirdParameter, invocation) {
const addresses = [
[invocation.parameterAddresses[0]],
[invocation.parameterAddresses[1]],
[invocation.parameterAddresses[2]]
];
return addresses;
}

When a custom function calling the parameterAddresses property runs, the parameter
address is returned following the format SheetName!RelativeCellAddress in the cell that
invoked the function. For example, if the input parameter is located on a sheet called
Costs in cell D8, the returned parameter address value will be Costs!D8 . If the custom
function has multiple parameters and more than one parameter address is returned, the
returned addresses will spill across multiple cells, descending vertically from the cell that
invoked the function.

7 Note

If a blank space or any of the following characters is in a worksheet name: ~ ` ! @ #


$ % ^ & ( ) - _ = + { } | ; : , ' < . >, then the worksheet name in the returned address
is enclosed in single quotation marks, so the format is
'SheetName'!RelativeCellAddress ; for example, 'Latest Prices'!F6 . If the single

quotation mark (apostrophe) character, ', is in the name, the returned address has
two such characters in a row; for example, 'Bob''s Region'!F6 .

Next steps
Learn how to use volatile values in your custom functions.

See also
Receive and handle data with custom functions
Autogenerate JSON metadata for custom functions
Manually create JSON metadata for custom functions
Create custom functions in Excel
Excel custom functions tutorial
Handle and return errors from your
custom function
Article • 07/21/2022

If something goes wrong while your custom function runs, return an error to inform the
user. If you have specific parameter requirements, such as only positive numbers, test
the parameters and throw an error if they aren't correct. You can also use a try...catch
block to catch any errors that occur while your custom function runs.

Detect and throw an error


Let's look at a case where you need to ensure that a zip code parameter is in the correct
format for the custom function to work. The following custom function uses a regular
expression to check the zip code. If the zip code format is correct, then it will look up
the city using another function and return the value. If the format isn't valid, the function
returns a #VALUE! error to the cell.

TypeScript

/**
* Gets a city name for the given U.S. zip code.
* @customfunction
* @param {string} zipCode
* @returns The city of the zip code.
*/
function getCity(zipCode: string): string {
let isValidZip = /(^\d{5}$)|(^\d{5}-\d{4}$)/.test(zipCode);
if (isValidZip) return cityLookup(zipCode);
let error = new
CustomFunctions.Error(CustomFunctions.ErrorCode.invalidValue, "Please
provide a valid U.S. zip code.");
throw error;
}

The CustomFunctions.Error object


The CustomFunctions.Error object is used to return an error back to the cell. When you
create the object, specify which error you want to use by choosing one of the following
ErrorCode enum values.
ErrorCode enum Excel Description
value cell
value

divisionByZero #DIV/0 The function is attempting to divide by zero.

invalidName #NAME? There is a typo in the function name. Note that this error is
supported as a custom function input error, but not as a custom
function output error.

invalidNumber #NUM! There is a problem with a number in the formula.

invalidReference #REF! The function refers to an invalid cell. Note that this error is
supported as a custom function input error, but not as a custom
function output error.

invalidValue #VALUE! A value in the formula is of the wrong type.

notAvailable #N/A The function or service isn't available.

nullReference #NULL! The ranges in the formula don't intersect.

The following code sample shows how to create and return an error for an invalid
number ( #NUM! ).

TypeScript

let error = new


CustomFunctions.Error(CustomFunctions.ErrorCode.invalidNumber);
throw error;

The #VALUE! and #N/A errors also support custom error messages. Custom error
messages are displayed in the error indicator menu, which is accessed by hovering over
the error flag on each cell with an error. The following example shows how to return a
custom error message with the #VALUE! error.

TypeScript

// You can only return a custom error message with the #VALUE! and #N/A
errors.
let error = new
CustomFunctions.Error(CustomFunctions.ErrorCode.invalidValue, "The parameter
can only contain lowercase characters.");
throw error;

Handle errors when working with dynamic arrays


In addition to returning a single error, a custom function can output a dynamic array
that includes an error. For example, a custom function could output the array [1],
[#NUM!],[3] . The following code sample shows how to input three parameters into a

custom function, replace one of the input parameters with a #NUM! error, and then
return a 2-dimensional array with the results of processing each input parameter.

JavaScript

/**
* Returns the #NUM! error as part of a 2-dimensional array.
* @customfunction
* @param {number} first First parameter.
* @param {number} second Second parameter.
* @param {number} third Third parameter.
* @returns {number[][]} Three results, as a 2-dimensional array.
*/
function returnInvalidNumberError(first, second, third) {
// Use the `CustomFunctions.Error` object to retrieve an invalid number
error.
const error = new CustomFunctions.Error(
CustomFunctions.ErrorCode.invalidNumber, // Corresponds to the #NUM!
error in the Excel UI.
);

// Enter logic that processes the first, second, and third input
parameters.
// Imagine that the second calculation results in an invalid number error.
const firstResult = first;
const secondResult = error;
const thirdResult = third;

// Return the results of the first and third parameter calculations and a
#NUM! error in place of the second result.
return [[firstResult], [secondResult], [thirdResult]];
}

Errors as custom function inputs


A custom function can evaluate even if the input range contains an error. For example, a
custom function can take the range A2:A7 as an input, even if A6:A7 contains an error.

To process inputs that contain errors, a custom function must have the JSON metadata
property allowErrorForDataTypeAny set to true . See Manually create JSON metadata for
custom functions for more information.

) Important
The allowErrorForDataTypeAny property can only be used with manually created
JSON metadata. This property doesn't work with the autogenerated JSON
metadata process.

Use try...catch blocks


In general, use try...catch blocks in your custom function to catch any potential errors
that occur. If you don't handle exceptions in your code, they will be returned to Excel. By
default, Excel returns #VALUE! for unhandled errors or exceptions.

In the following code sample, the custom function makes a fetch call to a REST service.
It's possible that the call will fail, for example, if the REST service returns an error or the
network goes down. If this happens, the custom function will return #N/A to indicate
that the web call failed.

TypeScript

/**
* Gets a comment from the hypothetical contoso.com/comments API.
* @customfunction
* @param {number} commentID ID of a comment.
*/
function getComment(commentID) {
let url = "https://www.contoso.com/comments/" + commentID;
return fetch(url)
.then(function (data) {
return data.json();
})
.then(function (json) {
return json.body;
})
.catch(function (error) {
throw new
CustomFunctions.Error(CustomFunctions.ErrorCode.notAvailable);
})
}

Next steps
Learn how to troubleshoot problems with your custom functions.

See also
Custom functions debugging
Custom functions requirement sets
Create custom functions in Excel
Autogenerate JSON metadata for
custom functions
Article • 07/13/2023

When an Excel custom function is written in JavaScript or TypeScript, JSDoc tags are
used to provide extra information about the custom function. We provide a Webpack
plugin that uses these JSDoc tags to automatically create the JSON metadata file at
build time. Using the plugin saves you from the effort of manually editing the JSON
metadata file.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

CustomFunctionsMetadataPlugin
The plugin is CustomFunctionsMetadataPlugin . To install and configure it, use the
following steps.

7 Note

The tool can be used only in a NodeJS-based project.


These instructions assume that your project uses Webpack and that you
have it installed and configured.
If your custom function add-in project is created with the Yeoman generator
for Office Add-ins, Webpack is installed and all of these steps are done
automatically, but when applicable, you must do the steps in Multiple custom
function source files manually.

1. Open a Command Prompt or bash shell and, in the root of the project, run npm
install custom-functions-metadata-plugin .

2. Open the webpack.config.js file and add the following line at the top: const
CustomFunctionsMetadataPlugin = require("custom-functions-metadata-plugin"); .

3. Scroll down to the plugins array and add the following to the top of the array.
Change the input path and filename as needed to match your project, but the
output value must be "functions.json". If you're using TypeScript, use the *.ts

source file name, not the transpiled *.js file.

JavaScript

new CustomFunctionsMetadataPlugin({
output: "functions.json",
input: "./src/functions/functions.js",
}),

Multiple custom function source files


If, and only if, you have organized your custom functions into multiple source files, there
are additional steps.

1. In the webpack.config.js file, replace the string value of input with an array of
string URLs that point to each of the files. The following is an example:

JavaScript

new CustomFunctionsMetadataPlugin({
output: "functions.json",
input: [
"./src/functions/someFunctions.js",
"./src/functions/otherFunctions.js"
],
}),

2. Scroll to the entry.functions property, and replace its value with the same array
you used in the preceding step. The following is an example:

JavaScript
entry: {
polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
taskpane: ["./src/taskpane/taskpane.js",
"./src/taskpane/taskpane.html"],
functions: [
"./src/functions/someFunctions.js",
"./src/functions/otherFunctions.js"
],
},

Run the tool


You don't have to do anything to run the tool. When Webpack runs, it creates the
functions.json file and puts it in memory in development mode, or in the /dist folder in
production mode.

Basics of JSDoc tags


Add the @customfunction tag in the code comments for a JavaScript or TypeScript
function to mark it as a custom function.

The function parameter types may be provided using the @param tag in JavaScript, or
from the Function type in TypeScript. For more information, see the @param tag and
Types sections.

Add a description to a function


The description is displayed to the user as help text when they need help to understand
what your custom function does. The description doesn't require any specific tag. Just
enter a short text description in the JSDoc comment. In general the description is placed
at the start of the JSDoc comment section, but it will work no matter where it is placed.

To see examples of the built-in function descriptions, open Excel, go to the Formulas
tab, and choose Insert function. You can then browse through all the function
descriptions, and also see your own custom functions listed.

In the following example, the phrase "Calculates the volume of a sphere." is the
description for the custom function.

JavaScript

/**
/* Calculates the volume of a sphere.
/* @customfunction VOLUME
...
*/

Supported JSDoc tags


The following JSDoc tags are supported in Excel custom functions.

@cancelable
@customfunction id name
@helpurl url
@param {type} name description
@requiresAddress
@requiresParameterAddresses
@returns {type}
@streaming
@volatile

@cancelable
Indicates that a custom function performs an action when the function is canceled.

The last function parameter must be of type CustomFunctions.CancelableInvocation . The


function can assign a function to the oncanceled property to denote the result when the
function is canceled.

If the last function parameter is of type CustomFunctions.CancelableInvocation , it will be


considered @cancelable even if the tag isn't present.

A function can't have both @cancelable and @streaming tags.

@customfunction
Syntax: @customfunction id name

This tag indicates that the JavaScript/TypeScript function is an Excel custom function. It
is required to create metadata for the custom function.

The following shows an example of this tag.

JavaScript
/**
* Increments a value once a second.
* @customfunction
* ...
*/

id
The id identifies a custom function.

If id isn't provided, the JavaScript/TypeScript function name is converted to


uppercase and disallowed characters are removed.
The id must be unique for all custom functions.
The allowed characters are limited to: A-Z, a-z, 0-9, underscores (_), and period (.).

In the following example, increment is the id and the name of the function.

JavaScript

/**
* Increments a value once a second.
* @customfunction INCREMENT
* ...
*/

name
Provides the display name for the custom function.

If name isn't provided, the id is also used as the name.


Allowed characters: Letters Unicode Alphabetic character , numbers, period (.),
and underscore (_).
Must start with a letter.
Maximum length is 128 characters.

In the following example, INC is the id of the function and increment is the name .

JavaScript

/**
* Increments a value once a second.
* @customfunction INC INCREMENT
* ...
*/

description
A description appears to users in Excel as they are entering the function and specifies
what the function does. A description doesn't require any specific tag. Add a description
to a custom function by adding a phrase to describe what the function does inside the
JSDoc comment. By default, whatever text is untagged in the JSDoc comment section
will be the description of the function.

In the following example, the phrase "A function that adds two numbers" is the
description for the custom function with the id property of ADD .

JavaScript

/**
* A function that adds two numbers.
* @customfunction ADD
* ...
*/

@helpurl
Syntax: @helpurl url

The provided url is displayed in Excel.

In the following example, the helpurl is http://www.contoso.com/weatherhelp .

JavaScript

/**
* A function which streams the temperature in a town you specify.
* @customfunction getTemperature
* @helpurl http://www.contoso.com/weatherhelp
* ...
*/

@param

JavaScript
JavaScript Syntax: @param {type} name description

{type} specifies the type info within curly braces. See the Types section for more

information about the types which may be used. If no type is specified, the default
type any will be used.
name specifies the parameter that the @param tag applies to. It is required.

description provides the description which appears in Excel for the function

parameter. It is optional.

To denote a custom function parameter as optional, put square brackets around the
parameter name. For example, @param {string} [text] Optional text .

7 Note

The default value for optional parameters is null .

The following example shows an ADD function that adds two or three numbers, with the
third number as an optional parameter.

JavaScript

/**
* A function which sums two, or optionally three, numbers.
* @customfunction ADDNUMBERS
* @param firstNumber {number} First number to add.
* @param secondNumber {number} Second number to add.
* @param [thirdNumber] {number} Optional third number you wish to add.
* ...
*/

TypeScript
TypeScript Syntax: @param name description

name specifies the parameter that the @param tag applies to. It is required.

description provides the description which appears in Excel for the function

parameter. It is optional.

See the Types section for more information about the function parameter types which
may be used.

To denote a custom function parameter as optional, do one of the following:

Use an optional parameter. For example: function f(text?: string)


Give the parameter a default value. For example: function f(text: string =
"abc")

For detailed description of the @param see: JSDoc

7 Note

The default value for optional parameters is null .

The following example shows the add function that adds two numbers.

ts

/**
* Adds two numbers.
* @customfunction
* @param first First number
* @param second Second number
* @returns The sum of the two numbers.
*/
function add(first: number, second: number): number {
return first + second;
}

@requiresAddress
Indicates that the address of the cell where the function is being evaluated should be
provided.

The last function parameter must be of type CustomFunctions.Invocation or a derived


type to use @requiresAddress . When the function is called, the address property will
contain the address.

The following sample shows how to use the invocation parameter in combination with
@requiresAddress to return the address of the cell that invoked your custom function.
See Invocation parameter for more information.

JavaScript

/**
* Return the address of the cell that invoked the custom function.
* @customfunction
* @param {number} first First parameter.
* @param {number} second Second parameter.
* @param {CustomFunctions.Invocation} invocation Invocation object.
* @requiresAddress
*/
function getAddress(first, second, invocation) {
const address = invocation.address;
return address;
}

@requiresParameterAddresses
Indicates that the function should return the addresses of input parameters.

The last function parameter must be of type CustomFunctions.Invocation or a derived


type to use @requiresParameterAddresses . The JSDoc comment must also include an
@returns tag specifying that the return value be a matrix, such as @returns {string[]

[]} or @returns {number[][]} . See Matrix types for additional information.

When the function is called, the parameterAddresses property will contain the addresses
of the input parameters.

The following sample shows how to use the invocation parameter in combination with
@requiresParameterAddresses to return the addresses of three input parameters. See

Detect the address of a parameter for more information.

JavaScript

/**
* Return the addresses of three parameters.
* @customfunction
* @param {string} firstParameter First parameter.
* @param {string} secondParameter Second parameter.
* @param {string} thirdParameter Third parameter.
* @param {CustomFunctions.Invocation} invocation Invocation object.
* @returns {string[][]} The addresses of the parameters, as a 2-dimensional
array.
* @requiresParameterAddresses
*/
function getParameterAddresses(firstParameter, secondParameter,
thirdParameter, invocation) {
const addresses = [
[invocation.parameterAddresses[0]],
[invocation.parameterAddresses[1]],
[invocation.parameterAddresses[2]]
];
return addresses;
}
@returns
Syntax: @returns {type}

Provides the type for the return value.

If {type} is omitted, the TypeScript type info will be used. If there is no type info, the
type will be any .

The following example shows the add function that uses the @returns tag.

ts

/**
* Adds two numbers.
* @customfunction
* @param first First number
* @param second Second number
* @returns The sum of the two numbers.
*/
function add(first: number, second: number): number {
return first + second;
}

@streaming
Used to indicate that a custom function is a streaming function.

The last parameter is of type CustomFunctions.StreamingInvocation<ResultType> . The


function returns void .

Streaming functions don't return values directly, instead they call setResult(result:
ResultType) using the last parameter.

Exceptions thrown by a streaming function are ignored. setResult() may be called with
Error to indicate an error result. For an example of a streaming function and more
information, see Make a streaming function.

Streaming functions can't be marked as @volatile.

@volatile
A volatile function is one whose result isn't the same from one moment to the next,
even if it takes no arguments or the arguments haven't changed. Excel re-evaluates cells
that contain volatile functions, together with all dependents, every time that a
calculation is done. For this reason, too much reliance on volatile functions can make
recalculation times slow, so use them sparingly.

Streaming functions can't be volatile.

The following function is volatile and uses the @volatile tag.

JavaScript

/**
* Simulates rolling a 6-sided die.
* @customfunction
* @volatile
*/
function roll6sided(): number {
return Math.floor(Math.random() * 6) + 1;
}

Types
By specifying a parameter type, Excel will convert values into that type before calling the
function. If the type is any , no conversion will be performed.

Value types
A single value may be represented using one of the following types: boolean , number ,
string .

Matrix type
Use a two-dimensional array type to have the parameter or return value be a matrix of
values. For example, the type number[][] indicates a matrix of numbers and string[][]
indicates a matrix of strings.

Error type
A non-streaming function can indicate an error by returning an Error type.

A streaming function can indicate an error by calling setResult() with an Error type.

Promise
A custom function can return a promise that provides the value when the promise is
resolved. If the promise is rejected, then the custom function will throw an error.

Other types
Any other type will be treated as an error.

Next steps
Learn about naming conventions for custom functions. Alternatively, learn how to
localize your functions which requires you to write your JSON file by hand.

See also
Manually create JSON metadata for custom functions
Create custom functions in Excel
Use data types with custom functions in
Excel
Article • 03/09/2023

Data types expand the Excel JavaScript API to support data types beyond the original
four cell value types (string, number, boolean, and error). Data types include support for
web images, formatted number values, entities, and arrays within entities.

These data types amplify the power of custom functions, because custom functions
accept data types as both input and output values. You can generate data types through
custom functions, or take existing data types as function arguments into calculations.
Once the JSON schema of a data type is set, this schema is maintained throughout the
calculations.

To learn more about using data types with an Excel add-in, see Overview of data types in
Excel add-ins.

How custom functions handle data types


Custom functions can recognize data types and accept them as parameter values. A
custom function can create a new data type for a return value. Custom functions use the
same JSON schema for data types as the Excel JavaScript API, and this JSON schema is
maintained as custom functions calculate and evaluate.

7 Note

Custom functions do not support the full functionality of the enhanced error
objects offered by data types. A custom function can accept a data types error
object, but it won't be maintained throughout calculation. At this time, custom
functions only support the errors included in the CustomFunctions.Error object.

Enable data types for custom functions


Custom functions projects include a JSON metadata file. This JSON metadata file differs
from the JSON schema used by data types APIs. To use the data types integration with
custom functions, the custom functions JSON metadata file must be manually updated
to include the property allowCustomDataForDataTypeAny . Set this property to true .
For a full description of the manual JSON metadata creation process, see Manually
create JSON metadata for custom functions. See allowCustomDataForDataTypeAny for
additional details about this property.

Output a formatted number value


The following code sample shows how to create a FormattedNumberCellValue data type
with a custom function. The function takes a basic number and a format setting as the
input parameters and returns a formatted number value data type as the output.

JavaScript

/**
* Take a number as the input value and return a formatted number value as
the output.
* @customfunction
* @param {number} value
* @param {string} format (e.g. "0.00%")
* @returns A formatted number value.
*/
function createFormattedNumber(value, format) {
return {
type: "FormattedNumber",
basicValue: value,
numberFormat: format
}
}

Input an entity value


The following code sample shows a custom function that takes an EntityCellValue data
type as an input. If the attribute parameter is set to text , then the function returns the
text property of the entity value. Otherwise, the function returns the basicValue

property of the entity value.

JavaScript

/**
* Accept an entity value data type as a function input.
* @customfunction
* @param {any} value
* @param {string} attribute
* @returns {any} The text value of the entity.
*/
function getEntityAttribute(value, attribute) {
if (value.type == "Entity") {
if (attribute == "text") {
return value.text;
} else {
return value.properties[attribute].basicValue;
}
} else {
return JSON.stringify(value);
}
}

Next steps
To experiment with custom functions and data types, install Script Lab in Excel and try
out the Data types: Custom functions snippet in our Samples library.

See also
Overview of data types in Excel add-ins
Excel data types core concepts
Configure your Office Add-in to use a shared runtime
Call Excel JavaScript APIs from a custom
function
Article • 03/28/2023

Call Excel JavaScript APIs from your custom functions to get range data and obtain more
context for your calculations. Calling Excel JavaScript APIs through a custom function
can be helpful when:

A custom function needs to get information from Excel before calculation. This
information might include document properties, range formats, custom XML parts,
a workbook name, or other Excel-specific information.
A custom function will set the cell's number format for the return values after
calculation.

) Important

To call Excel JavaScript APIs from your custom function, you'll need to use a shared
runtime. Use the Yeoman generator for Office Add-ins to install an Excel Custom
Functions using a Shared Runtime project or see Configure your Office Add-in to
use a shared runtime to learn more.

Code sample
To call Excel JavaScript APIs from a custom function, you first need a context. Use the
Excel.RequestContext object to get a context. Then use the context to call the APIs you
need in the workbook.

The following code sample shows how to use Excel.RequestContext to get a value from
a cell in the workbook. In this sample, the address parameter is passed into the Excel
JavaScript API Worksheet.getRange method and must be entered as a string. For
example, the custom function entered into the Excel UI must follow the pattern
=CONTOSO.GETRANGEVALUE("A1") , where "A1" is the address of the cell from which to

retrieve the value.

JavaScript

/**
* @customfunction
* @param {string} address The address of the cell from which to retrieve
the value.
* @returns The value of the cell at the input address.
**/
async function getRangeValue(address) {
// Retrieve the context object.
const context = new Excel.RequestContext();

// Use the context object to access the cell at the input address.
const range =
context.workbook.worksheets.getActiveWorksheet().getRange(address);
range.load("values");
await context.sync();

// Return the value of the cell at the input address.


return range.values[0][0];
}

Limitations of calling Excel JavaScript APIs


through a custom function
A custom functions add-in can call Excel JavaScript APIs, but you should be cautious
about which APIs it calls. Don't call Excel JavaScript APIs from a custom function that
change cells outside of the cell running the custom function. Changing other cells or the
Excel environment can result in poor performance, time outs, and infinite loops in the
Excel application. This means your custom functions shouldn't do any of the following:

Insert, delete, or format cells on the spreadsheet.


Change another cell's value.
Move, rename, delete, or add sheets to a workbook.
Add names to a workbook.
Set properties.
Change any of the Excel environment options, such as calculation mode or screen
views.

Your custom functions add-in can read information from cells outside the cell running
the custom function, but it shouldn't perform write operations to other cells. Instead,
make changes to other cells or to the Excel environment from the context of a ribbon
button or a task pane. In addition, custom function calculations shouldn't run while an
Excel recalculation is taking place, as this scenario creates unpredictable results.

Next steps
Fundamental programming concepts with the Excel JavaScript API
See also
Share data and events between Excel custom functions and task pane tutorial
Configure your Office Add-in to use a shared runtime
Extend custom functions with XLL user-
defined functions
Article • 03/22/2022

7 Note

An XLL add-in is an Excel add-in file with the file extension .xll. An XLL file is a type
of dynamic link library (DLL) file that can only be opened by Excel. XLL add-in files
must be written in C or C++. See Developing Excel XLLs to learn more.

If you have existing Excel XLL add-ins, you can build equivalent custom function add-ins
using the Excel JavaScript API to extend your solution features to other platforms, such
as Excel on the web or on a Mac. However, Excel JavaScript API add-ins don't have all of
the functionality available in XLL add-ins. Depending on the functionality your solution
uses, the XLL add-in may provide a better experience than the Excel JavaScript API add-
in in Excel on Windows.

) Important

The equivalent add-in feature is supported by the following platform and


applications. COM add-ins cannot be installed on any other platform, so on those
platforms the manifest element that is discussed later in this article,
EquivalentAddins , is ignored.

Excel, Word, and PowerPoint on Windows (Version 1904 or later)


Outlook on Windows (Version 2102 or later) against a supported Exchange
server version
Exchange Online
Exchange 2019 Cumulative Update 10 or later (KB5003612 )
Exchange 2016 Cumulative Update 21 or later (KB5003611 )

Specify equivalent XLL in the manifest


To enable compatibility with an existing XLL add-in, identify the equivalent XLL add-in in
the manifest of your Excel JavaScript API add-in. Excel will then use the XLL add-in's
functions instead of your Excel JavaScript API add-in custom functions when running on
Windows.
To set the equivalent XLL add-in for your custom functions, specify the FileName of the
XLL file. When the user opens a workbook with functions from the XLL file, Excel
converts the functions to compatible functions. The workbook then uses the XLL file
when opened in Excel on Windows, and it will use custom functions from your Excel
JavaScript API add-in when opened on the web or on a Mac.

The following example shows how to specify both a COM add-in and an XLL add-in as
equivalents in an Excel JavaScript API add-in manifest file. Often you will specify both.
For completeness, this example shows both in context. They are identified by their
ProgId and FileName respectively. The EquivalentAddins element must be positioned
immediately before the closing VersionOverrides tag. For more information on COM
add-in compatibility, see Make your Office Add-in compatible with an existing COM
add-in.

XML

<VersionOverrides>
...
<EquivalentAddins>
<EquivalentAddin>
<ProgId>ContosoCOMAddin</ProgId>
<Type>COM</Type>
</EquivalentAddin>

<EquivalentAddin>
<FileName>contosofunctions.xll</FileName>
<Type>XLL</Type>
</EquivalentAddin>
</EquivalentAddins>
</VersionOverrides>

7 Note

If an Excel JavaScript API add-in declares its custom functions to be compatible


with an XLL add-in, changing the manifest at a later time could break a user's
workbook because it will change the file format.

Custom function behavior for XLL compatible


functions
An add-in's XLL functions are converted to XLL compatible custom functions when a
spreadsheet is opened and there is an equivalent add-in available. On the next save, the
XLL functions are written to the file in a compatible mode so that they work with both
the XLL add-in and Excel JavaScript API add-in custom functions (when on other
platforms).

The following table compares features across XLL user-defined functions, XLL
compatible custom functions, and Excel JavaScript API add-in custom functions.

XLL user-defined XLL compatible custom Excel JavaScript


function functions API add-in custom
function

Supported Windows Windows, macOS, web Windows, macOS,


platforms browser web browser

Supported file XLSX, XLSB, XLSM, XLS XLSX, XLSB, XLSM XLSX, XLSB, XLSM
formats

Formula No Yes Yes


autocomplete

Streaming Possible via xlfRTD and Yes Yes


XLL callback.

Localization of No No. The Name and ID must Yes


functions match the existing XLL's
functions.

Volatile Yes Yes Yes


functions

Multi-threaded Yes Yes Yes


recalculation
support

Calculation No UI. Excel can be Users will see #BUSY! until a Users will see
behavior unresponsive during result is returned. #BUSY! until a result
calculation. is returned.

Requirement N/A CustomFunctions 1.1 and CustomFunctions 1.1


sets later and later

See also
Make your Office Add-in compatible with an existing COM add-in
Excel custom functions tutorial
Localize custom functions
Article • 03/22/2022

You can localize both your add-in and your custom function names. To do so, provide
localized function names in the functions' JSON file and locale information in the XML
manifest file.

) Important

Autogenerated metadata doesn't work for localization so you need to update the
JSON file manually. To learn how to do this, see Manually create JSON metadata
for custom functions

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

Localize function names


To localize your custom functions, create a new JSON metadata file for each language. In
each language JSON file, create name and description properties in the target
language. The default file for English is named functions.json. Use the locale in the
filename for each additional JSON file, such as functions-de.json to help identify them.

The name and description appear in Excel and are localized. However, the id of each
function isn't localized. The id property is how Excel identifies your function as unique
and shouldn't be changed once it is set.
The following JSON shows how to define a function with the id property "MULTIPLY."
The name and description property of the function is localized for German. Each
parameter name and description is also localized for German.

JSON

{
"id": "MULTIPLY",
"name": "SUMME",
"description": "Summe zwei Zahlen",
"helpUrl": "http://www.contoso.com",
"result": {
"type": "number",
"dimensionality": "scalar"
},
"parameters": [
{
"name": "eins",
"description": "Erste Nummer",
"dimensionality": "scalar"
},
{
"name": "zwei",
"description": "Zweite Nummer",
"dimensionality": "scalar"
},
],
}

Compare the previous JSON with the following JSON for English.

JSON

{
"id": "MULTIPLY",
"name": "Multiply",
"description": "Multiplies two numbers",
"helpUrl": "http://www.contoso.com",
"result": {
"type": "number",
"dimensionality": "scalar"
},
"parameters": [
{
"name": "one",
"description": "first number",
"dimensionality": "scalar"
},
{
"name": "two",
"description": "second number",
"dimensionality": "scalar"
},
],
}

Localize your add-in


After creating a JSON file for each language, update your XML manifest file with an
override value for each locale that specifies the URL of each JSON metadata file. The
following manifest XML shows a default en-us locale with an override JSON file URL for
de-de (Germany). The functions-de.json file contains the localized German function

names and ids.

XML

<DefaultLocale>en-us</DefaultLocale>
...
<Resources>
<bt:Urls>
<bt:Url id="Contoso.Functions.Metadata.Url"
DefaultValue="https://localhost:3000/dist/functions.json"/>
<bt:Override Locale="de-de"
Value="https://localhost:3000/dist/functions-de.json" />
</bt:url>

</bt:Urls>
</Resources>

For more information on the process of localizing an add-in, see Localization for Office
Add-ins.

Next steps
Learn about naming conventions for custom functions or discover error handling best
practices.

See also
Manually create JSON metadata for custom functions
Autogenerate JSON metadata for custom functions
Create custom functions in Excel
Manually create JSON metadata for
custom functions
Article • 03/14/2023

As described in the custom functions overview article, a custom functions project must
include both a JSON metadata file and a script (either JavaScript or TypeScript) file to
register a function, making it available for use. Custom functions are registered when
the user runs the add-in for the first time and after that are available to the same user in
all workbooks.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

We recommend using JSON autogeneration when possible instead of creating your own
JSON file. Autogeneration is less prone to user error and the yo office scaffolded files
already include this. For more information on JSDoc tags and the JSON autogeneration
process, see Autogenerate JSON metadata for custom functions.

However, you can make a custom functions project from scratch. This process requires
you to:

Write your JSON file.


Check that your manifest file is connected to your JSON file.
Associate your functions' id and name properties in the script file in order to
register your functions.

The following image explains the differences between using yo office scaffold files and
writing JSON from scratch.
7 Note

Remember to connect your manifest to the JSON file you create, through the
<Resources> section in your XML manifest file if you do not use the Yeoman
generator for Office Add-ins.

Authoring metadata and connecting to the


manifest
Create a JSON file in your project and provide all the details about your functions in it,
such as the function's parameters. See the following metadata example and the
metadata reference for a complete list of function properties.

Ensure your XML manifest file references your JSON file in the <Resources> section,
similar to the following example.

JSON

<Resources>
<bt:Urls>
<bt:Url id="JSON-URL"
DefaultValue="https://subdomain.contoso.com/config/customfunctions.json"/>
<bt:Url id="JS-URL"
DefaultValue="https://subdomain.contoso.com/dist/win32/ship/index.win32.bund
le"/>
<bt:Url id="HTML-URL"
DefaultValue="https://subdomain.contoso.com/index.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="namespace" DefaultValue="CONTOSO"/>
</bt:ShortStrings>
</Resources>

JSON metadata example


The following example shows the contents of a JSON metadata file for an add-in that
defines custom functions. The sections that follow this example provide detailed
information about the individual properties within this JSON example.

JSON

{
"allowCustomDataForDataTypeAny": true,
"allowErrorForDataTypeAny": true,
"functions": [
{
"id": "ADD",
"name": "ADD",
"description": "Add two numbers",
"helpUrl": "http://www.contoso.com/help",
"result": {
"type": "number",
"dimensionality": "scalar"
},
"parameters": [
{
"name": "first",
"description": "first number to add",
"type": "number",
"dimensionality": "scalar"
},
{
"name": "second",
"description": "second number to add",
"type": "number",
"dimensionality": "scalar"
}
]
},
{
"id": "GETDAY",
"name": "GETDAY",
"description": "Get the day of the week",
"helpUrl": "http://www.contoso.com/help",
"result": {
"dimensionality": "scalar"
},
"parameters": []
},
{
"id": "INCREMENTVALUE",
"name": "INCREMENTVALUE",
"description": "Count up from zero",
"helpUrl": "http://www.contoso.com/help",
"result": {
"dimensionality": "scalar"
},
"parameters": [
{
"name": "increment",
"description": "the number to be added each time",
"type": "number",
"dimensionality": "scalar"
}
],
"options": {
"stream": true,
"cancelable": true
}
},
{
"id": "SECONDHIGHEST",
"name": "SECONDHIGHEST",
"description": "Get the second highest number from a range",
"helpUrl": "http://www.contoso.com/help",
"result": {
"dimensionality": "scalar"
},
"parameters": [
{
"name": "range",
"description": "the input range",
"type": "number",
"dimensionality": "matrix"
}
]
}
]
}

7 Note

A complete sample JSON file is available in the OfficeDev/Excel-Custom-


Functions GitHub repository's commit history. As the project has been adjusted
to automatically generate JSON, a full sample of handwritten JSON is only available
in previous versions of the project.

Metadata reference

allowCustomDataForDataTypeAny
The allowCustomDataForDataTypeAny property is a boolean data type. Setting this value
to true allows a custom function to accept data types as parameters and return values.
To learn more, see Custom functions and data types.

7 Note

Unlike most of the other JSON metadata properties,


allowCustomDataForDataTypeAny is a top-level property and contains no sub-
properties. See the preceding JSON metadata code sample for an example of how
to format this property.

allowErrorForDataTypeAny
The allowErrorForDataTypeAny property is a boolean data type. Setting the value to
true allows a custom function to process errors as input values. All parameters with the
type any or any[][] can accept errors as input values when allowErrorForDataTypeAny
is set to true . The default allowErrorForDataTypeAny value is false .

7 Note

Unlike the other JSON metadata properties, allowErrorForDataTypeAny is a top-


level property and contains no sub-properties. See the preceding JSON metadata
code sample for an example of how to format this property.

functions
The functions property is an array of custom function objects. The following table lists
the properties of each object.
Property Data Required Description
type

description string No The description of the function that end users see in Excel.
For example, Converts a Celsius value to Fahrenheit.

helpUrl string No URL that provides information about the function. (It is
displayed in a task pane.) For example,
http://contoso.com/help/convertcelsiustofahrenheit.html .

id string Yes A unique ID for the function. This ID can only contain
alphanumeric characters and periods and should not be
changed after it is set.

name string Yes The name of the function that end users see in Excel. In Excel,
this function name is prefixed by the custom functions
namespace that's specified in the XML manifest file.

options object No Enables you to customize some aspects of how and when
Excel executes the function. See options for details.

parameters array Yes Array that defines the input parameters for the function. See
parameters for details.

result object Yes Object that defines the type of information that is returned
by the function. See result for details.

options
The options object enables you to customize some aspects of how and when Excel
executes the function. The following table lists the properties of the options object.

Property Data Required Description


type

cancelable boolean No If true , Excel calls the


CancelableInvocation handler whenever
Default the user takes an action that has the effect
value is of canceling the function; for example,
false . manually triggering recalculation or editing
a cell that is referenced by the function.
Cancelable functions are typically only used
for asynchronous functions that return a
single result and need to handle the
cancellation of a request for data. A
function can't use both the stream and
cancelable properties.
Property Data Required Description
type

requiresAddress boolean No If true , your custom function can access


the address of the cell that invoked it. The
Default address property of the invocation
value is parameter contains the address of the cell
false . that invoked your custom function. A
function can't use both the stream and
requiresAddress properties.

requiresParameterAddresses boolean No If true , your custom function can access


the addresses of the function's input
Default parameters. This property must be used in
value is combination with the dimensionality
false . property of the result object, and
dimensionality must be set to matrix . See
Detect the address of a parameter for more
information.

stream boolean No If true , the function can output repeatedly


to the cell even when invoked only once.
Default This option is useful for rapidly-changing
value is data sources, such as a stock price. The
false . function should have no return statement.
Instead, the result value is passed as the
argument of the
StreamingInvocation.setResult callback
function. For more information, see Make a
streaming function.

volatile boolean No If true , the function recalculates each time


Excel recalculates, instead of only when the
Default formula's dependent values have changed.
value is A function can't use both the stream and
false . volatile properties. If the stream and
volatile properties are both set to true ,
the volatile property will be ignored.

parameters
The parameters property is an array of parameter objects. The following table lists the
properties of each object.

Property Data Required Description


type
Property Data Required Description
type

description string No A description of the parameter. This is displayed in


Excel's IntelliSense.

dimensionality string No Must be either scalar (a non-array value) or matrix (a


2-dimensional array).

name string Yes The name of the parameter. This name is displayed in
Excel's IntelliSense.

type string No The data type of the parameter. Can be boolean ,


number , string , or any , which allows you to use of any
of the previous three types. If this property is not
specified, the data type defaults to any .

optional boolean No If true , the parameter is optional.

repeating boolean No If true , parameters populate from a specified array.


Note that functions all repeating parameters are
considered optional parameters by definition.

result
The result object defines the type of information that is returned by the function. The
following table lists the properties of the result object.

Property Data Required Description


type

dimensionality string No Must be either scalar (a non-array value) or matrix (a 2-


dimensional array).

type string No The data type of the result. Can be boolean , number ,
string , or any (which allows you to use of any of the
previous three types). If this property is not specified, the
data type defaults to any .

Associating function names with JSON


metadata
For a function to work properly, you need to associate the function's id property with
the JavaScript implementation. Make sure there is an association, otherwise the function
won't be registered and isn't useable in Excel. The following code sample shows how to
make the association using the CustomFunctions.associate() function. The sample
defines the custom function add and associates it with the object in the JSON metadata
file where the value of the id property is ADD.

JavaScript

/**
* Add two numbers
* @customfunction
* @param {number} first First number
* @param {number} second Second number
* @returns {number} The sum of the two numbers.
*/
function add(first, second) {
return first + second;
}

CustomFunctions.associate("ADD", add);

The following JSON shows the JSON metadata that is associated with the previous
custom function JavaScript code.

JSON

{
"functions": [
{
"description": "Add two numbers",
"id": "ADD",
"name": "ADD",
"parameters": [
{
"description": "First number",
"name": "first",
"type": "number"
},
{
"description": "Second number",
"name": "second",
"type": "number"
}
],
"result": {
"type": "number"
}
}
]
}
Keep in mind the following best practices when creating custom functions in your
JavaScript file and specifying corresponding information in the JSON metadata file.

In the JSON metadata file, ensure that the value of each id property contains only
alphanumeric characters and periods.

In the JSON metadata file, ensure that the value of each id property is unique
within the scope of the file. That is, no two function objects in the metadata file
should have the same id value.

Do not change the value of an id property in the JSON metadata file after it's
been associated with a corresponding JavaScript function name. You can change
the function name that end users see in Excel by updating the name property
within the JSON metadata file, but you should never change the value of an id
property after it's been established.

In the JavaScript file, specify a custom function association using


CustomFunctions.associate after each function.

The following sample shows the JSON metadata that corresponds to the functions
defined in the preceding JavaScript code sample. The id and name property values are
in uppercase, which is a best practice when describing your custom functions. You only
need to add this JSON if you are preparing your own JSON file manually and not using
autogeneration. For more information on autogeneration, see Autogenerate JSON
metadata for custom functions.

JSON

{
"$schema": "https://developer.microsoft.com/json-schemas/office-js/custom-
functions.schema.json",
"functions": [
{
"id": "ADD",
"name": "ADD",
...
},
{
"id": "INCREMENT",
"name": "INCREMENT",
...
}
]
}
Next steps
Learn the best practices for naming your function or discover how to localize your
function using the previously described handwritten JSON method.

See also
Autogenerate JSON metadata for custom functions
Custom functions parameter options
Create custom functions in Excel
Return multiple results from your
custom function
Article • 12/08/2022

You can return multiple results from your custom function which will be returned to
neighboring cells. This behavior is called spilling. When your custom function returns an
array of results, it's known as a dynamic array formula. For more information on dynamic
array formulas in Excel, see Dynamic arrays and spilled array behavior .

The following image shows how the SORT function spills down into neighboring cells.
Your custom function can also return multiple results like this.

To create a custom function that is a dynamic array formula, it must return a two-
dimensional array of values. If the results spill into neighboring cells that already have
values, the formula will display a #SPILL! error.

Code samples
The first example shows how to return a dynamic array that spills down.

JavaScript

/**
* Get text values that spill down.
* @customfunction
* @returns {string[][]} A dynamic array with multiple results.
*/
function spillDown() {
return [['first'], ['second'], ['third']];
}

The second example shows how to return a dynamic array that spills right.

JavaScript

/**
* Get text values that spill to the right.
* @customfunction
* @returns {string[][]} A dynamic array with multiple results.
*/
function spillRight() {
return [['first', 'second', 'third']];
}

The third example shows how to return a dynamic array that spills both down and right.

JavaScript

/**
* Get text values that spill both right and down.
* @customfunction
* @returns {string[][]} A dynamic array with multiple results.
*/
function spillRectangle() {
return [
['apples', 1, 'pounds'],
['oranges', 3, 'pounds'],
['pears', 5, 'crates']
];
}

The fourth example shows how to return a dynamic spill array from a streaming
function. The results spill down, like the first example, and increment once a second
based on the amount parameter. To learn more about streaming functions, see Make a
streaming function.

JavaScript

/**
* Increment the cells with a given amount every second. Creates a dynamic
spilled array with multiple results
* @customfunction
* @param {number} amount The amount to add to the cell value on each
increment.
* @param {CustomFunctions.StreamingInvocation<number[][]>} invocation
Parameter to send results to Excel or respond to the user canceling the
function. A dynamic array.
*/
function increment(amount: number, invocation:
CustomFunctions.StreamingInvocation<number[][]>): void {
let firstResult = 0;
let secondResult = 1;
let thirdResult = 2;

const timer = setInterval(() => {


firstResult += amount;
secondResult += amount;
thirdResult += amount;
invocation.setResult([[firstResult], [secondResult], [thirdResult]]);
}, 1000);

invocation.onCanceled = () => {
clearInterval(timer);
};
}

See also
Dynamic arrays and spilled array behavior
Options for Excel custom functions
Authentication for custom functions
without a shared runtime
Article • 08/23/2022

In some scenarios, a custom function that doesn't use a shared runtime will need to
authenticate the user in order to access protected resources. Custom functions that
don't use a shared runtime run in a JavaScript-only runtime. Because of this, if the add-
in has a task pane, you'll need to pass data back and forth between the JavaScript-only
runtime and the HTML-supporting runtime used by the task pane. You do this by using
the OfficeRuntime.storage object and a special Dialog API.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

7 Note

We recommend using custom functions with a shared runtime, unless you have a
specific reason not to use a shared runtime. Note that using a shared runtime
means your add-in will use WebView2 (Microsoft Edge Chromium-based) if
conditions are met, and otherwise your add-in will use Trident (Internet Explorer 11)
regardless of the Windows or Microsoft 365 version. For a description of the
WebView2 conditions, see Browsers and webview controls used by Office Add-ins.
For more information about runtimes, see Runtimes in Office Add-ins and
Runtimes.
OfficeRuntime.storage object
The JavaScript-only runtime doesn't have a localStorage object available on the global
window, where you typically store data. Instead, your code should share data between
custom functions and task panes by using OfficeRuntime.storage to set and get data.

Suggested usage
When you need to authenticate from a custom function add-in that doesn't use a
shared runtime, your code should check OfficeRuntime.storage to see if the access
token was already acquired. If not, use OfficeRuntime.displayWebDialog to authenticate
the user, retrieve the access token, and then store the token in OfficeRuntime.storage
for future use.

Dialog API
If a token doesn't exist, you should use the OfficeRuntime.dialog API to ask the user to
sign in. After a user enters their credentials, the resulting access token can be stored as
an item in OfficeRuntime.storage .

7 Note

The JavaScript-only runtime uses a dialog object that is slightly different from the
dialog object in the browser runtime used by task panes. They're both referred to
as the "Dialog API", but use OfficeRuntime.displayWebDialog to authenticate users
in the JavaScript-only runtime, not Office.ui.displayDialogAsync.

The following diagram outlines this basic process. The dotted line indicates that custom
functions and your add-in's task pane are both part of your add-in as a whole, though
they use separate runtimes.

1. You issue a custom function call from a cell in an Excel workbook.


2. The custom function uses OfficeRuntime.dialog to pass your user credentials to a
website.
3. This website then returns an access token to the custom function.
4. Your custom function then sets this access token to an item in the
OfficeRuntime.storage .

5. Your add-in's task pane accesses the token from OfficeRuntime.storage .


Storing the token
The following examples are from the Using OfficeRuntime.storage in custom functions
code sample. Refer to this code sample for a complete example of sharing data between
custom functions and the task pane in add-ins that don't use a shared runtime.

If the custom function authenticates, then it receives the access token and will need to
store it in OfficeRuntime.storage . The following code sample shows how to call the
storage.setItem method to store a value. The storeValue function is a custom function

that stores a value from the user. You can modify this to store any token value you need.

JavaScript

/**
* Stores a key-value pair into OfficeRuntime.storage.
* @customfunction
* @param {string} key Key of item to put into storage.
* @param {*} value Value of item to put into storage.
*/
function storeValue(key, value) {
return OfficeRuntime.storage.setItem(key, value).then(function (result) {
return "Success: Item with key '" + key + "' saved to storage.";
}, function (error) {
return "Error: Unable to save item with key '" + key + "' to storage.
" + error;
});
}
When the task pane needs the access token, it can retrieve the token from the
OfficeRuntime.storage item. The following code sample shows how to use the
storage.getItem method to retrieve the token.

JavaScript

/**
* Read a token from storage.
* @customfunction GETTOKEN
*/
function receiveTokenFromCustomFunction() {
const key = "token";
const tokenSendStatus = document.getElementById('tokenSendStatus');
OfficeRuntime.storage.getItem(key).then(function (result) {
tokenSendStatus.value = "Success: Item with key '" + key + "' read from
storage.";
document.getElementById('tokenTextBox2').value = result;
}, function (error) {
tokenSendStatus.value = "Error: Unable to read item with key '" + key +
"' from storage. " + error;
});
}

General guidance
Office Add-ins are web-based and you can use any web authentication technique. There
is no particular pattern or method you must follow to implement your own
authentication with custom functions. You may wish to consult the documentation
about various authentication patterns, starting with this article about authorizing via
external services.

Avoid using the following locations to store data when developing custom functions:

localStorage : custom functions that don't use a shared runtime don't have access
to the global window object and therefore have no access to data stored in
localStorage .
Office.context.document.settings : This location isn't secure and information can

be extracted by anyone using the add-in.

Dialog box API example


In the following code sample, the function getTokenViaDialog uses the
OfficeRuntime.displayWebDialog function to display a dialog box. This sample is
provided to show the capabilities of the method, not demonstrate how to authenticate.
JavaScript

/**
* Function retrieves a cached token or opens a dialog box if there is no
saved token. Note that this isn't a sufficient example of authentication but
is intended to show the capabilities of the displayWebDialog method.
* @param {string} url URL for a stored token.
*/
function getTokenViaDialog(url) {
return new Promise (function (resolve, reject) {
if (_dialogOpen) {
// Can only have one dialog box open at once. Wait for previous dialog
box's token.
let timeout = 5;
let count = 0;
const intervalId = setInterval(function () {
count++;
if(_cachedToken) {
resolve(_cachedToken);
clearInterval(intervalId);
}
if(count >= timeout) {
reject("Timeout while waiting for token");
clearInterval(intervalId);
}
}, 1000);
} else {
_dialogOpen = true;
OfficeRuntime.displayWebDialog(url, {
height: '50%',
width: '50%',
onMessage: function (message, dialog) {
_cachedToken = message;
resolve(message);
dialog.close();
return;
},
onRuntimeError: function(error, dialog) {
reject(error);
},
}).catch(function (e) {
reject(e);
});
}
});
}

Next steps
Learn how to debug custom functions.
See also
JavaScript-only runtime for custom functions
Excel custom functions tutorial
JavaScript-only runtime
JavaScript-only runtime for custom
functions
Article • 08/23/2022

Custom functions that don't use a shared runtime use a JavaScript-only runtime that is
designed to optimize performance of calculations.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

7 Note

We recommend using custom functions with a shared runtime, unless you have a
specific reason not to use a shared runtime. Note that using a shared runtime
means your add-in will use WebView2 (Microsoft Edge Chromium-based) if
conditions are met, and otherwise your add-in will use Trident (Internet Explorer 11)
regardless of the Windows or Microsoft 365 version. For a description of the
WebView2 conditions, see Browsers and webview controls used by Office Add-ins.
For more information about runtimes, see Runtimes in Office Add-ins and
Runtimes.

This JavaScript-only runtime provides access to APIs in the OfficeRuntime namespace


that can be used by custom functions and the task pane (which runs in a different
runtime) to store data.
Request external data
Within a custom function, you can request external data by using an API like Fetch or
by using XmlHttpRequest (XHR) , a standard web API that issues HTTP requests to
interact with servers.

Be aware that custom functions must use additional security measures when making
XmlHttpRequests, requiring Same Origin Policy and simple CORS .

A simple CORS implementation cannot use cookies and only supports simple methods
(GET, HEAD, POST). Simple CORS accepts simple headers with field names Accept ,
Accept-Language , Content-Language . You can also use a Content-Type header in simple
CORS, provided that the content type is application/x-www-form-urlencoded ,
text/plain , or multipart/form-data .

Store and access data


Within a custom function that doesn't use a shared runtime, you can store and access
data by using the OfficeRuntime.storage object. The Storage object is a persistent,
unencrypted, key-value storage system that provides an alternative to localStorage ,
which cannot be used by custom functions that use the JavaScript-only runtime. The
Storage object offers 10 MB of data per domain. Domains can be shared by more than

one add-in.

The Storage object is a shared storage solution, meaning multiple parts of an add-in are
able to access the same data. For example, tokens for user authentication may be stored
in the Storage object because it can be accessed by both a custom function (using the
JavaScript-only runtime) and a task pane (using a full webview runtime). Similarly, if two
add-ins share the same domain (for example, www.contoso.com/addin1 ,
www.contoso.com/addin2 ), they are also permitted to share information back and forth

through the Storage object. Note that add-ins which have different subdomains will
have different instances of Storage (for example, subdomain.contoso.com/addin1 ,
differentsubdomain.contoso.com/addin2 ).

Because the Storage object can be a shared location, it is important to realize that it is
possible to override key-value pairs.

The following methods are available on the Storage object.

getItem
getItems
setItem

setItems
removeItem

removeItems
getKeys

7 Note

There's no method for clearing all information (such as clear ). Instead, you should
instead use removeItems to remove multiple entries at a time.

OfficeRuntime.storage example
The following code sample calls the OfficeRuntime.storage.setItem method to set a key
and value into storage .

JavaScript

function StoreValue(key, value) {

return OfficeRuntime.storage.setItem(key, value).then(function (result) {


return "Success: Item with key '" + key + "' saved to storage.";
}, function (error) {
return "Error: Unable to save item with key '" + key + "' to storage.
" + error;
});
}

Next steps
Learn how to debug custom functions.

See also
Authentication for custom functions without a shared runtime
Create custom functions in Excel
Custom functions tutorial
JavaScript-only runtime
Custom functions debugging
Article • 03/09/2023

This article discusses debugging only for custom functions that don't use a shared
runtime. To debug custom functions add-ins that use a shared runtime, see Configure
your Office Add-in to use a shared runtime: Debug.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

7 Note

We recommend using custom functions with a shared runtime, unless you have a
specific reason not to use a shared runtime. Note that using a shared runtime
means your add-in will use WebView2 (Microsoft Edge Chromium-based) if
conditions are met, and otherwise your add-in will use Trident (Internet Explorer 11)
regardless of the Windows or Microsoft 365 version. For a description of the
WebView2 conditions, see Browsers and webview controls used by Office Add-ins.
For more information about runtimes, see Runtimes in Office Add-ins and
Runtimes.

 Tip

The debugging techniques that are described in this article don't work with projects
that are created with the Office Add-in project containing the manifest only
option in the Yeoman generator. The scripts that are referred to later in this article
aren't installed with that option. To debug an add-in that is created with this
option, see the instructions in one of the following articles, as appropriate.

Debug add-ins using developer tools in Microsoft Edge (Chromium-based)


Debug add-ins using developer tools in Internet Explorer
Debug Office Add-ins on a Mac

The process of debugging a custom function for add-ins that don't use a shared runtime
varies depending on the target platform (Windows, Mac, or web) and on whether you
are using Visual Studio Code or a different IDE. Use the links in the following table to
visit sections of this article that are relevant to your debugging scenario. In this table,
"CF-NSR" refers to custom functions in a non-shared runtime.

Target Visual Studio Code Other IDE


platform

Excel on Use VS Code and the browser Debugging CF-NSR that are running in
Windows development tools Excel on Windows outside VS Code isn't
supported. Debug against Excel on the
web.

Excel on Use VS Code and the browser Use the command line tools
the web development tools

Excel on VS Code debugging of CF-NSR that are Use the command line tools
Mac running in Excel on Mac isn't
supported. Debug against Excel on the
web.

Use the browser developer tools to debug


custom functions in Excel on the web
You can use the browser developer tools to debug custom functions that don't use a
shared runtime in Excel on the web. The following steps work for both Windows and
macOS.

Run your add-in from Visual Studio Code


1. Open your custom functions root project folder in Visual Studio Code (VS Code) .
2. Choose Terminal > Run Task and type or select Watch. This will monitor and
rebuild for any file changes.
3. Choose Terminal > Run Task and type or select Dev Server.
Sideload your add-in
1. Open Office on the web .

2. Open a new Excel workbook.

3. Open the Insert tab on the ribbon and, in the Add-ins section, choose Office Add-
ins.

4. On the Office Add-ins dialog, select the MY ADD-INS tab, choose Manage My
Add-ins, and then Upload My Add-in.

5. Browse to the add-in manifest file, and then select Upload.

7 Note
Once you've sideloaded to the document, it will remain sideloaded each time you
open the document.

Start debugging
1. Open developer tools in the browser. For Chrome and most browsers F12 will open
the developer tools.
2. In developer tools, open your source code script file using Cmd+P or Ctrl+P
(functions.js or functions.ts).
3. Set a breakpoint in the custom function source code.

If you need to change the code you can make edits in VS Code and save the changes.
Refresh the browser to see the changes loaded.

Use the command line tools to debug


If you aren't using VS Code, you can use the command line (such as bash, or PowerShell)
to run your add-in. You'll need to use the browser developer tools to debug your code
in Excel on the web. You cannot debug the desktop version of Excel using the command
line.

1. From the command line run npm run watch to watch for and rebuild when code
changes occur.

2. Open a second command line window (the first one will be blocked while running
the watch.)

3. If you want to start your add-in in the desktop version of Excel, run the following
command.

npm run start:desktop

Or if you prefer to start your add-in in Excel on the web run the following
command.

npm run start:web -- --document {url} (where {url} is the URL of an Excel file on

OneDrive or SharePoint)

7 Note

If you are developing on a Mac, enclose the {url} in single quotation marks.
Do not do this on Windows.
If your add-in doesn't sideload in the document, follow the steps in Sideload your
add-in to sideload your add-in. Then continue to the next section to start
debugging.

4. Open developer tools in the browser. For Chrome and most browsers F12 will open
the developer tools.

5. In developer tools, open your source code script file (functions.js or functions.ts).
Your custom functions code may be located near the end of the file.

6. In the custom function source code, apply a breakpoint by selecting a line of code.

If you need to change the code you can make edits in Visual Studio and save the
changes. Refresh the browser to see the changes loaded.

Commands for building and running your add-in


There are several build tasks available.

npm run watch : builds for development and automatically rebuilds when a source
file is saved
npm run build-dev : builds for development once

npm run build : builds for production


npm run dev-server : runs the web server used for development

You can use the following tasks to start debugging on desktop or online.

npm run start:desktop : Starts Excel on desktop and sideloads your add-in.

npm run start:web -- --document {url} (where {url} is the URL of an Excel file on

OneDrive or SharePoint): Starts Excel on the web and sideloads your add-in.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation marks.
Do not do this on Windows.

npm run stop : Stops Excel and debugging.

Next steps
Learn about Authentication for custom functions without a shared runtime.

See also
Custom functions troubleshooting
Error handling for custom functions in Excel
Create custom functions in Excel
JavaScript-only runtime
Volatile values in functions
Article • 03/22/2022

Volatile functions are functions in which the value changes each time the cell is
calculated. The value can change even if none of the function's arguments change.
These functions recalculate every time Excel recalculates. For example, imagine a cell
that calls the function NOW . Every time NOW is called, it will automatically return the
current date and time.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

Excel contains several built-in volatile functions, such as RAND and TODAY . For a
comprehensive list of Excel's volatile functions, see Volatile and Non-Volatile Functions.

Custom functions allow you to create your own volatile functions, which may be useful
when handling dates, times, random numbers, and modeling. For example, Monte Carlo
simulations require the generation of random inputs to determine an optimal
solution.

If choosing to autogenerate your JSON file, declare a volatile function with the JSDoc
comment tag @volatile . From more information on autogeneration, see Autogenerate
JSON metadata for custom functions.

An example of a volatile custom function follows, which simulates rolling a six-sided


dice.
JS

/**
* Simulates rolling a 6-sided dice.
* @customfunction
* @volatile
*/
function roll6sided() {
return Math.floor(Math.random() * 6) + 1;
}

Next steps
Learn about custom functions parameter options.

See also
Manually create JSON metadata for custom functions
Create custom functions in Excel
Receive and handle data with custom
functions
Article • 04/13/2023

One of the ways that custom functions enhances Excel's power is by receiving data from
locations other than the workbook, such as the web or a server (through WebSockets ).
You can request external data through an API like Fetch or by using XmlHttpRequest
(XHR) , a standard web API that issues HTTP requests to interact with servers.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

Functions that return data from external


sources
If a custom function retrieves data from an external source such as the web, it must:
1. Return a JavaScript Promise to Excel.
2. Resolve the Promise with the final value using the callback function.

Fetch example
In the following code sample, the webRequest function reaches out to a hypothetical
external API that tracks the number of people currently on the International Space
Station. The function returns a JavaScript Promise and uses fetch to request
information from the hypothetical API. The resulting data is transformed into JSON and
the names property is converted into a string, which is used to resolve the promise.

When developing your own functions, you may want to perform an action if the web
request does not complete in a timely manner or consider batching up multiple API
requests.

JS

/**
* Requests the names of the people currently on the International Space
Station.
* Note: This function requests data from a hypothetical URL. In practice,
replace the URL with a data source for your scenario.
* @customfunction
*/
function webRequest() {
let url = "https://www.contoso.com/NumberOfPeopleInSpace"; // This is a
hypothetical URL.
return new Promise(function (resolve, reject) {
fetch(url)
.then(function (response){
return response.json();
}
)
.then(function (json) {
resolve(JSON.stringify(json.names));
})
})
}

7 Note

Using fetch avoids nested callbacks and may be preferable to XHR in some cases.

XHR example
In the following code sample, the getStarCount function calls the Github API to discover
the amount of stars given to a particular user's repository. This is an asynchronous
function which returns a JavaScript Promise . When data is obtained from the web call,
the promise is resolved which returns the data to the cell.

TS

/**
* Gets the star count for a given Github organization or user and
repository.
* @customfunction
* @param userName string name of organization or user.
* @param repoName string name of the repository.
* @return number of stars.
*/
async function getStarCount(userName: string, repoName: string) {

const url = "https://api.github.com/repos/" + userName + "/" + repoName;

let xhttp = new XMLHttpRequest();

return new Promise(function(resolve, reject) {


xhttp.onreadystatechange = function() {
if (xhttp.readyState !== 4) return;

if (xhttp.status == 200) {
resolve(JSON.parse(xhttp.responseText).watchers_count);
} else {
reject({
status: xhttp.status,

statusText: xhttp.statusText
});
}
};

xhttp.open("GET", url, true);

xhttp.send();
});
}

Make a streaming function


Streaming custom functions enable you to output data to cells that updates repeatedly,
without requiring a user to explicitly refresh anything. This can be useful to check live
data from a service online, like the function in the custom functions tutorial.

To declare a streaming function, you can use either of the following two options.
The @streaming JSDoc tag.
The CustomFunctions.StreamingInvocation invocation parameter.

The following code sample is a custom function that adds a number to the result every
second. Note the following about this code.

Excel displays each new value automatically using the setResult method.
The second input parameter, invocation , is not displayed to end users in Excel
when they select the function from the autocomplete menu.
The onCanceled callback defines the function that runs when the function is
canceled.
Streaming isn't necessarily tied to making a web request. In this case, the function
isn't making a web request but is still getting data at set intervals, so it requires the
use of the streaming invocation parameter.

JS

/**
* Increments a value once a second.
* @customfunction INC increment
* @param {number} incrementBy Amount to increment
* @param {CustomFunctions.StreamingInvocation<number>} invocation
*/
function increment(incrementBy, invocation) {
let result = 0;
const timer = setInterval(() => {
result += incrementBy;
invocation.setResult(result);
}, 1000);

invocation.onCanceled = () => {
clearInterval(timer);
};
}

7 Note

For an example of how to return a dynamic spill array from a streaming function,
see Return multiple results from your custom function: Code samples.

Cancel a function
Excel cancels the execution of a function in the following situations.
When the user edits or deletes a cell that references the function.
When one of the arguments (inputs) for the function changes. In this case, a new
function call is triggered following the cancellation.
When the user triggers recalculation manually. In this case, a new function call is
triggered following the cancellation.

You can also consider setting a default streaming value to handle cases when a request
is made but you are offline.

7 Note

There is also a category of functions called cancelable functions which use the
@cancelable JSDoc tag. Cancelable functions allow a web request to be terminated

in the middle of the request.

A streaming function can't use the @cancelable tag, but streaming functions can
include an onCanceled callback function. Only asynchronous custom functions
which return one value can use the @cancelable JSDoc tag. See Autogenerate
JSON metadata: @cancelable to learn more about the @cancelable tag.

Use an invocation parameter


The invocation parameter is the last parameter of any custom function by default. The
invocation parameter gives context about the cell (such as its address and contents)

and allows you to use the setResult method and onCanceled event to define what a
function does when it streams ( setResult ) or is canceled ( onCanceled ).

The invocation handler needs to be of type CustomFunctions.StreamingInvocation or


CustomFunctions.CancelableInvocation to process web requests.

See Invocation parameter to learn about other potential uses of the invocation
argument and how it corresponds with the Invocation object.

Receiving data via WebSockets


Within a custom function, you can use WebSockets to exchange data over a persistent
connection with a server. Using WebSockets, your custom function can open a
connection with a server and then automatically receive messages from the server when
certain events occur, without having to explicitly poll the server for data.
WebSockets example
The following code sample establishes a WebSocket connection and then logs each
incoming message from the server.

JavaScript

let ws = new WebSocket('wss://bundles.office.com');

ws.onmessage(message) {
console.log(`Received: ${message}`);
}

ws.onerror(error){
console.err(`Failed: ${error}`);
}

Next steps
Learn about different parameter types your functions can use.
Discover how to batch multiple API calls.

See also
Volatile values in functions
Create JSON metadata for custom functions
Manually create JSON metadata for custom functions
Create custom functions in Excel
Excel custom functions tutorial
Troubleshoot custom functions
Article • 03/09/2023

When developing custom functions, you may encounter errors in the product while
creating and testing your functions.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

To resolve issues, you can enable runtime logging to capture errors and refer to Excel's
native error messages. Also, check for common mistakes such as leaving promises
unresolved.

Debugging custom functions


To debug custom functions add-ins that use a shared runtime, see Configure your Office
Add-in to use a shared runtime: Debug.

To debug custom functions add-ins that don't use a shared runtime, see Custom
functions debugging.

Enable runtime logging


If you're testing your add-in in Office on Windows, you should enable runtime logging.
Runtime logging delivers console.log statements to a separate log file you create to
help you uncover issues. The statements cover a variety of errors, including errors
pertaining to your add-in's XML manifest file, runtime conditions, or installation of your
custom functions. For more information about runtime logging, see Debug your add-in
with runtime logging.

Check for Excel error messages


Excel has a number of built-in error messages which are returned to a cell if there is
calculation error. Custom functions only use the following error messages: #NULL! ,
#DIV/0! , #VALUE! , #REF! , #NAME? , #NUM! , #N/A , and #BUSY! .

Generally, these errors correspond to the errors you might already be familiar with in
Excel. The are only a few exceptions specific to custom functions, listed here:

A #NAME error generally means there has been an issue registering your functions.
A #N/A error is also maybe a sign that that function while registered could not be
run. This is typically due to a missing CustomFunctions.associate command.
A #VALUE error typically indicates an error in the functions' script file.
A #REF! error may indicate that your function name is the same as a function
name in an add-in that already exists.

Clear the Office cache


Information about custom functions is cached by Office. Sometimes while developing
and repeatedly reloading an add-in with custom functions your changes may not
appear. You can fix this by clearing the Office cache. For more information, see Clear the
Office cache.

Common problems and solutions

Can't open add-in from localhost: Use a local loopback


exemption
If you see the error "We can't open this add-in from localhost," you will need to enable a
local loopback exemption. For details on how to do this, see this Microsoft support
article.

Runtime logging reports "TypeError: Network request


failed" on Excel on Windows
If you see the error "TypeError: Network request failed" in your runtime log while
making calls to your localhost server, you'll need to enable a local loopback exception.
For details on how to do this, see Option #2 in this Microsoft support article.

Ensure promises return


When Excel is waiting for a custom function to complete, it displays #BUSY! in the cell. If
your custom function code returns a promise, but the promise does not return a result,
Excel will continue showing #BUSY! . Check your functions to make sure that any
promises are properly returning a result to a cell.

Error: The dev server is already running on port 3000


Sometimes when running npm start you may see an error that the dev server is already
running on port 3000 (or whichever port your add-in uses). You can stop the dev server
by running npm stop or by closing the Node.js window. In some cases, it can take a few
minutes for the dev server to stop running.

My functions won't load: associate functions


In cases where your JSON has not been registered and you have authored your own
JSON metadata, you may see a #VALUE! error or receive a notification that your add-in
cannot be loaded. This usually means you need to associate each custom function with
its id property specified in the JSON metadata file. This is done by using the
CustomFunctions.associate() function. Typically this function call is made after each
function or at the end of the script file. If a custom function is not associated, it will not
work.

The following example shows an add function, followed by the function's name add
being associated with the corresponding JSON id ADD .

JavaScript

/**
* Add two numbers.
* @customfunction
* @param {number} first First number.
* @param {number} second Second number.
* @returns {number} The sum of the two numbers.
*/
function add(first, second) {
return first + second;
}
CustomFunctions.associate("ADD", add);

For more information on this process, see Associating function names with JSON
metadata.

Known issues
Known issues are tracked and reported in the Excel Custom Functions GitHub
repository .

Reporting feedback
If you are encountering issues that aren't documented here, let us know. There are two
ways to report issues.

In Excel on Windows or Mac


If using Excel on Windows or Mac, you can report feedback to the Office extensibility
team directly from Excel. To do this, select File -> Feedback -> Send a Frown. Sending a
frown will provide the necessary logs to understand the issue you are hitting.

In Github
Feel free to submit an issue you encounter either through the "Content feedback"
feature at the bottom of any documentation page, or by filing a new issue directly to the
custom functions repository .

Next steps
Learn how to make your custom functions compatible with XLL user-defined functions.

See also
Autogenerate JSON metadata for custom functions
Create custom functions in Excel
Custom functions debugging
OneNote add-ins documentation
With OneNote add-ins, you can use familiar web technologies such as HTML, CSS, and
JavaScript to build a solution that can run in OneNote on the web. Learn how to build,
test, debug, and publish OneNote add-ins.

About OneNote add-ins

e OVERVIEW

What are OneNote add-ins?

OneNote JavaScript API Overview

Microsoft Graph OneNote REST API Overview

f QUICKSTART

Build your first OneNote add-in

c HOW-TO GUIDE

Use the OneNote JavaScript API to interact with objects and notebook content

Test and debug a OneNote add-in

Deploy and publish a OneNote add-in

Key Office Add-ins concepts

e OVERVIEW

Office Add-ins platform overview

p CONCEPT

Core concepts for Office Add-ins

Design Office Add-ins

Develop Office Add-ins

Reso rces
Resources

i REFERENCE

Ask questions

Request features

Report issues

Office Add-ins additional resources


OneNote JavaScript API programming
overview
Article • 05/20/2023

OneNote introduces a JavaScript API for OneNote add-ins on the web. You can create
task pane add-ins, content add-ins, and add-in commands that interact with OneNote
objects and connect to web services or other web-based resources.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).

Components of an Office Add-in


Add-ins consist of two basic components:

A web application consisting of a webpage and any required JavaScript, CSS, or


other files. These files are hosted on a web server or web hosting service, such as
Microsoft Azure. In OneNote on the web, the web application displays in a
webview control or iframe.

An XML manifest that specifies the URL of the add-in's webpage and any access
requirements, settings, and capabilities for the add-in. This file is stored on the
client. OneNote add-ins use the same manifest format as other Office Add-ins.

Office Add-in = Manifest + Webpage


Using the JavaScript API
Add-ins use the runtime context of the Office application to access the JavaScript API.
The API has two layers:

A application-specific API for OneNote-specific operations, accessed through the


Application object.

A Common API that's shared across Office applications, accessed through the
Document object.

Accessing the application-specific API through the


Application object
Use the Application object to access OneNote objects such as Notebook, Section, and
Page. With application-specific APIs, you run batch operations on proxy objects. The
basic flow goes something like this:

1. Get the application instance from the context.

2. Create a proxy that represents the OneNote object you want to work with. You
interact synchronously with proxy objects by reading and writing their properties
and calling their methods.

3. Call load on the proxy to fill it with the property values specified in the parameter.
This call is added to the queue of commands.
7 Note

Method calls to the API (such as


context.application.getActiveSection().pages; ) are also added to the

queue.

4. Call context.sync to run all queued commands in the order that they were
queued. This synchronizes the state between your running script and the real
objects, and by retrieving properties of loaded OneNote objects for use in your
script. You can use the returned promise object for chaining additional actions.

For example:

JavaScript

async function getPagesInSection() {


await OneNote.run(async (context) => {

// Get the pages in the current section.


const pages = context.application.getActiveSection().pages;

// Queue a command to load the id and title for each page.


pages.load('id,title');

// Run the queued commands, and return a promise to indicate task


completion.
await context.sync();

// Read the id and title of each page.


$.each(pages.items, function(index, page) {
let pageId = page.id;
let pageTitle = page.title;
console.log(pageTitle + ': ' + pageId);
});
});
}

See Using the application-specific API model to learn more about the load / sync
pattern and other common practices in the OneNote JavaScript APIs.

You can find supported OneNote objects and operations in the API reference.

OneNote JavaScript API requirement sets


Requirement sets are named groups of API members. Office Add-ins use requirement
sets specified in the manifest or use a runtime check to determine whether an Office
application supports APIs that an add-in needs. For detailed information about OneNote
JavaScript API requirement sets, see OneNote JavaScript API requirement sets.

Accessing the Common API through the Document object


Use the Document object to access the Common API, such as the getSelectedDataAsync
and setSelectedDataAsync methods.

For example:

JavaScript

function getSelectionFromPage() {
Office.context.document.getSelectedDataAsync(
Office.CoercionType.Text,
{ valueFormat: "unformatted" },
function (asyncResult) {
const error = asyncResult.error;
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(error.message);
}
else $('#input').val(asyncResult.value);
});
}

OneNote add-ins support only the following Common APIs.

API Notes

Office.context.document.getSelectedDataAsync Office.CoercionType.Text and


Office.CoercionType.Matrix only

Office.context.document.setSelectedDataAsync Office.CoercionType.Text ,
Office.CoercionType.Image , and
Office.CoercionType.Html only

const mySetting = Settings are supported by content add-ins only


Office.context.document.settings.get(name);

Office.context.document.settings.set(name, Settings are supported by content add-ins only


value);

Office.EventType.DocumentSelectionChanged None.

In general, you use the Common API to do something that isn't supported in the
application-specific API. To learn more about using the Common API, see Common
JavaScript API object model.
OneNote object model diagram
The following diagram represents what's currently available in the OneNote JavaScript
API.

See also
Developing Office Add-ins
Learn about Microsoft 365 Developer Program
Build your first OneNote add-in
OneNote JavaScript API reference
Rubric Grader sample
Office Add-ins platform overview
Build your first OneNote task pane add-
in
Article • 05/02/2023

In this article, you'll walk through the process of building a OneNote task pane add-in.

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Create the add-in project


Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note
When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? OneNote

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample code
for a very basic task pane add-in.

The ./manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in
the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code that
facilitates interaction between the task pane and the Office client application.

Update the code


In your code editor, open the file ./src/taskpane/taskpane.js and add the following code
within the run function. This code uses the OneNote JavaScript API to set the page title
and add an outline to the body of the page.

JavaScript

try {
await OneNote.run(async (context) => {

// Get the current page.


const page = context.application.getActivePage();

// Queue a command to set the page title.


page.title = "Hello World";

// Queue a command to add an outline to the page.


const html = "<p><ol><li>Item #1</li><li>Item #2</li></ol></p>";
page.addOutline(40, 90, html);

// Run the queued commands.


await context.sync();
});
} catch (error) {
console.log("Error: " + error);
}

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Start the local web server. Run the following command in the root directory of your
project.
command line

npm run dev-server

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

3. In OneNote on the web , open a notebook and create a new page.

4. Choose Insert > Office Add-ins to open the Office Add-ins dialog.

If you're signed in with your consumer account, select the MY ADD-INS tab,
and then choose Upload My Add-in.

If you're signed in with your work or education account, select the MY


ORGANIZATION tab, and then select Upload My Add-in.

The following image shows the MY ADD-INS tab for consumer notebooks.

5. In the Upload Add-in dialog, browse to manifest.xml in your project folder, and
then choose Upload.

6. From the Home tab, choose the Show Taskpane button on the ribbon. The add-in
task pane opens in an iFrame next to the OneNote page.

7. At the bottom of the task pane, choose the Run link to set the page title and add
an outline to the body of the page.
Next steps
Congratulations, you've successfully created a OneNote task pane add-in! Next, learn
more about the core concepts of building OneNote add-ins.

OneNote JavaScript API programming overview

See also
Office Add-ins platform overview
Develop Office Add-ins
OneNote JavaScript API programming overview
OneNote JavaScript API reference
Rubric Grader sample
Using Visual Studio Code to publish
OneNote JavaScript API overview
Article • 05/02/2023

A OneNote add-in interacts with objects in OneNote on the web by using the Office
JavaScript API, which includes two JavaScript object models:

OneNote JavaScript API: These are the application-specific APIs for OneNote.
Introduced with Office 2016, the OneNote JavaScript API provides strongly-typed
objects that you can use to access objects in OneNote on the web.

Common APIs: Introduced with Office 2013, the Common API can be used to
access features such as UI, dialogs, and client settings that are common across
multiple types of Office applications.

This section of the documentation focuses on the OneNote JavaScript API, which you'll
use to develop the majority of functionality in add-ins that target OneNote on the web.
For information about the Common API, see Common JavaScript API object model.

Learn programming concepts


See the following articles for information about important programming concepts.

OneNote JavaScript API programming overview


Work with OneNote page content

Learn about API capabilities


For hands-on experience using the OneNote JavaScript API to interact with content in
OneNote on the web, complete the OneNote add-in quick start.

For detailed information about the OneNote JavaScript API object model, see the
OneNote JavaScript API reference documentation.

See also
OneNote add-ins documentation
OneNote add-ins overview
OneNote JavaScript API reference
Office client application and platform availability for Office Add-ins
onenote package
Reference

Classes
OneNote.Application Represents the top-level object that contains all globally
addressable OneNote objects such as notebooks, the active
notebook, and the active section.

OneNote.FloatingInk Represents a group of ink strokes.

OneNote.Image Represents an Image. An Image can be a direct child of a


PageContent object or a Paragraph object.

OneNote.InkAnalysis Represents ink analysis data for a given set of ink strokes.

OneNote.InkAnalysisLine Represents ink analysis data for an identified text line formed by
ink strokes.

OneNote.InkAnalysisLine Represents a collection of InkAnalysisLine objects.


Collection

OneNote.InkAnalysis Represents ink analysis data for an identified paragraph formed


Paragraph by ink strokes.

OneNote.InkAnalysis Represents a collection of InkAnalysisParagraph objects.


ParagraphCollection

OneNote.InkAnalysisWord Represents ink analysis data for an identified word formed by ink
strokes.

OneNote.InkAnalysisWord Represents a collection of InkAnalysisWord objects.


Collection

OneNote.InkStroke Represents a single stroke of ink.

OneNote.InkStrokeCollection Represents a collection of InkStroke objects.

OneNote.InkWord A container for the ink in a word in a paragraph.

OneNote.InkWordCollection Represents a collection of InkWord objects.

OneNote.Notebook Represents a OneNote notebook. Notebooks contain section


groups and sections.

OneNote.NotebookCollection Represents a collection of notebooks.

OneNote.NoteTag A container for the NoteTag in a paragraph.


OneNote.Outline Represents a container for Paragraph objects.

OneNote.Page Represents a OneNote page.

OneNote.PageCollection Represents a collection of pages.

OneNote.PageContent Represents a region on a page that contains top-level content


types such as Outline or Image. A PageContent object can be
assigned an XY position.

OneNote.PageContent Represents the contents of a page, as a collection of


Collection PageContent objects.

OneNote.Paragraph A container for the visible content on a page. A Paragraph can


contain any one ParagraphType type of content.

OneNote.ParagraphCollection Represents a collection of Paragraph objects.

OneNote.Point Represents a single point of ink stroke

OneNote.PointCollection Represents a collection of Point objects.

OneNote.RequestContext

OneNote.RichText Represents a RichText object in a Paragraph.

OneNote.Section Represents a OneNote section. Sections can contain pages.

OneNote.SectionCollection Represents a collection of sections.

OneNote.SectionGroup Represents a OneNote section group. Section groups can


contain sections and other section groups.

OneNote.SectionGroup Represents a collection of section groups.


Collection

OneNote.Table Represents a table in a OneNote page.

OneNote.TableCell Represents a cell in a OneNote table.

OneNote.TableCellCollection Contains a collection of TableCell objects.

OneNote.TableRow Represents a row in a table.

OneNote.TableRowCollection Contains a collection of TableRow objects.

Interfaces
OneNote.ImageOcrData Represents data obtained by OCR (optical character recognition)
of an image.
OneNote.InkStrokePointer Weak reference to an ink stroke object and its content parent.

OneNote.Interfaces. An interface describing the data returned by calling


ApplicationData application.toJSON() .

OneNote.Interfaces. Represents the top-level object that contains all globally


ApplicationLoadOptions addressable OneNote objects such as notebooks, the active
notebook, and the active section.

OneNote.Interfaces. An interface for updating data on the Application object, for use
ApplicationUpdateData in application.set({ ... }) .

OneNote.Interfaces.Collection Provides ways to load properties of only a subset of members of


LoadOptions a collection.

OneNote.Interfaces.Floating An interface describing the data returned by calling


InkData floatingInk.toJSON() .

OneNote.Interfaces.Floating Represents a group of ink strokes.


InkLoadOptions

OneNote.Interfaces.Image An interface describing the data returned by calling


Data image.toJSON() .

OneNote.Interfaces.Image Represents an Image. An Image can be a direct child of a


LoadOptions PageContent object or a Paragraph object.

OneNote.Interfaces.Image An interface for updating data on the Image object, for use in
UpdateData image.set({ ... }) .

OneNote.Interfaces.Ink An interface describing the data returned by calling


AnalysisData inkAnalysis.toJSON() .

OneNote.Interfaces.Ink An interface describing the data returned by calling


AnalysisLineCollectionData inkAnalysisLineCollection.toJSON() .

OneNote.Interfaces.Ink Represents a collection of InkAnalysisLine objects.


AnalysisLineCollectionLoad
Options

OneNote.Interfaces.Ink An interface for updating data on the InkAnalysisLineCollection


AnalysisLineCollectionUpdate object, for use in inkAnalysisLineCollection.set({ ... }) .
Data

OneNote.Interfaces.Ink An interface describing the data returned by calling


AnalysisLineData inkAnalysisLine.toJSON() .

OneNote.Interfaces.Ink Represents ink analysis data for an identified text line formed by
AnalysisLineLoadOptions ink strokes.

OneNote.Interfaces.Ink An interface for updating data on the InkAnalysisLine object, for


AnalysisLineUpdateData
use in inkAnalysisLine.set({ ... }) .

OneNote.Interfaces.Ink Represents ink analysis data for a given set of ink strokes.
AnalysisLoadOptions

OneNote.Interfaces.Ink An interface describing the data returned by calling


AnalysisParagraphCollection inkAnalysisParagraphCollection.toJSON() .
Data

OneNote.Interfaces.Ink Represents a collection of InkAnalysisParagraph objects.


AnalysisParagraphCollection
LoadOptions

OneNote.Interfaces.Ink An interface for updating data on the


AnalysisParagraphCollection InkAnalysisParagraphCollection object, for use in
UpdateData inkAnalysisParagraphCollection.set({ ... }) .

OneNote.Interfaces.Ink An interface describing the data returned by calling


AnalysisParagraphData inkAnalysisParagraph.toJSON() .

OneNote.Interfaces.Ink Represents ink analysis data for an identified paragraph formed


AnalysisParagraphLoad by ink strokes.
Options

OneNote.Interfaces.Ink An interface for updating data on the InkAnalysisParagraph


AnalysisParagraphUpdateData object, for use in inkAnalysisParagraph.set({ ... }) .

OneNote.Interfaces.Ink An interface for updating data on the InkAnalysis object, for use
AnalysisUpdateData in inkAnalysis.set({ ... }) .

OneNote.Interfaces.Ink An interface describing the data returned by calling


AnalysisWordCollectionData inkAnalysisWordCollection.toJSON() .

OneNote.Interfaces.Ink Represents a collection of InkAnalysisWord objects.


AnalysisWordCollectionLoad
Options

OneNote.Interfaces.Ink An interface for updating data on the InkAnalysisWordCollection


AnalysisWordCollection object, for use in inkAnalysisWordCollection.set({ ... }) .
UpdateData

OneNote.Interfaces.Ink An interface describing the data returned by calling


AnalysisWordData inkAnalysisWord.toJSON() .

OneNote.Interfaces.Ink Represents ink analysis data for an identified word formed by ink
AnalysisWordLoadOptions strokes.

OneNote.Interfaces.Ink An interface for updating data on the InkAnalysisWord object,


AnalysisWordUpdateData for use in inkAnalysisWord.set({ ... }) .

OneNote.Interfaces.InkStroke An interface describing the data returned by calling


CollectionData
inkStrokeCollection.toJSON() .

OneNote.Interfaces.InkStroke Represents a collection of InkStroke objects.


CollectionLoadOptions

OneNote.Interfaces.InkStroke An interface for updating data on the InkStrokeCollection object,


CollectionUpdateData for use in inkStrokeCollection.set({ ... }) .

OneNote.Interfaces.InkStroke An interface describing the data returned by calling


Data inkStroke.toJSON() .

OneNote.Interfaces.InkStroke Represents a single stroke of ink.


LoadOptions

OneNote.Interfaces.InkWord An interface describing the data returned by calling


CollectionData inkWordCollection.toJSON() .

OneNote.Interfaces.InkWord Represents a collection of InkWord objects.


CollectionLoadOptions

OneNote.Interfaces.InkWord An interface for updating data on the InkWordCollection object,


CollectionUpdateData for use in inkWordCollection.set({ ... }) .

OneNote.Interfaces.InkWord An interface describing the data returned by calling


Data inkWord.toJSON() .

OneNote.Interfaces.InkWord A container for the ink in a word in a paragraph.


LoadOptions

OneNote.Interfaces.Notebook An interface describing the data returned by calling


CollectionData notebookCollection.toJSON() .

OneNote.Interfaces.Notebook Represents a collection of notebooks.


CollectionLoadOptions

OneNote.Interfaces.Notebook An interface for updating data on the NotebookCollection


CollectionUpdateData object, for use in notebookCollection.set({ ... }) .

OneNote.Interfaces.Notebook An interface describing the data returned by calling


Data notebook.toJSON() .

OneNote.Interfaces.Notebook Represents a OneNote notebook. Notebooks contain section


LoadOptions groups and sections.

OneNote.Interfaces.NoteTag An interface describing the data returned by calling


Data noteTag.toJSON() .

OneNote.Interfaces.NoteTag A container for the NoteTag in a paragraph.


LoadOptions

OneNote.Interfaces.Outline An interface describing the data returned by calling


Data outline.toJSON() .
OneNote.Interfaces.Outline Represents a container for Paragraph objects.
LoadOptions

OneNote.Interfaces.Page An interface describing the data returned by calling


CollectionData pageCollection.toJSON() .

OneNote.Interfaces.Page Represents a collection of pages.


CollectionLoadOptions

OneNote.Interfaces.Page An interface for updating data on the PageCollection object, for


CollectionUpdateData use in pageCollection.set({ ... }) .

OneNote.Interfaces.Page An interface describing the data returned by calling


ContentCollectionData pageContentCollection.toJSON() .

OneNote.Interfaces.Page Represents the contents of a page, as a collection of


ContentCollectionLoad PageContent objects.
Options

OneNote.Interfaces.Page An interface for updating data on the PageContentCollection


ContentCollectionUpdateData object, for use in pageContentCollection.set({ ... }) .

OneNote.Interfaces.Page An interface describing the data returned by calling


ContentData pageContent.toJSON() .

OneNote.Interfaces.Page Represents a region on a page that contains top-level content


ContentLoadOptions types such as Outline or Image. A PageContent object can be
assigned an XY position.

OneNote.Interfaces.Page An interface for updating data on the PageContent object, for


ContentUpdateData use in pageContent.set({ ... }) .

OneNote.Interfaces.PageData An interface describing the data returned by calling


page.toJSON() .

OneNote.Interfaces.PageLoad Represents a OneNote page.


Options

OneNote.Interfaces.Page An interface for updating data on the Page object, for use in
UpdateData page.set({ ... }) .

OneNote.Interfaces.Paragraph An interface describing the data returned by calling


CollectionData paragraphCollection.toJSON() .

OneNote.Interfaces.Paragraph Represents a collection of Paragraph objects.


CollectionLoadOptions

OneNote.Interfaces.Paragraph An interface for updating data on the ParagraphCollection


CollectionUpdateData object, for use in paragraphCollection.set({ ... }) .

OneNote.Interfaces.Paragraph An interface describing the data returned by calling


Data
paragraph.toJSON() .

OneNote.Interfaces.Paragraph A container for the visible content on a page. A Paragraph can


LoadOptions contain any one ParagraphType type of content.

OneNote.Interfaces.Paragraph An interface for updating data on the Paragraph object, for use
UpdateData in paragraph.set({ ... }) .

OneNote.Interfaces.Point An interface describing the data returned by calling


CollectionData pointCollection.toJSON() .

OneNote.Interfaces.Point Represents a collection of Point objects.


CollectionLoadOptions

OneNote.Interfaces.Point An interface for updating data on the PointCollection object, for


CollectionUpdateData use in pointCollection.set({ ... }) .

OneNote.Interfaces.PointData An interface describing the data returned by calling


point.toJSON() .

OneNote.Interfaces.PointLoad Represents a single point of ink stroke


Options

OneNote.Interfaces.RichText An interface describing the data returned by calling


Data richText.toJSON() .

OneNote.Interfaces.RichText Represents a RichText object in a Paragraph.


LoadOptions

OneNote.Interfaces.Section An interface describing the data returned by calling


CollectionData sectionCollection.toJSON() .

OneNote.Interfaces.Section Represents a collection of sections.


CollectionLoadOptions

OneNote.Interfaces.Section An interface for updating data on the SectionCollection object,


CollectionUpdateData for use in sectionCollection.set({ ... }) .

OneNote.Interfaces.Section An interface describing the data returned by calling


Data section.toJSON() .

OneNote.Interfaces.Section An interface describing the data returned by calling


GroupCollectionData sectionGroupCollection.toJSON() .

OneNote.Interfaces.Section Represents a collection of section groups.


GroupCollectionLoadOptions

OneNote.Interfaces.Section An interface for updating data on the SectionGroupCollection


GroupCollectionUpdateData object, for use in sectionGroupCollection.set({ ... }) .

OneNote.Interfaces.Section An interface describing the data returned by calling


GroupData sectionGroup.toJSON() .
OneNote.Interfaces.Section Represents a OneNote section group. Section groups can
GroupLoadOptions contain sections and other section groups.

OneNote.Interfaces.Section Represents a OneNote section. Sections can contain pages.


LoadOptions

OneNote.Interfaces.TableCell An interface describing the data returned by calling


CollectionData tableCellCollection.toJSON() .

OneNote.Interfaces.TableCell Contains a collection of TableCell objects.


CollectionLoadOptions

OneNote.Interfaces.TableCell An interface for updating data on the TableCellCollection object,


CollectionUpdateData for use in tableCellCollection.set({ ... }) .

OneNote.Interfaces.TableCell An interface describing the data returned by calling


Data tableCell.toJSON() .

OneNote.Interfaces.TableCell Represents a cell in a OneNote table.


LoadOptions

OneNote.Interfaces.TableCell An interface for updating data on the TableCell object, for use in
UpdateData tableCell.set({ ... }) .

OneNote.Interfaces.TableData An interface describing the data returned by calling


table.toJSON() .

OneNote.Interfaces.TableLoad Represents a table in a OneNote page.


Options

OneNote.Interfaces.TableRow An interface describing the data returned by calling


CollectionData tableRowCollection.toJSON() .

OneNote.Interfaces.TableRow Contains a collection of TableRow objects.


CollectionLoadOptions

OneNote.Interfaces.TableRow An interface for updating data on the TableRowCollection object,


CollectionUpdateData for use in tableRowCollection.set({ ... }) .

OneNote.Interfaces.TableRow An interface describing the data returned by calling


Data tableRow.toJSON() .

OneNote.Interfaces.TableRow Represents a row in a table.


LoadOptions

OneNote.Interfaces.Table An interface for updating data on the Table object, for use in
UpdateData table.set({ ... }) .

OneNote.ParagraphInfo List information for paragraph.


Enums
OneNote.ErrorCodes

OneNote.EventType

OneNote.InsertLocation

OneNote.ListType

OneNote.NoteTagStatus

OneNote.NoteTagType

OneNote.NumberType

OneNote.PageContentType

OneNote.ParagraphStyle

OneNote.ParagraphType

Functions
OneNote.run(batch) Executes a batch script that performs actions on the OneNote
object model, using a new request context. When the promise is
resolved, any tracked objects that were automatically allocated
during execution will be released.

OneNote.run(object, batch) Executes a batch script that performs actions on the OneNote
object model, using the request context of a previously-created
API object.

OneNote.run(objects, batch) Executes a batch script that performs actions on the OneNote
object model, using the request context of previously-created
API objects.

Function Details

OneNote.run(batch)
Executes a batch script that performs actions on the OneNote object model, using a
new request context. When the promise is resolved, any tracked objects that were
automatically allocated during execution will be released.
TypeScript

export function run<T>(batch: (context: OneNote.RequestContext) =>


Promise<T>): Promise<T>;

Parameters
batch (context: OneNote.RequestContext) => Promise<T>
A function that takes in an OneNote.RequestContext and returns a promise (typically,
just the result of "context.sync()"). The context parameter facilitates requests to the
OneNote application. Since the Office add-in and the OneNote application run in
two different processes, the request context is required to get access to the OneNote
object model from the add-in.

Returns
Promise<T>

OneNote.run(object, batch)
Executes a batch script that performs actions on the OneNote object model, using
the request context of a previously-created API object.

TypeScript

export function run<T>(object: OfficeExtension.ClientObject, batch:


(context: OneNote.RequestContext) => Promise<T>): Promise<T>;

Parameters
object OfficeExtension.ClientObject
A previously-created API object. The batch will use the same request context as the
passed-in object, which means that any changes applied to the object will be picked
up by "context.sync()".

batch (context: OneNote.RequestContext) => Promise<T>


A function that takes in an OneNote.RequestContext and returns a promise (typically,
just the result of "context.sync()"). When the promise is resolved, any tracked objects
that were automatically allocated during execution will be released.

Returns
Promise<T>

OneNote.run(objects, batch)
Executes a batch script that performs actions on the OneNote object model, using
the request context of previously-created API objects.

TypeScript

export function run<T>(objects: OfficeExtension.ClientObject[], batch:


(context: OneNote.RequestContext) => Promise<T>): Promise<T>;

Parameters
objects OfficeExtension.ClientObject[]

batch (context: OneNote.RequestContext) => Promise<T>


A function that takes in an OneNote.RequestContext and returns a promise (typically,
just the result of "context.sync()"). When the promise is resolved, any tracked objects
that were automatically allocated during execution will be released.

Returns
Promise<T>
Work with OneNote page content
Article • 03/22/2022

In the OneNote add-ins JavaScript API, page content is represented by the following
object model.

A Page object contains a collection of PageContent objects.


A PageContent object contains a content type of Outline, Image, or Other.
An Outline object contains a collection of Paragraph objects.
A Paragraph object contains a content type of RichText, Image, Table, or Other.

To create an empty OneNote page, use one of the following methods.

Section.addPage
Page.insertPageAsSibling

Then use methods in the following objects to work with the page content, such as
Page.addOutline and Outline.appendHtml .

Page
Outline
Paragraph

The content and structure of a OneNote page are represented by HTML. Only a subset
of HTML is supported for creating or updating page content, as described below.

Supported HTML
The OneNote add-in JavaScript API supports the following HTML for creating and
updating page content.

<html> , <body> , <div> , <span> , <br/>

<p>
<img>

<a>

<ul> , <ol> , <li>


<table> , <tr> , <td>

<h1> ... <h6>


<b> , <em> , <strong> , <i> , <u> , <del> , <sup> , <sub> , <cite>

7 Note

Importing HTML into OneNote consolidates whitespace. The resulting content is


pasted into one outline.

OneNote does its best to translate HTML into page content while ensuring security for
users. HTML and CSS standards do not exactly match OneNote's content model, so
there will be differences in appearances, particularly with CSS stylings. We recommend
using the JavaScript objects if specific formatting is needed.

Accessing page contents


You are only able to access Page Content via Page#load for the currently active page. To
change the active page, invoke navigateToPage($page) .

Metadata such as title can still be queried for any page.

See also
OneNote JavaScript API programming overview
OneNote JavaScript API reference
Rubric Grader sample
Office Add-ins platform overview
Outlook add-ins documentation
With Outlook add-ins, you can use familiar web technologies such as HTML, CSS, and
JavaScript to build a solution that can run in Outlook across multiple platforms,
including on the web, Windows, Mac, and iOS. Learn how to build, test, debug, and
publish Outlook add-ins.

About Outlook add-ins

e OVERVIEW

What are Outlook add-ins?

f QUICKSTART

Build your first Outlook add-in

Build your first Outlook add-in with a unified manifest for Microsoft 365 (preview)

Explore Office JavaScript API using Script Lab

c HOW-TO GUIDE

Use the Outlook JavaScript API to interact with objects

Navigate and use the Mailbox requirement sets

Build an event-based add-in

Use Microsoft Graph to extend your add-in's capabilities

Test and debug an Outlook add-in

Deploy and publish an Outlook add-in

Key Office Add-ins concepts

e OVERVIEW

Office Add-ins platform overview

p CONCEPT

Core concepts for Office Add-ins


Design Office Add-ins

Develop Office Add-ins

Resources

i REFERENCE

Ask questions

Request features

Report issues

Office Add-ins additional resources


Outlook add-ins overview
Article • 05/20/2023

Outlook add-ins are integrations built by third parties into Outlook by using our web-
based platform. Outlook add-ins have three key aspects:

The same add-in and business logic works across desktop (Outlook on Windows
and Mac), web (Microsoft 365 and Outlook.com), and mobile.
Outlook add-ins consist of a manifest, which describes how the add-in integrates
into Outlook (for example, a button or a task pane), and JavaScript/HTML code,
which makes up the UI and business logic of the add-in.
Outlook add-ins can be acquired from AppSource or sideloaded by end-users or
administrators.

Outlook add-ins are different from COM or VSTO add-ins, which are older integrations
specific to Outlook running on Windows. Unlike COM add-ins, Outlook add-ins don't
have any code physically installed on the user's device or Outlook client. For an Outlook
add-in, Outlook reads the manifest and hooks up the specified controls in the UI, and
then loads the JavaScript and HTML. The web components all run in the context of a
browser or webview control in a sandbox.

) Important

COM and VSTO add-ins aren't supported in the new Outlook on Windows that's
currently in preview. These add-ins are still supported in the classic Outlook on
Windows desktop client. To learn more, see Develop Outlook add-ins for new
Outlook on Windows (preview).

The Outlook items that support add-ins include email messages, meeting requests,
responses and cancellations, and appointments. Each Outlook add-in defines the
context in which it is available, including the types of items and if the user is reading or
composing an item.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).
Extension points
Extension points are the ways that add-ins integrate with Outlook. The following are the
ways this can be done.

Add-ins can declare buttons that appear in command surfaces across messages
and appointments. For more information, see Add-in commands.

An add-in with command buttons on the ribbon

Add-ins can link off regular expression matches or detected entities in messages
and appointments. For more information, see Contextual Outlook add-ins.

A contextual add-in for a highlighted entity (an address)


Mailbox items available to add-ins
Outlook add-ins activate when the user is composing or reading a message or
appointment, but not other item types. However, add-ins are not activated if the current
message item, in a compose or read form, is one of the following:

Protected by Information Rights Management (IRM) or encrypted in other ways for


protection and accessed from Outlook on non-Windows clients. A digitally signed
message is an example since digital signing relies on one of these mechanisms.

) Important

Add-ins activate on digitally signed messages in Outlook on Windows


associated with a Microsoft 365 subscription. This support was introduced
with build 8711.1000.

Starting with Outlook build 13229.10000 on Windows, add-ins can now


activate on IRM-protected items. To turn on this capability, a tenant
administrator must enable the OBJMODEL usage right by setting the Allow
programmatic access custom policy option in Office. For further guidance,
see Usage rights and descriptions.
A delivery report or notification that has the message class IPM.Report.*, including
delivery and Non-Delivery Report (NDR) reports, and read, non-read, and delay
notifications.

A .msg or .eml file which is an attachment to another message.

A .msg or .eml file opened from the file system.

In a group mailbox, in a shared mailbox*, in another user's mailbox*, in an archive


mailbox, or in a public folder.

) Important

* Support for delegate access scenarios (for example, folders shared from
another user's mailbox) was introduced in requirement set 1.8, while shared
mailbox support was introduced in requirement set 1.13. To learn more, see
Enable shared folders and shared mailbox scenarios.

Using a custom form.

Created through Simple MAPI. Simple MAPI is used when an Office user creates or
sends an email from an Office application on Windows while Outlook is closed. For
example, a user can create an Outlook email while working in Word which triggers
an Outlook compose window without launching the full Outlook application. If,
however, Outlook is already running when the user creates the email from Word,
that isn't a Simple MAPI scenario so Outlook add-ins work in the compose form as
long as other activation requirements are met.

In general, Outlook can activate add-ins in read form for items in the Sent Items folder,
with the exception of add-ins that activate based on string matches of well-known
entities. For more information about the reasons behind this, see Support for well-
known entities.

Currently, there are additional considerations when designing and implementing add-
ins for mobile clients. To learn more, see Add support for add-in commands in Outlook
on mobile devices.

Supported clients
Outlook add-ins are supported in Outlook on Windows, Outlook on Mac, Outlook on
the web, Outlook on iOS, Outlook on Android, and Outlook.com. Not all of the newest
features are supported in all clients at the same time. Please refer to articles and API
references for those features to see which applications they may or may not be
supported in.

Get started building Outlook add-ins


To get started building Outlook add-ins, try the following:

Quick start - Build a simple task pane.


Tutorial - Learn how to create an add-in that inserts GitHub gists into a new
message.

See also
Learn about the Microsoft 365 Developer Program
Best practices for developing Office Add-ins
Design guidelines for Office Add-ins
License your Office and SharePoint Add-ins
Publish your Office Add-in
Make your solutions available in AppSource and within Office
Build your first Outlook add-in
Article • 03/28/2023

In this article, you'll walk through the process of building an Outlook task pane add-in
that displays at least one property of a selected message.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Visual Studio Code (VS Code) or your preferred code editor

Outlook 2016 or later on Windows (connected to a Microsoft 365 account) or


Outlook on the web

Create the add-in project


1. Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about
the data collection policies of Yeoman and the Office Add-in CLI tools.
Use the information that's provided to respond to the prompts as you
see fit.

When prompted, provide the following information to create your add-in


project.

Choose a project type - Office Add-in Task Pane project

Choose a script type - JavaScript

What do you want to name your add-in? - My Office Add-in

Which Office client application would you like to support? - Outlook


After you complete the wizard, the generator will create the project and install
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator
provides after the add-in project's been created. The step-by-step
instructions within this article provide all of the guidance you'll need to
complete this tutorial.

2. Navigate to the root folder of the web application project.

command line

cd "My Office Add-in"

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a very basic task pane add-in.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and Outlook.
Update the code
1. Open your project in VS Code or your preferred code editor.

 Tip

On Windows, you can navigate to the root directory of the project via the
command line and then enter code . to open that folder in VS Code. On
Mac, you'll need to add the code command to the path before you
can use that command to open the project folder in VS Code.

2. Open the file ./src/taskpane/taskpane.html and replace the entire <main>


element (within the <body> element) with the following markup. This new
markup adds a label where the script in ./src/taskpane/taskpane.js will write
data.

HTML

<main id="app-body" class="ms-welcome__main" style="display:


none;">
<h2 class="ms-font-xl"> Discover what Office Add-ins can do for
you today! </h2>
<p><label id="item-subject"></label></p>
<div role="button" id="run" class="ms-welcome__action ms-Button
ms-Button--hero ms-font-xl">
<span class="ms-Button-label">Run</span>
</div>
</main>

3. In your code editor, open the file ./src/taskpane/taskpane.js, then add the
following code to the run function. This code uses the Office JavaScript API to
get a reference to the current message and write its subject property value to
the task pane.

JavaScript

// Get a reference to the current message


const item = Office.context.mailbox.item;

// Write message property value to the task pane


document.getElementById("item-subject").innerHTML = "<b>Subject:
</b> <br/>" + item.subject;

Your taskpane.js file should now contain the following code.


JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT license.
* See LICENSE in the project root for license information.
*/

/* global document, Office */

Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
document.getElementById("sideload-msg").style.display = "none";
document.getElementById("app-body").style.display = "flex";
document.getElementById("run").onclick = run;
}
});

export async function run() {


// Get a reference to the current message
const item = Office.context.mailbox.item;

// Write message property value to the task pane


document.getElementById("item-subject").innerHTML = "<b>Subject:
</b> <br/>" + item.subject;
}

Try it out

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

1. Run the following command in the root directory of your project. When you
run this command, the local web server starts and your add-in will be
sideloaded.

command line

npm start
2. In Outlook, view a message in the Reading Pane , or open the message in its
own window.

3. Choose the Home tab (or the Message tab if you opened the message in a
new window), and then choose the Show Taskpane button on the ribbon to
open the add-in task pane.

7 Note

If you receive the error "We can't open this add-in from localhost" in the
task pane, follow the steps outlined in the troubleshooting article.

4. When prompted with the WebView Stop On Load dialog box, select OK.

7 Note

If you select Cancel, the dialog won't be shown again while this instance
of the add-in is running. However, if you restart your add-in, you'll see
the dialog again.

5. Scroll to the bottom of the task pane and choose the Run link to write the
message subject to the task pane.
Next steps
Congratulations, you've successfully created your first Outlook task pane add-in!
Next, learn more about the capabilities of an Outlook add-in and build a more
complex add-in by following along with the Outlook add-in tutorial.

See also
Using Visual Studio Code to publish
Build an Outlook add-in with the unified
manifest for Microsoft 365 (preview)
Article • 08/10/2023

There are two tools that you can use to create an Outlook Add-in project that uses the
unified manifest for Microsoft 365. This article describes how to do it with the Yeoman
generator for Office (also called "Yo Office"). Alternatively, you can create an Outlook
add-in project with the Teams Toolkit as described at Create Office Add-in projects with
Teams Toolkit (preview).

In this article, you'll walk through the process of building an Outlook task pane add-in
that displays a property of a selected message, triggers a notification on the reading
pane, and inserts text into a message on the compose pane. This add-in will use a
preview version of the unified, JSON-formatted manifest that Teams extensions, like
custom tabs and messaging extensions, use. For more information about this manifest,
see Unified manifest for Microsoft 365 (preview).

7 Note

The new manifest is available for preview and is supported only on Outlook for
Windows. It is subject to change based on feedback. We encourage experienced
add-in developers to experiment with it. The preview manifest should not be used
in production add-ins.

The preview is only supported in Office downloaded from a Microsoft 365 subscription
and installed on Windows.

 Tip

If you want to build an Outlook add-in using the XML manifest, see Build your first
Outlook add-in.

Create the add-in


You can create an Office Add-in with the unified manifest by using the Yeoman
generator for Office Add-ins. The Yeoman generator creates a Node.js project that can
be managed with Visual Studio Code or any other editor.
Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

.NET runtime for Windows. One of the tools used in the preview runs on .NET.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Visual Studio Code (VS Code) or your preferred code editor

Outlook on Windows (connected to a Microsoft 365 account)

Create the add-in project


1. Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note
When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type - Outlook Add-in with unified Microsoft 365


Manifest (preview)

What do you want to name your add-in? - Add-in with Unified Manifest

7 Note

For this preview, the add-in name cannot be more than 30 characters.

After you complete the wizard, the generator will create the project and install
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

2. Navigate to the root folder of the web application project.

command line

cd "Add-in with Unified Manifest"

Explore the project


The add-in project that you've created with the Yeoman generator contains sample code
for a very basic task pane add-in.

The ./manifest/manifest.json file in the root directory of the project defines the
settings and capabilities of the add-in.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in
the task pane.
The ./src/taskpane/taskpane.ts file contains code that calls the Office JavaScript
library to facilitate interaction between the task pane and Outlook.
The ./src/command/command.html file will be edited by WebPack at build time to
insert an HTML <script> tag that loads the JavaScript file that is transpiled from
the command.ts file.
The ./src/command/command.ts file has little code in it at first. Later in this article,
you'll add code to it that calls the Office JavaScript library and that executes when
a custom ribbon button is selected.

Update the code


1. Open your project in VS Code or your preferred code editor.

 Tip

On Windows, you can navigate to the root directory of the project via the
command line and then enter code . to open that folder in VS Code.

2. Open the file ./src/taskpane/taskpane.html and replace the entire <main>


element (within the <body> element) with the following markup. This new markup
adds a label where the script in ./src/taskpane/taskpane.ts will write data.

HTML

<main id="app-body" class="ms-welcome__main" style="display: none;">


<h2 class="ms-font-xl"> Discover what Office Add-ins can do for you
today! </h2>
<p><label id="item-subject"></label></p>
<div role="button" id="run" class="ms-welcome__action ms-Button ms-
Button--hero ms-font-xl">
<span class="ms-Button-label">Run</span>
</div>
</main>
Try it out

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If you're
prompted to install a certificate after you run one of the following commands,
accept the prompt to install the certificate that the Yeoman generator provides. You
may also have to run your command prompt or terminal as an administrator for the
changes to be made.

1. Open a command prompt as an administrator and run the following command in


the root directory of your project. When you run this command, the local web
server starts and your add-in will be sideloaded.

command line

npm start

7 Note

If this is the first time you've created an add-in on the computer, or the first
time in over a month, you'll be prompted to install security certificates.

2. Use the classic ribbon in Outlook. The remainder of these instructions assume this.

3. View a message in the Reading Pane , or open the message in its own window. A
new control group named Contoso Add-in appears on the Outlook Home tab (or
the Message tab if you opened the message in a new window). The group has a
button named Show Taskpane and one named Perform an action.

4. Select the Perform an action button. It executes a command to generate a small


informational notification at the bottom of the message header, just above the
message body.

5. When prompted with the WebView Stop On Load dialog box, select OK.

7 Note
If you select Cancel, the dialog won't be shown again while this instance of
the add-in is running. However, if you restart your add-in, you'll see the dialog
again.

6. To open the add-in task pane, choose Show Taskpane.

7 Note

If you receive the error "We can't open this add-in from localhost" in the task
pane, follow the steps outlined in the troubleshooting article.

7. When prompted with the WebView Stop On Load dialog box, select OK.

7 Note

If you select Cancel, the dialog won't be shown again while this instance of
the add-in is running. However, if you restart your add-in, you'll see the dialog
again.

8. Scroll to the bottom of the task pane and choose the Run link to copy the
message's subject to the task pane.

9. End the debugging session with the following command:

command line

npm stop

) Important

Closing the web server window doesn't reliably shut down the web server. If it
isn't properly shut down, you'll encounter problems as you change and rerun
the project.

10. Close all instances of Outlook.

Add a custom button to the ribbon


Add a custom button to the ribbon that inserts text into a message body.
1. Open your project in VS Code or your preferred code editor.

 Tip

On Windows, you can navigate to the root directory of the project via the
command line and then enter code . to open that folder in VS Code.

2. In your code editor, open the file ./src/command/command.ts and add the
following code to the end of the file. This function will insert Hello World at the
cursor point in message body.

TypeScript

function insertHelloWorld(event: Office.AddinCommands.Event) {


Office.context.mailbox.item.body.setSelectedDataAsync("Hello
World", {coercionType: Office.CoercionType.Text});

// Be sure to indicate when the add-in command function is complete


event.completed();
}

// Register the function with Office


Office.actions.associate("insertHelloWorld", insertHelloWorld);

3. Open the file ./manifest/manifest.json.

7 Note

When referring to nested JSON properties, this article uses dot notation.
When an item in an array is referenced, the bracketed zero-based number of
the item is used.

4. To write to a message, the add-in's permissions need to be raised. Scroll to the


property authorization.permissions.resourceSpecific[0].name and change the
value to MailboxItem.ReadWrite.User .

5. When an add-in command runs code instead of opening a task pane, it must run
the code in a runtime that is separate from the embedded webview where the task
pane code runs. So the manifest must specify an additional runtime. Scroll to the
property extension.runtimes and add the following object to the runtimes array.
Be sure to put a comma after the object that is already in the array. Note the
following about this markup.
The value of the actions[0].id property must be exactly the same as the
name of the function that you added to the commands.ts file, in this case
insertHelloWorld . In a later step, you'll refer to the item by this ID.

JSON

{
"id": "ComposeCommandsRuntime",
"type": "general",
"code": {
"page": "https://localhost:3000/commands.html",
"script": "https://localhost:3000/commands.js"
},
"lifetime": "short",
"actions": [
{
"id": "insertHelloWorld",
"type": "executeFunction",
"displayName": "insertHelloWorld"
}
]
}

6. The Show Taskpane button appears when the user is reading an email, but the
button for adding text should only appear when the user is composing a new
email (or replying to one). So the manifest must specify a new ribbon object. Scroll
to the property extension.ribbons and add the following object to the ribbons
array. Be sure to put a comma after the object that is already in the array. Note the
following about this JSON:

The only value in the contexts array is "mailCompose", so the button will
appear when in a compose (or reply) window but not in a message read
window where the Show Taskpane and Perform an action buttons appear.
Compare this value with the contexts array in the existing ribbon object,
whose value is ["mailRead"] .
The value of the tabs[0].groups[0].controls[0].actionId must be exactly
the same as the value of actions[0].id property in the runtime object you
created in an earlier step.

JSON

{
"contexts": ["mailCompose"],
"tabs": [
{
"builtInTabId": "TabDefault",
"groups": [
{
"id": "msgWriteGroup",
"label": "Contoso Add-in",
"icons": [
{ "size": 16, "file":
"https://localhost:3000/assets/icon-16.png" },
{ "size": 32, "file":
"https://localhost:3000/assets/icon-32.png" },
{ "size": 80, "file":
"https://localhost:3000/assets/icon-80.png" }
],
"controls": [
{
"id": "HelloWorldButton",
"type": "button",
"label": "Insert text",
"icons": [
{ "size": 16, "file":
"https://localhost:3000/assets/icon-16.png" },
{ "size": 32, "file":
"https://localhost:3000/assets/icon-32.png" },
{ "size": 80, "file":
"https://localhost:3000/assets/icon-80.png" }
],
"supertip": {
"title": "Insert text",
"description": "Inserts some text."
},
"actionId": "insertHelloWorld"
}
]
}
]
}
]
}

Try out the updated add-in


1. Open a command prompt as an administrator and run the following command in
the root directory of your project.

command line

npm start

2. In Outlook, open a new message window (or reply to an existing message). A new
control group named Contoso Add-in will appear on the Outlook Message tab.
The group has a button named Insert text.
3. Put the cursor anywhere in the message body and choose the Insert text button.

4. When prompted with the WebView Stop On Load dialog box, select OK.

7 Note

If you select Cancel, the dialog won't be shown again while this instance of
the add-in is running. However, if you restart your add-in, you'll see the dialog
again.

The phrase "Hello World" will be inserted at the cursor.

5. End the debugging session with the following command:

command line

npm stop

See also
Unified manifest for Microsoft 365 (preview)
Using Visual Studio Code to publish
Tutorial: Build a message compose
Outlook add-in
Article • 03/21/2023

This tutorial teaches you how to build an Outlook add-in that can be used in message
compose mode to insert content into the body of a message.

In this tutorial, you will:

" Create an Outlook add-in project


" Define buttons that will render in the compose message window
" Implement a first-run experience that collects information from the user and fetches
data from an external service
" Implement a UI-less button that invokes a function
" Implement a task pane that inserts content into the body of a message

Prerequisites
Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Visual Studio Code (VS Code) or your preferred code editor

Outlook 2016 or later on Windows (connected to a Microsoft 365 account) or


Outlook on the web

A GitHub account
Setup
The add-in that you'll create in this tutorial will read gists from the user's GitHub
account and add the selected gist to the body of a message. Complete the following
steps to create two new gists that you can use to test the add-in you're going to build.

1. Login to GitHub .

2. Create a new gist .

In the Gist description... field, enter Hello World Markdown.

In the Filename including extension... field, enter test.md.

Add the following markdown to the multiline textbox.

markdown

# Hello World

This is content converted from Markdown!

Here's a JSON sample:

```json
{
"foo": "bar"
}
```

Select the Create public gist button.

3. Create another new gist .

In the Gist description... field, enter Hello World Html.

In the Filename including extension... field, enter test.html.

Add the following markdown to the multiline textbox.

HTML

<html>
<head>
<style>
h1 {
font-family: Calibri;
}
</style>
</head>
<body>
<h1>Hello World!</h1>
<p>This is a test</p>
</body>
</html>

Select the Create public gist button.

Create an Outlook add-in project


1. Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type - Office Add-in Task Pane project

Choose a script type - JavaScript

What do you want to name your add-in? - Git the gist

Which Office client application would you like to support? - Outlook


After you complete the wizard, the generator will create the project and install
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

2. Navigate to the root directory of the project.

command line

cd "Git the gist"

3. This add-in will use the following libraries.

Showdown library to convert Markdown to HTML.


URI.js library to build relative URLs.
jquery library to simplify DOM interactions.

To install these tools for your project, run the following command in the root
directory of the project.

command line

npm install showdown urijs jquery --save

4. Open your project in VS Code or your preferred code editor.


 Tip

On Windows, you can navigate to the root directory of the project via the
command line and then enter code . to open that folder in VS Code. On Mac,
you'll need to add the code command to the path before you can use that
command to open the project folder in VS Code.

Update the manifest


The manifest for an add-in controls how it appears in Outlook. It defines the way the
add-in appears in the add-in list and the buttons that appear on the ribbon, and it sets
the URLs for the HTML and JavaScript files used by the add-in.

Specify basic information


Make the following updates in the manifest.xml file to specify some basic information
about the add-in.

1. Locate the <ProviderName> element and replace the default value with your
company name.

XML

<ProviderName>Contoso</ProviderName>

2. Locate the <Description> element, replace the default value with a description of
the add-in, and save the file.

XML

<Description DefaultValue="Allows users to access their GitHub


gists."/>

Test the generated add-in


Before going any further, let's test the basic add-in that the generator created to
confirm that the project is set up correctly.

7 Note
Office Add-ins should use HTTPS, not HTTP, even while you're developing. If you're
prompted to install a certificate after you run one of the following commands,
accept the prompt to install the certificate that the Yeoman generator provides. You
may also have to run your command prompt or terminal as an administrator for the
changes to be made.

1. Run the following command in the root directory of your project. When you run
this command, the local web server starts and your add-in will be sideloaded.

command line

npm start

2. In Outlook, open an existing message and select the Show Taskpane button.

3. When prompted with the WebView Stop On Load dialog box, select OK.

7 Note

If you select Cancel, the dialog won't be shown again while this instance of
the add-in is running. However, if you restart your add-in, you'll see the dialog
again.

If everything's been set up correctly, the task pane will open and render the add-
in's welcome page.
Define buttons
Now that you've verified the base add-in works, you can customize it to add more
functionality. By default, the manifest only defines buttons for the read message
window. Let's update the manifest to remove the buttons from the read message
window and define two new buttons for the compose message window:

Insert gist: a button that opens a task pane

Insert default gist: a button that invokes a function


Remove the MessageReadCommandSurface extension
point
Open the manifest.xml file and locate the <ExtensionPoint> element with type
MessageReadCommandSurface. Delete this <ExtensionPoint> element (including its
closing tag) to remove the buttons from the read message window.

Add the MessageComposeCommandSurface extension


point
Locate the line in the manifest that reads </DesktopFormFactor> . Immediately before this
line, insert the following XML markup. Note the following about this markup.

The <ExtensionPoint> with xsi:type="MessageComposeCommandSurface" indicates


that you're defining buttons to add to the compose message window.

By using an <OfficeTab> element with id="TabDefault" , you're indicating you


want to add the buttons to the default tab on the ribbon.

The <Group> element defines the grouping for the new buttons, with a label set
by the groupLabel resource.

The first <Control> element contains an <Action> element with


xsi:type="ShowTaskPane" , so this button opens a task pane.

The second <Control> element contains an <Action> element with


xsi:type="ExecuteFunction" , so this button invokes a JavaScript function
contained in the function file.

XML

<!-- Message Compose -->


<ExtensionPoint xsi:type="MessageComposeCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgComposeCmdGroup">
<Label resid="GroupLabel"/>
<Control xsi:type="Button" id="msgComposeInsertGist">
<Label resid="TaskpaneButton.Label"/>
<Supertip>
<Title resid="TaskpaneButton.Title"/>
<Description resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
<Control xsi:type="Button" id="msgComposeInsertDefaultGist">
<Label resid="FunctionButton.Label"/>
<Supertip>
<Title resid="FunctionButton.Title"/>
<Description resid="FunctionButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>insertDefaultGist</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>

Update resources in the manifest


The previous code references labels, tooltips, and URLs that you need to define before
the manifest will be valid. You'll specify this information in the <Resources> section of
the manifest.

1. Locate the <Resources> element in the manifest file and delete the entire element
(including its closing tag).

2. In that same location, add the following markup to replace the <Resources>
element you just removed.

XML

<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Commands.Url"
DefaultValue="https://localhost:3000/commands.html"/>
<bt:Url id="Taskpane.Url"
DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Git the gist"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Insert gist"/>
<bt:String id="TaskpaneButton.Title" DefaultValue="Insert gist"/>
<bt:String id="FunctionButton.Label" DefaultValue="Insert default
gist"/>
<bt:String id="FunctionButton.Title" DefaultValue="Insert default
gist"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Displays a
list of your gists and allows you to insert their contents into the
current message."/>
<bt:String id="FunctionButton.Tooltip" DefaultValue="Inserts the
content of the gist you mark as default into the current message."/>
</bt:LongStrings>
</Resources>

3. Save your changes to the manifest.

Reinstall the add-in


You must reinstall the add-in for the manifest changes to take effect.

1. If the web server is running, close the node command window.

2. Run the following command to start the local web server and automatically
sideload your add-in.

command line

npm start

After you've reinstalled the add-in, you can verify that it installed successfully by
checking for the commands Insert gist and Insert default gist in a compose message
window. Note that nothing will happen if you select either of these items, because you
haven't yet finished building this add-in.

If you're running this add-in in Outlook 2016 or later on Windows, you should see
two new buttons on the ribbon of the compose message window: Insert gist and
Insert default gist.
If you're running this add-in in Outlook on the web, you should see a new button
at the bottom of the compose message window. Select that button to see the
options Insert gist and Insert default gist.
Implement a first-run experience
This add-in needs to be able to read gists from the user's GitHub account and identify
which one the user has chosen as the default gist. In order to achieve these goals, the
add-in must prompt the user to provide their GitHub username and choose a default
gist from their collection of existing gists. Complete the steps in this section to
implement a first-run experience that will display a dialog to collect this information
from the user.

Collect data from the user


Let's start by creating the UI for the dialog itself. Within the ./src folder, create a new
subfolder named settings. In the ./src/settings folder, create a file named dialog.html,
and add the following markup to define a basic form with a text input for a GitHub
username and an empty list for gists that'll be populated via JavaScript.

HTML

<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<title>Settings</title>

<!-- Office JavaScript API -->


<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>

<!-- For more information on Fluent UI, visit


https://developer.microsoft.com/fluentui. -->
<link rel="stylesheet"
href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-
core/9.6.1/css/fabric.min.css"/>

<!-- Template styles -->


<link href="dialog.css" rel="stylesheet" type="text/css" />
</head>

<body class="ms-font-l">
<main>
<section class="ms-font-m ms-fontColor-neutralPrimary">
<div class="not-configured-warning ms-MessageBar ms-MessageBar--
warning">
<div class="ms-MessageBar-content">
<div class="ms-MessageBar-icon">
<i class="ms-Icon ms-Icon--Info"></i>
</div>
<div class="ms-MessageBar-text">
Oops! It looks like you haven't configured <strong>Git the
gist</strong> yet.
<br/>
Please configure your GitHub username and select a default gist,
then try that action again!
</div>
</div>
</div>
<div class="ms-font-xxl">Settings</div>
<div class="ms-Grid">
<div class="ms-Grid-row">
<div class="ms-TextField">
<label class="ms-Label">GitHub Username</label>
<input class="ms-TextField-field" id="github-user" type="text"
value="" placeholder="Please enter your GitHub username">
</div>
</div>
<div class="error-display ms-Grid-row">
<div class="ms-font-l ms-fontWeight-semibold">An error occurred:
</div>
<pre><code id="error-text"></code></pre>
</div>
<div class="gist-list-container ms-Grid-row">
<div class="list-title ms-font-xl ms-fontWeight-regular">Choose
Default Gist</div>
<form>
<div id="gist-list">
</div>
</form>
</div>
</div>
<div class="ms-Dialog-actions">
<div class="ms-Dialog-actionsRight">
<button class="ms-Dialog-action ms-Button ms-Button--primary"
id="settings-done" disabled>
<span class="ms-Button-label">Done</span>
</button>
</div>
</div>
</section>
</main>
<script type="text/javascript"
src="../../node_modules/jquery/dist/jquery.js"></script>
<script type="text/javascript" src="../helpers/gist-api.js"></script>
<script type="text/javascript" src="dialog.js"></script>
</body>

</html>

You may have noticed that the HTML file references a JavaScript file, gist-api.js, that
doesn't yet exist. This file will be created in the Fetch data from GitHub section below.
Next, create a file in the ./src/settings folder named dialog.css, and add the following
code to specify the styles that are used by dialog.html.

CSS

section {
margin: 10px 20px;
}

.not-configured-warning {
display: none;
}

.error-display {
display: none;
}

.gist-list-container {
margin: 10px -8px;
display: none;
}

.list-title {
border-bottom: 1px solid #a6a6a6;
padding-bottom: 5px;
}

ul {
margin-top: 10px;
}

.ms-ListItem-secondaryText,
.ms-ListItem-tertiaryText {
padding-left: 15px;
}

Now that you've defined the dialog UI, you can write the code that makes it actually do
something. Create a file in the ./src/settings folder named dialog.js and add the
following code. Note that this code uses jQuery to register events and uses the
messageParent method to send the user's choices back to the caller.

JavaScript

(function(){
'use strict';

// The Office initialize function must be run each time a new page is
loaded.
Office.initialize = function(reason){
jQuery(document).ready(function(){
if (window.location.search) {
// Check if warning should be displayed.
const warn = getParameterByName('warn');
if (warn) {
$('.not-configured-warning').show();
} else {
// See if the config values were passed.
// If so, pre-populate the values.
const user = getParameterByName('gitHubUserName');
const gistId = getParameterByName('defaultGistId');

$('#github-user').val(user);
loadGists(user, function(success){
if (success) {
$('.ms-ListItem').removeClass('is-selected');
$('input').filter(function() {
return this.value === gistId;
}).addClass('is-selected').attr('checked', 'checked');
$('#settings-done').removeAttr('disabled');
}
});
}
}

// When the GitHub username changes,


// try to load gists.
$('#github-user').on('change', function(){
$('#gist-list').empty();
const ghUser = $('#github-user').val();
if (ghUser.length > 0) {
loadGists(ghUser);
}
});

// When the Done button is selected, send the


// values back to the caller as a serialized
// object.
$('#settings-done').on('click', function() {
const settings = {};

settings.gitHubUserName = $('#github-user').val();

const selectedGist = $('.ms-ListItem.is-selected');


if (selectedGist) {
settings.defaultGistId = selectedGist.val();

sendMessage(JSON.stringify(settings));
}
});
});
};

// Load gists for the user using the GitHub API


// and build the list.
function loadGists(user, callback) {
getUserGists(user, function(gists, error){
if (error) {
$('.gist-list-container').hide();
$('#error-text').text(JSON.stringify(error, null, 2));
$('.error-display').show();
if (callback) callback(false);
} else {
$('.error-display').hide();
buildGistList($('#gist-list'), gists, onGistSelected);
$('.gist-list-container').show();
if (callback) callback(true);
}
});
}

function onGistSelected() {
$('.ms-ListItem').removeClass('is-selected').removeAttr('checked');
$(this).children('.ms-ListItem').addClass('is-selected').attr('checked',
'checked');
$('.not-configured-warning').hide();
$('#settings-done').removeAttr('disabled');
}

function sendMessage(message) {
Office.context.ui.messageParent(message);
}

function getParameterByName(name, url) {


if (!url) {
url = window.location.href;
}
name = name.replace(/[\[\]]/g, "\\$&");
const regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
})();

Update webpack config settings

Finally, open the webpack.config.js file found in the root directory of the project and
complete the following steps.

1. Locate the entry object within the config object and add a new entry for dialog .

JavaScript

dialog: "./src/settings/dialog.js",
After you've done this, the new entry object will look like this:

JavaScript

entry: {
polyfill: ["core-js/stable", "regenerator-runtime/runtime"],
taskpane: "./src/taskpane/taskpane.js",
commands: "./src/commands/commands.js",
dialog: "./src/settings/dialog.js",
},

2. Locate the plugins array within the config object. In the patterns array of the new
CopyWebpackPlugin object, add new entries for taskpane.css and dialog.css.

JavaScript

{
from: "./src/taskpane/taskpane.css",
to: "taskpane.css",
},
{
from: "./src/settings/dialog.css",
to: "dialog.css",
},

After you've done this, the new CopyWebpackPlugin object will look like this:

JavaScript

new CopyWebpackPlugin({
patterns: [
{
from: "./src/taskpane/taskpane.css",
to: "taskpane.css",
},
{
from: "./src/settings/dialog.css",
to: "dialog.css",
},
{
from: "assets/*",
to: "assets/[name][ext][query]",
},
{
from: "manifest*.xml",
to: "[name]." + buildType + "[ext]",
transform(content) {
if (dev) {
return content;
} else {
return content.toString().replace(new RegExp(urlDev, "g"),
urlProd);
}
},
},
]}),

3. In the same plugins array within the config object, add this new object to the end
of the array.

JavaScript

new HtmlWebpackPlugin({
filename: "dialog.html",
template: "./src/settings/dialog.html",
chunks: ["polyfill", "dialog"]
})

After you've done this, the new plugins array will look like this:

JavaScript

plugins: [
new HtmlWebpackPlugin({
filename: "taskpane.html",
template: "./src/taskpane/taskpane.html",
chunks: ["polyfill", "taskpane"],
}),
new CopyWebpackPlugin({
patterns: [
{
from: "./src/taskpane/taskpane.css",
to: "taskpane.css",
},
{
from: "./src/settings/dialog.css",
to: "dialog.css",
},
{
from: "assets/*",
to: "assets/[name][ext][query]",
},
{
from: "manifest*.xml",
to: "[name]." + buildType + "[ext]",
transform(content) {
if (dev) {
return content;
} else {
return content.toString().replace(new RegExp(urlDev, "g"),
urlProd);
}
},
},
],
}),
new HtmlWebpackPlugin({
filename: "commands.html",
template: "./src/commands/commands.html",
chunks: ["polyfill", "commands"],
}),
new HtmlWebpackPlugin({
filename: "dialog.html",
template: "./src/settings/dialog.html",
chunks: ["polyfill", "dialog"]
})
],

Fetch data from GitHub


The dialog.js file you just created specifies that the add-in should load gists when the
change event fires for the GitHub username field. To retrieve the user's gists from
GitHub, you'll use the GitHub Gists API .

Within the ./src folder, create a new subfolder named helpers. In the ./src/helpers
folder, create a file named gist-api.js, and add the following code to retrieve the user's
gists from GitHub and build the list of gists.

JavaScript

function getUserGists(user, callback) {


const requestUrl = 'https://api.github.com/users/' + user + '/gists';

$.ajax({
url: requestUrl,
dataType: 'json'
}).done(function(gists){
callback(gists);
}).fail(function(error){
callback(null, error);
});
}

function buildGistList(parent, gists, clickFunc) {


gists.forEach(function(gist) {

const listItem = $('<div/>')


.appendTo(parent);

const radioItem = $('<input>')


.addClass('ms-ListItem')
.addClass('is-selectable')
.attr('type', 'radio')
.attr('name', 'gists')
.attr('tabindex', 0)
.val(gist.id)
.appendTo(listItem);

const descPrimary = $('<span/>')


.addClass('ms-ListItem-primaryText')
.text(gist.description)
.appendTo(listItem);

const descSecondary = $('<span/>')


.addClass('ms-ListItem-secondaryText')
.text(' - ' + buildFileList(gist.files))
.appendTo(listItem);

const updated = new Date(gist.updated_at);

const descTertiary = $('<span/>')


.addClass('ms-ListItem-tertiaryText')
.text(' - Last updated ' + updated.toLocaleString())
.appendTo(listItem);

listItem.on('click', clickFunc);
});
}

function buildFileList(files) {

let fileList = '';

for (let file in files) {


if (files.hasOwnProperty(file)) {
if (fileList.length > 0) {
fileList = fileList + ', ';
}

fileList = fileList + files[file].filename + ' (' +


files[file].language + ')';
}
}

return fileList;
}

Run the following command to rebuild the project.

command line

npm run build


Implement a UI-less button
This add-in's Insert default gist button is a UI-less button that will invoke a JavaScript
function, rather than open a task pane like many add-in buttons do. When the user
selects the Insert default gist button, the corresponding JavaScript function will check
whether the add-in has been configured.

If the add-in has already been configured, the function will load the content of the
gist that the user has selected as the default and insert it into the body of the
message.

If the add-in hasn't yet been configured, then the settings dialog will prompt the
user to provide the required information.

Update the function file (HTML)


A function that's invoked by a UI-less button must be defined in the file that's specified
by the <FunctionFile> element in the manifest for the corresponding form factor. This
add-in's manifest specifies https://localhost:3000/commands.html as the function file.

Open the file ./src/commands/commands.html and replace the entire contents with the
following markup.

HTML

<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

<!-- Office JavaScript API -->


<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>

<script type="text/javascript"
src="../../node_modules/jquery/dist/jquery.js"></script>
<script type="text/javascript"
src="../../node_modules/showdown/dist/showdown.min.js"></script>
<script type="text/javascript"
src="../../node_modules/urijs/src/URI.min.js"></script>
<script type="text/javascript" src="../helpers/addin-config.js">
</script>
<script type="text/javascript" src="../helpers/gist-api.js"></script>
</head>

<body>
<!-- NOTE: The body is empty on purpose. Since functions in commands.js
are
invoked via a button, there is no UI to render. -->
</body>

</html>

You may have noticed that the HTML file references a JavaScript file, addin-config.js,
that doesn't yet exist. This file will be created in the Create a file to manage
configuration settings section later in this tutorial.

Update the function file (JavaScript)


Open the file ./src/commands/commands.js and replace the entire contents with the
following code. Note that if the insertDefaultGist function determines the add-in has
not yet been configured, it adds the ?warn=1 parameter to the dialog URL. Doing so
makes the settings dialog render the message bar that's defined in
./src/settings/dialog.html, to tell the user why they're seeing the dialog.

JavaScript

let config;
let btnEvent;

// The initialize function must be run each time a new page is loaded.
Office.initialize = function () {
};

function showError(error) {
Office.context.mailbox.item.notificationMessages.replaceAsync('github-
error', {
type: 'errorMessage',
message: error
}, function(result){
});
}

let settingsDialog;

function insertDefaultGist(event) {

config = getConfig();

// Check if the add-in has been configured.


if (config && config.defaultGistId) {
// Get the default gist content and insert.
try {
getGist(config.defaultGistId, function(gist, error) {
if (gist) {
buildBodyContent(gist, function (content, error) {
if (content) {
Office.context.mailbox.item.body.setSelectedDataAsync(content,
{coercionType: Office.CoercionType.Html}, function(result) {
event.completed();
});
} else {
showError(error);
event.completed();
}
});
} else {
showError(error);
event.completed();
}
});
} catch (err) {
showError(err);
event.completed();
}

} else {
// Save the event object so we can finish up later.
btnEvent = event;
// Not configured yet, display settings dialog with
// warn=1 to display warning.
const url = new URI('dialog.html?
warn=1').absoluteTo(window.location).toString();
const dialogOptions = { width: 20, height: 40, displayInIframe: true };

Office.context.ui.displayDialogAsync(url, dialogOptions,
function(result) {
settingsDialog = result.value;
settingsDialog.addEventHandler(Office.EventType.DialogMessageReceived,
receiveMessage);
settingsDialog.addEventHandler(Office.EventType.DialogEventReceived,
dialogClosed);
});
}
}

// Register the function.


Office.actions.associate("insertDefaultGist", insertDefaultGist);

function receiveMessage(message) {
config = JSON.parse(message.message);
setConfig(config, function(result) {
settingsDialog.close();
settingsDialog = null;
btnEvent.completed();
btnEvent = null;
});
}

function dialogClosed(message) {
settingsDialog = null;
btnEvent.completed();
btnEvent = null;
}

Create a file to manage configuration settings


The HTML function file references a file named addin-config.js, which doesn't yet exist.
In the ./src/helpers folder, create a file named addin-config.js and add the following
code. This code uses the RoamingSettings object to get and set configuration values.

JavaScript

function getConfig() {
const config = {};

config.gitHubUserName =
Office.context.roamingSettings.get('gitHubUserName');
config.defaultGistId =
Office.context.roamingSettings.get('defaultGistId');

return config;
}

function setConfig(config, callback) {


Office.context.roamingSettings.set('gitHubUserName',
config.gitHubUserName);
Office.context.roamingSettings.set('defaultGistId', config.defaultGistId);

Office.context.roamingSettings.saveAsync(callback);
}

Create new functions to process gists


Next, open the ./src/helpers/gist-api.js file and add the following functions. Note the
following:

If the gist contains HTML, the add-in will insert the HTML as is into the body of the
message.

If the gist contains Markdown, the add-in will use the Showdown library to
convert the Markdown to HTML, and will then insert the resulting HTML into the
body of the message.

If the gist contains anything other than HTML or Markdown, the add-in will insert it
into the body of the message as a code snippet.
JavaScript

function getGist(gistId, callback) {


const requestUrl = 'https://api.github.com/gists/' + gistId;

$.ajax({
url: requestUrl,
dataType: 'json'
}).done(function(gist){
callback(gist);
}).fail(function(error){
callback(null, error);
});
}

function buildBodyContent(gist, callback) {


// Find the first non-truncated file in the gist
// and use it.
for (let filename in gist.files) {
if (gist.files.hasOwnProperty(filename)) {
const file = gist.files[filename];
if (!file.truncated) {
// We have a winner.
switch (file.language) {
case 'HTML':
// Insert as is.
callback(file.content);
break;
case 'Markdown':
// Convert Markdown to HTML.
const converter = new showdown.Converter();
const html = converter.makeHtml(file.content);
callback(html);
break;
default:
// Insert contents as a <code> block.
let codeBlock = '<pre><code>';
codeBlock = codeBlock + file.content;
codeBlock = codeBlock + '</code></pre>';
callback(codeBlock);
}
return;
}
}
}
callback(null, 'No suitable file found in the gist');
}

Test the Insert default gist button


Save all of your changes and run npm start from the command prompt, if the server
isn't already running. Then complete the following steps to test the Insert default gist
button.

1. Open Outlook and compose a new message.

2. In the compose message window, select the Insert default gist button. You should
see a dialog where you can configure the add-in, starting with the prompt to set
your GitHub username.

3. In the settings dialog, enter your GitHub username and then either Tab or click
elsewhere in the dialog to invoke the change event, which should load your list of
public gists. Select a gist to be the default, and select Done.
4. Select the Insert default gist button again. This time, you should see the contents
of the gist inserted into the body of the email.

7 Note

Outlook on Windows: To pick up the latest settings, you may need to close
and reopen the compose message window.

Implement a task pane


This add-in's Insert gist button will open a task pane and display the user's gists. The
user can then select one of the gists to insert into the body of the message. If the user
hasn't yet configured the add-in, they'll be prompted to do so.

Specify the HTML for the task pane


In the project that you've created, the task pane HTML is specified in the file
./src/taskpane/taskpane.html. Open that file and replace the entire contents with the
following markup.

HTML

<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contoso Task Pane Add-in</title>

<!-- Office JavaScript API -->


<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>

<!-- For more information on Fluent UI, visit


https://developer.microsoft.com/fluentui. -->
<link rel="stylesheet"
href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-
core/9.6.1/css/fabric.min.css"/>

<!-- Template styles -->


<link href="taskpane.css" rel="stylesheet" type="text/css" />
</head>

<body class="ms-font-l ms-landing-page">


<main class="ms-landing-page__main">
<section class="ms-landing-page__content ms-font-m ms-fontColor-
neutralPrimary">
<div id="not-configured" style="display: none;">
<div class="centered ms-font-xxl ms-u-textAlignCenter">Welcome!
</div>
<div class="ms-font-xl" id="settings-prompt">Please choose the
<strong>Settings</strong> icon at the bottom of this window to configure
this add-in.</div>
</div>
<div id="gist-list-container" style="display: none;">
<form>
<div id="gist-list">
</div>
</form>
</div>
<div id="error-display" style="display: none;" class="ms-u-borderBase
ms-fontColor-error ms-font-m ms-bgColor-error ms-borderColor-error">
</div>
</section>
<button class="ms-Button ms-Button--primary" id="insert-button"
tabindex=0 disabled>
<span class="ms-Button-label">Insert</span>
</button>
</main>
<footer class="ms-landing-page__footer ms-bgColor-themePrimary">
<div class="ms-landing-page__footer--left">
<img src="../../assets/logo-filled.png" />
<h1 class="ms-font-xl ms-fontWeight-semilight ms-fontColor-white">Git
the gist</h1>
</div>
<div id="settings-icon" class="ms-landing-page__footer--right" aria-
label="Settings" tabindex=0>
<i class="ms-Icon enlarge ms-Icon--Settings ms-fontColor-white"></i>
</div>
</footer>
<script type="text/javascript"
src="../../node_modules/jquery/dist/jquery.js"></script>
<script type="text/javascript"
src="../../node_modules/showdown/dist/showdown.min.js"></script>
<script type="text/javascript"
src="../../node_modules/urijs/src/URI.min.js"></script>
<script type="text/javascript" src="../helpers/addin-config.js"></script>
<script type="text/javascript" src="../helpers/gist-api.js"></script>
<script type="text/javascript" src="taskpane.js"></script>
</body>

</html>

Specify the CSS for the task pane


In the project that you've created, the task pane CSS is specified in the file
./src/taskpane/taskpane.css. Open that file and replace the entire contents with the
following code.

css

/* Copyright (c) Microsoft. All rights reserved. Licensed under the MIT
license. See full license in root of repo. */
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: auto; }

body {
position: relative;
font-size: 16px; }

main {
height: 100%;
overflow-y: auto; }
footer {
width: 100%;
position: relative;
bottom: 0;
margin-top: 10px;}

p, h1, h2, h3, h4, h5, h6 {


margin: 0;
padding: 0; }

ul {
padding: 0; }

#settings-prompt {
margin: 10px 0;
}

#error-display {
padding: 10px;
}

#insert-button {
margin: 0 10px;
}

.clearfix {
display: block;
clear: both;
height: 0; }

.pointerCursor {
cursor: pointer; }

.invisible {
visibility: hidden; }

.undisplayed {
display: none; }

.ms-Icon.enlarge {
position: relative;
font-size: 20px;
top: 4px; }

.ms-ListItem-secondaryText,
.ms-ListItem-tertiaryText {
padding-left: 15px;
}

.ms-landing-page {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
-webkit-flex-wrap: nowrap;
flex-wrap: nowrap;
height: 100%; }

.ms-landing-page__main {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
-webkit-flex-wrap: nowrap;
flex-wrap: nowrap;
-webkit-flex: 1 1 0;
flex: 1 1 0;
height: 100%; }

.ms-landing-page__content {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
-webkit-flex-wrap: nowrap;
flex-wrap: nowrap;
height: 100%;
-webkit-flex: 1 1 0;
flex: 1 1 0;
padding: 20px; }

.ms-landing-page__content h2 {
margin-bottom: 20px; }

.ms-landing-page__footer {
display: -webkit-inline-flex;
display: inline-flex;
-webkit-justify-content: center;
justify-content: center;
-webkit-align-items: center;
align-items: center; }

.ms-landing-page__footer--left {
transition: background ease 0.1s, color ease 0.1s;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-justify-content: flex-start;
justify-content: flex-start;
-webkit-align-items: center;
align-items: center;
-webkit-flex: 1 0 0px;
flex: 1 0 0px;
padding: 20px; }

.ms-landing-page__footer--left:active {
cursor: default; }

.ms-landing-page__footer--left--disabled {
opacity: 0.6;
pointer-events: none;
cursor: not-allowed; }

.ms-landing-page__footer--left--disabled:active, .ms-landing-page__footer--
left--disabled:hover {
background: transparent; }

.ms-landing-page__footer--left img {
width: 40px;
height: 40px; }

.ms-landing-page__footer--left h1 {
-webkit-flex: 1 0 0px;
flex: 1 0 0px;
margin-left: 15px;
text-align: left;
width: auto;
max-width: auto;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; }

.ms-landing-page__footer--right {
transition: background ease 0.1s, color ease 0.1s;
padding: 29px 20px; }

.ms-landing-page__footer--right:active, .ms-landing-page__footer--
right:hover {
background: #005ca4;
cursor: pointer; }

.ms-landing-page__footer--right:active {
background: #005ca4; }

.ms-landing-page__footer--right--disabled {
opacity: 0.6;
pointer-events: none;
cursor: not-allowed; }

.ms-landing-page__footer--right--disabled:active, .ms-landing-page__footer--
right--disabled:hover {
background: transparent; }

Specify the JavaScript for the task pane


In the project that you've created, the task pane JavaScript is specified in the file
./src/taskpane/taskpane.js. Open that file and replace the entire contents with the
following code.

JavaScript
(function(){
'use strict';

let config;
let settingsDialog;

Office.initialize = function(reason){

jQuery(document).ready(function(){

config = getConfig();

// Check if add-in is configured.


if (config && config.gitHubUserName) {
// If configured, load the gist list.
loadGists(config.gitHubUserName);
} else {
// Not configured yet.
$('#not-configured').show();
}

// When insert button is selected, build the content


// and insert into the body.
$('#insert-button').on('click', function(){
const gistId = $('.ms-ListItem.is-selected').val();
getGist(gistId, function(gist, error) {
if (gist) {
buildBodyContent(gist, function (content, error) {
if (content) {

Office.context.mailbox.item.body.setSelectedDataAsync(content,
{coercionType: Office.CoercionType.Html}, function(result)
{
if (result.status === Office.AsyncResultStatus.Failed) {
showError('Could not insert gist: ' +
result.error.message);
}
});
} else {
showError('Could not create insertable content: ' + error);
}
});
} else {
showError('Could not retrieve gist: ' + error);
}
});
});

// When the settings icon is selected, open the settings dialog.


$('#settings-icon').on('click', function(){
// Display settings dialog.
let url = new
URI('dialog.html').absoluteTo(window.location).toString();
if (config) {
// If the add-in has already been configured, pass the existing
values
// to the dialog.
url = url + '?gitHubUserName=' + config.gitHubUserName +
'&defaultGistId=' + config.defaultGistId;
}

const dialogOptions = { width: 20, height: 40, displayInIframe: true


};

Office.context.ui.displayDialogAsync(url, dialogOptions,
function(result) {
settingsDialog = result.value;

settingsDialog.addEventHandler(Office.EventType.DialogMessageReceived,
receiveMessage);

settingsDialog.addEventHandler(Office.EventType.DialogEventReceived,
dialogClosed);
});
})
});
};

function loadGists(user) {
$('#error-display').hide();
$('#not-configured').hide();
$('#gist-list-container').show();

getUserGists(user, function(gists, error) {


if (error) {

} else {
$('#gist-list').empty();
buildGistList($('#gist-list'), gists, onGistSelected);
}
});
}

function onGistSelected() {
$('#insert-button').removeAttr('disabled');
$('.ms-ListItem').removeClass('is-selected').removeAttr('checked');
$(this).children('.ms-ListItem').addClass('is-selected').attr('checked',
'checked');
}

function showError(error) {
$('#not-configured').hide();
$('#gist-list-container').hide();
$('#error-display').text(error);
$('#error-display').show();
}

function receiveMessage(message) {
config = JSON.parse(message.message);
setConfig(config, function(result) {
settingsDialog.close();
settingsDialog = null;
loadGists(config.gitHubUserName);
});
}

function dialogClosed(message) {
settingsDialog = null;
}
})();

Test the Insert gist button


Save all of your changes and run npm start from the command prompt, if the server
isn't already running. Then, complete the following steps to test the Insert gist button.

1. Open Outlook and compose a new message.

2. In the compose message window, select the Insert gist button. You should see a
task pane open to the right of the compose form.

3. In the task pane, select the Hello World Html gist and select Insert to insert that
gist into the body of the message.

Next steps
In this tutorial, you've created an Outlook add-in that can be used in message compose
mode to insert content into the body of a message. To learn more about developing
Outlook add-ins, continue to the following article.

Outlook add-in APIs

See also
Office add-in manifests
Outlook add-in design guidelines
Debug function commands in Outlook add-ins
Develop Outlook add-ins for the new
Outlook on Windows (preview)
Article • 06/06/2023

The new Outlook on Windows desktop client unifies the Windows and web codebases
to create a more consistent Outlook experience for users and administrators. Its modern
and simplified interface has added capabilities and aims to improve productivity,
organization, and collaboration for users. More importantly, the new Outlook on
Windows supports Outlook web add-ins, so that you can continue to extend Outlook's
functionality.

Impact on VSTO and COM add-ins


The new Outlook on Windows aims to unify the extensibility experience across all
Outlook platforms. To provide a more reliable and stable add-in experience, VSTO and
COM add-ins aren't supported in the new Outlook on Windows. To ensure your add-in
continues to work in the new Outlook on Windows, you must migrate your VSTO or
COM add-in to an Outlook web add-in. Migrating to an Outlook web add-in not only
enables compatibility with the new Outlook on Windows, it also makes your solution
available to users on other platforms, such as Outlook on Mac, on mobile, or on the
web.

To help get you started on the migration process, review the following guidance.

The differences in features and scenarios supported by VSTO and COM add-ins
and Outlook web add-ins are being addressed. To determine whether your add-in
scenario is fully supported in an Outlook web add-in, see Supported scenarios in
Outlook web add-ins.
For guidance on how to transition your VSTO add-in to an Outlook web add-in,
see VSTO add-in developer's guide and Tutorial: Share code between both a VSTO
Add-in and an Office Add-in with a shared code library.
If you're new to Outlook web add-ins, try out the Outlook quick start to build your
first add-in.

7 Note

VSTO and COM add-ins are still supported in classic Outlook on Windows.
Supported scenarios in Outlook web add-ins
The development of the Outlook JavaScript API used by Outlook web add-ins is focused
on closing the gap on scenarios that are only supported by VSTO and COM add-in
solutions. This way, users who transition to the Outlook web add-in can continue to
have a seamless experience.

The following table identifies key Outlook scenarios and their support status in a web
add-in. This table will be updated as additional scenarios are supported. Periodically
check this section as you plan to migrate your VSTO or COM add-in.

Scenario Description Support status in Outlook


web add-ins

Spam email Enable users to report unsolicited and Supported. Improvements are
reporting and potentially unsafe messages and learn how in development to further
education to identify these messages. enhance the user experience.

Online meetings Enable users to create and join online Supported.


meetings.

Meeting Provide additional services for users when Supported.


enhancements they schedule meetings, such as location
selection, catering services, and room
lighting and temperature adjustments.

Online Automatically add themed signatures to Supported.


signatures messages and appointments.

Customer Enable users to send and retrieve Supported.


relationship information from their CRM system to track
management communications with existing and potential
(CRM) and customers.
tracking services

Content reuse Enable users to transfer and retrieve text Supported.


and other content types from partner
systems.

Mail item Enable users to transform mail items into Supported.


transformation other formats.

Project Enable users to create and track project Supported.


management work items from partner systems.
Scenario Description Support status in Outlook
web add-ins

Attachment Enable users to import or export Partially supported. Essential


management attachments from partner locations. features are yet to be
addressed to create a similar
experience to VSTO or COM
add-ins.

Message Enable users to encrypt and decrypt Partially supported. Essential


encryption messages. features are yet to be
addressed to create a similar
experience to VSTO or COM
add-ins.

Data loss Prevent users from forwarding mail items Partially supported. Essential
prevention that contain highly sensitive information. features are yet to be
addressed to create a similar
experience to VSTO or COM
add-ins.

Mail item Enable users to identify and classify Partially supported. Essential
classification messages that contain sensitive information. features are yet to be
addressed to create a similar
experience to VSTO or COM
add-ins.

Data sync Enable bidirectional synchronization of mail Partially supported. Essential


service items with partner systems. features are yet to be
addressed to create a similar
experience to VSTO or COM
add-ins.

Proofing mail Provide users with real-time proofreading Not currently supported.
items assistance as they compose messages.

There are various possibilities for extending the Outlook functionality through add-ins. If
your VSTO or COM add-in solution doesn't quite fit any of the scenarios in the table,
complete the survey to share your scenario .

Support for classic Outlook on Windows


The classic Outlook on Windows desktop client will continue to support the
development of new and existing Outlook web add-ins. Additionally, it will continue to
receive releases of the latest Outlook add-in features.
Test your add-in in the new Outlook on
Windows
Test your Outlook web add-in in the new Outlook on Windows today! To switch to the
new Outlook on Windows that's in preview, you must meet the following requirements.

Have a Microsoft 365 work or school account connected to Exchange Online. The
new client doesn't currently support on-premises, hybrid, or sovereign Exchange
accounts.

Have a minimum OS installation of Windows 10 Version 1809 (Build 17763).

Be a member of the Microsoft 365 Insider program .

To help you sign up and install the Outlook desktop client, see Getting started with the
new Outlook for Windows .

For guidance on how to sideload your add-in, see Sideload Outlook add-ins for testing.

Debug your add-in


To debug an add-in installed in the new Outlook on Windows desktop client, first
sideload the add-in to Outlook on the web. Then, follow the guidance in Debug add-ins
in Office on the web to use your browser's developer tools for debugging.

Development experience feedback


As you test your Outlook web add-in in the new Outlook on Windows, share feedback
on your experience with the developer community through GitHub .

See also
Blog post: New Outlook for Windows available to all Office Insiders
Podcast: Update on development with new Outlook for Windows
Outlook add-ins overview
Build your first Outlook add-in
VSTO add-in developer's guide
Tutorial: Share code between both a VSTO Add-in and an Office Add-in with a
shared code library
Outlook add-in APIs
Article • 03/21/2023

To use APIs in your Outlook add-in, you must specify the location of the Office.js library,
the requirement set, the schema, and the permissions. You'll primarily use the Office
JavaScript APIs exposed through the Mailbox object.

Office.js library
To interact with the Outlook add-in API, you need to use the JavaScript APIs in Office.js.
The content delivery network (CDN) for the library is
https://appsforoffice.microsoft.com/lib/1/hosted/Office.js . Add-ins submitted to

AppSource must reference Office.js by this CDN; they can't use a local reference.

Reference the CDN in a <script> tag in the <head> tag of the web page (.html, .aspx, or
.php file) that implements the UI of your add-in.

HTML

<script src="https://appsforoffice.microsoft.com/lib/1/hosted/Office.js"
type="text/javascript"></script>

As we add new APIs, the URL to Office.js will stay the same. We will change the version
in the URL only if we break an existing API behavior.

) Important

When developing an add-in for any Office client application, reference the Office
JavaScript API from inside the <head> section of the page. This ensures that the API
is fully initialized prior to any body elements.

Requirement sets
All Outlook APIs belong to the Mailbox requirement set. The Mailbox requirement set
has versions, and each new set of APIs that are released belongs to a higher version of
the set. Not all Outlook clients will support the newest set of APIs when they are
released, but if an Outlook client declares support for a requirement set, it will support
all the APIs in that requirement set.
To control which Outlook clients the add-in appears in, specify a minimum requirement
set version in the manifest. For example, if you specify requirement set version 1.3, the
add-in will not show up in any Outlook client that doesn't support a minimum version of
1.3.

Specifying a requirement set doesn't limit your add-in to the APIs in that version. If the
add-in specifies requirement set v1.1 but is running in an Outlook client that supports
v1.3, the add-in can still use v1.3 APIs. The requirement set only controls which Outlook
clients the add-in appears in.

To check the availability of any APIs from a requirement set greater than the one
specified in the manifest, you can use standard JavaScript:

JavaScript

if (item.somePropertyOrFunction) {
item.somePropertyOrFunction...
}

7 Note

These checks are not needed for any APIs that are in the requirement set version
specified in the manifest.

Specify the minimum requirement set that supports the critical set of APIs for your
scenario, without which features of your add-in won't work. You specify the requirement
set in the manifest. The markup varies depending on the manifest that you are using.

XML manifest: Use the <Requirements> element. Note that the <Methods> child
element of <Requirements> isn't supported in Outlook add-ins, so you can't
declare support for specific methods.
Unified manifest for Microsoft 365 (preview): Use the "extensions.capabilities"
property.

For more information, see Office add-in manifests, and Understanding Outlook API
requirement sets.

Permissions
Your add-in requires the appropriate permissions to use the APIs that it needs. In
general, you should specify the minimum permission needed for your add-in.
There are four levels of permissions; restricted, read item, read/write item, and
read/write mailbox. For more details. For more details, see Understanding Outlook add-
in permissions.

Mailbox object
Outlook add-ins primarily use a subset of the API exposed through the Mailbox object.
To access the objects and members specifically for use in Outlook add-ins, such as the
Item object, use the mailbox property of the Context object to access the Mailbox
object, as shown in the following line of code.

JavaScript

// Access the Item object.


const item = Office.context.mailbox.item;

Additionally, Outlook add-ins can use the following objects.

Office object: for initialization.

Context object: for access to content and display language properties.

RoamingSettings object: for saving Outlook add-in-specific custom settings to the


user's mailbox where the add-in is installed.

For information about using JavaScript in Outlook add-ins, see Outlook add-ins.

See also
Office add-in manifests
Understanding Outlook API requirement sets
Understanding Outlook add-in permissions
Privacy and security for Office Add-ins
Outlook JavaScript API requirement sets
Article • 07/13/2023

Outlook add-ins declare what API versions they require in their manifest. The markup
varies depending on whether you're using the XML manifest format or the unified
manifest for Microsoft 365 (preview).

XML Manifest

The API version is specified by the Requirements element. Outlook add-ins always
include a Set element with a Name attribute set to Mailbox and a MinVersion
attribute set to the minimum API requirement set that supports the add-in's
scenarios.

For example, the following manifest snippet indicates a minimum requirement set
of 1.1.

XML

<Requirements>
<Sets>
<Set Name="Mailbox" MinVersion="1.1" />
</Sets>
</Requirements>

All Outlook APIs belong to the Mailbox requirement set. The Mailbox requirement set
has versions, and each new set of APIs that we release belongs to a higher version of the
set. Not all Outlook clients support the newest set of APIs, but if an Outlook client
declares support for a requirement set, generally it supports all of the APIs in that
requirement set (check the documentation on a specific API or feature for any
exceptions).

Setting a minimum requirement set version in the manifest controls in which Outlook
client the add-in will appear. If a client doesn't support the minimum requirement set, it
doesn't load the add-in. For example, if requirement set version 1.3 is specified, this
means the add-in will not show up in any Outlook client that doesn't support at least
1.3.

7 Note
Although Outlook on Android and on iOS support up to requirement set 1.5, your
mobile add-in can now implement some APIs from later requirement sets. For more
information on which APIs are supported in Outlook mobile, see Outlook
JavaScript APIs supported in Outlook on mobile devices.

Use APIs from later requirement sets


Setting a requirement set doesn't limit the available APIs that the add-in can use. For
example, if the add-in specifies requirement set "Mailbox 1.1", but it's running in an
Outlook client which supports "Mailbox 1.3", the add-in can use APIs from requirement
set "Mailbox 1.3".

To use a newer API, developers can check if a particular application supports the
requirement set by doing the following:

JavaScript

if (Office.context.requirements.isSetSupported('Mailbox', '1.3')) {
// Perform actions.
}
else {
// Provide alternate flow/logic.
}

Alternatively, developers can check for the existence of a newer API by using standard
JavaScript technique.

JavaScript

if (item.somePropertyOrMethod !== undefined) {


// Use item.somePropertyOrMethod.
item.somePropertyOrMethod;
}

No such checks are necessary for any APIs which are present in the requirement set
version specified in the manifest.

Choose a minimum requirement set


Developers should use the earliest requirement set that contains the critical set of APIs
for their scenario, without which the add-in won't work.
Requirement sets supported by Exchange
servers and Outlook clients
In this section, we note the range of requirement sets supported by Exchange server and
Outlook clients. For details about server and client requirements for running Outlook
add-ins, see Outlook add-ins requirements.

) Important

If your target Exchange server and Outlook client support different requirement
sets, then you may be restricted to the lower requirement set range. For example, if
an add-in is running in Outlook 2019 on Windows (highest requirement set: 1.6)
against Exchange 2016 (highest requirement set: 1.5), your add-in may be limited
to requirement set 1.5.

Exchange server support


The following servers support Outlook add-ins.

Product Major Exchange Supported API requirement sets


version

Exchange Online Latest build 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11,
1.12, 1.13
IdentityAPI 1.31

Exchange on- 2019 1.1, 1.2, 1.3, 1.4, 1.5


premises2

2016 1.1, 1.2, 1.3, 1.4, 1.5

2013 1.1

7 Note

1
To require the Identity API set 1.3 in your Outlook add-in code, check if it's
supported by calling isSetSupported('IdentityAPI', '1.3') . Declaring it in the
Outlook add-in's manifest isn't supported. You can also determine if the API is
supported by checking that it's not undefined . For further details, see Using APIs
from later requirement sets.
2
Even if an add-in implements features from requirement sets not supported in an
Exchange on-premises environment, it can still be added to an Outlook client as
long as the requirement set specified in its manifest aligns with those supported by
Exchange on-premises. However, an implemented feature will only work if the
Outlook client in which the add-in is installed supports the minimum requirement
set needed by a feature. To determine the requirement sets supported by varying
Outlook clients, see Outlook client support. We recommend supplementing this
with the documentation on the specific feature for any exceptions.

Outlook client support


Add-ins are supported in Outlook on the following platforms.

Platform Major Office/Outlook version Supported API requirement sets

Windows - Microsoft 365 subscription 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.81, 1.91, 1.101,
- retail perpetual Outlook 2016 1.111, 1.121, 1.131
and later IdentityAPI 1.32

volume-licensed perpetual 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.81, 1.91
Outlook 2021 IdentityAPI 1.32

volume-licensed perpetual 1.1, 1.2, 1.3, 1.4, 1.5, 1.6


Outlook 2019

volume-licensed perpetual 1.1, 1.2, 1.3, 1.43


Outlook 2016

perpetual Outlook 2013 1.1, 1.2, 1.33, 1.43

Mac classic UI 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8
IdentityAPI 1.32

new UI4 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10,
1.11, 1.12
IdentityAPI 1.32

Android5 6 7 subscription 1.1, 1.2, 1.3, 1.4, 1.5

iOS5 6 7 subscription 1.1, 1.2, 1.3, 1.4, 1.5

Web modern Outlook UI when 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10,
browser5 8 connected to 1.11, 1.12, 1.13
Exchange Online: subscription, IdentityAPI 1.32
Outlook.com

classic Outlook UI when 1.1, 1.2, 1.3, 1.4, 1.5, 1.6


connected to
Platform Major Office/Outlook version Supported API requirement sets

Exchange on-premises

7 Note

1
Version support for more recent requirement sets in Outlook on Windows with a
Microsoft 365 subscription or a retail perpetual license as follows:

Support for 1.8 is available from Version 1910 (Build 12130.20272).


Support for 1.9 is available from Version 2008 (Build 13127.20296).
Support for 1.10 is available from Version 2104 (Build 13929.20296).
Support for 1.11 is available from Version 2110 (Build 14527.20226).
Support for 1.12 is available from Version 2206 (Build 15330.20196).
Support for 1.13 is available from Version 2304 (Build 16327.20248).

For more details according to your version, see the update history page for Office
2021 or Microsoft 365 and how to find your Office client version and update
channel .

2
To require the Identity API set 1.3 in your Outlook add-in code, check if it's
supported by calling isSetSupported('IdentityAPI', '1.3') . Declaring it in the
Outlook add-in's manifest isn't supported. You can also determine if the API is
supported by checking that it's not undefined . For further details, see Using APIs
from later requirement sets.

3 Support for 1.3 in Outlook 2013 was added as part of the December 8, 2015,
update for Outlook 2013 (KB3114349) . Support for 1.4 in Outlook 2013 was
added as part of the September 13, 2016, update for Outlook 2013 (KB3118280) .
Support for 1.4 in volume-licensed perpetual Outlook 2016 was added as part of
the July 3, 2018, update for Office 2016 (KB4022223) .

4
Support for the new Mac UI is available from Outlook Version 16.38.506. For more
information, see the Add-in support in Outlook on new Mac UI section.

5 Add-ins aren't supported in Outlook on Android, on iOS, and modern mobile web
with on-premises Exchange accounts. Certain iOS devices still support add-ins
when using on-premises Exchange accounts with classic Outlook on the web. For
information about supported devices, see Requirements for running Office Add-
ins.

6
Currently, there are additional considerations when designing and implementing
add-ins for mobile clients. For more details, see code considerations when adding
support for add-in commands in Outlook on mobile devices.

7
Although Outlook on Android and on iOS support up to requirement set 1.5, your
mobile add-in can now implement some APIs from later requirement sets, such as
event-based activation. For more information on which APIs are supported in
Outlook mobile, see Outlook JavaScript APIs supported in Outlook on mobile
devices.

8 Add-ins don't work in modern Outlook on the web on iPhone and Android
smartphones. For information about supported devices, see Requirements for
running Office Add-ins.

 Tip

You can distinguish between classic and modern Outlook in a web browser by
checking your mailbox toolbar.

modern

classic

Reference the Office JavaScript API production


library
To use APIs in any of the numbered requirement sets, you should reference the
production library on the Office.js content delivery network (CDN) . For information on
how to use preview APIs, see Test preview APIs.

Test preview APIs


New Outlook JavaScript APIs are first introduced in "preview" and later become part of a
specific, numbered requirement set after sufficient testing occurs and user feedback is
acquired. To provide feedback about a preview API, please use the feedback mechanism
at the end of the web page where the API is documented.

7 Note

Preview APIs are subject to change and aren't intended for use in a production
environment.

For more details about the preview APIs, see Outlook API preview requirement set.
outlook package
Reference

Interfaces
Office.Appointment The subclass of Item dealing with appointments.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Child interfaces:

AppointmentCompose
AppointmentRead

Office.AppointmentCompose The appointment organizer mode of Office.context.mailbox.item.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Parent interfaces:

ItemCompose
Appointment

Office.AppointmentForm The AppointmentForm object is used to access the currently


selected appointment.

Office.AppointmentRead The appointment attendee mode of Office.context.mailbox.item.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Parent interfaces:

ItemRead
Appointment

Office.AppointmentTime Provides the current dates and times of the appointment that
ChangedEventArgs raised the Office.EventType.AppointmentTimeChanged event.
Office.AttachmentContent Represents the content of an attachment on a message or
appointment item.

Office.AttachmentDetails Represents an attachment on an item from the server. Read


mode only.

An array of AttachmentDetails objects is returned as the


attachments property of an appointment or message item.

Office.AttachmentDetails Represents an attachment on an item. Compose mode only.


Compose
An array of AttachmentDetailsCompose objects is returned as the
attachments property of an appointment or message item.

Office.AttachmentsChanged Provides information about the attachments that raised the


EventArgs Office.EventType.AttachmentsChanged event.

Office.Body The body object provides methods for adding and updating the
content of the message or appointment. It is returned in the
body property of the selected item.

Office.Categories Represents the categories on an item.

In Outlook, a user can tag messages and appointments by using


a category to color-code them. The user defines categories in a
master list on their mailbox. They can then apply one or more
categories to an item.

Important: In Outlook on the web, you can't use the API to


manage categories applied to a message in Compose mode.

Office.CategoryDetails Represents a category's details like name and associated color.

Office.CoercionTypeOptions Provides an option for the data format.

Office.Contact Represents the details about a contact (similar to what's on a


physical contact or business card) extracted from the item's
body. Read mode only.

The list of contacts extracted from the body of an email message


or appointment is returned in the contacts property of the
Entities object returned by the getEntities or
getEntitiesByType method of the current item.

Office.CustomProperties The CustomProperties object represents custom properties that


are specific to a particular mail item and specific to an Outlook
add-in. For example, there might be a need for an add-in to save
some data that's specific to the current message that activated
the add-in. If the user revisits the same message in the future
and activates the add-in again, the add-in will be able to retrieve
the data that had been saved as custom properties.
To learn more about CustomProperties , see Get and set add-in
metadata for an Outlook add-in.

Office.DelayDeliveryTime The DelayDeliveryTime object enables you to manage the


delayed delivery date and time of a message.

Office.Diagnostics Provides diagnostic information to an Outlook add-in.

Office.EmailAddressDetails Provides the email properties of the sender or specified


recipients of an email message or appointment.

Office.EmailUser Represents an email account on an Exchange Server.

EmailUser objects are primarily received in MeetingSuggestion


and TaskSuggestion entities extracted from an Outlook item. To
learn more about this scenario, refer to Extract entity strings
from an Outlook item.

Office.EnhancedLocation Represents the set of locations on an appointment.

Office.EnhancedLocations Provides the current enhanced locations when the


ChangedEventArgs Office.EventType.EnhancedLocationsChanged event is raised.

Office.Entities Represents a collection of entities found in an email message or


appointment. Read mode only.

The Entities object is a container for the entity arrays returned


by the getEntities and getEntitiesByType methods when the
item (either an email message or an appointment) contains one
or more entities that have been found by the server. You can use
these entities in your code to provide additional context
information to the viewer, such as a map to an address found in
the item, or to open a dialer for a phone number found in the
item.

If no entities of the type specified in the property are present in


the item, the property associated with that entity is null. For
example, if a message contains a street address and a phone
number, the addresses property and phoneNumbers property
would contain information, and the other properties would be
null.

To be recognized as an address, the string must contain a United


States postal address that has at least a subset of the elements
of a street number, street name, city, state, and zip code.

To be recognized as a phone number, the string must contain a


North American phone number format.

Entity recognition relies on natural language recognition that is


based on machine learning of large amounts of data. The
recognition of an entity is non-deterministic and success
sometimes relies on the particular context in the item.

When the property arrays are returned by the getEntitiesByType


method, only the property for the specified entity contains data;
all other properties are null.

Office.From Provides a method to get the from value of a message in an


Outlook add-in.

Office.InfobarClickedEvent Provides basic details about the notification message that raised
Args the Office.EventType.InfobarClicked event.

Office.InfobarDetails Provides additional details about the notification message that


raised the Office.EventType.InfobarClicked event.

Office.InternetHeaders The InternetHeaders object represents custom internet headers


that are preserved after the message item leaves Exchange and
is converted to a MIME message.

Internet headers are stored as string key-value pairs on a per-


item basis.

Note: This object is intended for you to set and get your custom
headers on a message item. To learn more, see Get and set
internet headers on a message in an Outlook add-in.

Office.Item The item namespace is used to access the currently selected


message, meeting request, or appointment. You can determine
the type of the item by using the itemType property.

To see the full member list, refer to the Object Model page.

If you want to see IntelliSense for only a specific type or mode,


cast this item to one of the following:

AppointmentCompose
AppointmentRead
MessageCompose
MessageRead

Office.ItemCompose The compose mode of Office.context.mailbox.item.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Child interfaces:

AppointmentCompose
MessageCompose
Office.ItemRead The read mode of Office.context.mailbox.item.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Child interfaces:

AppointmentRead
MessageRead

Office.LocalClientTime Represents a date and time in the local client's time zone. Read
mode only.

Office.Location Provides methods to get and set the location of a meeting in an


Outlook add-in.

Office.LocationDetails Represents a location. Read-only.

Office.LocationIdentifier Represents the ID of a location.

Office.Mailbox Provides access to the Microsoft Outlook add-in object model.

Key properties:

diagnostics : Provides diagnostic information to an


Outlook add-in.
item : Provides methods and properties for accessing a
message or appointment in an Outlook add-in.
userProfile : Provides information about the user in an
Outlook add-in.

Office.MasterCategories Represents the categories master list on the mailbox.

In Outlook, a user can tag messages and appointments by using


a category to color-code them. The user defines categories in a
master list on their mailbox. They can then apply one or more
categories to an item.

Important: In delegate or shared scenarios, the delegate can get


the categories in the master list but can't add or remove
categories.

Office.MeetingSuggestion Represents a suggested meeting found in an item. Read mode


only.

The list of meetings suggested in an email message is returned


in the meetingSuggestions property of the Entities object that
is returned when the getEntities or getEntitiesByType method
is called on the active item.

The start and end values are string representations of a Date


object that contains the date and time at which the suggested
meeting is to begin and end. The values are in the default time
zone specified for the current user.

Office.Message A subclass of Item for messages.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Child interfaces:

MessageCompose
MessageRead

Office.MessageCompose The message compose mode of Office.context.mailbox.item.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Parent interfaces:

ItemCompose
Message

Office.MessageRead The message read mode of Office.context.mailbox.item.

Important: This is an internal Outlook object, not directly


exposed through existing interfaces. You should treat this as a
mode of Office.context.mailbox.item . For more information,
refer to the Object Model page.

Parent interfaces:

ItemRead
Message

Office.NotificationMessage The definition of the action for a notification message.


Action
Important: In modern Outlook on the web, the
NotificationMessageAction object is available in Compose mode
only.
Office.NotificationMessage An array of NotificationMessageDetails objects are returned by
Details the NotificationMessages.getAllAsync method.

Office.NotificationMessages The NotificationMessages object is returned as the


notificationMessages property of an item.

Office.Organizer Represents the appointment organizer, even if an alias or a


delegate was used to create the appointment. This object
provides a method to get the organizer value of an appointment
in an Outlook add-in.

Office.PhoneNumber Represents a phone number identified in an item. Read mode


only.

An array of PhoneNumber objects containing the phone numbers


found in an email message is returned in the phoneNumbers
property of the Entities object that is returned when you call
the getEntities method on the selected item.

Office.Recipients Represents recipients of an item. Compose mode only.

Office.RecipientsChanged Provides change status of recipients fields when the


EventArgs Office.EventType.RecipientsChanged event is raised.

Office.RecipientsChanged Represents RecipientsChangedEventArgs.changedRecipientFields


Fields object.

Office.Recurrence The Recurrence object provides methods to get and set the
recurrence pattern of appointments but only get the recurrence
pattern of meeting requests. It will have a dictionary with the
following keys: seriesTime , recurrenceType ,
recurrenceProperties , and recurrenceTimeZone (optional).

Office.RecurrenceChanged Provides updated recurrence object that raised the


EventArgs Office.EventType.RecurrenceChanged event.

Office.RecurrenceProperties Represents the properties of the recurrence.

Office.RecurrenceTimeZone Represents the time zone of the recurrence.

Office.ReplyFormAttachment A file or item attachment. Used when displaying a reply form.

Office.ReplyFormData A ReplyFormData object that contains body or attachment data


and a callback function. Used when displaying a reply form.

Office.RoamingSettings The settings created by using the methods of the


RoamingSettings object are saved per add-in and per user. That
is, they are available only to the add-in that created them, and
only from the user's mailbox in which they are saved.
While the Outlook add-in API limits access to these settings to
only the add-in that created them, these settings shouldn't be
considered secure storage. They can be accessed by Exchange
Web Services or Extended MAPI. They shouldn't be used to store
sensitive information, such as user credentials or security tokens.

The name of a setting is a String, while the value can be a String,


Number, Boolean, null, Object, or Array.

The RoamingSettings object is accessible via the


roamingSettings property in the Office.context namespace.

To learn more about RoamingSettings , see Get and set add-in


metadata for an Outlook add-in.

Office.SelectedItemDetails Represents the properties of a message that's currently selected


in Outlook.

Office.SensitivityLabel Provides methods to get or set the sensitivity label of a message


or appointment. For more information on sensitivity labels, see
Learn about sensitivity labels.

Office.SensitivityLabel Provides the change status of the sensitivity label applied to a


ChangedEventArgs message or appointment in compose mode. This information is
provided when the Office.EventType.SensitivityLabelChanged
event is raised.

Office.SensitivityLabelDetails Represents the properties of available sensitivity labels in


Outlook.

Office.SensitivityLabelsCatalog Provides methods to check the status of the catalog of sensitivity


labels in Outlook and retrieve all available sensitivity labels if the
catalog is enabled.

Office.SeriesTime The SeriesTime object provides methods to get and set the
dates and times of appointments in a recurring series and get
the dates and times of meeting requests in a recurring series.

Office.SessionData Provides methods to manage an item's session data.

Important: The entire SessionData object is limited to 50,000


characters per add-in.

Office.SharedProperties Represents the properties of an appointment or message in a


shared folder or shared mailbox.

For more information on how this object is used, see Enable


shared folders and shared mailbox scenarios in an Outlook add-
in.

Office.Subject Provides methods to get and set the subject of an appointment


or message in an Outlook add-in.
Office.TaskSuggestion Represents a suggested task identified in an item. Read mode
only.

The list of tasks suggested in an email message is returned in the


taskSuggestions property of the Entities object that is returned
when the getEntities or getEntitiesByType method is called on
the active item.

Office.Time The Time object is returned as the start or end property of an


appointment in compose mode.

Office.UserProfile Information about the user associated with the mailbox. This
includes their account type, display name, email address, and
time zone.

Enums
Office.MailboxEnums.Action Specifies the type of custom action in a notification message.
Type

Office.MailboxEnums. Specifies the formatting that applies to an attachment's content.


AttachmentContentFormat

Office.MailboxEnums. Specifies whether an attachment was added to or removed from


AttachmentStatus an item.

Office.MailboxEnums. Specifies an attachment's type.


AttachmentType

Office.MailboxEnums.Category Specifies the category color.


Color
Note: The actual color depends on how the Outlook client
renders it. In this case, the colors noted on each preset are for
the Outlook desktop client.

Office.MailboxEnums. Specifies a message's compose type.


ComposeType

Office.MailboxEnums.Days Specifies the day of week or type of day.

Office.MailboxEnums.Delegate This bitmask represents a delegate's permissions on a shared


Permissions folder.

Office.MailboxEnums.Entity Specifies an entity's type.


Type

Office.MailboxEnums.Infobar Action types supported by Office.EventType.InfobarClicked.


ActionType
Office.MailboxEnums.Infobar Type of notification allowed by Office.EventType.InfobarClicked.
Type

Office.MailboxEnums.Item Specifies the notification message type for an appointment or


NotificationMessageType message.

Office.MailboxEnums.Item Specifies an item's type.


Type

Office.MailboxEnums.Location Specifies an appointment location's type.


Type

Office.MailboxEnums.Month Specifies the month.

Office.MailboxEnums. Represents the current view of Outlook on the web.


OWAView

Office.MailboxEnums. Specifies the type of recipient of a message or appointment.


RecipientType

Office.MailboxEnums. Specifies the time zone applied to the recurrence.


RecurrenceTimeZone

Office.MailboxEnums. Specifies the type of recurrence.


RecurrenceType

Office.MailboxEnums. Specifies the type of response to a meeting invitation.


ResponseType

Office.MailboxEnums.Rest Specifies the version of the REST API that corresponds to a REST-
Version formatted item ID.

Office.MailboxEnums.Source Specifies the source of the selected data in an item (see


Property Office.mailbox.item.getSelectedDataAsync for details).

Office.MailboxEnums.Week Specifies the week of the month.


Number
Outlook add-in design guidelines
Article • 04/18/2023

Add-ins are a great way for partners to extend the functionality of Outlook beyond our
core feature set. Add-ins enable users to access external experiences, tasks, and content
without needing to leave their inbox. Once installed, Outlook add-ins are available on
every platform and device.

The following high-level guidelines will help you design and build a compelling add-in,
which brings the best of your app right into Outlook—on Windows, Web, iOS, Mac, and
Android.

Principles

Focus on a few key tasks; do them well


The best designed add-ins are simple to use, focused, and provide real value to users.
Because your add-in will run inside of Outlook, there is additional emphasis placed on
this principle. Outlook is a productivity app—it's where people go to get things done.

Your add-in will be an extension of our experience and it's important to make sure the
scenarios you enable feel like a natural fit inside Outlook. Think carefully about which of
your common use cases will benefit the most from having hooks to them from within
our email and calendaring experiences.

An add-in shouldn't attempt to do everything your app does. The focus should be on
the most frequently used, and appropriate, actions in the context of Outlook content.
Think about your call to action and make it clear what the user should do when your
task pane opens.

Make it feel as native as possible


Your add-in should be designed using patterns native to the platform that Outlook is
running on. To achieve this, be sure to respect and implement the interaction and visual
guidelines set forth by each platform. Outlook has its own guidelines and those are also
important to consider. A well-designed add-in will be an appropriate blend of your
experience, the platform, and Outlook.

This does mean that your add-in will have to visually be different when it runs in
Outlook on iOS versus on Android.
Make it enjoyable to use and get the details right
People enjoy using products that are both functionally and visually appealing. You can
help ensure the success of your add-in by crafting an experience where you've carefully
considered every interaction and visual detail. The necessary steps to complete a task
must be clear and relevant. Ideally, no action should be further than a click or two away.

Try not to take a user out of context to complete an action. A user should easily be able
to get in and out of your add-in and back to whatever they were doing before. An add-
in isn't meant to be a destination to spend a lot of time in—it's an enhancement to our
core functionality. If done properly, your add-in will help us deliver on the goal of
making people more productive.

Brand wisely
We value great branding, and we know it's important to provide users with your unique
experience. But we feel the best way to ensure your add-in's success is to build an
intuitive experience that subtly incorporates elements of your brand versus displaying
persistent or obtrusive brand elements that only distract a user from moving through
your system in an unencumbered manner.

A good way to incorporate your brand in a meaningful way is through the use of your
brand colors, icons, and voice—assuming these don't conflict with the preferred
platform patterns or accessibility requirements. Strive to keep the focus on content and
task completion, not brand attention.

7 Note

Ads should not be shown within add-ins in Outlook on iOS or on Android.

Design patterns

7 Note

While the above principles apply to all endpoints/platforms, the following patterns
and examples are specific to mobile add-ins in Outlook on iOS.

To help you create a well-designed add-in, we have templates that contain iOS mobile
patterns that work within the Outlook mobile environment. Leveraging these specific
patterns will help ensure your add-in feels native to both the iOS platform and Outlook
mobile. These patterns are also detailed later in this article. While not exhaustive, this is
the start of a library that we'll continue to build upon as we uncover additional
paradigms partners wish to include in their add-ins.

Overview
A typical add-in is made up of the following components.

Loading
When a user taps on your add-in, the UX should display as quickly as possible. If there is
any delay, use a progress bar or activity indicator. A progress bar should be used when
the amount of time is determinable and an activity indicator should be used when the
amount of time is indeterminable.

An example of loading pages on iOS

An example of loading pages on Android

Sign in/Sign up
Make your sign in (and sign up) flow straightforward and simple to use.
An example page to sign in and sign up on iOS

An example sign in page on Android

Brand bar
The first screen of your add-in should include your branding element. Designed for
recognition, the brand bar also helps set context for the user. Because the navigation
bar contains the name of your company/brand, it's unnecessary to repeat the brand bar
on subsequent pages.

An example of branding on iOS


An example of branding on Android

Margins
Mobile margins should be set to 15px (8% of screen) for each side, to align with Outlook
on iOS and 16px for each side to align with Outlook on Android.
Typography
Typography usage is aligned to Outlook on iOS and is kept simple for scannability.

Typography on iOS

Typography on Android
Color palette
Color usage is subtle in Outlook on iOS. To align, we ask that usage of color is localized
to actions and error states, with only the brand bar using a unique color.

Cells
Since the navigation bar cannot be used to label a page, use section titles to label
pages.

Examples of cells on iOS


Examples of cells on Android
Actions
Even if your app handles a multitude of actions, think about the most important ones
you want your add-in to perform, and concentrate on those.

Examples of actions on iOS


Examples of actions on Android
Buttons
Buttons are used when there are other UX elements below (vs. actions, where the action
is the last element on the screen).

Examples of buttons on iOS

Examples of buttons on Android


Tabs
Tabs can aid in content organization.

Examples of tabs on iOS

Examples of tabs on Android


Icons
Icons should follow the current Outlook on iOS design when possible. Use our standard
size and color.

Examples of icons on iOS

Examples of icons on Android


End-to-end examples
When Outlook mobile add-ins were launched, we worked closely with our partners who
were building add-ins. As a way to showcase the potential of their add-ins on Outlook
mobile, our designer put together end-to-end flows for each add-in, leveraging our
guidelines and patterns.

) Important

These examples are meant to highlight the ideal way to approach both the
interaction and visual design of an add-in and may not match the exact feature sets
in the shipped versions of the add-ins.

GIPHY
An example of GIPHY on iOS
An example of GIPHY on Android

Nimble
An example of Nimble on iOS
An example of Nimble on Android

Trello
An example of Trello on iOS
An example of Trello on Android
Dynamics CRM
An example of Dynamics CRM on iOS

An example of Dynamics CRM on Android


Contextual Outlook add-ins
Article • 03/21/2023

Contextual add-ins are Outlook add-ins that activate based on text in a message or
appointment. By using contextual add-ins, a user can initiate tasks related to a message
without leaving the message itself, which results in an easier and richer user experience.

7 Note

Contextual Outlook add-ins aren't supported when the add-in uses a Unified
manifest for Microsoft 365 (preview).

The following are examples of contextual add-ins.

Choosing an address to open a map of the location.


Choosing a string that opens a meeting suggestion add-in.
Choosing a phone number to add to your contacts.

7 Note

Contextual add-ins are not currently available in Outlook on Android and iOS. This
functionality will be made available in the future.

Support for this feature was introduced in requirement set 1.6. See clients and
platforms that support this requirement set.

How to make a contextual add-in


A contextual add-in's manifest must include an ExtensionPoint element with an
xsi:type attribute set to DetectedEntity . Within the <ExtensionPoint> element, the
add-in specifies the entities or regular expression that can activate it. If an entity is
specified, the entity can be any of the properties in the Entities object.

Thus, the add-in manifest must contain a rule of type ItemHasKnownEntity or


ItemHasRegularExpressionMatch. The following example shows how to specify that an
add-in should activate on messages with a detected entity that is a phone number.

XML
<ExtensionPoint xsi:type="DetectedEntity">
<Label resid="contextLabel" />
<!--If you opt to include RequestedHeight, it must be between 140px to
450px, inclusive.-->
<!--<RequestedHeight>360</RequestedHeight>-->
<SourceLocation resid="detectedEntityURL" />
<Rule xsi:type="RuleCollection" Mode="And">
<Rule xsi:type="ItemIs" ItemType="Message" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="PhoneNumber"
Highlight="all" />
</Rule>
</ExtensionPoint>

After a contextual add-in is associated with an account, it will automatically start when
the user clicks a highlighted entity or regular expression. For more information about
regular expressions for Outlook add-ins, see Use regular expression activation rules to
show an Outlook add-in.

There are several restrictions on contextual add-ins:

A contextual add-in can only exist in read add-ins (not compose add-ins).
You cannot specify the color of the highlighted entity.
An entity that is not highlighted will not launch a contextual add-in in a card.

Because an entity or regular expression that is not highlighted will not launch a
contextual add-in, add-ins must include at least one Rule element with the Highlight
attribute set to all .

7 Note

The EmailAddress and Url entity types do not support highlighting, so they cannot
be used to launch a contextual add-in. They can however be combined in a
RuleCollection rule type as an additional activation criteria.

How to launch a contextual add-in


A user launches a contextual add-in through text, either a known entity or a developer's
regular expression. Typically, a user identifies a contextual add-in because the entity is
highlighted. The following example shows how highlighting appears in a message. Here
the entity (an address) is colored blue and underlined with a dotted blue line. A user
launches the contextual add-in by clicking the highlighted entity.

Example of text with highlighted entity (an address)


When there are multiple entities or contextual add-ins in a message, there are a few
user interaction rules:

If there are multiple entities, the user has to click a different entity to launch the
add-in for it.
If an entity activates multiple add-ins, each add-in opens a new tab. The user
switches between tabs to change between add-ins. For example, a name and
address might trigger a phone add-in and a map.
If a single string contains multiple entities that activate multiple add-ins, the entire
string is highlighted, and clicking the string shows all add-ins relevant to the string
on separate tabs. For example, a string that describes a proposed meeting at a
restaurant might activate the Suggested Meeting add-in and a restaurant rating
add-in.

How a contextual add-in displays


An activated contextual add-in appears in a card, which is a separate window near the
entity. The card will normally appear below the entity and centered with respect to the
entity as much as possible. If there is not enough room below the entity, the card is
placed above it. The following screenshot shows the highlighted entity, and below it, an
activated add-in (Bing Maps) in a card.

Example of an add-in displayed in a card


To close the card and the add-in, a user clicks anywhere outside of the card.

Current contextual add-ins


The following contextual add-ins are installed by default for users with Outlook add-ins.

Bing Maps
Suggested Meetings

See also
Outlook add-in: Contoso Order Number (sample contextual add-in that activates
based on a regular expression match)
Write your first Outlook add-in
Use regular expression activation rules to show an Outlook add-in
Entities object
Module extension Outlook add-ins
Article • 03/21/2023

Module extension add-ins appear in the Outlook navigation bar, right alongside mail,
tasks, and calendars. A module extension is not limited to using mail and appointment
information. You can create applications that run inside Outlook to make it easy for your
users to access business information and productivity tools without ever leaving
Outlook.

 Tip

Module extensions aren't supported in the Unified manifest for Microsoft 365
(preview), but you can create a very similar experience for users by making a
personal tab that opens in Outlook. In the early preview period for the unified
manifest in Outlook Add-ins, it isn't possible to combine an Outlook Add-in and a
personal tab in the same manifest and install them as a unit. We're working on this,
but in the meantime, you must create separate apps for the add-in and the
personal tab. They can both use files on the same domain.

7 Note

Module extensions are only supported by Outlook 2016 or later on Windows.

Open a module extension


To open a module extension, users click on the module's name or icon in the Outlook
navigation bar. If the user has compact navigation selected, the navigation bar has an
icon that shows an extension is loaded.

If the user is not using compact navigation, the navigation bar has two looks. With one
extension loaded, it shows the name of the add-in.
When more than one add-in is loaded, it shows the word Add-ins. Clicking either will
open the extension's user interface.

When you click on an extension, Outlook replaces the built-in module with your custom
module so that your users can interact with the add-in. You can use some of the features
of the Outlook JavaScript API in your add-in. APIs that logically assume a specific
Outlook item, such as a message or appointment, don't work in module extensions. The
module can also include function commands in the Outlook ribbon that interact with
the add-in's page. To facilitate this, your function commands call the Office.onReady or
Office.initialize method and the Event.completed method. To walk through how a
module extension Outlook add-in is configured, see the Outlook module extensions
billable hours sample .

The following screenshot shows an add-in that is integrated in the Outlook navigation
bar and has ribbon commands that will update the page of the add-in.

Example
The following is a section of a manifest file that defines a module extension.
XML

<!-- Add Outlook module extension point -->


<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">

<!-- Begin override of existing elements -->


<Description resid="residVersionOverrideDesc" />

<Requirements>
<bt:Sets DefaultMinVersion="1.3">
<bt:Set Name="Mailbox" />
</bt:Sets>
</Requirements>
<!-- End override of existing elements -->

<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<!-- Set the URL of the file that contains the
JavaScript function that controls the extension -->
<FunctionFile resid="residFunctionFileUrl" />

<!--New Extension Point - Module for a ModuleApp -->


<ExtensionPoint xsi:type="Module">
<SourceLocation resid="residExtensionPointUrl" />
<Label resid="residExtensionPointLabel" />

<CommandSurface>
<CustomTab id="idTab">
<Group id="idGroup">
<Label resid="residGroupLabel" />

<Control xsi:type="Button" id="group.changeToAssociate">


<Label resid="residChangeToAssociateLabel" />
<Supertip>
<Title resid="residChangeToAssociateLabel" />
<Description resid="residChangeToAssociateDesc" />
</Supertip>
<Icon>
<bt:Image size="16" resid="residAssociateIcon16" />
<bt:Image size="32" resid="residAssociateIcon32" />
<bt:Image size="80" resid="residAssociateIcon80" />
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>changeToAssociateRate</FunctionName>
</Action>
</Control>

</Group>
<Label resid="residCustomTabLabel" />
</CustomTab>
</CommandSurface>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>

<Resources>
<bt:Images>
<bt:Image id="residAddinIcon16"
DefaultValue="https://localhost:8080/Executive-16.png" />
<bt:Image id="residAddinIcon32"
DefaultValue="https://localhost:8080/Executive-32.png" />
<bt:Image id="residAddinIcon80"
DefaultValue="https://localhost:8080/Executive-80.png" />

<bt:Image id="residAssociateIcon16"
DefaultValue="https://localhost:8080/Associate-16.png" />
<bt:Image id="residAssociateIcon32"
DefaultValue="https://localhost:8080/Associate-32.png" />
<bt:Image id="residAssociateIcon80"
DefaultValue="https://localhost:8080/Associate-80.png" />
</bt:Images>

<bt:Urls>
<bt:Url id="residFunctionFileUrl"
DefaultValue="https://localhost:8080/" />
<bt:Url id="residExtensionPointUrl"
DefaultValue="https://localhost:8080/" />
</bt:Urls>

<!--Short strings must be less than 30 characters long -->


<bt:ShortStrings>
<bt:String id="residExtensionPointLabel"
DefaultValue="Billable Hours" />
<bt:String id="residGroupLabel"
DefaultValue="Change billing rate" />
<bt:String id="residCustomTabLabel"
DefaultValue="Billable hours" />

<bt:String id="residChangeToAssociateLabel"
DefaultValue="Associate" />
</bt:ShortStrings>

<bt:LongStrings>
<bt:String id="residVersionOverrideDesc"
DefaultValue="Version override description" />

<bt:String id="residChangeToAssociateDesc"
DefaultValue="Change to the associate billing rate:
$127/hr" />
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

See also
Office add-in manifests
Add-in commands
Outlook module extensions Billable hours sample
Activate your Outlook add-in without
the Reading Pane enabled or a message
selected
Article • 05/20/2023

With a simple manifest configuration, you can create Outlook add-ins for the Message
Read surface that activate a task pane without the Reading Pane enabled or a message
first selected from the mailbox. Follow the walkthrough to learn more and unlock
additional capabilities for your add-in. For example, you can enable your users to access
content from different data sources, such as OneDrive or a customer relationship
management (CRM) system, directly from their Outlook client.

7 Note

Support for this feature was introduced in requirement set 1.13. See clients and
platforms that support this requirement set.

Set up your environment


Complete the Outlook quick start to create an add-in project with the Yeoman generator
for Office Add-ins.

Configure the manifest

7 Note

This feature isn't currently supported in the Unified manifest for Microsoft 365
(preview), but the team is working on making this available.

To activate your add-in with the Reading Pane turned off or without a message selected,
you must add the SupportsNoItemContext child element to the <Action> element and
set its value to true . As this feature can only be implemented with a task pane in
Message Read mode, the following elements must also be configured.

The xsi:type attribute value of the <ExtensionPoint> element must be set to


MessageReadCommandSurface .
The xsi:type attribute value of the <Action> element must be set to
ShowTaskpane .

1. In your preferred code editor, open the Outlook quick start project you created.

2. Open the manifest.xml file located at the root of the project.

3. Select the entire <VersionOverrides> node and replace it with the following XML.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.13">
<bt:Set Name="Mailbox"/>
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<!-- Message Read mode-->
<ExtensionPoint
xsi:type="MessageReadCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgReadGroup">
<Label resid="GroupLabel"/>
<Control xsi:type="Button"
id="msgReadOpenPaneButton">
<Label
resid="TaskpaneButton.Label"/>
<Supertip>
<Title
resid="TaskpaneButton.Label"/>
<Description
resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16"
resid="Icon.16x16"/>
<bt:Image size="32"
resid="Icon.32x32"/>
<bt:Image size="80"
resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation
resid="Taskpane.Url"/>
<!-- Enables your add-in to
activate without the Reading Pane enabled or a message selected. -->

<SupportsNoItemContext>true</SupportsNoItemContext>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Taskpane.Url"
DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Test
walkthrough"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show
Taskpane"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip"
DefaultValue="Opens a task pane."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

4. Save your changes.

Configure the task pane


1. In your project, navigate to the taskpane folder, then open taskpane.html.

2. Replace the entire <body> element with the following markup.

HTML

<body class="ms-font-m ms-welcome ms-Fabric">


<header class="ms-welcome__header ms-bgColor-neutralLighter">
<img width="90" height="90" src="../../assets/logo-filled.png"
alt="logo" title="Add-in logo" />
<h1 class="ms-font-su">Activate your add-in without enabling
the Reading Pane or selecting a message</h1>
</header>
<section id="sideload-msg" class="ms-welcome__main">
<h2 class="ms-font-xl">Please <a target="_blank"
href="https://learn.microsoft.com/office/dev/add-ins/testing/test-
debug-office-add-ins#sideload-an-office-add-in-for-
testing">sideload</a> your add-in to see app body.</h2>
</section>
<main id="app-body" class="ms-welcome__main" style="display:
none;">
<ul class="ms-List ms-welcome__features">
<li class="ms-ListItem">
<i class="ms-Icon ms-Icon--CheckList ms-font-xl"></i>
<span class="ms-font-m">Item multi-select is
automatically enabled when the <b>SupportsNoItemContext</b> manifest
element is set to <code>true</code>. You can test this by selecting
multiple messages in Outlook, then choosing <b>Show Taskpane</b> from
the ribbon.</span>
</li>
<li class="ms-ListItem">
<i class="ms-Icon ms-Icon--Pin ms-font-xl"></i>
<span class="ms-font-m">Support to pin the task pane is
also automatically enabled. You can test this by selecting the
<b>pin</b> icon from the top right corner of the task pane.</span>
</li>
<li class="ms-ListItem">
<i class="ms-Icon ms-Icon--DockRight ms-font-xl"></i>
<span class="ms-font-m">This feature can only be
implemented with a task pane.</span>
</li>
<li class="ms-ListItem">
<i class="ms-Icon ms-Icon--Design ms-font-xl"></i>
<span class="ms-font-m">Implement your scenario using
this feature today! For example, enable your users to access content
from different data sources, such as OneDrive or your customer
relationship management (CRM) system, without first selecting a
message.</span>
</li>
</ul>
</main>
</body>

3. Save your changes.

Update the task pane JavaScript file


1. From the taskpane folder, open taskpane.js.
2. Navigate to the Office.onReady function and replace its contents with the
following code.

JavaScript

if (info.host === Office.HostType.Outlook) {


document.getElementById("sideload-msg").style.display = "none";
document.getElementById("app-body").style.display = "flex";
}

3. Save your changes.

Try it out
1. From a terminal, run the following code in the root directory of your project. This
starts the local web server and sideloads your add-in.

command

npm start

 Tip

If your add-in doesn't automatically sideload, follow the instructions in


Sideload Outlook add-ins for testing to manually sideload it in Outlook.

2. Navigate to your inbox and do one of the following:

Turn off your Reading Pane. For guidance, see the "Turn on, turn off, or move
the Reading Pane" section of Use and configure the Reading Pane to preview
messages .
Deselect a message, if applicable. To deselect a message, hold the Ctrl key
and select the message.

3. Select Show Taskpane from the ribbon.

4. Explore and test the suggestions listed in the task pane.

Support for the item multi-select and pinnable


task pane features
When the <SupportsNoItemContext> element in the manifest is set to true , it
automatically enables the item multi-select and pinnable task pane features, even if
these features aren't explicitly configured in the manifest.

See also
Activate your Outlook add-in on multiple messages
Implement a pinnable task pane in Outlook
Activate your Outlook add-in on
multiple messages
Article • 06/19/2023

With the item multi-select feature, your Outlook add-in can now activate and perform
operations on multiple selected messages in one go. Certain operations, such as
uploading messages to your Customer Relationship Management (CRM) system or
categorizing numerous items, can now be easily completed with a single click.

The following sections walk you through how to configure your add-in to retrieve the
subject line of multiple messages in read mode.

7 Note

Support for the item multi-select feature was introduced in requirement set 1.13.
See clients and platforms that support this requirement set.

Set up your environment


Complete the Outlook quick start to create an add-in project with the Yeoman generator
for Office Add-ins.

Configure the manifest

7 Note

The item multi-select feature isn't currently supported in the Unified manifest for
Microsoft 365 (preview), but the team is working on making this available.

To enable your add-in to activate on multiple selected messages, you must add the
SupportsMultiSelect child element to the <Action> element and set its value to true .
As item multi-select only supports messages at this time, the <ExtensionPoint>
element's xsi:type attribute value must be set to MessageReadCommandSurface or
MessageComposeCommandSurface .

1. In your preferred code editor, open the Outlook quick start project you created.

2. Open the manifest.xml file located at the root of the project.


3. Assign the <Permissions> element the ReadWriteMailbox value.

XML

<Permissions>ReadWriteMailbox</Permissions>

4. Select the entire <VersionOverrides> node and replace it with the following XML.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.13">
<bt:Set Name="Mailbox"/>
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<!-- Message Read mode-->
<ExtensionPoint
xsi:type="MessageReadCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgReadGroup">
<Label resid="GroupLabel"/>
<Control xsi:type="Button"
id="msgReadOpenPaneButton">
<Label
resid="TaskpaneButton.Label"/>
<Supertip>
<Title
resid="TaskpaneButton.Label"/>
<Description
resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16"
resid="Icon.16x16"/>
<bt:Image size="32"
resid="Icon.32x32"/>
<bt:Image size="80"
resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation
resid="Taskpane.Url"/>
<!-- Enables your add-in to
activate on multiple selected messages. -->

<SupportsMultiSelect>true</SupportsMultiSelect>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Taskpane.Url"
DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Item Multi-
select"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show
Taskpane"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip"
DefaultValue="Opens a pane which displays an option to retrieve the
subject line of selected messages."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

7 Note

Item multi-select can also be enabled without the <SupportsMultiSelect>


element if the <SupportsNoItemContext> element is included in the
manifest. To learn more, see Activate your Outlook add-in without the
Reading Pane enabled or a message selected.

5. Save your changes.

Configure the task pane


Item multi-select relies on the SelectedItemsChanged event to determine when
messages are selected or deselected. This event requires a task pane implementation.

1. From the ./src/taskpane folder, open taskpane.html.

2. In the <body> element, replace the entire <main> element with the following
markup.

HTML

<main id="app-body" class="ms-welcome__main">


<h2 class="ms-font-xl">Retrieve the subject line of multiple
messages with one click!</h2>
<ul id="selected-items"></ul>
<div role="button" id="run" class="ms-welcome__action ms-Button ms-
Button--hero ms-font-xl">
<span class="ms-Button-label">Run</span>
</div>
</main>

3. Save your changes.

Implement a handler for the


SelectedItemsChanged event
To alert your add-in when the SelectedItemsChanged event occurs, you must register an
event handler using the addHandlerAsync method.

1. From the ./src/taskpane folder, open taskpane.js.

2. In the Office.onReady() callback function, replace the existing code with the
following:

JavaScript

if (info.host === Office.HostType.Outlook) {


document.getElementById("sideload-msg").style.display = "none";
document.getElementById("app-body").style.display = "flex";
document.getElementById("run").onclick = run;

// Register an event handler to identify when messages are


selected.

Office.context.mailbox.addHandlerAsync(Office.EventType.SelectedItemsCh
anged, run, asyncResult => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("Event handler added.");


});
}

Retrieve the subject line of selected messages


Now that you've registered an event handler, you then call the getSelectedItemsAsync
method to retrieve the subject line of the selected messages and log them to the task
pane. The getSelectedItemsAsync method can also be used to get other message
properties, such as the item ID, item type ( Message is the only supported type at this
time), and item mode ( Read or Compose ).

7 Note

Additional message properties, such as conversationId , internetMessageId , and


hasAttachment , are in preview in Outlook on Windows. To preview these properties,
you must install Version 2305 (Build 16501.20210) or later. For more information on
these properties, see Office.SelectedItemDetails.

1. In taskpane.js, navigate to the run function and insert the following code.

JavaScript

// Clear list of previously selected messages, if any.


const list = document.getElementById("selected-items");
while (list.firstChild) {
list.removeChild(list.firstChild);
}

// Retrieve the subject line of the selected messages and log it to a


list in the task pane.
Office.context.mailbox.getSelectedItemsAsync(asyncResult => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

asyncResult.value.forEach(item => {
const listItem = document.createElement("li");
listItem.textContent = item.subject;
list.appendChild(listItem);
});
});
2. Save your changes.

Try it out
1. From a terminal, run the following code in the root directory of your project. This
starts the local web server and sideloads your add-in.

command

npm start

 Tip

If your add-in doesn't automatically sideload, follow the instructions in


Sideload Outlook add-ins for testing to manually sideload it in Outlook.

2. In Outlook, ensure the Reading Pane is enabled. To enable the Reading Pane, see
Use and configure the Reading Pane to preview messages .

3. Navigate to your inbox and choose multiple messages by holding Ctrl while
selecting messages.

4. Select Show Taskpane from the ribbon.

5. In the task pane, select Run to view a list of the selected messages' subject lines.
Item multi-select behavior and limitations
Item multi-select only supports messages within an Exchange mailbox in both read and
compose modes. An Outlook add-in only activates on multiple messages if the following
conditions are met.

The messages must be selected from one Exchange mailbox at a time. Non-
Exchange mailboxes aren't supported.
The messages must be selected from one mailbox folder at a time. An add-in
doesn't activate on multiple messages if they're located in different folders, unless
Conversations view is enabled. For more information, see Multi-select in
conversations.
An add-in must implement a task pane in order to detect the
SelectedItemsChanged event.
The Reading Pane in Outlook must be enabled. An exception to this is if the item
multi-select feature is enabled through the <SupportsNoItemContext> element in
the manifest. To learn more, see Activate your Outlook add-in without the Reading
Pane enabled or a message selected.
A maximum of 100 messages can be selected at a time.

7 Note

Meeting invites and responses are considered messages, not appointments, and
can therefore be included in a selection.

Multi-select in conversations
Item multi-select supports Conversations view whether it's enabled on your mailbox
or on specific folders. The following table describes expected behaviors when
conversations are expanded or collapsed, when the conversation header is selected, and
when conversation messages are located in a different folder from the one currently in
view.

Selection Expanded conversation view Collapsed conversation view

Conversation If the conversation header is the only The newest message (that is, the
header is item selected, an add-in supporting first message in the conversation
selected multi-select doesn't activate. However, if stack) is included in the message
other non-header messages are also selection.
selected, the add-in will only activate on
those and not the selected header. If the newest message in the
conversation is located in another
folder from the one currently in
view, the subsequent message in
the stack located in the current
folder is included in the selection.

Selected All chosen conversation messages are Not applicable. Only the
conversation included in the selection. conversation header is available for
messages are selection in collapsed conversation
located in the view.
same folder as
the one
currently in
view
Selection Expanded conversation view Collapsed conversation view

Selected All chosen conversation messages are Not applicable. Only the
conversation included in the selection. conversation header is available for
messages are selection in collapsed conversation
located in view.
different
folders from
the one
currently in
view

Next steps
Now that you've enabled your add-in to operate on multiple selected messages, you
can extend your add-in's capabilities and further enhance the user experience. Explore
performing more complex operations by using the selected messages' item IDs with
services such as Exchange Web Services (EWS) and Microsoft Graph.

See also
Office add-in manifests
Call web services from an Outlook add-in
Overview of Microsoft Graph
Activate your Outlook add-in without the Reading Pane enabled or a message
selected
Implement a pinnable task pane in
Outlook
Article • 04/27/2023

The task pane UX shape for add-in commands opens a vertical task pane to the right of
an open message or meeting request, allowing the add-in to provide UI for more
detailed interactions (filling in multiple fields, etc.). This task pane can be shown in the
Reading Pane when viewing a list of messages, allowing for quick processing of a
message.

However, by default, if a user has an add-in task pane open for a message in the
Reading Pane, and then selects a new message, the task pane is automatically closed.
For a heavily-used add-in, the user may prefer to keep that pane open, eliminating the
need to reactivate the add-in on each message. With pinnable task panes, your add-in
can give the user that option.

7 Note

Although the pinnable task panes feature was introduced in requirement set 1.5,
it's currently only available to Microsoft 365 subscribers using the following:

Outlook 2016 or later on Windows (Build 7668.2000 or later for users in the
Current or Microsoft 365 Insider Channels, Build 7900.xxxx or later for users in
Deferred channels)
Outlook 2016 or later on Mac (Version 16.13.503 or later)
Modern Outlook on the web

) Important

Pinnable task panes are not available for the following:

Appointments/Meetings
Outlook.com

Support task pane pinning


The first step is to add pinning support, which is done in the add-in manifest. The
markup varies depending on the type of manifest.
XML Manifest

Add the SupportsPinning element to the <Action> element that describes the task
pane button. The following is an example.

XML

<!-- Task pane button -->


<Control xsi:type="Button" id="msgReadOpenPaneButton">
<Label resid="paneReadButtonLabel" />
<Supertip>
<Title resid="paneReadSuperTipTitle" />
<Description resid="paneReadSuperTipDescription" />
</Supertip>
<Icon>
<bt:Image size="16" resid="green-icon-16" />
<bt:Image size="32" resid="green-icon-32" />
<bt:Image size="80" resid="green-icon-80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="readTaskPaneUrl" />
<SupportsPinning>true</SupportsPinning>
</Action>
</Control>

The <SupportsPinning> element is defined in the VersionOverrides v1.1 schema, so


you will need to include a VersionOverrides element both for v1.0 and v1.1.

For a full example, see the msgReadOpenPaneButton control in the command-demo


sample manifest .

7 Note

A pinnable task pane can also be enabled without the <SupportsPinning> element
if the <SupportsNoItemContext> element is included in the manifest. To learn
more, see Activate your Outlook add-in without the Reading Pane enabled or a
message selected.

Handling UI updates based on currently


selected message
To update your task pane's UI or internal variables based on the current item, you'll
need to register an event handler to get notified of the change.
Implement the event handler
The event handler should accept a single parameter, which is an object literal. The type
property of this object will be set to Office.EventType.ItemChanged . When the event is
called, the Office.context.mailbox.item object is already updated to reflect the
currently selected item.

JavaScript

function itemChanged(eventArgs) {
// Update UI based on the new current item
UpdateTaskPaneUI(Office.context.mailbox.item);
}

) Important

The implementation of event handlers for an ItemChanged event should check


whether or not the Office.content.mailbox.item is null.

JavaScript

// Example implementation
function UpdateTaskPaneUI(item)
{
// Assuming that item is always a read item (instead of a compose
item).
if (item != null) console.log(item.subject);
}

Register the event handler


Use the Office.context.mailbox.addHandlerAsync method to register your event handler
for the Office.EventType.ItemChanged event. This should be done in the
Office.initialize function for your task pane.

JavaScript

Office.initialize = function (reason) {


$(document).ready(function () {

// Set up ItemChanged event


Office.context.mailbox.addHandlerAsync(Office.EventType.ItemChanged,
itemChanged);
UpdateTaskPaneUI(Office.context.mailbox.item);
});
};

Deploy to users
If you plan to publish your Outlook add-in to AppSource , and it's configured with a
pinnable task pane, your add-in content must not be static and must clearly display data
related to the message that is open or selected in the mailbox. This ensures that your
add-in will pass AppSource validation.

See also
For an example add-in that implements a pinnable task pane, see command-demo on
GitHub.
Add-ins for Outlook on mobile devices
Article • 07/13/2023

Add-ins now work in Outlook on mobile devices, using the same APIs available for other
Outlook endpoints. If you've built an add-in for Outlook already, it's easy to get it
working on Outlook mobile.

Outlook mobile add-ins are supported on all Microsoft 365 business accounts and
Outlook.com accounts. However, support is not currently available on Gmail accounts.

An example task pane in Outlook on iOS

An example task pane in Outlook on Android


What's different on mobile?
The small size and quick interactions make designing for mobile a challenge. To
ensure quality experiences for customers, any add-in declaring mobile support
must meet certain validation criteria to be approved in AppSource.
The add-in MUST adhere to the UI guidelines.
The scenario for the add-in MUST make sense on mobile.

7 Note

Add-ins using the Unified manifest for Microsoft 365 (preview) aren't currently
supported on mobile devices.
In general, only Message Read mode is supported at this time. That means
MobileMessageReadCommandSurface is the only ExtensionPoint you should declare in

the mobile section of your manifest. However, there are a couple of exceptions.

1. Appointment Organizer mode is supported for online meeting provider


integrated add-ins which instead declare the
MobileOnlineMeetingCommandSurface extension point. For more
information on this scenario, see Create an Outlook mobile add-in for an
online-meeting provider.
2. Appointment Attendee mode is supported for integrated add-ins created by
providers of note-taking and customer relationship management (CRM)
applications. Such add-ins should instead declare the
MobileLogEventAppointmentAttendee extension point. For more information
on this scenario, see Log appointment notes to an external application in
Outlook mobile add-ins.
3. Event-based add-ins that activate on the OnNewMessageCompose event must
declare the LaunchEvent extension point. For more information, see
Implement event-based activation in Outlook mobile add-ins.

The makeEwsRequestAsync API isn't supported on mobile since the mobile app
uses REST APIs to communicate with the server. If your app backend needs to
connect to the Exchange server, you can use the callback token to make REST API
calls. For details, see Use the Outlook REST APIs from an Outlook add-in.

When you submit your add-in to the store with MobileFormFactor in the manifest,
you'll need to agree to our developer addendum for add-ins on iOS, and you must
submit your Apple Developer ID for verification.

Finally, your manifest needs to declare MobileFormFactor , and include the correct
types of controls and icon sizes. To learn more, see Add support for add-in
commands in Outlook on mobile devices.

What makes a good scenario for Outlook


mobile add-ins?
Remember that the average Outlook session length on a phone is much shorter than on
a PC. That means your add-in must be fast, and the scenario must allow the user to get
in, get out, and get on with their email workflow.

Here are examples of scenarios that make sense in Outlook mobile.


The add-in brings valuable information into Outlook, helping users triage their
email and respond appropriately. For example, a customer relationship
management (CRM) add-in that lets the user see customer information and share
appropriate information.

The add-in adds value to the user's email content by saving the information to a
tracking, collaboration, or similar system. For example, an add-in that lets users
turn emails into task items for project tracking, or help tickets for a support team.

An example user interaction to create a Trello card from an email message on iOS

An example user interaction to create a Trello card from an email message on Android
Testing your add-ins on mobile
To test an add-in on Outlook mobile, first sideload an add-in using a Microsoft 365 or
Outlook.com account in Outlook on the web, on Windows, or on Mac. Make sure your
manifest is properly formatted to contain MobileFormFactor or it won't load in Outlook
mobile.

After your add-in is working, make sure to test it on different screen sizes, including
phones and tablets. You should make sure it meets accessibility guidelines for contrast,
font size, and color, as well as being usable with a screen reader such as VoiceOver on
iOS or TalkBack on Android.

Troubleshooting on mobile can be hard since you may not have the tools you're used
to. However, one option for troubleshooting on iOS is to use Fiddler (check out this
tutorial on using it with an iOS device ).
7 Note

Modern Outlook on the web on iPhone and Android smartphones is no longer


required or available for testing Outlook add-ins. Additionally, add-ins aren't
supported in Outlook on Android, on iOS, and modern mobile web with on-
premises Exchange accounts. Certain iOS devices still support add-ins when using
on-premises Exchange accounts with classic Outlook on the web. For information
about supported devices, see Requirements for running Office Add-ins.

Next steps
Learn how to:

Add support for add-in commands in Outlook on mobile devices.


Implement supported Outlook JavaScript APIs.
Design a great mobile experience for your add-in.
Get an access token and call Outlook REST APIs from your add-in.
Add support for add-in commands in
Outlook on mobile devices
Article • 07/13/2023

Using add-in commands in Outlook on mobile devices allows your users to access the
same functionality (with some limitations) that they already have in Outlook on the web,
on Windows, and on Mac. Adding support for Outlook mobile requires updating the
add-in manifest and possibly changing your code for mobile scenarios.

Update the manifest

7 Note

Add-ins using the Unified manifest for Microsoft 365 (preview) aren't currently
supported on mobile devices.

The first step to enabling add-in commands in Outlook mobile is to define them in the
add-in manifest. The VersionOverrides v1.1 schema defines a new form factor for
mobile, MobileFormFactor.

This element contains all of the information for loading the add-in in mobile clients. This
enables you to define completely different UI elements and JavaScript files for the
mobile experience.

The following example shows a single task pane button in a MobileFormFactor element.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
...
<MobileFormFactor>
<FunctionFile resid="residUILessFunctionFileUrl" />
<ExtensionPoint xsi:type="MobileMessageReadCommandSurface">
<Group id="mobileMsgRead">
<Label resid="groupLabel" />
<Control xsi:type="MobileButton" id="TaskPaneBtn">
<Label resid="residTaskPaneButtonName" />
<Icon xsi:type="bt:MobileIconList">
<bt:Image size="25" scale="1" resid="tp0icon" />
<bt:Image size="25" scale="2" resid="tp0icon" />
<bt:Image size="25" scale="3" resid="tp0icon" />
<bt:Image size="32" scale="1" resid="tp0icon" />
<bt:Image size="32" scale="2" resid="tp0icon" />
<bt:Image size="32" scale="3" resid="tp0icon" />

<bt:Image size="48" scale="1" resid="tp0icon" />


<bt:Image size="48" scale="2" resid="tp0icon" />
<bt:Image size="48" scale="3" resid="tp0icon" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="residTaskpaneUrl" />
</Action>
</Control>
</Group>
</ExtensionPoint>
</MobileFormFactor>
...
</VersionOverrides>

This is very similar to the elements that appear in a DesktopFormFactor element, with
some notable differences.

The OfficeTab element isn't used.


The ExtensionPoint element must have only one child element. If the add-in only
adds one button, the child element should be a Control element. If the add-in adds
more than one button, the child element should be a Group element that contains
multiple Control elements.
There is no Menu type equivalent for the Control element.
The Supertip element isn't used.
The required icon sizes are different. Mobile add-ins minimally must support
25x25, 32x32 and 48x48 pixel icons.

Code considerations
Designing an add-in for mobile introduces some additional considerations.

Use REST instead of Exchange Web Services


The Office.context.mailbox.makeEwsRequestAsync method isn't supported in Outlook
mobile. Add-ins should prefer to get information from the Office.js API when possible. If
add-ins require information not exposed by the Office.js API, then they should use the
Outlook REST APIs to access the user's mailbox.

Mailbox requirement set 1.5 introduced a new version of


Office.context.mailbox.getCallbackTokenAsync that can request an access token
compatible with the REST APIs, and a new Office.context.mailbox.restUrl property that
can be used to find the REST API endpoint for the user.

Pinch zoom
By default users can use the "pinch zoom" gesture to zoom in on task panes. If this
doesn't make sense for your scenario, be sure to disable pinch zoom in your HTML.

Close task panes


In Outlook mobile, task panes take up the entire screen and by default require the user
to close them to return to the message. Consider using the
Office.context.ui.closeContainer method to close the task pane when your scenario is
complete.

Compose mode and appointments


Currently, add-ins in Outlook mobile only support activation when reading messages.
Add-ins aren't activated when composing messages or when viewing or composing
appointments. However, there are some exceptions.

1. Online meeting provider integrated add-ins activate in Appointment Organizer


mode. For more information about this exception (including available APIs), see
Create an Outlook mobile add-in for an online-meeting provider.
2. Add-ins that log appointment notes and other details to customer relationship
management (CRM) or note-taking services activate in Appointment Attendee
mode. For more information about this exception (including available APIs), see
Log appointment notes to an external application in Outlook mobile add-ins.
3. Event-based add-ins activate when the OnNewMessageCompose event occurs. For
more information about this exception (including additional supported APIs), see
Implement event-based activation in Outlook mobile add-ins.

Supported APIs
Although Outlook mobile supports up to Mailbox requirement set 1.5, you can now
implement additional APIs from later requirement sets to further extend the capability
of your add-in on Outlook mobile. For guidance on which APIs you can implement in
your mobile add-in, see Outlook JavaScript APIs supported in Outlook on mobile
devices.
See also
Requirement sets supported by Exchange servers and Outlook clients
Outlook JavaScript APIs supported in Outlook on mobile devices
Outlook JavaScript APIs supported in
Outlook on mobile devices
Article • 07/13/2023

Outlook on Android and on iOS support up to Mailbox requirement set 1.5. To further
extend the capabilities of an Outlook mobile add-in, certain APIs from later requirement
sets, previously available only to Outlook desktop and web clients, are now enabled for
mobile support. This article outlines the APIs supported in Outlook mobile and any
implementation exceptions.

Supported APIs
The following table lists a subset of APIs from requirement sets beyond 1.5 that can now
be implemented in Outlook mobile add-ins. Even if the minimum requirement set
specified in the manifest of your add-in is greater than 1.5, as long as the API used from
the later requirement set is supported, the add-in will appear and activate in Outlook on
Android or on iOS. For more information on how to specify the minimum requirement
set in your add-in, see Outlook JavaScript API requirement sets.

API Minimum Supported


requirement Outlook
set modes

Office.context.mailbox.item.addFileAttachmentFromBase64Async Mailbox 1.8 Message


Compose

Office.context.mailbox.item.body.setSignatureAsync Mailbox 1.10 Message


Compose

Office.context.mailbox.item.disableClientSignatureAsync Mailbox 1.10 Message


Compose

Office.context.mailbox.item.from.getAsync Mailbox 1.7 Message


Compose

Office.context.mailbox.item.getComposeTypeAsync Mailbox 1.10 Message


Compose

Unsupported APIs
Although Outlook mobile supports up to requirement set 1.5, there are some APIs from
these earlier requirement sets that aren't supported. The following table lists these APIs
and also notes features that aren't supported in certain Outlook modes.

API Minimum Unsupported


requirement set Outlook modes

Office.context.officeTheme Mailbox preview - Message Read


- Message
Compose
- Appointment
Attendee
- Appointment
Organizer

Office.context.mailbox.ewsUrl Mailbox 1.1 - Message Read


- Message
Compose
- Appointment
Attendee
- Appointment
Organizer

Office.context.mailbox.convertToEwsId Mailbox 1.3 - Message Read


- Message
Compose
- Appointment
Attendee
- Appointment
Organizer

Office.context.mailbox.convertToRestId Mailbox 1.3 - Message Read


- Message
Compose
- Appointment
Attendee
- Appointment
Organizer

Office.context.mailbox.displayAppointmentForm Mailbox 1.1 - Message Read


- Message
Compose
- Appointment
Attendee
- Appointment
Organizer

Office.context.mailbox.displayMessageForm Mailbox 1.1 - Message Read


- Message
Compose
- Appointment
Attendee
API Minimum Unsupported
requirement set Outlook modes

- Appointment
Organizer

Office.context.mailbox.displayNewAppointmentForm Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.getCallbackTokenAsync(options, Mailbox 1.5 - Message


callback) Compose
- Appointment
Organizer

Office.context.mailbox.getCallbackTokenAsync(callback, Mailbox 1.1 (Read - Message Read


userContext) mode support) - Message
Compose
Mailbox 1.3 - Appointment
(Compose mode Attendee
support) - Appointment
Organizer

Office.context.mailbox.makeEwsRequestAsync Mailbox 1.1 - Message Read


- Message
Compose
- Appointment
Attendee
- Appointment
Organizer

Office.context.mailbox.item.dateTimeModified Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.item.displayReplyAllForm Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.item.displayReplyForm Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.item.getEntities Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.item.getEntitiesByType Mailbox 1.1 - Message Read


- Appointment
Attendee
API Minimum Unsupported
requirement set Outlook modes

Office.context.mailbox.item.getFilteredEntitiesByName Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.item.getRegexMatches Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.item.getRegexMatchesByName Mailbox 1.1 - Message Read


- Appointment
Attendee

Office.context.mailbox.item.bcc.addAsync Mailbox 1.1 - Message


Compose
Office.context.mailbox.item.cc.addAsync

Office.context.mailbox.item.to.addAsync

Office.context.mailbox.item.bcc.setAsync Mailbox 1.1 - Message


Compose
Office.context.mailbox.item.cc.setAsync

Office.context.mailbox.item.to.setAsync

Office.context.mailbox.item.body.getTypeAsync Mailbox 1.1 - Message


Compose

Office.context.mailbox.item.body.prependAsync Mailbox 1.1 - Message


Compose

Office.context.mailbox.item.body.setAsync Mailbox 1.1 - Message


Compose

Office.context.mailbox.item.subject.setAsync Mailbox 1.1 - Message


Compose

See also
Outlook JavaScript API requirement sets
Add-ins for Outlook on mobile devices
Add support for add-in commands in Outlook on mobile devices
Requirement sets supported by Exchange servers and Outlook clients
Log appointment notes to an external
application in Outlook mobile add-ins
Article • 07/13/2023

Saving your appointment notes and other details to a customer relationship


management (CRM) or note-taking application can help you keep track of meetings
you've attended.

In this article, you'll learn how to set up your Outlook mobile add-in to enable users to
log notes and other details about their appointments to your CRM or note-taking
application. Throughout this article, we'll be using a fictional CRM service provider
named "Contoso".

Supported clients
Logging notes to an external application from an Outlook mobile add-in is supported in
Outlook on Android and on iOS with a Microsoft 365 subscription.

Set up your environment


Complete the Outlook quick start to create an add-in project with the Yeoman generator
for Office Add-ins.

Capture and view appointment notes


You can opt to implement a function command or task pane. To update your add-in,
select the tab for either function command or task pane, then follow the instructions.

Function command

This option will enable a user to log and view their notes and other details about
their appointments when they select a function command from the ribbon.

Configure the manifest


To enable users to log appointment notes with your add-in, you must configure the
MobileLogEventAppointmentAttendee extension point in the manifest under the
parent element MobileFormFactor . Other form factors aren't supported.
7 Note

Add-ins using the Unified manifest for Microsoft 365 (preview) aren't
currently supported on mobile devices.

1. In your code editor, open the quick start project.

2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including open and close tags)
and replace it with the following XML. Make sure to replace all references to
Contoso with your company's information.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/
1.1" xsi:type="VersionOverridesV1_1">
<Description resid="residDescription"></Description>
<Requirements>
<bt:Sets>
<bt:Set Name="Mailbox" MinVersion="1.3"/>
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<FunctionFile resid="residFunctionFile"/>
<ExtensionPoint
xsi:type="AppointmentAttendeeCommandSurface">
<OfficeTab id="TabDefault">
<Group id="apptReadGroup">
<Label resid="residDescription"/>
<Control xsi:type="Button"
id="apptReadOpenPaneButton">
<Label resid="residLabel"/>
<Supertip>
<Title resid="residLabel"/>
<Description resid="residTooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="icon-16"/>
<bt:Image size="32" resid="icon-32"/>
<bt:Image size="80" resid="icon-80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>logCRMEvent</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
<MobileFormFactor>
<FunctionFile resid="residFunctionFile"/>
<ExtensionPoint
xsi:type="MobileLogEventAppointmentAttendee">
<Control xsi:type="MobileButton"
id="appointmentReadFunctionButton">
<Label resid="residLabel"/>
<Icon>
<bt:Image size="25" scale="1" resid="icon-16"/>
<bt:Image size="25" scale="2" resid="icon-16"/>
<bt:Image size="25" scale="3" resid="icon-16"/>
<bt:Image size="32" scale="1" resid="icon-32"/>
<bt:Image size="32" scale="2" resid="icon-32"/>
<bt:Image size="32" scale="3" resid="icon-32"/>
<bt:Image size="48" scale="1" resid="icon-48"/>
<bt:Image size="48" scale="2" resid="icon-48"/>
<bt:Image size="48" scale="3" resid="icon-48"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>logCRMEvent</FunctionName>
</Action>
</Control>
</ExtensionPoint>
</MobileFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="icon-16"
DefaultValue="https://contoso.com/assets/icon-16.png"/>
<bt:Image id="icon-32"
DefaultValue="https://contoso.com/assets/icon-32.png"/>
<bt:Image id="icon-48"
DefaultValue="https://contoso.com/assets/icon-48.png"/>
<bt:Image id="icon-80"
DefaultValue="https://contoso.com/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="residFunctionFile"
DefaultValue="https://contoso.com/commands.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="residDescription" DefaultValue="Log
appointment notes and other details to Contoso CRM."/>
<bt:String id="residLabel" DefaultValue="Log to Contoso
CRM"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="residTooltip" DefaultValue="Log notes to
Contoso CRM for this appointment."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

 Tip

To learn more about manifests for Outlook add-ins, see Office add-in
manifests and Add support for add-in commands in Outlook on mobile
devices.

Capture appointment notes


In this section, learn how your add-in can extract appointment details when the
user selects the Log button.

1. From the same quick start project, open the file


./src/commands/commands.js in your code editor.

2. Replace the entire content of the commands.js file with the following
JavaScript.

JavaScript

var event;

Office.initialize = function (reason) {


// Add any initialization code here.
};

function logCRMEvent(appointmentEvent) {
event = appointmentEvent;
console.log(`Subject: ${Office.context.mailbox.item.subject}`);
Office.context.mailbox.item.body.getAsync(
"html",
{ asyncContext: "This is passed to the callback" },
function callback(result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
event.completed({ allowEvent: true });
} else {
console.error("Failed to get body.");
event.completed({ allowEvent: false });
}
}
);
}
// Register the function.
Office.actions.associate("logCRMEvent", logCRMEvent);

Next, update the commands.html file to reference commands.js.

1. From the same quick start project, open the ./src/commands/commands.html


file in your code editor.

2. Find and replace <script type="text/javascript"


src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js">

</script> with the following:

HTML

<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js">
</script>
<script type="text/javascript" src="commands.js"></script>

View appointment notes


The Log button label can be toggled to display View by setting the EventLogged
custom property reserved for this purpose. When the user selects the View button,
they can look at their logged notes for this appointment.

Your add-in defines the log viewing experience. For example, you can display the
logged appointment notes in a dialog when the user selects the View button. For
details on using dialogs, refer to Use the Office dialog API in your Office Add-ins.

Add the following function to ./src/commands/commands.js. This function sets the


EventLogged custom property on the current appointment item.

JavaScript

function updateCustomProperties() {
Office.context.mailbox.item.loadCustomPropertiesAsync(
function callback(customPropertiesResult) {
if (customPropertiesResult.status ===
Office.AsyncResultStatus.Succeeded) {
let customProperties = customPropertiesResult.value;
customProperties.set("EventLogged", true);
customProperties.saveAsync(
function callback(setSaveAsyncResult) {
if (setSaveAsyncResult.status ===
Office.AsyncResultStatus.Succeeded) {
console.log("EventLogged custom property saved
successfully.");
event.completed({ allowEvent: true });
event = undefined;
}
}
);
}
}
);
}

Then, call it after the add-in successfully logs the appointment notes. For example,
you can call it from logCRMEvent as shown in the following function.

JavaScript

function logCRMEvent(appointmentEvent) {
event = appointmentEvent;
console.log(`Subject: ${Office.context.mailbox.item.subject}`);
Office.context.mailbox.item.body.getAsync(
"html",
{ asyncContext: "This is passed to the callback" },
function callback(result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
// Replace `event.completed({ allowEvent: true });` with the
following statement.
updateCustomProperties();
} else {
console.error("Failed to get body.");
event.completed({ allowEvent: false });
}
}
);
}

Delete the appointment log


If you'd like to enable your users to undo logging or delete the logged
appointment notes so a replacement log can be saved, you have two options.

1. Use Microsoft Graph to clear the custom properties object when the user
selects the appropriate button on the ribbon.

2. Add the following function to ./src/commands/commands.js to clear the


EventLogged custom property on the current appointment item.

JavaScript
function clearCustomProperties() {
Office.context.mailbox.item.loadCustomPropertiesAsync(
function callback(customPropertiesResult) {
if (customPropertiesResult.status ===
Office.AsyncResultStatus.Succeeded) {
var customProperties = customPropertiesResult.value;
customProperties.remove("EventLogged");
customProperties.saveAsync(
function callback(removeSaveAsyncResult) {
if (removeSaveAsyncResult.status ===
Office.AsyncResultStatus.Succeeded) {
console.log("Custom properties cleared");
event.completed({ allowEvent: true });
event = undefined;
}
}
);
}
}
);
}

Then, call it when you want to clear the custom property. For example, you can call
it from logCRMEvent if setting the log failed in some way as shown in the following
function.

JavaScript

function logCRMEvent(appointmentEvent) {
event = appointmentEvent;
console.log(`Subject: ${Office.context.mailbox.item.subject}`);
Office.context.mailbox.item.body.getAsync(
"html",
{ asyncContext: "This is passed to the callback" },
function callback(result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
updateCustomProperties();
} else {
console.error("Failed to get body.");
// Replace `event.completed({ allowEvent: false });` with the
following statement.
clearCustomProperties();
}
}
);
}

Test and validate


1. Follow the usual guidance to test and validate your add-in.
2. After you sideload the add-in in Outlook on the web, Windows, or Mac, restart
Outlook on your Android or iOS mobile device.
3. Open an appointment as an attendee, then verify that under the Meeting Insights
card, there's a new card with your add-in's name alongside the Log button.

UI: Log the appointment notes


As a meeting attendee, you should see a screen similar to the following image when you
open a meeting.

UI: View the appointment log


After successfully logging the appointment notes, the button should now be labeled
View instead of Log. You should see a screen similar to the following image.

Available APIs
The following APIs are available for this feature.

Dialog APIs
Office.AddinCommands.Event
Office.CustomProperties
Office.RoamingSettings
Appointment Read (attendee) APIs except the following:
Office.context.mailbox.item.categories
Office.context.mailbox.item.enhancedLocation
Office.context.mailbox.item.isAllDayEvent
Office.context.mailbox.item.recurrence
Office.context.mailbox.item.sensitivity
Office.context.mailbox.item.seriesId

To learn more about APIs that are supported in Outlook on mobile devices, see Outlook
JavaScript APIs supported in Outlook on mobile devices.

Restrictions
Several restrictions apply.

The Log button name can't be changed. However, there's a way for a different
label to be displayed by setting a custom property on the appointment item. For
more details, refer to the View appointment notes section for function command
or task pane as appropriate.
The EventLogged custom property must be used if you want to toggle the label of
the Log button to View and back.
The add-in icon should be in grayscale using hex code #919191 or its equivalent in
other color formats .
The add-in should extract the meeting details from the appointment form within
the one-minute timeout period. However, any time spent in a dialog box the add-
in opened for authentication, for example, is excluded from the timeout period.

See also
Add-ins for Outlook on mobile devices
Add support for add-in commands in Outlook on mobile devices
Outlook JavaScript APIs supported in Outlook on mobile devices
Implement event-based activation in
Outlook mobile add-ins
Article • 07/13/2023

With the event-based activation feature, develop an add-in to automatically activate and
complete operations when certain events occur in Outlook on Android or on iOS, such
as composing a new message.

The following sections walk you through how to develop an Outlook mobile add-in that
automatically adds a signature to new messages being composed. This highlights a
sample scenario of how you can implement event-based activation in your mobile add-
in. Significantly enhance the mobile user experience by exploring other scenarios in your
add-in today.

To learn how to implement an event-based add-in for Outlook desktop clients, see
Configure your Outlook add-in for event-based activation.

7 Note

Outlook on Android and on iOS only support up to Mailbox requirement set 1.5.
However, to support the event-based activation feature, some APIs from later
requirement sets have been enabled on mobile clients. For more information on
this exception, see Additional supported APIs.

Supported clients
The add-in you develop in this walkthrough is supported in Outlook on Android and on
iOS with a Microsoft 365 subscription.

Set up your environment


Complete the Outlook quick start, which creates an add-in project with the Yeoman
generator for Office Add-ins.

Configure the manifest

7 Note
The Unified Microsoft 365 manifest (preview) isn't currently supported on mobile
devices.

To enable an event-based add-in on Outlook mobile, you must configure the following
elements in the VersionOverridesV1_1 node of the manifest.

In the Runtimes element, specify the HTML file that references the event-handling
JavaScript file.
Add the MobileFormFactor element to make your add-in available in Outlook
mobile.
Set the xsi:type of the ExtensionPoint element to LaunchEvent. This enables the
event-based activation feature in your Outlook mobile add-in.
In the LaunchEvent element, set the Type to OnNewMessageCompose and specify the
JavaScript function name of the event handler in the FunctionName attribute.

1. In your code editor, open the quick start project you created.

2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including the open and close tags)
and replace it with the following XML.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.5">
<bt:Set Name="Mailbox"/>
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<!-- The HTML file that references or contains the
JavaScript event handlers.
This is used by Outlook on mobile devices. -->
<Runtimes>
<Runtime resid="WebViewRuntime.Url">
</Runtime>
</Runtimes>
<!-- Defines the add-in for Outlook on Windows, on Mac,
and on the web. -->
<DesktopFormFactor>
<FunctionFile resid="Commands.Url"/>
<ExtensionPoint
xsi:type="MessageReadCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgReadGroup">
<Label resid="GroupLabel"/>
<Control xsi:type="Button"
id="msgReadOpenPaneButton">
<Label
resid="TaskpaneButton.Label"/>
<Supertip>
<Title
resid="TaskpaneButton.Label"/>
<Description
resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16"
resid="Icon.16x16"/>
<bt:Image size="32"
resid="Icon.32x32"/>
<bt:Image size="80"
resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation
resid="Taskpane.Url"/>
</Action>
</Control>
<Control xsi:type="Button"
id="ActionButton">
<Label resid="ActionButton.Label"/>
<Supertip>
<Title
resid="ActionButton.Label"/>
<Description
resid="ActionButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16"
resid="Icon.16x16"/>
<bt:Image size="32"
resid="Icon.32x32"/>
<bt:Image size="80"
resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">

<FunctionName>action</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
<!-- Defines the add-in for Outlook mobile. -->
<MobileFormFactor>
<!-- Configures event-based activation. -->
<ExtensionPoint xsi:type="LaunchEvent">
<LaunchEvents>
<LaunchEvent Type="OnNewMessageCompose"
FunctionName="onNewMessageComposeHandler"/>
</LaunchEvents>
<!-- Identifies the runtime to be used (also
referenced by the Runtime element). -->
<SourceLocation resid="WebViewRuntime.Url"/>
</ExtensionPoint>
</MobileFormFactor>
</Host>
</Hosts>
<!-- This manifest uses a fictitious web server, contoso.com,
to host the add-in's files.
Replace these instances with the information of the web
server that hosts your add-in's files. -->
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://contoso.com/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://contoso.com/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://contoso.com/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Commands.Url"
DefaultValue="https://contoso.com/commands.html"/>
<bt:Url id="Taskpane.Url"
DefaultValue="https://contoso.com/taskpane.html"/>
<bt:Url id="WebViewRuntime.Url"
DefaultValue="https://contoso.com/commands.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Event-based
activation on mobile"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show
Taskpane"/>
<bt:String id="ActionButton.Label"
DefaultValue="Perform an action"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip"
DefaultValue="Opens a pane displaying all available properties."/>
<bt:String id="ActionButton.Tooltip"
DefaultValue="Perform an action when clicked."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

4. Save your changes.


 Tip

To learn more about manifests for Outlook add-ins, see Outlook add-in manifests
and Add support for add-in commands in Outlook on mobile devices.

Implement the event handler


To enable your add-in to complete tasks when the OnNewMessageCompose event occurs,
you must implement a JavaScript event handler. In this section, you'll create the
onNewMessageComposeHandler function that adds a signature to a new message being

composed, then shows a message to notify that the signature was added.

1. From the same quick start project, navigate to the ./src directory, then create a
new folder named launchevent.

2. In the ./src/launchevent folder, create a new file named launchevent.js.

3. Open the launchevent.js file you created and add the following JavaScript code.

JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed
under the MIT license.
* See LICENSE in the project root for license information.
*/

// Add start-up logic code here, if any.


Office.onReady();

function onNewMessageComposeHandler(event) {
const item = Office.context.mailbox.item;
const signatureIcon =
"iVBORw0KGgoAAAANSUhEUgAAACcAAAAnCAMAAAC7faEHAAAAAXNSR0IArs4c6QAAAARnQU
1BAACxjwv8YQUAAAAzUExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAKMFRskAAAAQdFJOUwAQIDBAUGBwgI+fr7/P3+8jGoKKAAAACXBI
WXMAAA7DAAAOwwHHb6hkAAABT0lEQVQ4T7XT2ZalIAwF0DAJhMH+/6+tJOQqot6X6joPiou
NBo3w9/Hd6+hrYnUt6vhLcjEAJevVW0zJxABSlcunhERpjY+UKoNN5+ZgDGu2onNz0OngjP
2FM1VdyBW1LtvGeYrBLs7U5I1PTXZt+zifcS3Icw2GcS3vxRY3Vn/iqx31hUyTnV515kdTf
baNhZLI30AceqDiIo4tyKEmJpKdP5M4um+nUwfDWxAXdzqMNKQ14jLdL5ntXzxcRF440mhS
6yu882Kxa30RZcUIjTCJg7lscsR4VsMjfX9Q0Vuv/Wd3YosD1J4LuSRtaL7bzXGN1wx2cyt
UdncDuhA3fu6HPTiCvpQUIjZ3sCcHVbvLtbNTHlysx2w9/s27m9gEb+7CTri6hR1wcTf2gV
f3wBRe3CMbcHYvTODkXhnD0+178K/pZ9+n/C1ru/2HAPwAo7YM1X4+tLMAAAAASUVORK5CY
II=";

// Get the sender's account information.


item.from.getAsync((result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
console.log(result.error.message);
event.completed({ allowEvent: false });
return;
}

// Create a signature based on the sender's information.


const name = result.value.displayName;
const options = { asyncContext: name, isInline: true };
item.addFileAttachmentFromBase64Async(signatureIcon,
"signatureIcon.png", options, (result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
console.log(result.error.message);
event.completed({ allowEvent: false });
return;
}

// Add the created signature to the message.


const signature = "<img src='cid:signatureIcon.png'>" +
result.asyncContext;
item.body.setSignatureAsync(signature, { coercionType:
Office.CoercionType.Html }, (result) => {
if (result.status === Office.AsyncResultStatus.Failed)
{
console.log(result.error.message);
event.completed({ allowEvent: false });
return;
}

// Show a notification when the signature is added to


the message.
// Important: Only the InformationalMessage type is
supported in Outlook mobile at this time.
const notification = {
type:
Office.MailboxEnums.ItemNotificationMessageType.InformationalMessage,
message: "Company signature added.",
icon: "none",
persistent: false
};

item.notificationMessages.addAsync("signature_notification",
notification, (result) => {
if (result.status ===
Office.AsyncResultStatus.Failed) {
console.log(result.error.message);
event.completed({ allowEvent: false });
return;
}

event.completed({ allowEvent: true });


});
});
});
});
}
4. Save your changes.

Add a reference to the event-handling


JavaScript file
Ensure that the HTML file you specified in the <Runtime> element of your manifest has
a reference to the JavaScript file that contains your event handler.

1. Navigate to the ./src/commands folder, then open commands.html.

2. Immediately before the closing head tag ( </head> ), add a script entry for the
JavaScript file that contains the event handler.

HTML

<script type="text/javascript" src="../launchevent/launchevent.js">


</script>

3. Save you changes.

Test and validate your add-in


1. Follow the guidance to test and validate your add-in.

2. Sideload your add-in in Outlook on Windows, on Mac, or on the web.

3. Open Outlook on Android or on iOS. If you have Outlook already open on your
device, restart it.

4. Create a new message. The event-based add-in adds the signature to the message.
If you have a signature saved on your mobile device, it will briefly appear in the
message you create, but will be immediately replaced by the signature added by
the add-in.
Behavior and limitations
As you develop an event-based add-in for Outlook mobile, be mindful of the following
feature behaviors and limitations.

Only the OnNewMessageCompose event is supported on Outlook mobile at this time.


This event occurs when a new message (including reply, reply all, and forward) is
created. The OnNewMessageCompose event doesn't occur when you edit an existing
draft.
Because event-based add-ins are expected to be short-running and lightweight, an
add-in is allowed to run for a maximum of 60 seconds from the time it activates. To
signal that your add-in has completed processing an event, your event handler
must call the event.completed method. The add-in operation also ends when the
user closes the compose window or sends the message.
Only one add-in can run at a time. If multiple event-based add-ins are installed on
a user's account, they will run sequentially.
If you tap and hold the Outlook icon on your mobile device, then select New mail
to create a new message, the event-based add-in may take a few seconds to
initialize and complete processing the event.
If no changes are made to a new message being composed, a draft won't be
saved, even if the event-based add-in adds a signature using the
Office.context.mailbox.item.body.setSignatureAsync method.
In an event-based add-in that manages signatures, if you select Reply from the
bottom of a message, the add-in activates and adds the signature to the message.
However, the signature won't be visible in the current view. To view your message
with the added signature, expand the compose window to full screen.
To enhance your add-in's functionality, you can use supported APIs from later
requirement sets in compose mode. For more information, see Additional
supported APIs.

Additional supported APIs


Although Outlook mobile supports APIs up to Mailbox requirement set 1.5, to further
extend the capability of your event-based add-in in Outlook mobile, additional APIs
from later requirement sets are now supported in compose mode.

Office.context.mailbox.item.addFileAttachmentFromBase64Async
Office.context.mailbox.item.disableClientSignatureAsync
Office.context.mailbox.item.from.getAsync
Office.context.mailbox.item.getComposeTypeAsync
Office.context.mailbox.item.body.setSignatureAsync

To learn more about APIs that are supported in Outlook on mobile devices, see Outlook
JavaScript APIs supported in Outlook on mobile devices.

Deploy to users
Event-based add-ins must be deployed by an organization's administrator. For guidance
on how to deploy your add-in via the Microsoft 365 admin center, see the "Deploy to
users" section of Configure your Outlook add-in for event-based activation.

See also
Configure your Outlook add-in for event-based activation
Add-ins for Outlook on mobile devices
Add mobile support to an Outlook add-in
Outlook JavaScript APIs supported in Outlook on mobile devices
Create an Outlook add-in for an online-
meeting provider
Article • 03/21/2023

Setting up an online meeting is a core experience for an Outlook user, and it's easy to
create a Teams meeting with Outlook. However, creating an online meeting in Outlook
with a non-Microsoft service can be cumbersome. By implementing this feature, service
providers can streamline the online meeting creation and joining experience for their
Outlook add-in users.

) Important

This feature is supported in Outlook on the web, Windows, Mac, Android, and iOS
with a Microsoft 365 subscription.

In this article, you'll learn how to set up your Outlook add-in to enable users to organize
and join a meeting using your online-meeting service. Throughout this article, we'll use
a fictional online-meeting service provider, "Contoso".

Set up your environment


Complete the Outlook quick start which creates an add-in project with the Yeoman
generator for Office Add-ins.

Configure the manifest


To enable users to create online meetings with your add-in, you must configure the
manifest. The markup differs depending on two variables:

The type of target platform; either mobile or non-mobile.


The type of manifest; either XML or Unified manifest for Microsoft 365 (preview).

If your add-in uses an XML manifest, and the add-in will only be supported in Outlook
on the web, Windows, and Mac, select the Windows, Mac, web tab for guidance.
However, if your add-in will also be supported in Outlook on Android and iOS, select the
Mobile tab.

If the add-in uses the unified manifest (preview), select the Unified manifest for
Microsoft 365 (developer preview) tab.
) Important

Online meeting providers aren't yet supported for the unified manifest (preview).
We're working on providing that support soon.

Windows, Mac, web

1. In your code editor, open the Outlook quick start project you created.

2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including open and close tags)
and replace it with the following XML.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Description resid="residDescription"></Description>
<Requirements>
<bt:Sets>
<bt:Set Name="Mailbox" MinVersion="1.3"/>
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<FunctionFile resid="residFunctionFile"/>
<ExtensionPoint xsi:type="AppointmentOrganizerCommandSurface">
<OfficeTab id="TabDefault">
<Group id="apptComposeGroup">
<Label resid="residDescription"/>
<Control xsi:type="Button" id="insertMeetingButton">
<Label resid="residLabel"/>
<Supertip>
<Title resid="residLabel"/>
<Description resid="residTooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="icon-16"/>
<bt:Image size="32" resid="icon-32"/>
<bt:Image size="64" resid="icon-64"/>
<bt:Image size="80" resid="icon-80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>insertContosoMeeting</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="icon-16"
DefaultValue="https://contoso.com/assets/icon-16.png"/>
<bt:Image id="icon-32"
DefaultValue="https://contoso.com/assets/icon-32.png"/>
<bt:Image id="icon-48"
DefaultValue="https://contoso.com/assets/icon-48.png"/>
<bt:Image id="icon-64"
DefaultValue="https://contoso.com/assets/icon-64.png"/>
<bt:Image id="icon-80"
DefaultValue="https://contoso.com/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="residFunctionFile"
DefaultValue="https://contoso.com/commands.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="residDescription" DefaultValue="Contoso
meeting"/>
<bt:String id="residLabel" DefaultValue="Add a contoso
meeting"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="residTooltip" DefaultValue="Add a contoso meeting
to this appointment."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

 Tip

To learn more about manifests for Outlook add-ins, see Office add-in manifests
and Add support for add-in commands in Outlook on mobile devices.

Implement adding online meeting details


In this section, learn how your add-in script can update a user's meeting to include
online meeting details. The following applies to all supported platforms.

1. From the same quick start project, open the file ./src/commands/commands.js in
your code editor.

2. Replace the entire content of the commands.js file with the following JavaScript.

JavaScript

// 1. How to construct online meeting details.


// Not shown: How to get the meeting organizer's ID and other details
from your service.
const newBody = '<br>' +
'<a href="https://contoso.com/meeting?id=123456789"
target="_blank">Join Contoso meeting</a>' +
'<br><br>' +
'Phone Dial-in: +1(123)456-7890' +
'<br><br>' +
'Meeting ID: 123 456 789' +
'<br><br>' +
'Want to test your video connection?' +
'<br><br>' +
'<a href="https://contoso.com/testmeeting" target="_blank">Join
test meeting</a>' +
'<br><br>';

let mailboxItem;

// Office is ready.
Office.onReady(function () {
mailboxItem = Office.context.mailbox.item;
}
);

// 2. How to define and register a function command named


`insertContosoMeeting` (referenced in the manifest)
// to update the meeting body with the online meeting details.
function insertContosoMeeting(event) {
// Get HTML body from the client.
mailboxItem.body.getAsync("html",
{ asyncContext: event },
function (getBodyResult) {
if (getBodyResult.status ===
Office.AsyncResultStatus.Succeeded) {
updateBody(getBodyResult.asyncContext,
getBodyResult.value);
} else {
console.error("Failed to get HTML body.");
getBodyResult.asyncContext.completed({ allowEvent:
false });
}
}
);
}
// Register the function.
Office.actions.associate("insertContosoMeeting", insertContosoMeeting);

// 3. How to implement a supporting function `updateBody`


// that appends the online meeting details to the current body of
the meeting.
function updateBody(event, existingBody) {
// Append new body to the existing body.
mailboxItem.body.setAsync(existingBody + newBody,
{ asyncContext: event, coercionType: "html" },
function (setBodyResult) {
if (setBodyResult.status ===
Office.AsyncResultStatus.Succeeded) {
setBodyResult.asyncContext.completed({ allowEvent: true
});
} else {
console.error("Failed to set HTML body.");
setBodyResult.asyncContext.completed({ allowEvent:
false });
}
}
);
}

Testing and validation


Follow the usual guidance to test and validate your add-in, then sideload the manifest in
Outlook on the web, Windows, or Mac. If your add-in also supports mobile, restart
Outlook on your Android or iOS device after sideloading. Once the add-in is sideloaded,
create a new meeting and verify that the Microsoft Teams or Skype toggle is replaced
with your own.

Create meeting UI
As a meeting organizer, you should see screens similar to the following three images
when you create a meeting.
 


Join meeting UI
As a meeting attendee, you should see a screen similar to the following image when you
view the meeting.

) Important

The Join button is only supported in Outlook on the web, Mac, Android, and iOS. If
you only see a meeting link, but don't see the Join button in a supported client, it
may be that the online-meeting template for your service isn't registered on our
servers. See the Register your online-meeting template section for details.

Register your online-meeting template


Registering your online-meeting add-in is optional. It only applies if you want to surface
the Join button in meetings, in addition to the meeting link. Once you've developed
your online-meeting add-in and would like to register it, create a GitHub issue using the
following guidance. We'll contact you to coordinate a registration timeline.
) Important

The Join button is only supported in Outlook on the web, Mac, Android, and iOS.

1. Create a new GitHub issue .


2. Set the Title of the new issue to "Outlook: Register the online-meeting template
for my-service", replacing my-service with your service name.
3. In the issue body, replace the existing text with the string you set in the newBody or
similar variable from the Implement adding online meeting details section earlier
in this article.
4. Click Submit new issue.

Available APIs
The following APIs are available for this feature.

Appointment Organizer APIs


Office.context.mailbox.item.body (Body.getAsync, Body.setAsync)
Office.context.mailbox.item.end (Time)
Office.context.mailbox.item.loadCustomPropertiesAsync (CustomProperties)
Office.context.mailbox.item.location (Location)
Office.context.mailbox.item.optionalAttendees (Recipients)
Office.context.mailbox.item.requiredAttendees (Recipients)
Office.context.mailbox.item.start (Time)
Office.context.mailbox.item.subject (Subject)
Office.context.roamingSettings (RoamingSettings)
Handle auth flow
Dialog APIs

Restrictions
Several restrictions apply.

Applicable only to online-meeting service providers.


Only admin-installed add-ins will appear on the meeting compose screen,
replacing the default Teams or Skype option. User-installed add-ins won't activate.
The add-in icon should be in grayscale using hex code #919191 or its equivalent in
other color formats .
Only one function command is supported in Appointment Organizer (compose)
mode.
The add-in should update the meeting details in the appointment form within the
one-minute timeout period. However, any time spent in a dialog box the add-in
opened for authentication, for example, is excluded from the timeout period.

See also
Add-ins for Outlook on mobile devices
Add support for add-in commands in Outlook on mobile devices
On-send feature for Outlook add-ins
Article • 03/21/2023

The on-send feature for Outlook add-ins provides a way to handle a message or
meeting item, or block users from certain actions, and allows an add-in to set certain
properties on send.

7 Note

The on-send feature isn't supported in add-ins that use the Unified manifest for
Microsoft 365 (preview). Achieve similar effects by using event-based activation
and implementing a handler for the OnMessageSend or OnAppointmentSend
events, or both.

For example, use the on-send feature to:

Prevent a user from sending sensitive information or leaving the subject line blank.
Add a specific recipient to the CC line in messages, or to the optional recipients
line in meetings.

The on-send feature is triggered by the ItemSend event type and is UI-less.

For information about limitations related to the on-send feature, see Limitations later in
this article.

7 Note

Smart Alerts is a newer version of the on-send feature. It was released in


requirement set 1.12 and introduced the OnMessageSend and OnAppointmentSend
events. Similar to the on-send feature, Smart Alerts enables your add-in to check
that certain conditions are met before a mail item is sent. Smart Alerts differentiates
itself from the on-send feature as follows:

It offers send mode options when you want to provide your users with
optional recommendations instead of mandatory conditions.
It allows your add-in to be published to AppSource if the send mode property
is set to the prompt user or soft block option. To learn more about publishing
an event-based add-in, see AppSource listing options for your event-based
Outlook add-in.
For more information on the differences between Smart Alerts and the on-send
feature, see Differences between Smart Alerts and the on-send feature. We invite
you to try out Smart Alerts by completing the walkthrough.

Supported clients and platforms


The following table shows supported client-server combinations for the on-send feature,
including the minimum required Cumulative Update where applicable. Excluded
combinations are not supported.

Client Exchange Exchange 2016 on- Exchange 2019 on-


Online premises premises
(Cumulative Update 6 (Cumulative Update 1
or later) or later)

Windows: Yes Yes Yes


Version 1910 (Build
12130.20272) or later

Mac: Yes Yes Yes


build 16.47 or later

Web browser: Yes Not applicable Not applicable


modern Outlook UI

Web browser: Not Yes Yes


classic Outlook UI applicable

7 Note

The on-send feature was officially released in requirement set 1.8 (see current
server and client support for details). However, note that the feature's support
matrix is a superset of the requirement set's.

) Important

Add-ins that use the on-send feature aren't allowed in AppSource .

How does the on-send feature work?


You can use the on-send feature to build an Outlook add-in that integrates the
ItemSend synchronous event. This event detects that the user is pressing the Send

button (or the Send Update button for existing meetings) and can be used to block the
item from sending if the validation fails. For example, when a user triggers a message
send event, an Outlook add-in that uses the on-send feature can:

Read and validate the email message contents.


Verify that the message includes a subject line.
Set a predetermined recipient.

Validation is done on the client side in Outlook when the send event is triggered, and
the add-in has up to 5 minutes before it times out. If validation fails, the sending of the
item is blocked, and an error message is displayed in an information bar that prompts
the user to take action.

7 Note

In Outlook on the web, when the on-send feature is triggered in a message being
composed within the Outlook browser tab, the item is popped out to its own
browser window or tab in order to complete validation and other processing.

The following screenshot shows an information bar that notifies the sender to add a
subject.

The following screenshot shows an information bar that notifies the sender that blocked
words were found.
Limitations
The on-send feature currently has the following limitations.

Append-on-send feature – If you call item.body.AppendOnSendAsync in the on-


send handler, an error is returned.

AppSource – You can't publish Outlook add-ins that use the on-send feature to
AppSource as they will fail AppSource validation. Add-ins that use the on-send
feature should be deployed by administrators. If you want the option to publish
your add-in to AppSource, consider using Smart Alerts instead, which is a newer
version of the on-send feature. To learn more about Smart Alerts and how to
deploy these add-ins, see Use Smart Alerts and the OnMessageSend and
OnAppointmentSend events in your Outlook add-in and AppSource listing options
for your event-based Outlook add-in.

) Important

When running npm run validate to validate your add-in's manifest, you'll
receive the error, "Mailbox add-in containing ItemSend event is invalid.
Mailbox add-in manifest contains ItemSend event in VersionOverrides which is
not allowed." This message appears because add-ins that use the ItemSend
event, which is required for this version of the on-send feature, can't be
published to AppSource. You'll still be able to sideload and run your add-in,
provided that no other validation errors are found.
Manifest – Only one ItemSend event is supported per add-in. If you have two or
more ItemSend events in a manifest, the manifest will fail validation.

Performance – Multiple roundtrips to the web server that hosts the add-in can
affect the performance of the add-in. Consider the effects on performance when
you create add-ins that require multiple message- or meeting-based operations.

Send Later (Mac only) – If there are on-send add-ins, the Send Later feature will be
unavailable.

Also, it's not recommended that you call item.close() in the on-send event handler as
closing the item should happen automatically after the event is completed.

Mailbox type/mode limitations


On-send functionality is only supported for user mailboxes in Outlook on the web,
Windows, and Mac. In addition to situations where add-ins don't activate as noted in
the Mailbox items available to add-ins section of the Outlook add-ins overview page,
the functionality is not currently supported for offline mode where that mode is
available.

In cases where Outlook add-ins don't activate, the on-send add-in won't run and the
message will be sent.

However, if the on-send feature is enabled and available but the mailbox scenario is
unsupported, Outlook won't allow sending.

Multiple on-send add-ins


If multiple on-send add-ins are installed, the add-ins will run in the order in which they
are received from APIs getAppManifestCall or getExtensibilityContext . If the first add-
in allows sending, the second add-in can change something that would make the first
one block sending. However, the first add-in won't run again if all installed add-ins have
allowed sending.

For example, Add-in1 and Add-in2 both use the on-send feature. Add-in1 is installed
first, and Add-in2 is installed second. Add-in1 verifies that the word Fabrikam appears in
the message as a condition for the add-in to allow send. However, Add-in2 removes any
occurrences of the word Fabrikam. The message will send with all instances of Fabrikam
removed (due to the order of installation of Add-in1 and Add-in2).
Deploy Outlook add-ins that use on-send
We recommend that administrators deploy Outlook add-ins that use the on-send
feature. Administrators have to ensure that the on-send add-in:

Is always present any time a compose item is opened (for email: new, reply, or
forward).
Can't be closed or disabled by the user.

Install Outlook add-ins that use on-send


The on-send feature in Outlook requires that add-ins are configured for the send event
types. Select the platform you'd like to configure.

Windows

Add-ins for Outlook on Windows that use the on-send feature should run for any
users who have them installed. However, if users are required to run the add-in to
meet compliance standards, then the group policy Block send when web add-ins
can't load must be set to Enabled on each applicable machine.

To set mailbox policies, administrators can download the Administrative Templates


tool then access the latest administrative templates by running the Local Group
Policy Editor, gpedit.msc.

7 Note

In older versions of the Administrative Templates tool, the policy name was
Disable send when web extensions can't load. Substitute in this name in later
steps if needed.

What the policy does

For compliance reasons, administrators may need to ensure that users cannot send
message or meeting items until the latest on-send add-in is available to run.
Administrators must enable the group policy Block send when web add-ins can't
load so that all add-ins are updated from Exchange and available to verify each
message or meeting item meets expected rules and regulations on send.
Policy Result
status

Disabled The currently downloaded manifests of the on-send add-ins (not necessarily the
latest versions) run on message or meeting items being sent. This is the default
status/behavior.

Enabled After the latest manifests of the on-send add-ins are downloaded from
Exchange, the add-ins are run on message or meeting items being sent.
Otherwise, send is blocked.

Manage the on-send policy


By default, the on-send policy is disabled. Administrators can enable the on-send
policy by ensuring the user's group policy setting Block send when web add-ins
can't load is set to Enabled. To disable the policy for a user, the administrator
should set it to Disabled. To manage this policy setting, you can do the following:

1. Download the latest Administrative Templates tool .


2. Open the Local Group Policy Editor (gpedit.msc).
3. Navigate to User Configuration > Administrative Templates > Microsoft
Outlook 2016 > Security > Trust Center.
4. Select the Block send when web add-ins can't load setting.
5. Open the link to edit policy setting.
6. In the Block send when web add-ins can't load dialog window, select Enabled
or Disabled as appropriate then select OK or Apply to put the update into
effect.

On-send feature scenarios


The following are the supported and unsupported scenarios for add-ins that use the on-
send feature.

Event handlers are dynamically defined


Your add-in's event handlers must be defined by the time Office.initialize or
Office.onReady() is called (for further information, see Startup of an Outlook add-in

and Initialize your Office Add-in). If your handler code is dynamically defined by certain
circumstances during initialization, you must create a stub function to call the handler
once it's completely defined. The stub function must be referenced in the <Event>
element's FunctionName attribute of your manifest. This workaround ensures that your
handler is defined and ready to be referenced once Office.initialize or
Office.onReady() runs.

If your handler isn't defined once your add-in is initialized, the sender will be notified
that "The callback function is unreachable" through an information bar in the mail item.

User mailbox has the on-send add-in feature enabled but


no add-ins are installed
In this scenario, the user will be able to send message and meeting items without any
add-ins executing.

User mailbox has the on-send add-in feature enabled and


add-ins that supports on-send are installed and enabled
Add-ins will run during the send event, which will then either allow or block the user
from sending.

Mailbox delegation, where mailbox 1 has full access


permissions to mailbox 2

Web browser (classic Outlook)

Scenario Mailbox 1 Mailbox 2 Outlook Result Supported?


on-send on-send web
feature feature session
(classic)

1 Enabled Enabled New session Mailbox 1 cannot Not currently


send a message or supported. As a
meeting item from workaround, use
mailbox 2. scenario 3.

2 Disabled Enabled New session Mailbox 1 cannot Not currently


send a message or supported. As a
meeting item from workaround, use
mailbox 2. scenario 3.

3 Enabled Enabled Same On-send add-ins Supported.


session assigned to
mailbox 1 run on-
send.
Scenario Mailbox 1 Mailbox 2 Outlook Result Supported?
on-send on-send web
feature feature session
(classic)

4 Enabled Disabled New session No on-send add- Supported.


ins run; message
or meeting item is
sent.

Web browser (modern Outlook), Windows, Mac


To enforce on-send, administrators should ensure the policy has been enabled on both
mailboxes. To learn how to support delegate access in an add-in, see Enable shared
folders and shared mailbox scenarios.

User mailbox with on-send add-in feature/policy enabled,


add-ins that support on-send are installed and enabled
and offline mode is enabled
On-send add-ins will run according to the online state of the user, the add-in backend,
and Exchange.

User's state

The on-send add-ins will run during send if the user is online. If the user is offline, the
on-send add-ins will not run during send and the message or meeting item will not be
sent.

Add-in backend's state

An on-send add-in will run if its backend is online and reachable. If the backend is
offline, send is disabled.

Exchange's state

The on-send add-ins will run during send if the Exchange server is online and reachable.
If the on-send add-in cannot reach Exchange and the applicable policy or cmdlet is
turned on, send is disabled.

7 Note
On Mac in any offline state, the Send button (or the Send Update button for
existing meetings) is disabled and a notification displayed that their organization
doesn't allow send when the user is offline.

User can edit item while on-send add-ins are working on


it
While on-send add-ins are processing an item, the user can edit the item by adding, for
example, inappropriate text or attachments. If you want to prevent the user from editing
the item while your add-in is processing on send, you can implement a workaround
using a dialog. This workaround can be used in Outlook on the web (classic), Windows,
and Mac.

) Important

Modern Outlook on the web: To prevent the user from editing the item while your
add-in is processing on send, you should set the OnSendAddinsEnabled flag to
true as described in the Install Outlook add-ins that use on-send section earlier in

this article.

In your on-send handler:

1. Call displayDialogAsync to open a dialog so that mouse clicks and keystrokes are
disabled.

) Important

To get this behavior in classic Outlook on the web, you should set the
displayInIframe property to true in the options parameter of the
displayDialogAsync call.

2. Implement processing of the item.

3. Close the dialog. Also, handle what happens if the user closes the dialog.

Code examples
The following code examples show you how to create a simple on-send add-in. To
download the code sample that these examples are based on, see Outlook-Add-in-On-
Send .

 Tip

If you use a dialog with the on-send event, make sure to close the dialog before
completing the event.

Manifest, version override, and event


The Outlook-Add-in-On-Send code sample includes two manifests:

Contoso Message Body Checker.xml – Shows how to check the body of a message
for restricted words or sensitive information on send.

Contoso Subject and CC Checker.xml – Shows how to add a recipient to the CC line

and verify that the message includes a subject line on send.

In the Contoso Message Body Checker.xml manifest file, you include the function file and
function name that should be called on the ItemSend event. The operation runs
synchronously.

XML

<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<!-- The functionfile and function name to call on message send.
-->
<!-- In this case, the function validateBody will be called
within the JavaScript code referenced in residUILessFunctionFileUrl. -->
<FunctionFile resid="residUILessFunctionFileUrl" />
<ExtensionPoint xsi:type="Events">
<Event Type="ItemSend" FunctionExecution="synchronous"
FunctionName="validateBody" />
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>

) Important

If you are using Visual Studio 2019 to develop your on-send add-in, you may get a
validation warning like the following: "This is an invalid xsi:type
'http://schemas.microsoft.com/office/mailappversionoverrides/1.1:Events'." To work
around this, you'll need a newer version of the MailAppVersionOverridesV1_1.xsd
which has been provided as a GitHub gist in a blog about this warning .

For the Contoso Subject and CC Checker.xml manifest file, the following example shows
the function file and function name to call on message send event.

XML

<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<!-- The functionfile and function name to call on message send.
-->
<!-- In this case the function validateSubjectAndCC will be
called within the JavaScript code referenced in residUILessFunctionFileUrl.
-->
<FunctionFile resid="residUILessFunctionFileUrl" />
<ExtensionPoint xsi:type="Events">
<Event Type="ItemSend" FunctionExecution="synchronous"
FunctionName="validateSubjectAndCC" />
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>

The on-send API requires VersionOverrides v1_1 . The following shows you how to add
the VersionOverrides node in your manifest.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<!-- On-send requires VersionOverridesV1_1 -->
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
...
</VersionOverrides>
</VersionOverrides>

7 Note

For more information, see the following:

Manifest configuration for Outlook Add-ins


Office Add-ins manifest

Event and item objects, and body.getAsync and


body.setAsync methods

To access the currently selected message or meeting item (in this example, the newly
composed message), use the Office.context.mailbox.item namespace. The ItemSend
event is automatically passed by the on-send feature to the function specified in the
manifest—in this example, the validateBody function.

JavaScript

let mailboxItem;

Office.initialize = function (reason) {


mailboxItem = Office.context.mailbox.item;
}

// Entry point for Contoso Message Body Checker add-in before send is
allowed.
// <param name="event">ItemSend event is automatically passed by on-send
code to the function specified in the manifest.</param>
function validateBody(event) {
mailboxItem.body.getAsync("html", { asyncContext: event },
checkBodyOnlyOnSendCallBack);
}

The validateBody function gets the current body in the specified format (HTML) and
passes the ItemSend event object that the code wants to access in the callback function.
In addition to the getAsync method, the Body object also provides a setAsync method
that you can use to replace the body with the specified text.

7 Note

For more information, see Event Object and Body.getAsync.

NotificationMessages object and event.completed method

The checkBodyOnlyOnSendCallBack function uses a regular expression to determine


whether the message body contains blocked words. If it finds a match against an array
of restricted words, it then blocks the email from being sent and notifies the sender via
the information bar. To do this, it uses the notificationMessages property of the Item
object to return a NotificationMessages object. It then adds a notification to the item by
calling the addAsync method, as shown in the following example.

JavaScript

// Determine whether the body contains a specific set of blocked words. If


it contains the blocked words, block email from being sent. Otherwise allow
sending.
// <param name="asyncResult">ItemSend event passed from the calling
function.</param>
function checkBodyOnlyOnSendCallBack(asyncResult) {
const listOfBlockedWords = new Array("blockedword", "blockedword1",
"blockedword2");
const wordExpression = listOfBlockedWords.join('|');

// \b to perform a "whole words only" search using a regular expression


in the form of \bword\b.
// i to perform case-insensitive search.
const regexCheck = new RegExp('\\b(' + wordExpression + ')\\b', 'i');
const checkBody = regexCheck.test(asyncResult.value);

if (checkBody) {
mailboxItem.notificationMessages.addAsync('NoSend', { type:
'errorMessage', message: 'Blocked words have been found in the body of this
email. Please remove them.' });
// Block send.
asyncResult.asyncContext.completed({ allowEvent: false });
}

// Allow send.
asyncResult.asyncContext.completed({ allowEvent: true });
}

The following are the parameters for the addAsync method.

NoSend – A string that is a developer-specified key to reference a notification

message. You can use it to modify this message later. The key can't be longer than
32 characters.
type – One of the properties of the JSON object parameter. Represents the type of

a message; the types correspond to the values of the


Office.MailboxEnums.ItemNotificationMessageType enumeration. Possible values
are progress indicator, information message, or error message. In this example,
type is an error message.

message – One of the properties of the JSON object parameter. In this example,
message is the text of the notification message.

To signal that the add-in has finished processing the ItemSend event triggered by the
send operation, call the event.completed({allowEvent:Boolean}) method. The
allowEvent property is a Boolean. If set to true , send is allowed. If set to false , the

email message is blocked from sending.

7 Note

For more information, see notificationMessages and completed.

replaceAsync , removeAsync , and getAllAsync methods

In addition to the addAsync method, the NotificationMessages object also includes


replaceAsync , removeAsync , and getAllAsync methods. These methods are not used in

this code sample. For more information, see NotificationMessages.

Subject and CC checker code


The following code example shows you how to add a recipient to the CC line and verify
that the message includes a subject on send. This example uses the on-send feature to
allow or disallow an email from sending.

JavaScript

// Invoke by Contoso Subject and CC Checker add-in before send is allowed.


// <param name="event">ItemSend event is automatically passed by on-send
code to the function specified in the manifest.</param>
function validateSubjectAndCC(event) {
shouldChangeSubjectOnSend(event);
}

// Determine whether the subject should be changed. If it is already


changed, allow send. Otherwise change it.
// <param name="event">ItemSend event passed from the calling function.
</param>
function shouldChangeSubjectOnSend(event) {
mailboxItem.subject.getAsync(
{ asyncContext: event },
function (asyncResult) {
addCCOnSend(asyncResult.asyncContext);
//console.log(asyncResult.value);
// Match string.
const checkSubject = (new RegExp(/\
[Checked\]/)).test(asyncResult.value)
// Add [Checked]: to subject line.
subject = '[Checked]: ' + asyncResult.value;

// Determine whether a string is blank, null, or undefined.


// If yes, block send and display information bar to notify
sender to add a subject.
if (asyncResult.value === null ||
(/^\s*$/).test(asyncResult.value)) {
mailboxItem.notificationMessages.addAsync('NoSend', { type:
'errorMessage', message: 'Please enter a subject for this email.' });
asyncResult.asyncContext.completed({ allowEvent: false });
}
else {
// If can't find a [Checked]: string match in subject, call
subjectOnSendChange function.
if (!checkSubject) {
subjectOnSendChange(subject, asyncResult.asyncContext);
//console.log(checkSubject);
}
else {
// Allow send.
asyncResult.asyncContext.completed({ allowEvent: true
});
}
}
});
}

// Add a CC to the email. In this example, CC


contoso@contoso.onmicrosoft.com
// <param name="event">ItemSend event passed from calling function</param>
function addCCOnSend(event) {
mailboxItem.cc.setAsync(['Contoso@contoso.onmicrosoft.com'], {
asyncContext: event });
}

// Determine whether the subject should be changed. If it is already


changed, allow send, otherwise change it.
// <param name="subject">Subject to set.</param>
// <param name="event">ItemSend event passed from the calling function.
</param>
function subjectOnSendChange(subject, event) {
mailboxItem.subject.setAsync(
subject,
{ asyncContext: event },
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
mailboxItem.notificationMessages.addAsync('NoSend', { type:
'errorMessage', message: 'Unable to set the subject.' });

// Block send.
asyncResult.asyncContext.completed({ allowEvent: false });
}
else {
// Allow send.
asyncResult.asyncContext.completed({ allowEvent: true });
}
});
}
To learn more about how to add a recipient to the CC line and verify that the email
message includes a subject line on send, and to see the APIs you can use, see the
Outlook-Add-in-On-Send sample . The code is well commented.

Debug Outlook add-ins that use on-send


For instructions on how to debug your on-send add-in, see Debug function commands
in Outlook add-ins.

 Tip

If the error "The callback function is unreachable" appears when your users run
your add-in and your add-in's event handler is dynamically defined, you must
create a stub function as a workaround. See Event handlers are dynamically
defined for more information.

See also
Overview of Outlook add-ins architecture and features
Add-in Command Demo Outlook add-in
Prepend or append content to a
message or appointment body on send
Article • 05/24/2023

The prepend-on-send and append-on-send features enable your Outlook add-in to


insert content to the body of a message or appointment when the mail item is sent.
These features further boost your users' productivity and security by enabling them to:

Add sensitivity and classification labels to their messages and appointments for
easier item identification and organization.
Insert disclaimers for legal purposes.
Add standardized headers for marketing and communication purposes.

In this walkthrough, you'll develop an add-in that prepends a header and appends a
disclaimer when a message is sent.

7 Note

Support for the append-on-send feature was introduced in requirement set 1.9,
while support for the prepend-on-send feature was introduced in requirement set
1.13. See clients and platforms that support these requirement sets.

Preview prepend-on-send in Outlook on Mac


To preview the prepend-on-send feature in Outlook on Mac, install Version 16.70.212.0
or later.

Set up your environment


Complete the Outlook quick start which creates an add-in project with the Yeoman
generator for Office Add-ins.

Configure the manifest


To configure the manifest, select the tab for the type of manifest you'll use.

XML Manifest
To enable the prepend-on-send and append-on-send features in your add-in, you
must include the AppendOnSend permission in the collection of ExtendedPermissions.
Additionally, you'll configure function commands to prepend and append content
to the message body.

1. In your code editor, open the quick start project you created.

2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including open and close tags)
and replace it with the following XML.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/
1.1" xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.13">
<bt:Set Name="Mailbox" />
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<FunctionFile resid="Commands.Url" />
<ExtensionPoint xsi:type="MessageComposeCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgComposeGroup">
<Label resid="GroupLabel" />
<Control xsi:type="Button"
id="msgComposeOpenPaneButton">
<Label resid="TaskpaneButton.Label" />
<Supertip>
<Title resid="TaskpaneButton.Label" />
<Description resid="TaskpaneButton.Tooltip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16" />
<bt:Image size="32" resid="Icon.32x32" />
<bt:Image size="80" resid="Icon.80x80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="Taskpane.Url" />
</Action>
</Control>
<!-- Configure the prepend-on-send function
command. -->
<Control xsi:type="Button" id="PrependButton">
<Label resid="PrependButton.Label"/>
<Supertip>
<Title resid="PrependButton.Label"/>
<Description resid="PrependButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">

<FunctionName>prependHeaderOnSend</FunctionName>
</Action>
</Control>
<!-- Configure the append-on-send function command.
-->
<Control xsi:type="Button" id="AppendButton">
<Label resid="AppendButton.Label"/>
<Supertip>
<Title resid="AppendButton.Label"/>
<Description resid="AppendButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">

<FunctionName>appendDisclaimerOnSend</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>

<!-- Append-on-send and prepend-on-send are supported in


Message Compose and Appointment Organizer modes.
To support these features when creating a new
appointment, configure the AppointmentOrganizerCommandSurface
extension point. -->

</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Commands.Url"
DefaultValue="https://localhost:3000/commands.html" />
<bt:Url id="Taskpane.Url"
DefaultValue="https://localhost:3000/taskpane.html" />
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Contoso Add-in"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show
Taskpane"/>
<bt:String id="PrependButton.Label" DefaultValue="Prepend
header on send"/>
<bt:String id="AppendButton.Label" DefaultValue="Append
disclaimer on send"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Opens
a pane displaying all available properties."/>
<bt:String id="PrependButton.Tooltip" DefaultValue="Prepend
the Contoso header on send."/>
<bt:String id="AppendButton.Tooltip" DefaultValue="Append
the Contoso disclaimer on send."/>
</bt:LongStrings>
</Resources>
<!-- Configures the prepend-on-send and append-on-send
features. The same value, AppendOnSend, is used for both features.
-->
<ExtendedPermissions>
<ExtendedPermission>AppendOnSend</ExtendedPermission>
</ExtendedPermissions>
</VersionOverrides>
</VersionOverrides>

4. Save your changes.

 Tip

The prepend-on-send and append-on-send features must be activated by the user


through a task pane or function command button. If you want content to be
prepended or appended on send without additional action from the user, you can
implement these features in an event-based activation add-in.

 Tip

To learn more about manifests for Outlook add-ins, see Office add-in manifests.
Implement the prepend-on-send handler
In this section, you'll implement the JavaScript code to prepend a sample company
header to a mail item when it's sent.

1. Navigate to the ./src/commands folder of your project and open the commands.js
file.

2. Insert the following function at the end of the file.

JavaScript

function prependHeaderOnSend(event) {
// It's recommended to call the getTypeAsync method and pass its
returned value to the options.coercionType parameter of the
prependOnSendAsync call.
Office.context.mailbox.item.body.getTypeAsync(
{
asyncContext: event
},
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

// Sets the header to be prepended to the body of the message on


send.
const bodyFormat = asyncResult.value;
// Because of the various ways in which HTML text can be
formatted, the content may render differently when it's prepended to
the mail item body.
// In this scenario, a <br> tag is added to the end of the HTML
string to preserve its format.
const header = '<div style="border:3px solid #000;padding:15px;">
<h1 style="text-align:center;">Contoso Limited</h1></div><br>';

Office.context.mailbox.item.body.prependOnSendAsync(
header,
{
asyncContext: asyncResult.asyncContext,
coercionType: bodyFormat
},
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("The header will be prepended when the mail item


is sent.");
asyncResult.asyncContext.completed();
}
);
});
}

3. Save your changes.

Implement the append-on-send handler


In this section, you'll implement the JavaScript code to append a sample company
disclaimer to a mail item when it's sent.

1. In the same commands.js file, insert the following function after the
prependHeaderOnSend function.

JavaScript

function appendDisclaimerOnSend(event) {
// Calls the getTypeAsync method and passes its returned value to the
options.coercionType parameter of the appendOnSendAsync call.
Office.context.mailbox.item.body.getTypeAsync(
{
asyncContext: event
},
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

// Sets the disclaimer to be appended to the body of the message on


send.
const bodyFormat = asyncResult.value;
const disclaimer =
'<p style = "color:blue"> <i>This and subsequent emails on the
same topic are for discussion and information purposes only. Only those
matters set out in a fully executed agreement are legally binding. This
email may contain confidential information and should not be shared
with any third party without the prior written agreement of Contoso. If
you are not the intended recipient, take no action and contact the
sender immediately.<br><br>Contoso Limited (company number 01624297) is
a company registered in England and Wales whose registered office is at
Contoso Campus, Thames Valley Park, Reading RG6 1WG</i></p>';

Office.context.mailbox.item.body.appendOnSendAsync(
disclaimer,
{
asyncContext: asyncResult.asyncContext,
coercionType: bodyFormat
},
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("The disclaimer will be appended when the mail item


is sent.");
asyncResult.asyncContext.completed();
}
);
});
}

2. Save your changes.

Register the JavaScript functions


1. In the same commands.js file, insert the following after the
appendDisclaimerOnSend function. These calls map the function name specified in

the manifest's <FunctionName> element to its JavaScript counterpart.

JavaScript

Office.actions.associate("prependHeaderOnSend", prependHeaderOnSend);
Office.actions.associate("appendDisclaimerOnSend",
appendDisclaimerOnSend);

2. Save your changes.

Try it out
1. Run the following command in the root directory of your project. When you run
this command, the local web server will start if it's not already running and your
add-in will be sideloaded.

command line

npm start

7 Note

If your add-in wasn't automatically sideloaded, follow the instructions in


Sideload Outlook add-ins for testing to manually sideload the add-in in
Outlook.

2. Create a new message, and add yourself to the To line.

3. (Optional) Enter text in the body of the message.

4. From the ribbon or overflow menu, select Prepend header.

5. From the ribbon or overflow menu, select Append disclaimer.

6. Send the message, then open it from your Inbox or Sent Items folder to view the
inserted content.

 Tip

Because content is only prepended or appended once the message is sent,


the sender will only be able to view the added content from their Inbox or
Sent Items folder. If you require the sender to view the added content before
the message is sent, see Insert data in the body when composing an
appointment or message in Outlook.

Review feature behavior and limitations


As you implement prepend-on-send and append-on-send in your add-in, keep the
following in mind.

Prepend-on-send and append-on-send are only supported in compose mode.

The string to be prepended or appended must not exceed 5,000 characters.


HTML can't be prepended or appended to a plain text body of a message or
appointment. However, plain text can be added to an HTML-formatted body of a
message or appointment.

Any formatting applied to prepended or appended content doesn't affect the style
of the rest of the mail item's body.

Prepend-on-send and append-on-send can't be implemented in the same add-in


that implements the on-send feature. As an alternative, consider implementing
Smart Alerts instead.

When implementing Smart Alerts in the same add-in, the prepend-on-send and
append-on-send operations occur before the OnMessageSend and
OnAppointmentSend event handler operations.

If multiple active add-ins use prepend-on-send or append-on-send, the order of


the content to be inserted depends on the order in which the add-in ran. For
prepend-on-send, the content of the add-in that runs last appears at the top of
the mail item body before the previously prepended content. For append-on-send,
the content of the add-in that runs last appears at the bottom of the mail item
body after the previously appended content.

Delegate and shared mailbox scenarios are supported as long as the add-in that
implements prepend-on-send or append-on-send is enabled on the shared
mailbox or owner's account.

Troubleshoot your add-in


If you encounter an error while implementing the prepend-on-send and append-on-
send features, refer to the following table for guidance.

Error Description Resolution

DataExceedsMaximumSize The content to be Shorten the string you pass to the data
appended or prepended is parameter of your prependOnSendAsync or
longer than 5,000 appendOnSendAsync call.
characters.
Error Description Resolution

InvalidFormatError The message or Only plain text can be inserted into a plain text
appointment body is in body of a message or appointment. To verify the
plain text format, but the format of the mail item being composed, call
coercionType passed to Office.context.mailbox.item.body.getTypeAsync ,
the prependOnSendAsync or then pass its returned value to your
appendOnSendAsync prependOnSendAsync or appendOnSendAsync call.
method is set to
Office.CoercionType.Html .

See also
Office add-in manifests
Office.Body
Use Smart Alerts and the OnMessageSend and OnAppointmentSend events in
your Outlook add-in
Implement an integrated spam-
reporting add-in (preview)
Article • 07/21/2023

With the number of unsolicited emails on the rise, security is at the forefront of add-in
usage. Currently, partner spam-reporting add-ins are added to the Outlook ribbon, but
they usually appear towards the end of the ribbon or in the overflow menu. This makes
it harder for users to locate the add-in to report unsolicited emails. In addition to
configuring how messages are processed when they're reported, developers also need
to complete additional tasks to show processing dialogs or supplemental information to
the user.

The integrated spam reporting feature eases the task of developing individual add-in
components from scratch. More importantly, it displays your add-in in a prominent spot
on the Outlook ribbon, making it easier for users to locate it and report spam messages.
Implement this feature in your add-in to:

Improve how unsolicited messages are tracked.


Provide better guidance to users on how to report suspicious messages.
Enable an organization's security operations center (SOC) or IT administrators to
easily perform spam and phishing simulations for educational purposes.

) Important

The integrated spam reporting feature is currently in preview in Outlook on


Windows. Features in preview shouldn't be used in production add-ins. We invite
you to try out this feature in test or development environments and welcome
feedback on your experience through GitHub (see the Feedback section at the end
of this page).

Preview the integrated spam reporting feature


To preview the integrated spam reporting feature in Outlook on Windows, you must
install Version 2307 (Build 16626.10000) or later. Then, join the Microsoft 365 Insider
program and select the Beta Channel option to access Office beta builds.

Set up your environment


Complete the Outlook quick start, which creates an add-in project with the Yeoman
generator for Office Add-ins.

Configure the manifest


To implement the integrated spam reporting feature in your add-in, you must configure
the VersionOverridesV1_1 node of your manifest accordingly.

In Outlook on Windows, an add-in that implements the integrated spam reporting


feature runs in a JavaScript-only runtime. As such, you must specify the JavaScript
file that contains the code to handle the spam-reporting event in the Override
child element of the Runtime element.
To activate the add-in in the Outlook ribbon and prevent it from appearing at the
end of the ribbon or in the overflow section, set the xsi:type attribute of the
<ExtensionPoint> element to ReportPhishingCommandSurface.
To customize the ribbon button and preprocessing dialog, you must define the
ReportPhishingCustomization node.

A user reports an unsolicited message through the add-in's button in the


ribbon. The button shows the preprocessing dialog to the user and activates the
SpamReporting event, which is then handled by the JavaScript event handler. To
configure the ribbon button, set the xsi:type attribute of the Control element
to Button . Then, set the xsi:type attribute of the Action child element to
ExecuteFunction and specify the name of the spam-reporting event handler in

its <FunctionName> child element. A spam-reporting add-in can only


implement function commands.

The preprocessing dialog is shown to a user when they report a message. In the
dialog, you can share guidance on the reporting process and include options for
the user to provide more information about the message being reported. To
customize the title, description, and options of the preprocessing dialog, you
must include the PreProcessingDialog element in your manifest.
The following is an example of a <VersionOverrides> node configured for spam
reporting.

1. In your preferred code editor, open the add-in project you created.

2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including the open and close tags)
and replace it with the following code.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.13">
<bt:Set Name="Mailbox"/>
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<Runtimes>
<Runtime resid="WebViewRuntime.Url">
<!-- References the JavaScript file that contains the spam-
reporting event handler. This is used by Outlook on Windows. -->
<Override type="javascript" resid="JSRuntime.Url"/>
</Runtime>
</Runtimes>
<DesktopFormFactor>
<FunctionFile resid="WebViewRuntime.Url"/>
<!-- Implements the integrated spam reporting feature in the
add-in. -->
<ExtensionPoint xsi:type="ReportPhishingCommandSurface">
<ReportPhishingCustomization>
<!-- Configures the ribbon button. -->
<Control xsi:type="Button" id="spamReportingButton">
<Label resid="spamButton.Label"/>
<Supertip>
<Title resid="spamButton.Label"/>
<Description resid="spamSuperTip.Text"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>onSpamReport</FunctionName>
</Action>
</Control>
<!-- Configures the preprocessing dialog. -->
<PreProcessingDialog>
<Title resid="PreProcessingDialog.Label"/>
<Description resid="PreProcessingDialog.Text"/>
<ReportingOptions>
<Title resid="OptionsTitle.Label"/>
<Option resid="Option1.Label"/>
<Option resid="Option2.Label"/>
<Option resid="Option3.Label"/>
</ReportingOptions>
<FreeTextLabel resid="FreeText.Label"/>
<MoreInfo>
<MoreInfoText resid="MoreInfo.Label"/>
<MoreInfoUrl resid="MoreInfo.Url"/>
</MoreInfo>
</PreProcessingDialog>
<!-- Identifies the runtime to be used. This is also
referenced by the Runtime element. -->
<SourceLocation resid="WebViewRuntime.Url"/>
</ReportPhishingCustomization>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="WebViewRuntime.Url"
DefaultValue="https://localhost:3000/commands.html"/>
<bt:Url id="JSRuntime.Url"
DefaultValue="https://localhost:3000/commands.js"/>
<bt:Url id="MoreInfo.Url"
DefaultValue="https://www.contoso.com/spamreporting"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="spamButton.Label" DefaultValue="Report Spam
Message"/>
<bt:String id="PreProcessingDialog.Label" DefaultValue="Report
Spam Message"/>
<bt:String id="OptionsTitle.Label" DefaultValue="Why are you
reporting this email?"/>
<bt:String id="FreeText.Label" DefaultValue="Provide additional
information, if any:"/>
<bt:String id="MoreInfo.Label" DefaultValue="To learn more
about reporting unsolicited messages, see "/>
<bt:String id="Option1.Label" DefaultValue="Received spam
email."/>
<bt:String id="Option2.Label" DefaultValue="Received a phishing
email."/>
<bt:String id="Option3.Label" DefaultValue="I'm not sure this
is a legitimate email."/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="spamSuperTip.Text" DefaultValue="Report an
unsolicited message."/>
<bt:String id="PreProcessingDialog.Text" DefaultValue="Thank
you for reporting this message."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

4. Save your changes.

Implement the event handler


When your add-in is used to report a message, it generates a SpamReporting event,
which is then processed by the event handler in the JavaScript file of your add-in. To
map the name of the event handler you specified in the <FunctionName> element of
your manifest to its JavaScript counterpart, you must call Office.actions.associate in your
code.

Your event handler is responsible for processing the reported message, such as
forwarding a copy of the message to an internal system for further investigation. To
efficiently send a copy of the reported message, call the getAsFileAsync method in your
event handler. This gets the Base64-encoded EML format of a message, which you can
then forward to your internal system.

) Important

To test the getAsFileAsync method while it's still in preview in Outlook on


Windows, you must configure your computer's registry.

Outlook on Windows includes a local copy of the production and beta versions of
Office.js instead of loading from the content delivery network (CDN). By default, the
local production copy of the API is referenced. To reference the local beta copy of
the API, you must configure your computer's registry as follows:

1. In the registry, navigate to


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Outlook\Options\WebExt\D

eveloper . If the key doesn't exist, create it.

2. Create an entry named EnableBetaAPIsInJavaScript and set its value to 1 .

Once the event handler has completed processing the message, it must call the
event.completed method. In addition to signaling to the add-in that the spam-reporting
event has been processed, event.completed can also be used to customize a post-
processing dialog to show to the user or perform additional operations on the message,
such as deleting it from the inbox. For a list of properties you can include in a JSON
object to pass as a parameter to the event.completed method, see
Office.AddinCommands.EventCompletedOptions.

7 Note

Code added after the event.completed call isn't guaranteed to run.

The following is an example of a spam-reporting event handler that calls the


getAsFileAsync method.
1. In your add-in project, navigate to the ./src directory. Then, create a new folder
named spamreporting.

2. In the ./src/spamreporting folder, create a new file named spamreporting.js.

3. Open the newly created spamreporting.js file and add the following JavaScript
code.

JavaScript

// Handles the SpamReporting event to process a reported message.


function onSpamReport(event) {
// Gets the Base64-encoded EML format of a reported message.
Office.context.mailbox.item.getAsFileAsync({ asyncContext: event },
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(`Error encountered during message processing:
${asyncResult.error.message}`);
return;
}

// Run additional processing operations here.

/**
* Signals that the spam-reporting event has completed processing.
* It then moves the reported message to the Junk Email folder of
the mailbox, then
* shows a post-processing dialog to the user. If an error occurs
while the message
* is being processed, the `onErrorDeleteItem` property determines
whether the message
* will be deleted.
*/
const event = asyncResult.asyncContext;
event.completed({
onErrorDeleteItem: true,
postProcessingAction: "moveToSpamFolder",
showPostProcessingDialog: {
title: "Contoso Spam Reporting",
description: "Thank you for reporting this message.",
},
});
});
}

// IMPORTANT: To ensure your add-in is supported in the Outlook client


on Windows, remember to map the event handler name specified in the
manifest to its JavaScript counterpart
if (Office.context.platform === Office.PlatformType.PC ||
Office.context.platform == null) {
Office.actions.associate("onSpamReport", onSpamReport);
}
4. Save your changes.

The following is a sample post-processing dialog shown to the user once the add-in
completes processing a reported message.

 Tip

As you develop a spam-reporting add-in that will run in Outlook on Windows, keep
the following in mind.

Imports aren't currently supported in the JavaScript file that contains the code
to handle the spam-reporting event.
Code included in the Office.onReady() and Office.initialize functions
won't run. You must add any add-in startup logic, such as checking the user's
Outlook version, to your event handlers instead.

Update the commands HTML file


1. In the ./src/commands folder, open commands.html.

2. Immediately before the closing head tag ( </head> ), replace the existing script
entry with the following code.

HTML

<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/beta/hosted/office.js">
</script>
<script type="text/javascript" src="../spamreporting/spamreporting.js">
</script>

3. Save your changes.


Update the webpack config settings
1. From the root directory of your add-in project, open the webpack.config.js file.

2. Locate the plugins array within the config object and add this new object to the
beginning of the array.

JavaScript

new CopyWebpackPlugin({
patterns: [
{
from: "./src/spamreporting/spamreporting.js",
to: "spamreporting.js",
},
],
}),

3. Save your changes.

Test and validate your add-in


1. Sideload the add-in in Outlook on Windows.
2. Choose a message from your inbox, then select the add-in's button from the
ribbon.
3. In the preprocessing dialog, choose a reason for reporting the message and add
information about the message, if configured. Then, select Report.
4. (Optional) In the post-processing dialog, select OK.

Review feature behavior and limitations


As you develop and test the integrated spam reporting feature in your add-in, be
mindful of its characteristics and limitations.

A spam-reporting add-in can run for a maximum of five minutes once it's
activated. Any processing that occurs beyond five minutes will cause the add-in to
time out. If the add-in times out, a dialog will be shown to the user to notify them
of this.
A spam-reporting add-in can be used to report a message even if the Reading
Pane of the Outlook client is turned off.

Only one message can be reported at a time. If a user attempts to report another
message while the previous one is still being processed, a dialog will be shown to
them to notify them of this.

The add-in can still process the reported message even if the user navigates away
from the selected message.

The buttons that appear in the preprocessing and post-processing dialogs aren't
customizable. Additionally, the text and buttons in the timeout and ongoing report
dialogs can't be modified.

The integrated spam reporting and event-based activation features must use the
same runtime. Multiple runtimes aren't currently supported in Outlook. To learn
more about runtimes, see Runtimes in Office Add-ins.

See also
Office Add-ins manifest
Runtimes in Office Add-ins
Configure your Outlook add-in for event-based
activation
Article • 07/06/2023

Without the event-based activation feature, a user has to explicitly launch an add-in to complete their
tasks. This feature enables your add-in to run tasks based on certain events, particularly for operations that
apply to every item. You can also integrate with the task pane and function commands.

By the end of this walkthrough, you'll have an add-in that runs whenever a new item is created and sets
the subject.

7 Note

Support for this feature was introduced in requirement set 1.10, with additional events now available
in subsequent requirement sets. For details about an event's minimum requirement set and the
clients and platforms that support it, see Supported events and Requirement sets supported by
Exchange servers and Outlook clients.

The add-in from the following walkthrough only runs in Outlook on Windows, on Mac, and on the
web. To learn how to implement an event-based add-in that runs in Outlook on mobile devices, see
Implement event-based activation in Outlook mobile add-ins.

Supported events
The following table lists events that are currently available and the supported clients for each event. When
an event is raised, the handler receives an event object which may include details specific to the type of
event. The Description column includes a link to the related object where applicable.

Event canonical name Unified manifest for Microsoft Description Minimum


and XML manifest name 365 name requirement
set and
supported
clients

OnNewMessageCompose newMessageComposeCreated On composing a new message 1.10


(includes reply, reply all, and
forward) but not on editing, for - Windows1
example, a draft. - Web
browser
- New Mac
UI
- Android2
- iOS2

OnNewAppointmentOrganizer newAppointmentOrganizerCreated On creating a new appointment but 1.10


not on editing an existing one.
- Windows1
- Web
browser
Event canonical name Unified manifest for Microsoft Description Minimum
and XML manifest name 365 name requirement
set and
supported
clients

- New Mac
UI

OnMessageAttachmentsChanged messageAttachmentsChanged On adding or removing attachments 1.11


while composing a message.
- Windows1
Event-specific data object: - Web
AttachmentsChangedEventArgs browser
- New Mac
UI

OnAppointmentAttachmentsChanged appointmentAttachmentsChanged On adding or removing attachments 1.11


while composing an appointment.
- Windows1
Event-specific data object: - Web
AttachmentsChangedEventArgs browser
- New Mac
UI

OnMessageRecipientsChanged messageRecipientsChanged On adding or removing recipients 1.11


while composing a message.
- Windows1
Event-specific data object: - Web
RecipientsChangedEventArgs browser
- New Mac
UI

OnAppointmentAttendeesChanged appointmentAttendeesChanged On adding or removing attendees 1.11


while composing an appointment.
- Windows1
Event-specific data object: - Web
RecipientsChangedEventArgs browser
- New Mac
UI

OnAppointmentTimeChanged appointmentTimeChanged On changing date/time while 1.11


composing an appointment.
- Windows1
Event-specific data object: - Web
AppointmentTimeChangedEventArgs browser
- New Mac
UI

OnAppointmentRecurrenceChanged appointmentRecurrenceChanged On adding, changing, or removing 1.11


the recurrence details while
composing an appointment. If the - Windows1
date/time is changed, the - Web
OnAppointmentTimeChanged event will browser
also be fired. - New Mac
UI
Event-specific data object:
RecurrenceChangedEventArgs
Event canonical name Unified manifest for Microsoft Description Minimum
and XML manifest name 365 name requirement
set and
supported
clients

OnInfoBarDismissClicked infoBarDismissClicked On dismissing a notification while 1.11


composing a message or
appointment item. Only the add-in - Windows1
that added the notification will be - Web
notified. browser
- New Mac
Event-specific data object: UI
InfobarClickedEventArgs

OnMessageSend messageSending On sending a message item. To learn 1.12


more, see the Smart Alerts
walkthrough. - Windows1
- Web
browser
- New Mac
UI

OnAppointmentSend appointmentSending On sending an appointment item. To 1.12


learn more, see the Smart Alerts
walkthrough. - Windows1
- Web
browser
- New Mac
UI

OnMessageCompose messageComposeOpened On composing a new message 1.12


(includes reply, reply all, and
forward) or editing a draft. - Windows1
- Web
browser
- New Mac
UI

OnAppointmentOrganizer appointmentOrganizerOpened On creating a new appointment or 1.12


editing an existing one.
- Windows1
- Web
browser
- New Mac
UI

OnMessageFromChanged Not available On changing the mail account in the 1.13


From field of a message being
composed. To learn more, see - Windows1
Automatically update your signature - Web
when switching between Exchange browser
accounts.
Preview

- New Mac
UI
Event canonical name Unified manifest for Microsoft Description Minimum
and XML manifest name 365 name requirement
set and
supported
clients

OnAppointmentFromChanged Not available On changing the mail account in the 1.13


organizer field of an appointment
being composed. To learn more, see - Web
Automatically update your signature browser
when switching between Exchange
accounts. Preview

- New Mac
UI

OnSensitivityLabelChanged Not available On changing the sensitivity label 1.13


while composing a message or
appointment. To learn how to - Windows1
manage the sensitivity label of a mail - Web
item, see Manage the sensitivity browser
label of your message or
appointment in compose mode. Preview

Event-specific data object: - New Mac


SensitivityLabelChangedEventArgs UI

7 Note

1 Event-based add-ins in Outlook on Windows require a minimum of Windows 10 Version 1903 (Build
18362) or Windows Server 2019 Version 1903 to run.

2 Outlook on mobile supports APIs up to Mailbox requirement set 1.5. However, support is now
enabled for additional APIs and features introduced in later requirement sets, such as the
OnNewMessageCompose event. To learn more, see Implement event-based activation in Outlook mobile

add-ins.

Set up your environment


Complete the Outlook quick start which creates an add-in project with the Yeoman generator for Office
Add-ins.

Configure the manifest


To configure the manifest, select the tab for the type of manifest you are using.

XML Manifest

To enable event-based activation of your add-in, you must configure the Runtimes element and
LaunchEvent extension point in the VersionOverridesV1_1 node of the manifest.

1. In your code editor, open the quick start project.


2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including open and close tags) and replace it with
the following XML, then save your changes.

XML

<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.10">
<bt:Set Name="Mailbox" />
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<!-- Event-based activation happens in a lightweight runtime.-->
<Runtimes>
<!-- HTML file including reference to or inline JavaScript event handlers.
This is used by Outlook on the web and Outlook on the new Mac UI. -->
<Runtime resid="WebViewRuntime.Url">
<!-- JavaScript file containing event handlers. This is used by Outlook on
Windows. -->
<Override type="javascript" resid="JSRuntime.Url"/>
</Runtime>
</Runtimes>
<DesktopFormFactor>
<FunctionFile resid="Commands.Url" />
<ExtensionPoint xsi:type="MessageReadCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgReadGroup">
<Label resid="GroupLabel" />
<Control xsi:type="Button" id="msgReadOpenPaneButton">
<Label resid="TaskpaneButton.Label" />
<Supertip>
<Title resid="TaskpaneButton.Label" />
<Description resid="TaskpaneButton.Tooltip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16" />
<bt:Image size="32" resid="Icon.32x32" />
<bt:Image size="80" resid="Icon.80x80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="Taskpane.Url" />
</Action>
</Control>
<Control xsi:type="Button" id="ActionButton">
<Label resid="ActionButton.Label"/>
<Supertip>
<Title resid="ActionButton.Label"/>
<Description resid="ActionButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>action</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>

<!-- Can configure other command surface extension points for add-in command
support. -->

<!-- Enable launching the add-in on the included events. -->


<ExtensionPoint xsi:type="LaunchEvent">
<LaunchEvents>
<LaunchEvent Type="OnNewMessageCompose"
FunctionName="onNewMessageComposeHandler"/>
<LaunchEvent Type="OnNewAppointmentOrganizer"
FunctionName="onNewAppointmentComposeHandler"/>

<!-- Other available events -->


<!--
<LaunchEvent Type="OnMessageAttachmentsChanged"
FunctionName="onMessageAttachmentsChangedHandler" />
<LaunchEvent Type="OnAppointmentAttachmentsChanged"
FunctionName="onAppointmentAttachmentsChangedHandler" />
<LaunchEvent Type="OnMessageRecipientsChanged"
FunctionName="onMessageRecipientsChangedHandler" />
<LaunchEvent Type="OnAppointmentAttendeesChanged"
FunctionName="onAppointmentAttendeesChangedHandler" />
<LaunchEvent Type="OnAppointmentTimeChanged"
FunctionName="onAppointmentTimeChangedHandler" />
<LaunchEvent Type="OnAppointmentRecurrenceChanged"
FunctionName="onAppointmentRecurrenceChangedHandler" />
<LaunchEvent Type="OnInfoBarDismissClicked"
FunctionName="onInfobarDismissClickedHandler" />
<LaunchEvent Type="OnMessageSend" FunctionName="onMessageSendHandler"
SendMode="PromptUser" />
<LaunchEvent Type="OnAppointmentSend"
FunctionName="onAppointmentSendHandler" SendMode="PromptUser" />
<LaunchEvent Type="OnMessageCompose"
FunctionName="onMessageComposeHandler" />
<LaunchEvent Type="OnAppointmentOrganizer"
FunctionName="onAppointmentOrganizerHandler" />
-->
</LaunchEvents>
<!-- Identifies the runtime to be used (also referenced by the Runtime
element). -->
<SourceLocation resid="WebViewRuntime.Url"/>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-
16.png"/>
<bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-
32.png"/>
<bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-
80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Commands.Url" DefaultValue="https://localhost:3000/commands.html" />
<bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html" />
<bt:Url id="WebViewRuntime.Url"
DefaultValue="https://localhost:3000/commands.html" />
<!-- Entry needed for Outlook on Windows. -->
<bt:Url id="JSRuntime.Url" DefaultValue="https://localhost:3000/launchevent.js"
/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Contoso Add-in"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show Taskpane"/>
<bt:String id="ActionButton.Label" DefaultValue="Perform an action"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Opens a pane displaying all
available properties."/>
<bt:String id="ActionButton.Tooltip" DefaultValue="Perform an action when
clicked."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

Outlook on Windows uses a JavaScript file, while Outlook on the web and on the new Mac UI use an
HTML file that can reference the same JavaScript file. You must provide references to both these files
in the Resources node of the manifest as the Outlook platform ultimately determines whether to use
HTML or JavaScript based on the Outlook client. As such, to configure event handling, provide the
location of the HTML in the <Runtime> element, then in its Override child element provide the
location of the JavaScript file inlined or referenced by the HTML.

 Tip

To learn about runtimes in add-ins, see Runtimes in Office Add-ins.


To learn more about manifests for Outlook add-ins, see Office add-in manifests.

Implement event handling


You have to implement handling for your selected events.

In this scenario, you'll add handling for composing new items.

1. From the same quick start project, create a new folder named launchevent under the ./src directory.

2. In the ./src/launchevent folder, create a new file named launchevent.js.

3. Open the file ./src/launchevent/launchevent.js in your code editor and add the following JavaScript
code.

JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT
license.
* See LICENSE in the project root for license information.
*/

function onNewMessageComposeHandler(event) {
setSubject(event);
}
function onNewAppointmentComposeHandler(event) {
setSubject(event);
}
function setSubject(event) {
Office.context.mailbox.item.subject.setAsync(
"Set by an event-based add-in!",
{
"asyncContext": event
},
function (asyncResult) {
// Handle success or error.
if (asyncResult.status !== Office.AsyncResultStatus.Succeeded) {
console.error("Failed to set subject: " + JSON.stringify(asyncResult.error));
}

// Call event.completed() after all work is done.


asyncResult.asyncContext.completed();
});
}

// IMPORTANT: To ensure your add-in is supported in the Outlook client on Windows,


remember to map the event handler name specified in the manifest to its JavaScript
counterpart.
if (Office.context.platform === Office.PlatformType.PC || Office.context.platform ==
null) {
Office.actions.associate("onNewMessageComposeHandler", onNewMessageComposeHandler);
Office.actions.associate("onNewAppointmentComposeHandler",
onNewAppointmentComposeHandler);
}

4. Save your changes.

) Important

Windows: At present, imports aren't supported in the JavaScript file where you implement the
handling for event-based activation.

 Tip

Event-based add-ins running in Outlook on Windows don't run code included in the
Office.onReady() and Office.initialize functions. We recommend adding your add-in startup logic,

such as checking the user's Outlook version, to your event handlers instead.

Update the commands HTML file


1. In the ./src/commands folder, open commands.html.

2. Immediately before the closing head tag ( </head> ), add a script entry to include the event-handling
JavaScript code.

HTML

<script type="text/javascript" src="../launchevent/launchevent.js"></script>


3. Save your changes.

Update webpack config settings


1. Open the webpack.config.js file found in the root directory of the project and complete the
following steps.

2. Locate the plugins array within the config object and add this new object at the beginning of the
array.

JavaScript

new CopyWebpackPlugin({
patterns: [
{
from: "./src/launchevent/launchevent.js",
to: "launchevent.js",
},
],
}),

3. Save your changes.

Try it out
1. Run the following commands in the root directory of your project. When you run npm start , the local
web server will start (if it's not already running) and your add-in will be sideloaded.

command line

npm run build

command line

npm start

7 Note

If your add-in wasn't automatically sideloaded, then follow the instructions in Sideload Outlook
add-ins for testing to manually sideload the add-in in Outlook.

2. In Outlook on the web, create a new message.


3. In Outlook on the new Mac UI, create a new message.

4. In Outlook on Windows, create a new message.


Troubleshooting guide
As you develop your event-based add-in, you may need to troubleshoot issues, such as your add-in not
loading or the event not occurring. If you run into development issues, refer to the next four sections for
troubleshooting guidance.

Review event-based activation prerequisites


Verify that the add-in is installed on a supported Outlook client. Event-based activation isn't
supported in Outlook on iOS or Android at this time.
Verify that your Outlook client supports the minimum requirement set needed to handle the event.
Event-based activation was introduced in requirement set 1.10, with additional events now supported
in subsequent requirements sets. For more information, see Supported events and Requirement sets
supported by Exchange servers and Outlook clients. If you're developing an add-in that uses the
Smart Alerts feature, see the Supported clients and platform section.
Review the expected behavior and limitations of the event-based activation and Smart Alerts
features.

Check manifest and JavaScript requirements


Ensure that the following conditions are met in your add-in's manifest.
Verify that your add-in's source file location URL is publicly available and isn't blocked by a
firewall. This URL is specified in your manifest's SourceLocation element.
Verify that the <Runtimes> element correctly references the HTML or JavaScript file containing
the event handlers. Outlook on Windows uses the JavaScript file during runtime, while Outlook on
the web and on new Mac UI use the HTML file. For an example of how this is configured in the
manifest, see Configure the manifest.

Verify that your event-handling JavaScript file referenced by the Outlook client on Windows calls
Office.actions.associate . This ensures that the event handler name specified in the manifest is

mapped to its JavaScript counterpart.

 Tip
If your event-based add-in has only one JavaScript file referenced by Outlook on the web,
Windows, and Mac, it's recommended to check on which platform the add-in is running to
determine when to call Office.actions.associate , as shown in the following code.

JavaScript

if (Office.context.platform === Office.PlatformType.PC || Office.context.platform


== null) {
Office.actions.associate("onNewMessageComposeHandler",
onNewMessageComposeHandler);
Office.actions.associate("onNewAppointmentComposeHandler",
onNewAppointmentComposeHandler);
}

The JavaScript code of event-based add-ins that run in Outlook on Windows only supports
ECMAScript 2016 and earlier specifications. Some examples of programming syntax to avoid are as
follows.
Avoid using async and await statements in your code. Including these in your JavaScript code will
cause the add-in to time out.
Avoid using the conditional (ternary) operator as it will prevent your add-in from loading.

If your add-in has only one JavaScript file referenced by Outlook on the web, Windows, and Mac, you
must limit your code to ECMAScript 2016 to ensure that your add-in runs in Outlook on Windows.
However, if you have a separate JavaScript file referenced by Outlook on the web and Mac, you can
implement a later ECMAScript specification in that file.

Debug your add-in


As you make changes to your add-in, be aware that:
If you update the manifest, remove the add-in, then sideload it again. If you're using Outlook on
Windows, you must also close and reopen Outlook.
If you make changes to files other than the manifest, close and reopen the Outlook desktop client,
or refresh the browser tab running Outlook on the web.
If you're still unable to see your changes after performing these steps, clear your Office cache.

As you test your add-in in Outlook on Windows:

Check Event Viewer for any reported add-in errors.

1. In Event Viewer, select Windows Logs > Application.


2. From the Actions panel, select Filter Current Log.
3. From the Logged dropdown, select your preferred log time frame.
4. Select the Error checkbox.
5. In the Event IDs field, enter 63.
6. Select OK to apply your filters.
Verify that the bundle.js file is downloaded to the following folder in File Explorer. Replace text
enclosed in [] with your applicable information.

text

%LOCALAPPDATA%\Microsoft\Office\16.0\Wef\{[Outlook profile GUID]}\[Outlook mail


account encoding]\Javascript\[Add-in ID]_[Add-in Version]_[locale]

 Tip

If the bundle.js file doesn't appear in the Wef folder, try the following:
If your add-in is installed or sideloaded, restart Outlook.
Remove your add-in from Outlook, then sideload it again.

As you test your add-in in Outlook on Windows or Mac, enable runtime logging to identify possible
manifest and add-in installation issues. For guidance on how to use runtime logging, see Debug your
add-in with runtime logging.

Set breakpoints in your code to debug your add-in. For platform-specific instructions, see Debug
your event-based Outlook add-in.

Seek additional help


If you still need help after performing the recommended troubleshooting steps, open a GitHub issue .
Include screenshots, video recordings, or runtime logs to supplement your report.

Deploy to users
Event-based add-ins are restricted to admin-managed deployments only, even if they're acquired from
AppSource. If users acquire the add-in from AppSource or the in-app Office Store, they won't be able to
activate the event-based function of the add-in. To learn more about listing your event-based add-in in
AppSource, see AppSource listing options for your event-based Outlook add-in.

Admin deployments are done by uploading the manifest to the Microsoft 365 admin center. In the admin
portal, expand the Settings section in the navigation pane then select Integrated apps. On the Integrated
apps page, choose the Upload custom apps action.

) Important

Add-ins that use the Smart Alerts feature can only be published to AppSource if the manifest's send
mode property is set to the prompt user or soft block option. If an add-in's send mode property is
set to block, it can only be deployed by an organization's admin as it will fail AppSource validation.

Deploy manifest updates


Because event-based add-ins are deployed by admins, any change you make to the manifest requires
admin consent through the Microsoft 365 admin center. Until the admin accepts your changes, users in
their organization are blocked from using the add-in. To learn more about the admin consent process, see
Admin consent for installing event-based add-ins.

Event-based activation behavior and limitations


Add-in launch-event handlers are expected to be short-running, lightweight, and as noninvasive as
possible. After activation, your add-in will time out within approximately 300 seconds, the maximum
length of time allowed for running event-based add-ins. To signal that your add-in has completed
processing a launch event, your associated event handler must call the event.completed method. (Note
that code included after the event.completed statement isn't guaranteed to run.) Each time an event that
your add-in handles is triggered, the add-in is reactivated and runs the associated event handler, and the
timeout window is reset. The add-in ends after it times out, or the user closes the compose window or
sends the item.

If the user has multiple add-ins that subscribed to the same event, the Outlook platform launches the add-
ins in no particular order. Currently, only five event-based add-ins can be actively running.

In all supported Outlook clients, the user must remain on the current mail item where the add-in was
activated for it to complete running. Navigating away from the current item (for example, switching to
another compose window or tab) terminates the add-in operation. The add-in also ceases operation when
the user sends the message or appointment they're composing.

When developing an event-based add-in to run in the Outlook on Windows client, be mindful of the
following:

Imports aren't supported in the JavaScript file where you implement the handling for event-based
activation.
Add-ins don't run code included in Office.onReady() and Office.initialize . We recommend
adding any startup logic, such as checking the user's Outlook version, to your event handlers instead.

Some Office.js APIs that change or alter the UI aren't allowed from event-based add-ins. The following are
the blocked APIs.

Under Office.context.auth :
getAccessToken

getAccessTokenAsync

7 Note

OfficeRuntime.auth is supported in all Outlook versions that support event-based activation


and single sign-on (SSO), while Office.auth is only supported in certain Outlook builds. For
more information, see Enable single sign-on (SSO) or cross-origin resource sharing (CORS)
in your event-based Outlook add-in.

Under Office.context.mailbox :
displayAppointmentForm

displayMessageForm
displayNewAppointmentForm

displayNewMessageForm

Under Office.context.mailbox.item :
close

Under Office.context.ui :
displayDialogAsync

messageParent

Preview features in event handlers (Outlook on Windows)


Outlook on Windows includes a local copy of the production and beta versions of Office.js instead of
loading from the content delivery network (CDN). By default, the local production copy of the API is
referenced. To reference the local beta copy of the API, you must configure your computer's registry. This
will enable you to test preview features in your event handlers in Outlook on Windows.

1. In the registry, navigate to


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Outlook\Options\WebExt\Developer . If the key

doesn't exist, create it.

2. Create an entry named EnableBetaAPIsInJavaScript and set its value to 1 .


Enable single sign-on (SSO)
To enable SSO in your event-based add-in, you must add its JavaScript file to a well-known URI. For
guidance on how to configure this resource, see Enable single sign-on (SSO) or cross-origin resource
sharing (CORS) in your event-based Outlook add-in.

Request external data


You can request external data by using an API like Fetch or by using XMLHttpRequest (XHR) ,a
standard web API that issues HTTP requests to interact with servers.

Be aware that you must use additional security measures when using XMLHttpRequest objects, requiring
Same Origin Policy and simple CORS (Cross-Origin Resource Sharing) .

A simple CORS implementation:

Can't use cookies.


Only supports simple methods, such as GET , HEAD , and POST .
Accepts simple headers with field names Accept , Accept-Language , or Content-Language .
Can use the Content-Type , provided that the content type is application/x-www-form-urlencoded ,
text/plain , or multipart/form-data .

Can't have event listeners registered on the object returned by XMLHttpRequest.upload .


Can't use ReadableStream objects in requests.

To enable your event-based add-in to make CORS request, you must add the add-in and its JavaScript file
to a well-known URI. For guidance on how to configure this resource, see Enable single sign-on (SSO) or
cross-origin resource sharing (CORS) in your event-based Outlook add-in.

7 Note

Full CORS support is available in Outlook on the web, Mac, and Windows (starting in Version 2201,
Build 16.0.14813.10000).

See also
Office add-in manifests
How to debug event-based add-ins
AppSource listing options for your event-based Outlook add-in
Smart Alerts and OnMessageSend walkthrough
Automatically update your signature when switching between mail accounts
Office Add-ins code samples:
Use Outlook event-based activation to encrypt attachments, process meeting request attendees
and react to appointment date/time changes
Use Outlook event-based activation to set the signature
Use Outlook event-based activation to tag external recipients
Use Outlook Smart Alerts
Verify the sensitivity label of a message
Use Smart Alerts and the
OnMessageSend and
OnAppointmentSend events in your
Outlook add-in
Article • 07/05/2023

The OnMessageSend and OnAppointmentSend events take advantage of Smart Alerts, which
allows you to run logic after a user selects Send in their Outlook message or
appointment. Your event handler allows you to give your users the opportunity to
improve their emails and meeting invites before they're sent.

The following walkthrough uses the OnMessageSend event. By the end of this
walkthrough, you'll have an add-in that runs whenever a message is being sent and
checks if the user forgot to add a document or picture they mentioned in their email.

7 Note

The OnMessageSend and OnAppointmentSend events were introduced in requirement


set 1.12. See clients and platforms that support this requirement set.

Prerequisites
The OnMessageSend event is available through the event-based activation feature. To
understand how to configure your add-in to use this feature, use other available events,
debug your add-in, and more, see Configure your Outlook add-in for event-based
activation.

Supported clients and platforms


The following table lists supported client-server combinations for the Smart Alerts
feature, including the minimum required Exchange Server Cumulative Update where
applicable. Excluded combinations aren't supported.

Client Exchange Exchange 2019 on-premises Exchange 2016 on-premises


Online (Cumulative Update 12 or (Cumulative Update 22 or
later) later)
Client Exchange Exchange 2019 on-premises Exchange 2016 on-premises
Online (Cumulative Update 12 or (Cumulative Update 22 or
later) later)

Windows Yes Yes Yes


Version 2206 (Build
15330.20196) or
later

Mac Yes Not applicable Not applicable


Version 16.65.827.0
or later

Web browser Yes Not applicable Not applicable


(modern UI)

iOS Not Not applicable Not applicable


applicable

Android Not Not applicable Not applicable


applicable

Set up your environment


Complete the Outlook quick start, which creates an add-in project with the Yeoman
generator for Office Add-ins.

Configure the manifest


To configure the manifest, select the tab for the type of manifest you are using.

XML Manifest

1. In your code editor, open the quick start project.

2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including open and close tags)
and replace it with the following XML, then save your changes.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.12">
<bt:Set Name="Mailbox" />
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<!-- Event-based activation happens in a lightweight runtime.-->
<Runtimes>
<!-- HTML file including reference to or inline JavaScript
event handlers.
This is used by Outlook on the web and on the new Mac UI.
-->
<Runtime resid="WebViewRuntime.Url">
<!-- JavaScript file containing event handlers. This is used
by Outlook on Windows. -->
<Override type="javascript" resid="JSRuntime.Url"/>
</Runtime>
</Runtimes>
<DesktopFormFactor>
<FunctionFile resid="Commands.Url" />
<ExtensionPoint xsi:type="MessageReadCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgReadGroup">
<Label resid="GroupLabel" />
<Control xsi:type="Button" id="msgReadOpenPaneButton">
<Label resid="TaskpaneButton.Label" />
<Supertip>
<Title resid="TaskpaneButton.Label" />
<Description resid="TaskpaneButton.Tooltip" />
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16" />
<bt:Image size="32" resid="Icon.32x32" />
<bt:Image size="80" resid="Icon.80x80" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="Taskpane.Url" />
</Action>
</Control>
<Control xsi:type="Button" id="ActionButton">
<Label resid="ActionButton.Label"/>
<Supertip>
<Title resid="ActionButton.Label"/>
<Description resid="ActionButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>action</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>

<!-- Can configure other command surface extension points for


add-in command support. -->

<!-- Enable launching the add-in on the included event. -->


<ExtensionPoint xsi:type="LaunchEvent">
<LaunchEvents>
<LaunchEvent Type="OnMessageSend"
FunctionName="onMessageSendHandler" SendMode="PromptUser" />
</LaunchEvents>
<!-- Identifies the runtime to be used (also referenced by
the Runtime element). -->
<SourceLocation resid="WebViewRuntime.Url"/>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Commands.Url"
DefaultValue="https://localhost:3000/commands.html" />
<bt:Url id="Taskpane.Url"
DefaultValue="https://localhost:3000/taskpane.html" />
<bt:Url id="WebViewRuntime.Url"
DefaultValue="https://localhost:3000/commands.html" />
<!-- Entry needed for Outlook on Windows. -->
<bt:Url id="JSRuntime.Url"
DefaultValue="https://localhost:3000/launchevent.js" />
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Contoso Add-in"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show
Taskpane"/>
<bt:String id="ActionButton.Label" DefaultValue="Perform an
action"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Opens a
pane displaying all available properties."/>
<bt:String id="ActionButton.Tooltip" DefaultValue="Perform an
action when clicked."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

 Tip

For a list of send mode options available with the OnMessageSend and
OnAppointmentSend events, see Available send mode options.

To learn more about manifests for Outlook add-ins, see Office add-in
manifests.

Implement event handling


You have to implement handling for your selected event.

In this scenario, you'll add handling for sending a message. Your add-in will check for
certain keywords in the message. If any of those keywords are found, it will then check if
there are any attachments. If there are no attachments, your add-in will recommend to
the user to add the possibly missing attachment.

1. From the same quick start project, create a new folder named launchevent under
the ./src directory.

2. In the ./src/launchevent folder, create a new file named launchevent.js.

3. Open the file ./src/launchevent/launchevent.js in your code editor and add the
following JavaScript code.

JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed
under the MIT license.
* See LICENSE in the project root for license information.
*/

function onMessageSendHandler(event) {
Office.context.mailbox.item.body.getAsync(
"text",
{ asyncContext: event },
getBodyCallback
);
}
function getBodyCallback(asyncResult){
let event = asyncResult.asyncContext;
let body = "";
if (asyncResult.status !== Office.AsyncResultStatus.Failed &&
asyncResult.value !== undefined) {
body = asyncResult.value;
} else {
let message = "Failed to get body text";
console.error(message);
event.completed({ allowEvent: false, errorMessage: message });
return;
}

let matches = hasMatches(body);


if (matches) {
Office.context.mailbox.item.getAttachmentsAsync(
{ asyncContext: event },
getAttachmentsCallback);
} else {
event.completed({ allowEvent: true });
}
}

function hasMatches(body) {
if (body == null || body == "") {
return false;
}

const arrayOfTerms = ["send", "picture", "document", "attachment"];


for (let index = 0; index < arrayOfTerms.length; index++) {
const term = arrayOfTerms[index].trim();
const regex = RegExp(term, 'i');
if (regex.test(body)) {
return true;
}
}

return false;
}

function getAttachmentsCallback(asyncResult) {
let event = asyncResult.asyncContext;
if (asyncResult.value.length > 0) {
for (let i = 0; i < asyncResult.value.length; i++) {
if (asyncResult.value[i].isInline == false) {
event.completed({ allowEvent: true });
return;
}
}

event.completed({ allowEvent: false, errorMessage: "Looks like you


forgot to include an attachment?" });
} else {
event.completed({ allowEvent: false, errorMessage:
"Looks like you're forgetting to include an attachment?" });
}
}

// IMPORTANT: To ensure your add-in is supported in the Outlook client


on Windows, remember to map the event handler name specified in the
manifest to its JavaScript counterpart.
if (Office.context.platform === Office.PlatformType.PC ||
Office.context.platform == null) {
Office.actions.associate("onMessageSendHandler",
onMessageSendHandler);
}

) Important

When developing your Smart Alerts add-in to run in Outlook on Windows, keep the
following in mind.

Imports aren't currently supported in the JavaScript file where you implement
the handling for event-based activation.
To ensure your add-in runs as expected when an OnMessageSend or
OnAppointmentSend event occurs in Outlook on Windows, call

Office.actions.associate in the JavaScript file where your handlers are

implemented. This maps the event handler name specified in the manifest to
its JavaScript counterpart. If this call isn't included in your JavaScript file and
the send mode property of your manifest is set to soft block or isn't specified,
your users will be blocked from sending messages or meetings.

Update the commands HTML file


1. In the ./src/commands folder, open commands.html.

2. Immediately before the closing head tag ( </head> ), add a script entry for the
event-handling JavaScript code.

JavaScript

<script type="text/javascript" src="../launchevent/launchevent.js">


</script>

3. Save your changes.


Update webpack config settings
1. Open the webpack.config.js file found in the root directory of the project and
complete the following steps.

2. Locate the plugins array within the config object and add this new object to the
beginning of the array.

JavaScript

new CopyWebpackPlugin({
patterns: [
{
from: "./src/launchevent/launchevent.js",
to: "launchevent.js",
},
],
}),

3. Save your changes.

Try it out
1. Run the following commands in the root directory of your project. When you run
npm start , the local web server will start (if it's not already running) and your add-

in will be sideloaded.

command line

npm run build

command line

npm start

7 Note

If your add-in wasn't automatically sideloaded, then follow the instructions in


Sideload Outlook add-ins for testing to manually sideload the add-in in
Outlook.
2. In your preferred Outlook client, create a new message and set the subject. In the
body, add some text. For example, "Hey, here's a picture of my dog!".

3. Send the message. A dialog should pop up with a recommendation for you to add
an attachment.

4. Add an attachment then send the message again. There should be no alert this
time.

Debug your add-in


For guidance on how to troubleshoot your Smart Alerts add-in, see the
"Troubleshooting guide" section of Configure your Outlook add-in for event-based
activation.

Deploy to users
Similar to other event-based add-ins, add-ins that use the Smart Alerts feature must be
deployed by an organization's administrator. For guidance on how to deploy your add-
in via the Microsoft 365 admin center, see the "Deploy to users" section in Configure
your Outlook add-in for event-based activation.

) Important
Add-ins that use the Smart Alerts feature can only be published to AppSource if the
manifest's send mode property is set to the soft block or prompt user option. If an
add-in's send mode property is set to block, it can only be deployed by an
organization's admin as it will fail AppSource validation. To learn more about
publishing your event-based add-in to AppSource, see AppSource listing options
for your event-based Outlook add-in.

Smart Alerts feature behavior and scenarios


The following sections include guidance on the send mode options and the behavior of
the feature in certain scenarios.

Available send mode options


When you configure your add-in to respond to the OnMessageSend or OnAppointmentSend
event, you must include the send mode property in the manifest. Its markup varies
depending on the type of manifest your add-in uses.

XML manifest: Set the SendMode property of the LaunchEvent element.


Unified manifest for Microsoft 365 (preview): Set the "sendMode" option of the
event object in the "autoRunEvents" array.

If the conditions implemented by your add-in aren't met or your add-in is unavailable
when the event occurs, a dialog is shown to the user to alert them that additional
actions may be needed before the mail item can be sent. The send mode property
determines the options available to the user in the dialog.

The following table lists the available send mode options.

Send mode option canonical XML manifest Unified manifest for Microsoft 365
name name name

prompt user PromptUser promptUser

soft block SoftBlock softBlock

block Block block

prompt user
If the item doesn't meet the add-in's conditions, the user can choose Send Anyway in
the alert, or address the issue then try to send the item again. If the add-in is taking a
long time to process the item, the user will be prompted with the option to stop running
the add-in and choose Send Anyway. In the event the add-in is unavailable (for
example, there's an error loading the add-in), the item will be sent.

Use the prompt user option in your add-in if one of the following applies.

The condition checked by the add-in isn't mandatory, but is nice to have in the
message or appointment being sent.
You'd like to recommend an action and allow the user to decide whether they want
to apply it to the message or appointment being sent.

Some scenarios where the prompt user option is applied include suggesting to tag the
message or appointment as low or high importance and recommending to apply a color
category to the item.

soft block

Default option if the send mode property of your manifest isn't configured. The user is
alerted that the item they're sending doesn't meet the add-in's conditions and they
must address the issue before trying to send the item again. However, if the add-in is
unavailable (for example, there's an error loading the add-in), the item will be sent.

Use the soft block option in your add-in when you want a condition to be met before a
message or appointment can be sent, but you don't want the user to be blocked from
sending the item if the add-in is unavailable. Sample scenarios where the soft block
option is used include prompting the user to set a message or appointment's
importance level and checking that the appropriate signature is applied before the item
is sent.

block
The item isn't sent if any of the following situations occur.

The item doesn't meet the add-in's conditions.


The add-in is unable to connect to the server.
There's an error loading the add-in.

Use the block option if the add-in's conditions are mandatory, even if the add-in is
unavailable. For example, the block option is ideal when users are required to apply a
sensitivity label to a message or appointment before it can be sent.

Add-in is unavailable
If the add-in is unavailable when a message or appointment is being sent (for example,
an error occurs that prevents the add-in from loading), the user is alerted. The options
available to the user differ depending on the send mode option applied to the add-in.

If the prompt user or soft block option is used, the user can choose Send Anyway to
send the item without the add-in checking it, or Try Later to let the item be checked by
the add-in when it becomes available again.

If the block option is used, the user can't send the item until the add-in becomes
available.
Long-running add-in operations
If the add-in runs for more than five seconds, but less than five minutes, the user is
alerted that the add-in is taking longer than expected to process the message or
appointment.

If the prompt user option is used, the user can choose Send Anyway to send the item
without the add-in completing its check. Alternatively, the user can select Don't Send to
stop the add-in from processing.

However, if the soft block or block option is used, the user will not be able to send the
item until the add-in completes processing it.

OnMessageSend and OnAppointmentSend add-ins should be short-running and lightweight.


To avoid the long-running operation dialog, use other events to process conditional
checks before the OnMessageSend or OnAppointmentSend event is activated. For example,
if the user is required to encrypt attachments for every message or appointment,
consider using the OnMessageAttachmentsChanged or OnAppointmentAttachmentsChanged
event to perform the check.

Add-in timed out


If the add-in runs for five minutes or more, it will time out. If the prompt user option is
used, the user can choose Send Anyway to send the item without the add-in
completing its check. Alternatively, the user can choose Don't Send.
If the soft block or block option is used, the user can't send the item until the add-in
completes its check. The user must attempt to send the item again to reactivate the
add-in.

Limitations
Because the OnMessageSend and OnAppointmentSend events are supported through the
event-based activation feature, the same feature limitations apply to add-ins that
activate as a result of these events. For a description of these limitations, see Event-
based activation behavior and limitations.

In addition to these constraints, only one instance each of the OnMessageSend and
OnAppointmentSend event can be declared in the manifest. If you require multiple

OnMessageSend or OnAppointmentSend events, you must declare each one in a separate

add-in.

While a Smart Alerts dialog message can be changed to suit your add-in scenario using
the errorMessage property of the event.completed method, the following can't be
customized.

The dialog's title bar. Your add-in's name is always displayed there.
The message's format. For example, you can't change the text's font size and color
or insert a bulleted list.
The dialog options. For example, the Send Anyway and Don't Send options are
fixed and depend on the send mode option you select.
Event-based activation processing and progress information dialogs. For example,
the text and options that appear in the timeout and long-running operation
dialogs can't be changed.
Differences between Smart Alerts and the on-
send feature
While Smart Alerts and the on-send feature provide your users the opportunity to
improve their messages and meeting invites before they're sent, Smart Alerts is a newer
feature that offers you more flexibility with how you prompt your users for further
action. Key differences between the two features are outlined in the following table.

Attribute Smart Alerts On-send

Minimum Mailbox 1.12 Mailbox 1.8


supported
requirement
set

Supported - Windows - Windows


Outlook - Web browser (modern UI) - Web browser (classic and
clients - Mac (new UI) modern UI)
- Mac (classic and new UI)

Supported XML manifest XML manifest


events - OnMessageSend - ItemSend
- OnAppointmentSend
Unified manifest for Microsoft
Unified manifest for Microsoft 365 365 (preview)
(preview) - Not supported
- "messageSending"
- "appointmentSending"

Manifest XML manifest XML manifest


extension - LaunchEvent - Events
property
Unified manifest for Microsoft 365 Unified manifest for Microsoft
(preview) 365 (preview)
- "autoRunEvents" - Not supported

Supported - prompt user Block


send mode - soft block
options - block

To learn more about each option, see


Available send mode options.

Maximum One OnMessageSend and one One ItemSend event.


number of OnAppointmentSend event.
supported
events in an
add-in
Attribute Smart Alerts On-send

Add-in Add-in can be published to AppSource if its Add-in can't be published to


deployment send mode property is set to the soft block AppSource. It must be deployed
or prompt user option. Otherwise, the add- by an organization's administrator.
in must be deployed by an organization's
administrator.

Additional No additional configuration is needed once Depending on the organization's


configuration the manifest is uploaded to the Microsoft compliance standards and the
for add-in 365 admin center. Outlook client used, certain
installation mailbox policies must be
configured to install the add-in.

See also
Office add-in manifests
Configure your Outlook add-in for event-based activation
Event-based activation troubleshooting guide
How to debug event-based add-ins
AppSource listing options for your event-based Outlook add-in
Office Add-ins code sample: Use Outlook Smart Alerts
Office Add-ins code sample: Verify the sensitivity label of a message
Automatically update your signature
when switching between Exchange
accounts
Article • 05/20/2023

Applying the correct signature to messages when using multiple Exchange accounts is
now made easier with the addition of the OnMessageFromChanged and
OnAppointmentFromChanged events to the event-based activation feature. The

OnMessageFromChanged event occurs when the account in the From field of a message
being composed is changed, while the OnAppointmentFromChanged event occurs when the
organizer of a meeting being composed is changed. These events further extend the
capabilities of signature add-ins and allow them to:

Provide users with the convenience to apply custom signatures for each of their
accounts.
Enable mailbox delegates to more accurately and efficiently manage outgoing
messages and meeting requests from multiple mailboxes.
Ensure that users' messages and appointments meet their organization's
communication and marketing policies.

The following sections walk you through how to develop an event-based add-in that
handles the OnMessageFromChanged event to automatically update a message's signature
when the mail account in the From field is changed.

7 Note

The OnMessageFromChanged and OnAppointmentFromChanged events were introduced


in requirement set 1.13. For information about client support for these events, see
Supported clients and platforms.

Supported clients and platforms


The following tables list client-server combinations that support the
OnMessageFromChanged and OnAppointmentFromChanged events. Select the tab for the
applicable event.

OnMessageFromChanged event
Client Exchange Exchange 2019 on- Exchange 2016 on-
Online premises (Cumulative premises (Cumulative
Update 12 or later) Update 22 or later)

Windows Supported Supported Supported


Version 2304
(Build
16327.20248) or
later

Mac Supported Not applicable Not applicable


Version 16.69.116
or later (preview)

Web browser Supported Not applicable Not applicable


(modern UI)

iOS Not Not applicable Not applicable


applicable

Android Not Not applicable Not applicable


applicable

Prerequisites
To test the walkthrough, you must have at least two Exchange accounts.

To preview the OnMessageFromChanged and OnAppointmentFromChanged events in Outlook


on Mac, install Version 16.69.116 or later.

Set up your environment


Complete the Outlook quick start, which creates an add-in project with the Yeoman
generator for Office Add-ins.

Configure the manifest

7 Note

The OnMessageFromChanged and OnAppointmentFromChanged events aren't yet


supported for the Unified manifest for Microsoft 365 (preview).
To enable the add-in to activate when the OnMessageFromChanged event occurs, the
Runtimes element and LaunchEvent extension point must be configured in the
VersionOverridesV1_1 node of the manifest.

In addition to the OnMessageFromChanged event, the OnNewMessageCompose event is also


configured in the manifest, so that a signature is added to a message being composed if
a default Outlook signature isn't already configured on the current account.

1. In your code editor, open the quick start project.

2. Open the manifest.xml file located at the root of your project.

3. Select the entire <VersionOverrides> node (including open and close tags),
replace it with the following XML, then save your changes.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
<Requirements>
<bt:Sets DefaultMinVersion="1.13">
<bt:Set Name="Mailbox"/>
</bt:Sets>
</Requirements>
<Hosts>
<Host xsi:type="MailHost">
<Runtimes>
<!-- HTML file that references or contains inline JavaScript
event handlers.
This is used by event-based activation add-ins in
Outlook on the web and Outlook on the new Mac UI. -->
<Runtime resid="WebViewRuntime.Url">
<!-- JavaScript file that contains the event handlers.
This is used by event-based activation add-ins in
Outlook on Windows. -->
<Override type="javascript" resid="JSRuntime.Url"/>
</Runtime>
</Runtimes>
<DesktopFormFactor>
<FunctionFile resid="Commands.Url"/>
<ExtensionPoint xsi:type="MessageComposeCommandSurface">
<OfficeTab id="TabDefault">
<Group id="msgComposeGroup">
<Label resid="GroupLabel"/>
<Control xsi:type="Button"
id="msgComposeOpenPaneButton">
<Label resid="TaskpaneButton.Label"/>
<Supertip>
<Title resid="TaskpaneButton.Label"/>
<Description resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
<Control xsi:type="Button" id="ActionButton">
<Label resid="ActionButton.Label"/>
<Supertip>
<Title resid="ActionButton.Label"/>
<Description resid="ActionButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ExecuteFunction">
<FunctionName>action</FunctionName>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
<!-- Configures event-based activation. -->
<ExtensionPoint xsi:type="LaunchEvent">
<LaunchEvents>
<LaunchEvent Type="OnNewMessageCompose"
FunctionName="onNewMessageComposeHandler"/>
<LaunchEvent Type="OnMessageFromChanged"
FunctionName="onMessageFromChangedHandler"/>
</LaunchEvents>
<!-- Identifies the runtime to be used (also referenced by
the <Runtime> element). -->
<SourceLocation resid="WebViewRuntime.Url"/>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16"
DefaultValue="https://localhost:3000/assets/icon-16.png"/>
<bt:Image id="Icon.32x32"
DefaultValue="https://localhost:3000/assets/icon-32.png"/>
<bt:Image id="Icon.80x80"
DefaultValue="https://localhost:3000/assets/icon-80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="Commands.Url"
DefaultValue="https://localhost:3000/commands.html"/>
<bt:Url id="Taskpane.Url"
DefaultValue="https://localhost:3000/taskpane.html"/>
<bt:Url id="JSRuntime.Url"
DefaultValue="https://localhost:3000/launchevent.js"/>
<bt:Url id="WebViewRuntime.Url"
DefaultValue="https://localhost:3000/commands.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="GroupLabel" DefaultValue="Contoso Add-in"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="Show
Taskpane"/>
<bt:String id="ActionButton.Label" DefaultValue="Perform an
action"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Opens a
pane displaying all available properties."/>
<bt:String id="ActionButton.Tooltip" DefaultValue="Perform an
action when clicked."/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</VersionOverrides>

 Tip

To learn about runtimes in add-ins, see Runtimes in Office Add-ins.


To learn more about manifests for Outlook add-ins, see Office add-in
manifests.

Implement the event handlers


Event handlers must be configured for the OnNewMessageCompose and
OnMessageFromChanged events. The onNewMessageComposeHandler function adds a
signature to a newly created message if a default one isn't already configured on the
current account. When the account in the From field is changed, the
onMessageFromChangedHandler function updates the signature based on this newly

selected account.

1. From the same quick start project, navigate to the ./src directory, then create a
new folder named launchevent.

2. In the ./src/launchevent folder, create a new file named launchevent.js.


3. Open the file ./src/launchevent/launchevent.js in your code editor and add the
following JavaScript code.

JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed
under the MIT license.
* See LICENSE in the project root for license information.
*/

// The OnNewMessageCompose event handler that adds a signature to a new


message.
function onNewMessageComposeHandler(event) {
const item = Office.context.mailbox.item;

// Check if a default Outlook signature is already configured.


item.isClientSignatureEnabledAsync({ asyncContext: event },
(result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
console.log(result.error.message);
return;
}

// Add a signature if there's no default Outlook signature


configured.
if (result.value === false) {
item.body.setSignatureAsync(
"<i>This is a sample signature.</i>",
{ asyncContext: result.asyncContext, coercionType:
Office.CoercionType.Html },
addSignatureCallback
);
}
});
}

// The OnMessageFromChanged event handler that updates the signature


when the email address in the From field is changed.
function onMessageFromChangedHandler(event) {
const item = Office.context.mailbox.item;
const signatureIcon =

"iVBORw0KGgoAAAANSUhEUgAAACcAAAAnCAMAAAC7faEHAAAAAXNSR0IArs4c6QAAAARnQU
1BAACxjwv8YQUAAAAzUExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAKMFRskAAAAQdFJOUwAQIDBAUGBwgI+fr7/P3+8jGoKKAAAACXBI
WXMAAA7DAAAOwwHHb6hkAAABT0lEQVQ4T7XT2ZalIAwF0DAJhMH+/6+tJOQqot6X6joPiou
NBo3w9/Hd6+hrYnUt6vhLcjEAJevVW0zJxABSlcunhERpjY+UKoNN5+ZgDGu2onNz0OngjP
2FM1VdyBW1LtvGeYrBLs7U5I1PTXZt+zifcS3Icw2GcS3vxRY3Vn/iqx31hUyTnV515kdTf
baNhZLI30AceqDiIo4tyKEmJpKdP5M4um+nUwfDWxAXdzqMNKQ14jLdL5ntXzxcRF440mhS
6yu882Kxa30RZcUIjTCJg7lscsR4VsMjfX9Q0Vuv/Wd3YosD1J4LuSRtaL7bzXGN1wx2cyt
UdncDuhA3fu6HPTiCvpQUIjZ3sCcHVbvLtbNTHlysx2w9/s27m9gEb+7CTri6hR1wcTf2gV
f3wBRe3CMbcHYvTODkXhnD0+178K/pZ9+n/C1ru/2HAPwAo7YM1X4+tLMAAAAASUVORK5CY
II=";
// Get the currently selected From account.
item.from.getAsync({ asyncContext: event }, (result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
console.log(result.error.message);
return;
}

// Create a signature based on the currently selected From


account.
const name = result.value.displayName;
const options = { asyncContext: { event: result.asyncContext,
name: name }, isInline: true };
item.addFileAttachmentFromBase64Async(signatureIcon,
"signatureIcon.png", options, (result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
console.log(result.error.message);
return;
}

// Add the created signature to the mail item.


const signature = "<img src='cid:signatureIcon.png'>" +
result.asyncContext.name;
item.body.setSignatureAsync(
signature,
{ asyncContext: result.asyncContext.event,
coercionType: Office.CoercionType.Html },
addSignatureCallback
);
});
});
}

// Callback function to add a signature to the mail item.


function addSignatureCallback(result) {
if (result.status === Office.AsyncResultStatus.Failed) {
console.log(result.error.message);
return;
}

console.log("Successfully added signature.");


result.asyncContext.completed();
}

// IMPORTANT: To ensure your add-in is supported in the Outlook client


on Windows, remember to
// map the event handler name specified in the manifest's LaunchEvent
element to its JavaScript counterpart.
if (Office.context.platform === Office.PlatformType.PC ||
Office.context.platform == null) {
Office.actions.associate("onNewMessageComposeHandler",
onNewMessageComposeHandler);
Office.actions.associate("onMessageFromChangedHandler",
onMessageFromChangedHandler);
}
) Important

Windows: At present, imports aren't supported in the JavaScript file where you
implement the handling for event-based activation.

 Tip

Event-based add-ins running in Outlook on Windows don't run code included in


the Office.onReady() and Office.initialize functions. We recommend adding
your add-in startup logic, such as checking the user's Outlook version, to your
event handlers instead.

Update the commands HTML file


1. From the ./src/commands folder, open commands.html.

2. To run the add-in in Outlook on Mac, replace the existing script tag with the
following code. Otherwise, skip to the next step.

HTML

<script type="text/javascript"
src="https://appsforoffice.microsoft.com/lib/beta/hosted/office.js">
</script>

3. Add the following code below the existing script tag.

HTML

<script type="text/javascript" src="../launchevent/launchevent.js">


</script>

4. Save your changes.

Update webpack config settings


1. From the root directory of the project, open the webpack.config.js file.
2. Locate the plugins array within the config object and add the following new
object to the beginning of the array.

JavaScript

new CopyWebpackPlugin({
patterns: [
{
from: "./src/launchevent/launchevent.js",
to: "launchevent.js",
},
],
}),

3. Save your changes.

Try it out
1. Run the following commands in the root directory of your project. When you run
npm start , the local web server will start (if it isn't already running) and your add-
in will be sideloaded.

command line

npm run build

command line

npm start

7 Note

If your add-in wasn't automatically sideloaded, then follow the instructions in


Sideload Outlook add-ins for testing to manually sideload the add-in in
Outlook.

2. In your preferred Outlook client, create a new message. If you don't have a default
Outlook signature configured, the add-in adds one to the newly created message.
3. Enable the From field, if applicable. For guidance on how to enable it, see the
"Why is the From button missing?" section of Change the account used to send
email messages .

4. Select From, then choose a different Exchange account. Alternatively, manually


enter the Exchange email address by selecting From > Other Email Address. An
updated signature is added to the message, replacing the previous one.

Troubleshoot your add-in


For guidance on how to troubleshoot your event-based activation add-in, see the
"Troubleshooting guide" section of Configure your Outlook add-in for event-based
activation.
Deploy to users
Similar to other event-based add-ins, add-ins that use the OnMessageFromChanged and
OnAppointmentFromChanged events must be deployed by an organization's administrator.

For guidance on how to deploy your add-in via the Microsoft 365 admin center, see the
"Deploy to users" section of Configure your Outlook add-in for event-based activation.

Event behavior and limitations


Because the OnMessageFromChanged and OnAppointmentFromChanged events are supported
through the event-based activation feature, the same behavior and limitations apply to
add-ins that activate as a result of this event. For a detailed description, see Event-based
activation behavior and limitations.

In addition to these characteristics, the following aspects also apply when an add-in
activates on these events.

The OnMessageFromChanged event is only supported in message compose mode,


while the OnAppointmentFromChanged event is only supported in appointment
compose mode.
In Outlook on Windows, only the OnMessageFromChanged event is supported.
The OnMessageFromChanged and OnAppointmentFromChanged events only support
Exchange accounts. In messages being composed, the Exchange account is
selected from the From field dropdown list or manually entered in the field. In
appointments being composed, the Exchange account is selected from the
organizer field dropdown list. If a user switches to a non-Exchange account in the
From or organizer field, the Outlook client automatically clears out the signature
set by the previously selected account.
Delegate and shared mailbox scenarios are supported.
The OnAppointmentFromChanged event isn't supported in Microsoft 365 group
calendars . If a user switches from their Exchange account to a Microsoft 365
group calendar account in the organizer field, the Outlook client automatically
clears out the signature set by the Exchange account.
When switching to another Exchange account in the From or organizer field, the
add-ins for the previously selected account, if any, are terminated, and the add-ins
associated with the newly selected account are loaded before the
OnMessageFromChanged or OnAppointmentFromChanged event is initiated.

Email account aliases are supported. When an alias for the current account is
selected in the From or organizer field, the OnMessageFromChanged or
OnAppointmentFromChanged event occurs without reloading the account's add-ins.
When the From or organizer field dropdown list is opened by mistake or the same
account that appears in the From or organizer field is reselected, the
OnMessageFromChanged or OnAppointmentFromChanged event occurs, but the

account's add-ins aren't terminated or reloaded.

See also
Configure your Outlook add-in for event-based activation
AppSource listing options for your event-based Outlook add-in
Debug your event-based Outlook add-in
Article • 06/29/2023

This article discusses the key debugging stages to enable and set breakpoints in your
code as you implement event-based activation in your add-in. The event-based
activation feature was introduced in requirement set 1.10, with additional events now
available in subsequent requirement sets. For more information, see Supported events.
Before you proceed, review the event-based troubleshooting guide for additional
guidance.

To begin debugging, select the tab for your applicable client.

Windows

If you used the Yeoman generator for Office Add-ins to create your add-in project
(for example, by doing the event-based activation walkthrough), follow the Created
with Yeoman generator option throughout this article. Otherwise, follow the Other
steps. Visual Studio Code should be at least version 1.56.1.

Mark your add-in for debugging and set the


debugger port
1. Set the registry key
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Wef\Developer\[Add-in

ID]\UseDirectDebugger . Replace [Add-in ID] with your add-in's ID from the

manifest.

XML manifest: Use the value of the <Id> element child of the root
<OfficeApp> element.
Unified manifest for Microsoft 365 (preview): Use the value of the "id"
property of the root anonymous { ... } object.

7 Note

If the Developer key (folder) doesn't already exist under


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\ , complete the
following steps to create it.
a. Right-click the WEF key (folder) and select New > Key.
b. Name the new key Developer.
Created with Yeoman generator: In a command line window, navigate to the
root of your add-in folder then run the following command.

command line

npm start

In addition to building the code and starting the local server, this command
sets the UseDirectDebugger registry key for this add-in to 1 .

Other: Add the UseDirectDebugger registry key to


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\Developer\[Add-in

ID]\ . Replace [Add-in ID] with your add-in's ID from the manifest. Set the

registry key to 1 .

2. In the registry key


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Wef\Developer\[Add-in
ID] , where [Add-in ID] is your add-in's ID from the manifest, create a new

DWORD value with the following configuration.

Value name: DebuggerPort


Value data (hexadecimal): 00002407

This sets the debugger port to 9223 .

3. Start Outlook or restart it if it's already open.

4. Perform the action to initiate the event you're developing for, such as creating
a new message to initiate the OnNewMessageCompose event. The Debug Event-
based handler dialog should appear. Do not interact with the dialog yet.
Configure Visual Studio Code

Created with Yeoman generator


1. Back in the command line window, run the following to open your add-in
project in Visual Studio Code.

command line

code .

2. In Visual Studio Code, open the ./.vscode/launch.json file and add the
following excerpt to your list of configurations. Save your changes.

JSON

{
"name": "Direct Debugging",
"type": "node",
"request": "attach",
"port": 9223,
"timeout": 600000,
"trace": true
}

Other
1. Create a new folder called Debugging (perhaps in your Desktop folder).

2. Open Visual Studio Code.

3. Go to File > Open Folder, navigate to the folder you just created, then choose
Select Folder.

4. On the Activity Bar, select Run and Debug (Ctrl+Shift+D).


5. Select the create a launch.json file link.

6. In the Select Environment dropdown, select Edge: Launch to create a


launch.json file.

7. Add the following excerpt to your list of configurations. Save your changes.

JSON

{
"name": "Direct Debugging",
"type": "node",
"request": "attach",
"port": 9223,
"timeout": 600000,
"trace": true
}

Attach the debugger


The bundle.js file of an add-in contains the JavaScript code of your add-in. It's
created when Outlook on Windows is opened. When Outlook starts, the bundle.js
file of each installed add-in is cached in the Wef folder of your machine.
1. To find the add-in's bundle.js file, navigate to the following folder in File
Explorer. Replace text enclosed in [] with your applicable Outlook and add-in
information.

text

%LOCALAPPDATA%\Microsoft\Office\16.0\Wef\{[Outlook profile GUID]}\


[Outlook mail account encoding]\Javascript\[Add-in ID]_[Add-in
Version]_[locale]

 Tip

If the bundle.js file doesn't appear in the Wef folder, try the following:

If your add-in is installed or sideloaded, restart Outlook.


Remove your add-in from Outlook, then sideload it again.

2. Open bundle.js in Visual Studio Code.

3. Place breakpoints in bundle.js where you want the debugger to stop.

4. In the DEBUG dropdown, select Direct Debugging, then select the Start
Debugging icon.

Run the debugger


1. After confirming that the debugger is attached, return to Outlook, and in the
Debug Event-based handler dialog, choose OK .

2. You can now hit your breakpoints in Visual Studio Code, enabling you to
debug your event-based activation code.
Stop the debugger
To stop debugging the rest of the current Outlook on Windows session, in the
Debug Event-based handler dialog, choose Cancel. To re-enable debugging, restart
Outlook.

To prevent the Debug Event-based handler dialog from popping up and stop
debugging for subsequent Outlook sessions, delete the associated registry key,
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Wef\Developer\[Add-in

ID]\UseDirectDebugger , or set its value to 0 .

See also
Configure your Outlook add-in for event-based activation
Event-based activation troubleshooting guide
Debug your add-in with runtime logging
AppSource listing options for your
event-based Outlook add-in
Article • 02/14/2023

Add-ins must be deployed by an organization's admins for end-users to access the


event-based activation feature. Event-based activation is restricted if the end-user
acquired the add-in directly from AppSource . For example, if the Contoso add-in
includes an event-based function, the automatic invocation of the add-in only happens
if the add-in was installed for the end-user by their organization's admin. Otherwise, the
automatic invocation of the add-in is blocked.

An end-user or admin can acquire add-ins through AppSource or the in-app Office
Store. If your add-in's primary scenario or workflow requires event-based activation, you
may want to restrict your add-in to admin deployment only. To enable that restriction,
we can provide flight code URLs for add-ins in AppSource. Thanks to the flight codes,
only end-users with these special URLs can access the listing. The following is an
example URL.

https://appsource.microsoft.com/product/office/WA200002862?

flightCodes=EventBasedTest1

Users and admins can't explicitly search for an add-in by its name in AppSource or the
in-app Office Store when a flight code is enabled for it. As the add-in creator, you can
privately share these flight codes with organization admins for add-in deployment.

7 Note

While end-users can install the add-in using a flight code, the add-in won't include
event-based activation.

) Important

Add-ins that use the Smart Alerts feature can only be published to AppSource if
the manifest's SendMode property is set to the SoftBlock or PromptUser option. If
an add-in's SendMode property is set to Block , it can only be deployed by an
organization's admin as it will fail AppSource validation.

Specify a flight code


To specify the flight code for your add-in, share the code in the Notes for certification
when you publish your add-in. Important: Flight codes are case-sensitive.

Deploy add-in with flight code


After the flight codes are set, you'll receive the URL from the app certification team. You
can then share the URL with admins privately.

To deploy the add-in, the admin can use the following steps.

Sign in to admin.microsoft.com or AppSource.com with your Microsoft 365 admin


account. If the add-in has single sign-on (SSO) enabled, global admin credentials
are needed.
Open the flight code URL into a web browser.
On the add-in listing page, select Get it now. You should be redirected to the
integrated app portal.

Unrestricted AppSource listing


If your add-in doesn't use event-based activation for critical scenarios (that is, your add-
in works well without automatic invocation), consider listing your add-in in AppSource
without any special flight codes. If an end-user gets your add-in from AppSource,
automatic activation won't happen for the user. However, they can use other
components of your add-in such as a task pane or function command.

) Important

This is a temporary restriction. In future, we plan to enable event-based add-in


activation for end-users who directly acquire your add-in.
Update existing add-ins to include event-based
activation
You can update your existing add-in to include event-based activation then resubmit it
for validation and decide if you want a restricted or unrestricted AppSource listing.

After the updated add-in is approved, organization admins who have previously
deployed the add-in will receive an update message in the Integrated apps section of
the admin center. The message advises the admin about the event-based activation
changes. After the admin accepts the changes, the update will be deployed to end-
users. To learn more about the admin consent process, see Admin consent for installing
event-based add-ins.

For end-users who installed the add-in on their own, the event-based activation feature
won't work even after the add-in has been updated.

Admin consent for installing event-based add-


ins
Whenever an event-based add-in is deployed from the Integrated Apps screen, the
admin gets details about the add-in's event-based activation capabilities in the
deployment wizard. The details appear in the App Permissions and Capabilities section.
The admin should see all the events where the add-in can automatically activate.
Similarly, when an existing add-in is updated to event-based functionality, the admin
sees an "Update Pending" status on the add-in. The updated add-in is deployed only if
the admin consents to the changes noted in the App Permissions and Capabilities
section, including the set of events where the add-in can automatically activate.

Each time you add any new event-based activation function to your add-in, admins will
see the update flow in the admin portal and need to provide consent for additional
events. To learn more about the update and consent process, see Manage apps in the
Integrated apps portal.
See also
Configure your Outlook add-in for event-based activation
Enable shared folders and shared
mailbox scenarios in an Outlook add-in
Article • 05/30/2023

This article describes how to enable shared folders (also known as delegate access) and
shared mailbox scenarios in your Outlook add-in, including which permissions the Office
JavaScript API supports.

7 Note

Shared folder support was introduced in requirement set 1.8, while shared mailbox
support was introduced in requirement set 1.13. For information about client
support for these features, see Supported clients and platforms.

Supported clients and platforms


The following table shows supported client-server combinations for this feature,
including the minimum required Cumulative Update where applicable. Excluded
combinations aren't supported.

Client Exchange Exchange Exchange Exchange


Online 2019 on- 2016 on- 2013 on-
premises premises premises
(Cumulative (Cumulative
Update 1 or Update 6 or
later) later)

Windows Supported Supported* Supported* Supported*


Shared folders: Version 1910
(Build 12130.20272) or later

Shared mailboxes: Version


2304 (Build 16327.20248) or
later

Mac Supported Supported Supported Supported


Build 16.47 or later

Web browser (modern Supported Not applicable Not applicable Not


Outlook UI) applicable
Client Exchange Exchange Exchange Exchange
Online 2019 on- 2016 on- 2013 on-
premises premises premises
(Cumulative (Cumulative
Update 1 or Update 6 or
later) later)

Web browser (classic Outlook Not Not applicable Not applicable Not
UI) applicable applicable

7 Note

* Support for this feature in an on-premises Exchange environment is available


starting in Outlook on Windows Version 2206 (Build 15330.20000) for the Current
Channel and Version 2207 (Build 15427.20000) for the Monthly Enterprise Channel.

Supported setups
The following sections describe supported configurations for shared mailboxes and
shared folders. The feature APIs may not work as expected in other configurations.
Select the platform you'd like to learn how to configure.

Windows

Shared folders

The mailbox owner must first provide access to a delegate using one of the
following options.

Set up delegate access from the mailbox in Outlook on Windows. To learn


more, see Allow someone else to manage your mail and calendar .

Set up delegate access from the Microsoft 365 admin center. This option can
only be completed by administrators. To learn more, see Give mailbox
permissions to another Microsoft 365 user.

Set up delegate access from the Exchange admin center. This option can only
be completed by administrators. To learn more, see Manage permissions for
recipients.
Once access is provided, the delegate must then follow the instructions outlined in
the "Add another person's mailbox to your profile" section of the article Manage
another person's mail and calendar items .

Shared mailboxes
Exchange server admins can create and manage shared mailboxes for sets of users
to access. Exchange Online and on-premises Exchange environments are supported.

An Exchange Server feature known as "automapping" is on by default which means


that subsequently the shared mailbox should automatically appear in a user's
Outlook app after Outlook has been closed and reopened. However, if an admin
turned off automapping, the user must follow the manual steps outlined in the
"Add a shared mailbox to Outlook" section of the article Open and use a shared
mailbox in Outlook .

2 Warning

Do NOT sign into the shared mailbox with a password. The feature APIs won't
work in that case.

To learn more about where add-ins do and don't activate in general, refer to the
Mailbox items available to add-ins section of the Outlook add-ins overview page.

Supported permissions
The following table describes the permissions that the Office JavaScript API supports for
delegates and shared mailbox users.

Permission Value Description

Read 1 (000001) Can read items.

Write 2 (000010) Can create items.

DeleteOwn 4 (000100) Can delete only the items they created.

DeleteAll 8 (001000) Can delete any items.

EditOwn 16 (010000) Can edit only the items they created.

EditAll 32 (100000) Can edit any items.


7 Note

Currently the API supports getting existing permissions, but not setting
permissions.

The DelegatePermissions object is implemented using a bitmask to indicate the


permissions. Each position in the bitmask represents a particular permission and if it's
set to 1 then the user has the respective permission. For example, if the second bit from
the right is 1 , then the user has Write permission. You can see an example of how to
check for a specific permission in the Perform an operation as delegate or shared
mailbox user section later in this article.

Sync across shared folder clients


A delegate's updates to the owner's mailbox are usually synced across mailboxes
immediately.

However, if REST or Exchange Web Services (EWS) operations were used to set an
extended property on an item, such changes could take a few hours to sync. We
recommend you instead use the CustomProperties object and related APIs to avoid such
a delay. To learn more, see the custom properties section of the "Get and set metadata
in an Outlook add-in" article.

) Important

In a delegate scenario, you can't use EWS with the tokens currently provided by
office.js API.

Configure the manifest


To enable shared folders and shared mailbox scenarios in your add-in, you must enable
the required permissions in the manifest.

First, to support REST calls from a delegate, the add-in must request the read/write
mailbox permission. The markup varies depending on the type of manifest.

XML manifest: Set the <Permissions> element to ReadWriteMailbox.


Unified manifest for Microsoft 365 (preview): Set the "name" property of an
object in the "authorization.permissions.resourceSpecific" array to
"Mailbox.ReadWrite.User".
Second, enable support for shared folders. The markup varies depending on the type of
manifest.

XML Manifest

Set the SupportsSharedFolders element to true in the manifest under the parent
element DesktopFormFactor . At present, other form factors aren't supported.

XML

...
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides"
xsi:type="VersionOverridesV1_0">
<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
...
<Hosts>
<Host xsi:type="MailHost">
<DesktopFormFactor>
<SupportsSharedFolders>true</SupportsSharedFolders>
<FunctionFile resid="residDesktopFuncUrl" />
<ExtensionPoint xsi:type="MessageReadCommandSurface">
<!-- configure selected extension point -->
</ExtensionPoint>

<!-- You can define more than one ExtensionPoint element as


needed -->

</DesktopFormFactor>
</Host>
</Hosts>
...
</VersionOverrides>
</VersionOverrides>
...

Perform an operation as delegate or shared


mailbox user
You can get an item's shared properties in Compose or Read mode by calling the
item.getSharedPropertiesAsync method. This returns a SharedProperties object that
currently provides the user's permissions, the owner's email address, the REST API's base
URL, and the target mailbox.
The following example shows how to get the shared properties of a message or
appointment, check if the delegate or shared mailbox user has Write permission, and
make a REST call.

JavaScript

function performOperation() {
Office.context.mailbox.getCallbackTokenAsync({
isRest: true
},
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded &&
asyncResult.value !== "") {
Office.context.mailbox.item.getSharedPropertiesAsync({
// Pass auth token along.
asyncContext: asyncResult.value
},
function (asyncResult1) {
let sharedProperties = asyncResult1.value;
let delegatePermissions = sharedProperties.delegatePermissions;

// Determine if user can do the expected operation.


// E.g., do they have Write permission?
if ((delegatePermissions &
Office.MailboxEnums.DelegatePermissions.Write) != 0) {
// Construct REST URL for your operation.
// Update <version> placeholder with actual Outlook REST API
version e.g. "v2.0".
// Update <operation> placeholder with actual operation.
let rest_url = sharedProperties.targetRestUrl +
"/<version>/users/" + sharedProperties.targetMailbox + "/<operation>";

$.ajax({
url: rest_url,
dataType: 'json',
headers:
{
"Authorization": "Bearer " + asyncResult1.asyncContext
}
}
).done(
function (response) {
console.log("success");
}
).fail(
function (error) {
console.log("error message");
}
);
}
}
);
}
}
);
}

 Tip

As a delegate, you can use REST to get the content of an Outlook message
attached to an Outlook item or group post.

Handle calling REST on shared and non-shared


items
If you want to call a REST operation on an item, whether or not the item is shared, you
can use the getSharedPropertiesAsync API to determine if the item is shared. After that,
you can construct the REST URL for the operation using the appropriate object.

JavaScript

if (item.getSharedPropertiesAsync) {
// In Windows, Mac, and the web client, this indicates a shared item so
use SharedProperties properties to construct the REST URL.
// Add-ins don't activate on shared items in mobile so no need to handle.

// Perform operation for shared item.


} else {
// In general, this isn't a shared item, so construct the REST URL using
info from the Call REST APIs article:
// https://learn.microsoft.com/office/dev/add-ins/outlook/use-rest-api

// Perform operation for non-shared item.


}

Limitations
Depending on your add-in's scenarios, there are a few limitations for you to consider
when handling shared folder or shared mailbox situations.

Message Compose mode


In Message Compose mode, getSharedPropertiesAsync isn't supported in Outlook on
the web or on Windows unless the following conditions are met.
a. Delegate access/Shared folders

1. The mailbox owner starts a message. This can be a new message, a reply, or a
forward.
2. They save the message then move it from their own Drafts folder to a folder
shared with the delegate.
3. The delegate opens the draft from the shared folder then continues composing.

b. Shared mailbox (applies to Outlook on Windows only)

1. A shared mailbox user starts a message. This can be a new message, a reply, or a
forward.
2. They save the message then move it from their own Drafts folder to a folder in the
shared mailbox.
3. Another shared mailbox user opens the draft from the shared mailbox then
continues composing.

The message is now in a shared context and add-ins that support these shared scenarios
can get the item's shared properties. After the message has been sent, it's usually found
in the sender's Sent Items folder.

REST and EWS


Your add-in can use REST. To enable REST access to the owner's mailbox or to the
shared mailbox as applicable, the add-in must request the read/write mailbox
permission in the manifest. The markup varies depending on the type of manifest.

XML manifest: Set the <Permissions> element to ReadWriteMailbox.


Unified manifest for Microsoft 365 (preview): Set the "name" property of an
object in the "authorization.permissions.resourceSpecific" array to
"Mailbox.ReadWrite.User".

EWS isn't supported.

User or shared mailbox hidden from an address list


If an admin hid a user or shared mailbox address from an address list like the global
address list (GAL), affected mail items opened in the mailbox report
Office.context.mailbox.item as null. For example, if the user opens a mail item in a

shared mailbox that's hidden from the GAL, Office.context.mailbox.item representing


that mail item is null.
See also
Allow someone else to manage your mail and calendar
Calendar sharing in Microsoft 365
Add a shared mailbox to Outlook
How to order manifest elements
Mask (computing)
JavaScript bitwise operators
Manifest configuration for Outlook add-
ins
Article • 05/25/2023

An Outlook add-in consists of two components: the add-in manifest and a web app
supported by the JavaScript library for Office Add-ins (office.js). The manifest describes
how the add-in integrates across Outlook clients.

You can learn about manifests at Office Add-in manifests. This article focuses on aspects
of the manifest that are primarily relevant to Outlook.

Permissions
Outlook add-ins have a special set of permissions that don't apply to add-ins for other
Office host applications. These permissions must be configured in the manifest. For
details about these permissions and their effects, see Understanding Outlook add-in
permissions.

XML Manifest

The <Permissions> element contains the required permissions for the add-in. In
general, you should specify the minimum necessary permission that your add-in
needs, depending on the exact methods that you plan to use. For example, a mail
add-in that activates in compose forms and only reads but doesn't write to item
properties like item.requiredAttendees, and doesn't call
mailbox.makeEwsRequestAsync to access any Exchange Web Services operations
should specify ReadItem permission. The following is an example of setting an
Outlook permission.

XML

<OfficeApp>
...
<Permissions>ReadWriteItem</Permissions>
...
</OfficeApp>

Activation rules
7 Note

Activation rules aren't supported in Outlook add-ins that use the unified manifest
for Microsoft 365.

Activation rules are specified in the <Rule> element. The <Rule> element can appear as
a child of the <OfficeApp> element in 1.1 manifests.

Activation rules can be used to activate an add-in based on one or more of the
following conditions on the currently selected item.

7 Note

Activation rules only apply to clients that don't support the <VersionOverrides>
element.

The item type and/or message class

The presence of a specific type of known entity, such as an address or phone


number

A regular expression match in the body, subject, or sender email address

The presence of an attachment

For details and samples of activation rules, see Activation rules for Outlook add-ins.

Form settings

7 Note

Form settings aren't supported in Outlook add-ins that use the unified manifest for
Microsoft 365.

The <FormSettings> element is used by older Outlook clients, which only support
schema 1.1 and not <VersionOverrides>. Using this element, developers define how the
add-in will appear in such clients. There are two parts—ItemRead and ItemEdit.
ItemRead is used to specify how the add-in appears when the user reads messages and
appointments. ItemEdit describes how the add-in appears while the user is composing a
reply, new message, new appointment or editing an appointment as an organizer.
These settings are directly related to the activation rules in the <Rule> element. For
example, if an add-in specifies that it should appear on a message in compose mode, an
ItemEdit form must be specified.

Next steps: add-in commands


After defining a basic manifest, define add-in commands for your add-in. Add-in
commands present a button on the ribbon so users can activate your add-in in a simple,
intuitive way. For more information, see Add-in commands.

For an example add-in that defines add-in commands, see command-demo .

Next steps: Add mobile support


Add-ins can optionally add support for Outlook mobile. Outlook mobile supports add-in
commands in a similar fashion to Outlook on Windows and on Mac. For more
information, see Add support for add-in commands in Outlook on mobile devices.

See also
Localization for Office Add-ins
Privacy, permissions, and security for Outlook add-ins
Outlook add-in APIs
Office Add-ins manifest
Understanding Outlook add-in permissions
Use regular expression activation rules to show an Outlook add-in
Match strings in an Outlook item as well-known entities
Activation rules for contextual Outlook
add-ins
Article • 03/21/2023

Outlook activates some types of add-ins if the message or appointment that the user is
reading or composing satisfies the activation rules of the add-in. This is true for all add-
ins that use the 1.1 manifest schema. The user can then choose the add-in from the
Outlook UI to start it for the current item.

7 Note

Outlook Add-in features that depend on activation rules aren't supported when the
add-in uses a Unified manifest for Microsoft 365 (preview).

The following figure shows Outlook add-ins activated in the add-in bar for the message
in the Reading Pane.

Specify activation rules in a manifest


To have Outlook activate an add-in for specific conditions, specify activation rules in the
add-in manifest by using one of the following Rule elements.

Rule element (MailApp complexType) - Specifies an individual rule.


Rule element (RuleCollection complexType) - Combines multiple rules using logical
operations.
7 Note

The Rule element that you use to specify an individual rule is of the abstract Rule
complex type. Each of the following types of rules extends this abstract Rule
complex type. So when you specify an individual rule in a manifest, you must use
the xsi:type attribute to further define one of the following types of rules.

For example, the following rule defines an ItemIs rule. <Rule xsi:type="ItemIs"
ItemType="Message" />

The FormType attribute applies to activation rules in the manifest v1.1 but is not
defined in VersionOverrides v1.0. So it can't be used when ItemIs is used in the
VersionOverrides node.

The following table lists the types of rules that are available. You can find more
information following the table and in the specified articles under Create Outlook add-
ins for read forms.

Rule name Applicable Description


forms

ItemIs Read, Checks to see whether the current item is of the


Compose specified type (message or appointment). Can
also check the item class and form type.and
optionally, item message class.

ItemHasAttachment Read Checks to see whether the selected item contains


an attachment.

ItemHasKnownEntity Read Checks to see whether the selected item contains


one or more well-known entities. More
information: Match strings in an Outlook item as
well-known entities.

ItemHasRegularExpressionMatch Read Checks to see whether the sender's email


address, the subject, and/or the body of the
selected item contains a match to a regular
expression.More information: Use regular
expression activation rules to show an Outlook
add-in.

RuleCollection Read, Combines a set of rules so that you can form


Compose more complex rules.
ItemIs rule
The ItemIs complex type defines a rule that evaluates to true if the current item
matches the item type, and optionally the item message class if it's stated in the rule.

Specify one of the following item types in the ItemType attribute of an ItemIs rule. You
can specify more than one ItemIs rule in a manifest. The ItemType simpleType defines
the types of Outlook items that support Outlook add-ins.

Value Description

Appointment Specifies an item in an Outlook calendar. This includes a meeting item that has
been responded to and has an organizer and attendees, or an appointment that
does not have an organizer or attendee and is simply an item on the calendar.
This corresponds to the IPM.Appointment message class in Outlook.

Message Specifies one of the following items received in typically the Inbox.
An email message. This corresponds to the IPM.Note message class in
Outlook.
A meeting request, response, or cancellation. This corresponds to the
following message classes in Outlook.

IPM.Schedule.Meeting.Request

IPM.Schedule.Meeting.Neg

IPM.Schedule.Meeting.Pos

IPM.Schedule.Meeting.Tent

IPM.Schedule.Meeting.Canceled

The FormType attribute is used to specify the mode (read or compose) in which the add-
in should activate.

7 Note

The ItemIs FormType attribute is defined in schema v1.1 and later but not in
VersionOverrides v1.0. Do not include the FormType attribute when defining add-in
commands.

After an add-in is activated, you can use the mailbox.item property to obtain the
currently selected item in Outlook, and the item.itemType property to obtain the type of
the current item.
You can optionally use the ItemClass attribute to specify the message class of the item,
and the IncludeSubClasses attribute to specify whether the rule should be true when
the item is a subclass of the specified class.

For more information about message classes, see Item Types and Message Classes.

The following example is an ItemIs rule that lets users see the add-in in the Outlook
add-in bar when the user is reading a message.

XML

<Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" />

The following example is an ItemIs rule that lets users see the add-in in the Outlook
add-in bar when the user is reading a message or appointment.

XML

<Rule xsi:type="RuleCollection" Mode="Or">


<Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" />
<Rule xsi:type="ItemIs" ItemType="Appointment" FormType="Read" />
</Rule>

ItemHasAttachment rule
The ItemHasAttachment complex type defines a rule that checks if the selected item
contains an attachment.

XML

<Rule xsi:type="ItemHasAttachment" />

ItemHasKnownEntity rule
Before an item is made available to an add-in, the server examines it to determine
whether the subject and body contain any text that is likely to be one of the known
entities. If any of these entities are found, it is placed in a collection of known entities
that you access by using the getEntities or getEntitiesByType method of that item.

You can specify a rule by using ItemHasKnownEntity that shows your add-in when an
entity of the specified type is present in the item. You can specify the following known
entities in the EntityType attribute of an ItemHasKnownEntity rule.
Address
Contact
EmailAddress
MeetingSuggestion
PhoneNumber
TaskSuggestion
URL

You can optionally include a regular expression in the RegularExpression attribute so


that your add-in is only shown when an entity that matches the regular expression in
present. To obtain matches to regular expressions specified in ItemHasKnownEntity rules,
you can use the getRegExMatches or getFilteredEntitiesByName method for the
currently selected Outlook item.

The following example shows a collection of Rule elements that show the add-in when
one of the specified well-known entities is present in the message.

XML

<Rule xsi:type="RuleCollection" Mode="Or">


<Rule xsi:type="ItemHasKnownEntity" EntityType="Address" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="MeetingSuggestion" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="TaskSuggestion" />
</Rule>

The following example shows an ItemHasKnownEntity rule with a RegularExpression


attribute that activates the add-in when a URL that contains the word "contoso" is
present in a message.

XML

<Rule xsi:type="ItemHasKnownEntity" EntityType="Url"


RegularExpression="contoso" />

For more information about entities in activation rules, see Match strings in an Outlook
item as well-known entities.

ItemHasRegularExpressionMatch rule
The ItemHasRegularExpressionMatch complex type defines a rule that uses a regular
expression to match the contents of the specified property of an item. If text that
matches the regular expression is found in the specified property of the item, Outlook
activates the add-in bar and displays the add-in. You can use the getRegExMatches or
getRegExMatchesByName method of the object that represents the currently selected item
to obtain matches for the specified regular expression.

The following example shows an ItemHasRegularExpressionMatch that activates the add-


in when the body of the selected item contains "apple", "banana", or "coconut", ignoring
case.

XML

<Rule xsi:type="ItemHasRegularExpressionMatch" RegExName="fruits"


RegExValue="apple|banana|coconut" PropertyName="BodyAsPlaintext"
IgnoreCase="true" />

For more information about using the ItemHasRegularExpressionMatch rule, see Use
regular expression activation rules to show an Outlook add-in.

RuleCollection rule
The RuleCollection complex type combines multiple rules into a single rule. You can
specify whether the rules in the collection should be combined with a logical OR or a
logical AND by using the Mode attribute.

When a logical AND is specified, an item must match all the specified rules in the
collection to show the add-in. When a logical OR is specified, an item that matches any
of the specified rules in the collection will show the add-in.

You can combine RuleCollection rules to form complex rules. The following example
activates the add-in when the user is viewing an appointment or message item and the
subject or body of the item contains an address.

XML

<Rule xsi:type="RuleCollection" Mode="And">


<Rule xsi:type="RuleCollection" Mode="Or">
<Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" />
<Rule xsi:type="ItemIs" ItemType="Appointment" FormType="Read"/>
</Rule>
<Rule xsi:type="ItemHasKnownEntity" EntityType="Address" />
</Rule>

The following example activates the add-in when the user is composing a message, or
when the user is viewing an appointment and the subject or body of the appointment
contains an address.
XML

<Rule xsi:type="RuleCollection" Mode="Or">


<Rule xsi:type="ItemIs" ItemType="Message" FormType="Edit" />
<Rule xsi:type="RuleCollection" Mode="And">
<Rule xsi:type="ItemIs" ItemType="Appointment" FormType="Read" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="Address" />
</Rule>
</Rule>

Limits for rules and regular expressions


To provide a satisfactory experience with Outlook add-ins, you should adhere to the
activation and API usage guidelines. The following table shows general limits for regular
expressions and rules but there are specific rules for different applications. For more
information, see Limits for activation and JavaScript API for Outlook add-ins and
Troubleshoot Outlook add-in activation.

Add-in element Guidelines

Manifest Size No larger than 256 KB.

Rules No more than 15 rules.

ItemHasKnownEntity An Outlook rich client will apply the rule against the first 1 MB of the body,
and not to the rest of the body.
Add-in element Guidelines

Regular Expressions For ItemHasKnownEntity or ItemHasRegularExpressionMatch rules for all


Outlook applications:

Specify no more than 5 regular expressions in activation rules for an


Outlook add-in. You cannot install an add-in if you exceed that limit.
Specify regular expressions whose anticipated results are returned
by the getRegExMatches method call within the first 50 matches.
Important: Text is highlighted based on strings that result from
matching the regular expression. However, the highlighted
occurrences may not exactly match what should result from actual
regular expression assertions like negative look-ahead (?!text) ,
look-behind (?<=text) , and negative look-behind (?<!text) . For
example, if you use the regular expression under(?!score) on "Like
under, under score, and underscore", the string "under" is
highlighted for all occurrences instead of just the first two.
Specify regular expressions whose match does not exceed the limits
in the following table.

Limit on length of a Outlook rich Outlook on iOS and


regex match clients Android

Item body is plain text 1.5 KB 3 KB

Item body it HTML 3 KB 3 KB

See also
Create Outlook add-ins for compose forms
Limits for activation and JavaScript API for Outlook add-ins
Use regular expression activation rules to show an Outlook add-in
Match strings in an Outlook item as well-known entities
Add support for add-in commands in
Outlook on mobile devices
Article • 07/13/2023

Using add-in commands in Outlook on mobile devices allows your users to access the
same functionality (with some limitations) that they already have in Outlook on the web,
on Windows, and on Mac. Adding support for Outlook mobile requires updating the
add-in manifest and possibly changing your code for mobile scenarios.

Update the manifest

7 Note

Add-ins using the Unified manifest for Microsoft 365 (preview) aren't currently
supported on mobile devices.

The first step to enabling add-in commands in Outlook mobile is to define them in the
add-in manifest. The VersionOverrides v1.1 schema defines a new form factor for
mobile, MobileFormFactor.

This element contains all of the information for loading the add-in in mobile clients. This
enables you to define completely different UI elements and JavaScript files for the
mobile experience.

The following example shows a single task pane button in a MobileFormFactor element.

XML

<VersionOverrides
xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1"
xsi:type="VersionOverridesV1_1">
...
<MobileFormFactor>
<FunctionFile resid="residUILessFunctionFileUrl" />
<ExtensionPoint xsi:type="MobileMessageReadCommandSurface">
<Group id="mobileMsgRead">
<Label resid="groupLabel" />
<Control xsi:type="MobileButton" id="TaskPaneBtn">
<Label resid="residTaskPaneButtonName" />
<Icon xsi:type="bt:MobileIconList">
<bt:Image size="25" scale="1" resid="tp0icon" />
<bt:Image size="25" scale="2" resid="tp0icon" />
<bt:Image size="25" scale="3" resid="tp0icon" />
<bt:Image size="32" scale="1" resid="tp0icon" />
<bt:Image size="32" scale="2" resid="tp0icon" />
<bt:Image size="32" scale="3" resid="tp0icon" />

<bt:Image size="48" scale="1" resid="tp0icon" />


<bt:Image size="48" scale="2" resid="tp0icon" />
<bt:Image size="48" scale="3" resid="tp0icon" />
</Icon>
<Action xsi:type="ShowTaskpane">
<SourceLocation resid="residTaskpaneUrl" />
</Action>
</Control>
</Group>
</ExtensionPoint>
</MobileFormFactor>
...
</VersionOverrides>

This is very similar to the elements that appear in a DesktopFormFactor element, with
some notable differences.

The OfficeTab element isn't used.


The ExtensionPoint element must have only one child element. If the add-in only
adds one button, the child element should be a Control element. If the add-in adds
more than one button, the child element should be a Group element that contains
multiple Control elements.
There is no Menu type equivalent for the Control element.
The Supertip element isn't used.
The required icon sizes are different. Mobile add-ins minimally must support
25x25, 32x32 and 48x48 pixel icons.

Code considerations
Designing an add-in for mobile introduces some additional considerations.

Use REST instead of Exchange Web Services


The Office.context.mailbox.makeEwsRequestAsync method isn't supported in Outlook
mobile. Add-ins should prefer to get information from the Office.js API when possible. If
add-ins require information not exposed by the Office.js API, then they should use the
Outlook REST APIs to access the user's mailbox.

Mailbox requirement set 1.5 introduced a new version of


Office.context.mailbox.getCallbackTokenAsync that can request an access token
compatible with the REST APIs, and a new Office.context.mailbox.restUrl property that
can be used to find the REST API endpoint for the user.

Pinch zoom
By default users can use the "pinch zoom" gesture to zoom in on task panes. If this
doesn't make sense for your scenario, be sure to disable pinch zoom in your HTML.

Close task panes


In Outlook mobile, task panes take up the entire screen and by default require the user
to close them to return to the message. Consider using the
Office.context.ui.closeContainer method to close the task pane when your scenario is
complete.

Compose mode and appointments


Currently, add-ins in Outlook mobile only support activation when reading messages.
Add-ins aren't activated when composing messages or when viewing or composing
appointments. However, there are some exceptions.

1. Online meeting provider integrated add-ins activate in Appointment Organizer


mode. For more information about this exception (including available APIs), see
Create an Outlook mobile add-in for an online-meeting provider.
2. Add-ins that log appointment notes and other details to customer relationship
management (CRM) or note-taking services activate in Appointment Attendee
mode. For more information about this exception (including available APIs), see
Log appointment notes to an external application in Outlook mobile add-ins.
3. Event-based add-ins activate when the OnNewMessageCompose event occurs. For
more information about this exception (including additional supported APIs), see
Implement event-based activation in Outlook mobile add-ins.

Supported APIs
Although Outlook mobile supports up to Mailbox requirement set 1.5, you can now
implement additional APIs from later requirement sets to further extend the capability
of your add-in on Outlook mobile. For guidance on which APIs you can implement in
your mobile add-in, see Outlook JavaScript APIs supported in Outlook on mobile
devices.
See also
Requirement sets supported by Exchange servers and Outlook clients
Outlook JavaScript APIs supported in Outlook on mobile devices
Outlook add-in requirements
Article • 03/28/2022

For Outlook add-ins to load and function properly, there are a number of requirements
for both the servers and the clients.

Client requirements
The client must be one of the supported applications for Outlook add-ins. The
following clients support add-ins.
Outlook 2013 or later on Windows
Outlook 2016 or later on Mac
Outlook on iOS
Outlook on Android
Outlook on the web for Exchange 2016 or later
Outlook on the web for Exchange 2013
Outlook.com

The client must be connected to an Exchange server or Microsoft 365 using a


direct connection. When configuring the client, the user must choose an Exchange,
Office, or Outlook.com account type. If the client is configured to connect with
POP3 or IMAP, add-ins will not load.

Mail server requirements


If the user is connected to Microsoft 365 or Outlook.com, mail server requirements are
all taken care of already. However, for users connected to on-premises installations of
Exchange Server, the following requirements apply.

The server must be Exchange 2013 or later.


Exchange Web Services (EWS) must be enabled and must be exposed to the
Internet. Many add-ins require EWS to function properly.
The server must have a valid authentication certificate in order for the server to
issue valid identity tokens. New installations of Exchange Server include a default
authentication certificate. For more information, see Digital certificates and
encryption in Exchange 2016 and Set-AuthConfig.
To access add-ins from AppSource , the client access servers must be able to
communicate with AppSource.
Add-in server requirements
Add-in files (HTML, JavaScript, etc.) can be hosted on any web server platform desired.
The only requirement is that the server must be configured to use HTTPS, and the SSL
certificate must be trusted by the client.

See also
Requirements for running Office Add-ins
Office client application and platform availability for Office Add-ins (Outlook
section)
Outlook JavaScript API requirement set support
Use the Outlook REST APIs from an
Outlook add-in
Article • 03/28/2023

The Office.context.mailbox.item namespace provides access to many of the common


fields of messages and appointments. However, in some scenarios an add-in may need
to access data that is not exposed by the namespace. For example, the add-in may rely
on custom properties set by an outside app, or it needs to search the user's mailbox for
messages from the same sender. In these scenarios, the Outlook REST APIs is the
recommended method to retrieve the information.

) Important

The Outlook REST APIs are deprecated

It was previously announced that the Outlook REST endpoints will be fully
decommissioned on November 30, 2022 (see the November 2020 announcement).
However, the decommission date has been postponed. The new date in 2023 will
be announced six months before the decommission is enforced through the
Microsoft 365 Developer Blog . For more information on this recent update, see
the November 2022 announcement . Although the decommission date has been
postponed, we highly encourage you to migrate your add-ins to use Microsoft
Graph. For guidance, see Compare Microsoft Graph and Outlook REST API
endpoints.

To assist you with the migration, active add-ins are able to keep using the REST
service until extended support ends for Outlook 2019 on October 14, 2025. Add-in
traffic that uses the service will be automatically identified for exemption based on
the add-in's manifest ID. This exemption applies to privately released and
AppSource-hosted add-ins. It also includes new add-ins developed after the
decommission date.

Get an access token


The Outlook REST APIs require a bearer token in the Authorization header. Typically
apps use OAuth2 flows to retrieve a token. However, add-ins can retrieve a token
without implementing OAuth2 by using the new
Office.context.mailbox.getCallbackTokenAsync method introduced in the Mailbox
requirement set 1.5.
By setting the isRest option to true , you can request a token compatible with the REST
APIs.

Add-in permissions and token scope


It is important to consider what level of access your add-in will need via the REST APIs.
In most cases, the token returned by getCallbackTokenAsync will provide read-only
access to the current item only. This is true even if your add-in specifies the read/write
item permission level in its manifest.

If your add-in will require write access to the current item or other items in the user's
mailbox, your add-in must specify the read/write mailbox permission. level in its
manifest. In this case, the token returned will contain read/write access to the user's
messages, events, and contacts.

Example
JavaScript

Office.context.mailbox.getCallbackTokenAsync({isRest: true},
function(result){
if (result.status === "succeeded") {
const accessToken = result.value;

// Use the access token.


getCurrentItem(accessToken);
} else {
// Handle the error.
}
});

Get the item ID


To retrieve the current item via REST, your add-in will need the item's ID, properly
formatted for REST. This is obtained from the Office.context.mailbox.item.itemId
property, but some checks should be made to ensure that it is a REST-formatted ID.

In Outlook on mobile devices, the value returned by


Office.context.mailbox.item.itemId is a REST-formatted ID and can be used as-is.

In other Outlook clients, the value returned by


Office.context.mailbox.item.itemId is an EWS-formatted ID, and must be
converted using the Office.context.mailbox.convertToRestId method.
Note you must also convert Attachment ID to a REST-formatted ID in order to use
it. The reason the IDs must be converted is that EWS IDs can contain non-URL safe
values which will cause problems for REST.

Your add-in can determine which Outlook client it is loaded in by checking the
Office.context.mailbox.diagnostics.hostName property.

Example
JavaScript

function getItemRestId() {
if (Office.context.mailbox.diagnostics.hostName === 'OutlookIOS') {
// itemId is already REST-formatted.
return Office.context.mailbox.item.itemId;
} else {
// Convert to an item ID for API v2.0.
return Office.context.mailbox.convertToRestId(
Office.context.mailbox.item.itemId,
Office.MailboxEnums.RestVersion.v2_0
);
}
}

Get the REST API URL


The final piece of information your add-in needs to call the REST API is the hostname it
should use to send API requests. This information is in the Office.context.mailbox.restUrl
property.

Example
JavaScript

// Example: https://outlook.office.com
const restHost = Office.context.mailbox.restUrl;

Call the API


After your add-in has the access token, item ID, and REST API URL, it can either pass that
information to a back-end service which calls the REST API, or it can call it directly using
AJAX. The following example calls the Outlook Mail REST API to get the current
message.

) Important

For on-premises Exchange deployments, client-side requests using AJAX or similar


libraries fail because CORS isn't supported in that server setup.

JavaScript

function getCurrentItem(accessToken) {
// Get the item's REST ID.
const itemId = getItemRestId();

// Construct the REST URL to the current item.


// Details for formatting the URL can be found at
// https://learn.microsoft.com/previous-versions/office/office-365-
api/api/version-2.0/mail-rest-operations#get-messages.
const getMessageUrl = Office.context.mailbox.restUrl +
'/v2.0/me/messages/' + itemId;

$.ajax({
url: getMessageUrl,
dataType: 'json',
headers: { 'Authorization': 'Bearer ' + accessToken }
}).done(function(item){
// Message is passed in `item`.
const subject = item.Subject;
...
}).fail(function(error){
// Handle error.
});
}

See also
For an example that calls the REST APIs from an Outlook add-in, see command-
demo on GitHub.
Outlook REST APIs are also available through the Microsoft Graph endpoint but
there are some key differences, including how your add-in gets an access token.
For more information, see Outlook REST API via Microsoft Graph.
Call web services from an Outlook add-
in
Article • 05/20/2023

Your add-in can use Exchange Web Services (EWS) from a computer that is running
Exchange Server, a web service that is available on the server that provides the source
location for the add-in's UI, or a web service that is available on the Internet. This article
provides an example that shows how an Outlook add-in can request information from
EWS.

) Important

EWS calls and operations aren't supported in add-ins running in Outlook on iOS
and Android.

The way you call a web service varies based on where the web service is located. The
following tables list the different ways you can call a web service based on location.

Web service Way to call the web service


location

The Exchange Use the mailbox.makeEwsRequestAsync method to call EWS operations that
server that add-ins support. The Exchange server that hosts the mailbox also exposes EWS.
hosts the client
mailbox

The web server Call the web service by using standard JavaScript techniques. The JavaScript
that provides code in the UI frame runs in the context of the web server that provides the UI.
the source Therefore, it can call web services on that server without causing a cross-site
location for the scripting error.
add-in UI

All other Create a proxy for the web service on the web server that provides the source
locations location for the UI. If you do not provide a proxy, cross-site scripting errors will
prevent your add-in from running. One way to provide a proxy is by using
JSON/P. For more information, see Privacy and security for Office Add-ins.

Using the makeEwsRequestAsync method to


access EWS operations
You can use the mailbox.makeEwsRequestAsync method to make an EWS request to the
Exchange server that hosts the user's mailbox.

EWS supports different operations on an Exchange server; for example, item-level


operations to copy, find, update, or send an item, and folder-level operations to create,
get, or update a folder. To perform an EWS operation, create an XML SOAP request for
that operation. When the operation finishes, you get an XML SOAP response that
contains data that is relevant to the operation. EWS SOAP requests and responses follow
the schema defined in the Messages.xsd file. Like other EWS schema files, the
Message.xsd file is located in the IIS virtual directory that hosts EWS.

To use the makeEwsRequestAsync method to initiate an EWS operation, provide the


following:

The XML for the SOAP request for that EWS operation, as an argument to the data
parameter

A callback function (as the callback argument)

Any optional input data for that callback function (as the userContext argument)

When the EWS SOAP request is complete, Outlook calls the callback function with one
argument, which is an AsyncResult object. The callback function can access two
properties of the AsyncResult object: the value property, which contains the XML SOAP
response of the EWS operation, and optionally, the asyncContext property, which
contains any data passed as the userContext parameter. Typically, the callback function
then parses the XML in the SOAP response to get any relevant information, and
processes that information accordingly.

Tips for parsing EWS responses


When parsing a SOAP response from an EWS operation, note the following browser-
dependent issues.

Specify the prefix for a tag name when using the DOM method
getElementsByTagName , to include support for Internet Explorer and the Trident

webview.

getElementsByTagName behaves differently depending on browser type. For


example, an EWS response can contain the following XML (formatted and
abbreviated for display purposes).

XML
<t:ExtendedProperty><t:ExtendedFieldURI PropertySetId="00000000-0000-
0000-0000-000000000000"
PropertyName="MyProperty"
PropertyType="String"/>
<t:Value>{
...
}</t:Value></t:ExtendedProperty>

Code, as in the following, would work on a browser like Chrome to get the XML
enclosed by the ExtendedProperty tags.

JavaScript

const mailbox = Office.context.mailbox;


mailbox.makeEwsRequestAsync(mailbox.item.itemId, function(result) {
const response = $.parseXML(result.value);
const extendedProps =
response.getElementsByTagName("ExtendedProperty")
});

For the Trident (Internet Explorer) webview, you must include the t: prefix of the
tag name, as follows.

JavaScript

const mailbox = Office.context.mailbox;


mailbox.makeEwsRequestAsync(mailbox.item.itemId, function(result) {
const response = $.parseXML(result.value);
const extendedProps =
response.getElementsByTagName("t:ExtendedProperty")
});

Use the DOM property textContent to get the contents of a tag in an EWS
response, as follows.

JavaScript

content = $.parseJSON(value.textContent);

Other properties such as innerHTML may not work on the Trident (Internet Explorer)
webview for some tags in an EWS response.

Example
The following example calls makeEwsRequestAsync to use the GetItem operation to get
the subject of an item. This example includes the following three functions.

getSubjectRequest – Takes an item ID as input, and returns the XML for the SOAP

request to call GetItem for the specified item.

sendRequest – Calls getSubjectRequest to get the SOAP request for the selected

item, then passes the SOAP request and the callback function, callback , to
makeEwsRequestAsync to get the subject of the specified item.

callback – Processes the SOAP response which includes any subject and other

information about the specified item.

JavaScript

function getSubjectRequest(id) {
// Return a GetItem operation request for the subject of the specified
item.
const result =
'<?xml version="1.0" encoding="utf-8"?>' +
'<soap:Envelope xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"'
+
' xmlns:xsd="https://www.w3.org/2001/XMLSchema"' +
' xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"'
+
'
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">' +
' <soap:Header>' +
' <RequestServerVersion Version="Exchange2013"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types"
soap:mustUnderstand="0" />' +
' </soap:Header>' +
' <soap:Body>' +
' <GetItem
xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">' +
' <ItemShape>' +
' <t:BaseShape>IdOnly</t:BaseShape>' +
' <t:AdditionalProperties>' +
' <t:FieldURI FieldURI="item:Subject"/>' +
' </t:AdditionalProperties>' +
' </ItemShape>' +
' <ItemIds><t:ItemId Id="' + id + '"/></ItemIds>' +
' </GetItem>' +
' </soap:Body>' +
'</soap:Envelope>';

return result;
}

function sendRequest() {
// Create a local variable that contains the mailbox.
const mailbox = Office.context.mailbox;

mailbox.makeEwsRequestAsync(getSubjectRequest(mailbox.item.itemId),
callback);
}

function callback(asyncResult) {
const result = asyncResult.value;
const context = asyncResult.context;

// Process the returned response here.


}

EWS operations that add-ins support


Outlook add-ins can access a subset of operations that are available in EWS via the
makeEwsRequestAsync method. If you are unfamiliar with EWS operations and how to use
the makeEwsRequestAsync method to access an operation, start with a SOAP request
example to customize your data argument.

The following describes how you can use the makeEwsRequestAsync method.

1. In the XML, substitute any item IDs and relevant EWS operation attributes with
appropriate values.

2. Include the SOAP request as an argument for the data parameter of


makeEwsRequestAsync .

3. Specify a callback function and call makeEwsRequestAsync .

4. In the callback function, verify the results of the operation in the SOAP response.

5. Use the results of the EWS operation according to your needs.

The following table lists the EWS operations that add-ins support. To see examples of
SOAP requests and responses, choose the link for each operation. For more information
about EWS operations, see EWS operations in Exchange.

EWS operation Description

CopyItem operation Copies the specified items and puts the new items in a designated folder
in the Exchange store.

CreateFolder Creates folders in the specified location in the Exchange store.


operation

CreateItem operation Creates the specified items in the Exchange store.


EWS operation Description

ExpandDL operation Displays the full membership of distribution lists.

FindConversation Enumerates a list of conversations in the specified folder in the Exchange


operation store.

FindFolder operation Finds subfolders of an identified folder and returns a set of properties
that describe the set of subfolders.

FindItem operation Identifies items that are located in a specified folder in the Exchange
store.

GetConversationItems Gets one or more sets of items that are organized in nodes in a
operation conversation.

GetFolder operation Gets the specified properties and contents of folders from the Exchange
store.

GetItem operation Gets the specified properties and contents of items from the Exchange
store.

GetUserAvailability Provides detailed information about the availability of a set of users,


operation rooms, and resources within a specified time period.

MarkAsJunk Moves email messages to the Junk Email folder, and adds or removes
operation senders of the messages from the blocked senders list accordingly.

MoveItem operation Moves items to a single destination folder in the Exchange store.

ResolveNames Resolves ambiguous email addresses and display names.


operation

SendItem operation Sends email messages that are located in the Exchange store.

UpdateFolder Modifies the properties of existing folders in the Exchange store.


operation

UpdateItem operation Modifies the properties of existing items in the Exchange store.

7 Note

FAI (Folder Associated Information) items cannot be updated (or created) from an
add-in. These hidden messages are stored in a folder and are used to store a
variety of settings and auxiliary data. Attempting to use the UpdateItem operation
will throw an ErrorAccessDenied error: "Office extension is not allowed to update
this type of item". As an alternative, you may use the EWS Managed API to update
these items from a Windows client or a server application. Caution is recommended
as internal, service-type data structures are subject to change and could break your
solution.

Authentication and permission considerations


for makeEwsRequestAsync
When you use the makeEwsRequestAsync method, the request is authenticated by using
the email account credentials of the current user. The makeEwsRequestAsync method
manages the credentials for you so that you do not have to provide authentication
credentials with your request.

7 Note

The server administrator must use the New-WebServicesVirtualDirectory or the


Set-WebServicesVirtualDirectory cmdlet to set the OAuthAuthentication parameter
to true on the Client Access server EWS directory in order to enable the
makeEwsRequestAsync method to make EWS requests.

To use the makeEwsRequestAsync method, your add-in must request the read/write
mailbox permission in the manifest. The markup varies depending on the type of
manifest.

XML manifest: Set the <Permissions> element to ReadWriteMailbox.


Unified manifest for Microsoft 365 (preview): Set the "name" property of an
object in the "authorization.permissions.resourceSpecific" array to
"Mailbox.ReadWrite.User".

For information about using the read/write mailbox permission, see read/write mailbox
permission.

See also
Privacy and security for Office Add-ins
Addressing same-origin policy limitations in Office Add-ins
EWS reference for Exchange
Mail apps for Outlook and EWS in Exchange

See the following for creating backend services for add-ins using ASP.NET Web API.

Create a web service for an Office Add-in using the ASP.NET Web API
The basics of building an HTTP service using ASP.NET Web API
Get and set Outlook item data in read or
compose forms
Article • 10/04/2022

Starting in version 1.1 of the Office Add-ins manifests schema, Outlook can activate
add-ins when the user is viewing or composing an item. Depending on whether an add-
in is activated in a read or compose form, the properties that are available to the add-in
on the item differ as well.

For example, the dateTimeCreated and dateTimeModified properties are defined only
for an item that has already been sent (item is subsequently viewed in a read form) but
not when the item is being created (in a compose form). Another example is the bcc
property, which is only meaningful when a message is being authored (in a compose
form), and is not accessible to the user in a read form.

Item properties available in compose and read


forms
Table 1 shows the item-level properties in the Office JavaScript API that are available in
each mode (read and compose) of mail add-ins. Typically, those properties available in
read forms are read-only, and those available in compose forms are read/write, with the
exception of the itemId, conversationId, and itemType properties, which are always read-
only regardless.

For the remaining item-level properties available in compose forms, because the add-in
and user can possibly be reading or writing the same property at the same time, the
methods to get or set them in compose mode are asynchronous, and hence the type of
the objects returned by these properties may also be different in compose forms than in
read forms. For more information about using asynchronous methods to get or set
item-level properties in compose mode, see Get and set item data in a compose form in
Outlook.

Table 1. Item properties available in compose and read forms

Item type Property Property type in Property type in compose


read forms forms

Appointments and dateTimeCreated JavaScript Date Property not available


messages object
Item type Property Property type in Property type in compose
read forms forms

Appointments and dateTimeModified JavaScript Date Property not available


messages object

Appointments and itemClass String Property not available


messages

Appointments and itemId String Property not available


messages

Appointments and itemType String in ItemType String in ItemType


messages enumeration enumeration (read-only)

Appointments and attachments AttachmentDetails Property not available


messages

Appointments and body Body Body


messages

Appointments and normalizedSubject String Property not available


messages

Appointments and subject String Subject


messages

Appointments end JavaScript Date Time


object

Appointments location String Location

Appointments optionalAttendees EmailAddressDetails Recipients

Appointments organizer EmailAddressDetails Organizer

Appointments requiredAttendees EmailAddressDetails Recipients

Appointments start JavaScript Date Time


object

Messages bcc Property not Recipients


available

Messages cc EmailAddressDetails Recipients

Messages conversationId String String (read-only)

Messages from EmailAddressDetails From

Messages internetMessageId Integer Property not available


Item type Property Property type in Property type in compose
read forms forms

Messages sender EmailAddressDetails Property not available

Messages to EmailAddressDetails Recipients

Use Exchange Server callback tokens from a


read add-in
If your Outlook add-in is activated in read forms, you can get an Exchange callback
token. This token can be used in server-side code to access the full item via Exchange
Web Services (EWS).

By specifying the read item permission in the add-in manifest, you can use the
mailbox.getCallbackTokenAsync method to get an Exchange callback token, the
mailbox.ewsUrl property to get the URL of the EWS endpoint for the user's mailbox, and
item.itemId to get the EWS ID for the selected item. You can then pass the callback
token, EWS endpoint URL, and the EWS item ID to server-side code to access the
GetItem operation, to get more properties of the item.

Access EWS from a read or compose add-in


You can also use the mailbox.makeEwsRequestAsync method to access the Exchange
Web Services (EWS) operations GetItem and UpdateItem directly from the add-in. You
can use these operations to get and set many properties of a specified item. This
method is available to Outlook add-ins regardless of whether the add-in has been
activated in a read or compose form, as long as you specify the read/write mailbox
permission in the add-in manifest. For more information about the read/write mailbox
permission, see Understanding Outlook add-in permissions

For more information about using makeEwsRequestAsync to access EWS operations,


see Call web services from an Outlook add-in.

See also
Get and set item data in a compose form in Outlook
Call web services from an Outlook add-in
Get and set categories
Article • 03/21/2023

In Outlook, a user can apply categories to messages and appointments as a means of


organizing their mailbox data. The user defines the master list of color-coded categories
for their mailbox, and can then apply one or more of those categories to any message
or appointment item. Each category in the master list is represented by the name and
color that the user specifies. You can use the Office JavaScript API to manage the
categories master list on the mailbox and the categories applied to an item.

7 Note

Support for this feature was introduced in requirement set 1.8. See clients and
platforms that support this requirement set.

Manage categories in the master list


Only categories in the master list on your mailbox are available for you to apply to a
message or appointment. You can use the API to add, get, and remove master
categories.

) Important

For the add-in to manage the categories master list, it must request the read/write
mailbox permission in the manifest. The markup varies depending on the type of
manifest.

XML manifest: Set the <Permissions> element to ReadWriteMailbox.


Unified manifest for Microsoft 365 (preview): Set the "name" property of an
object in the "authorization.permissions.resourceSpecific" array to
"Mailbox.ReadWrite.User".

Add master categories


The following example shows how to add a category named "Urgent!" to the master list
by calling addAsync on mailbox.masterCategories.

JavaScript
const masterCategoriesToAdd = [
{
"displayName": "Urgent!",
"color": Office.MailboxEnums.CategoryColor.Preset0
}
];

Office.context.mailbox.masterCategories.addAsync(masterCategoriesToAdd,
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Successfully added categories to master list");
} else {
console.log("masterCategories.addAsync call failed with error: " +
asyncResult.error.message);
}
});

Get master categories


The following example shows how to get the list of categories by calling getAsync on
mailbox.masterCategories.

JavaScript

Office.context.mailbox.masterCategories.getAsync(function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log("Action failed with error: " +
asyncResult.error.message);
} else {
const masterCategories = asyncResult.value;
console.log("Master categories:");
masterCategories.forEach(function (item) {
console.log("-- " + JSON.stringify(item));
});
}
});

Remove master categories


The following example shows how to remove the category named "Urgent!" from the
master list by calling removeAsync on mailbox.masterCategories.

JavaScript

const masterCategoriesToRemove = ["Urgent!"];

Office.context.mailbox.masterCategories.removeAsync(masterCategoriesToRemove
, function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Successfully removed categories from master list");
} else {
console.log("masterCategories.removeAsync call failed with error: "
+ asyncResult.error.message);
}
});

Manage categories on a message or


appointment
You can use the API to add, get, and remove categories for a message or appointment
item.

) Important

Only categories in the master list on your mailbox are available for you to apply to
a message or appointment. See the earlier section Manage categories in the
master list for more information.

In Outlook on the web, you can't use the API to manage categories on a message
in Compose mode.

Add categories to an item


The following example shows how to apply the category named "Urgent!" to the current
item by calling addAsync on item.categories .

JavaScript

const categoriesToAdd = ["Urgent!"];

Office.context.mailbox.item.categories.addAsync(categoriesToAdd, function
(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Successfully added categories");
} else {
console.log("categories.addAsync call failed with error: " +
asyncResult.error.message);
}
});

Get an item's categories


The following example shows how to get the categories applied to the current item by
calling getAsync on item.categories .

JavaScript

Office.context.mailbox.item.categories.getAsync(function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log("Action failed with error: " +
asyncResult.error.message);
} else {
const categories = asyncResult.value;
console.log("Categories:");
categories.forEach(function (item) {
console.log("-- " + JSON.stringify(item));
});
}
});

Remove categories from an item


The following example shows how to remove the category named "Urgent!" from the
current item by calling removeAsync on item.categories .

JavaScript

const categoriesToRemove = ["Urgent!"];

Office.context.mailbox.item.categories.removeAsync(categoriesToRemove,
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Successfully removed categories");
} else {
console.log("categories.removeAsync call failed with error: " +
asyncResult.error.message);
}
});

See also
Outlook permissions
Get and set internet headers on a
message in an Outlook add-in
Article • 03/28/2023

Background
A common requirement in Outlook add-ins development is to store custom properties
associated with an add-in at different levels. At present, custom properties are stored at
the item or mailbox level.

Item level - For properties that apply to a specific item, use the CustomProperties
object. For example, store a customer code associated with the person who sent
the email.
Mailbox level - For properties that apply to all the mail items in the user's mailbox,
use the RoamingSettings object. For example, store a user's preference to show the
temperature in a particular scale.

Both types of properties aren't preserved after the item leaves the Exchange server, so
the email recipients can't get any properties set on the item. Therefore, developers can't
access those settings or other Multipurpose Internet Mail Extensions (MIME) properties
to enable better read scenarios.

While there's a way for you to set the internet headers through Exchange Web Services
(EWS) requests, in some scenarios, making an EWS request won't work. For example, in
Compose mode on Outlook desktop, the item ID isn't synced on saveAsync in cached
mode.

 Tip

To learn more about using these options, see Get and set add-in metadata for an
Outlook add-in.

Purpose of the internet headers API


Introduced in requirement set 1.8, the internet headers APIs enable developers to:

Stamp information on an email that persists after it leaves Exchange across all
clients.
Read information on an email that persisted after the email left Exchange across all
clients in mail read scenarios.
Access the entire MIME header of the email.

Set internet headers while composing a


message
Use the item.internetHeaders property to manage the custom internet headers you
place on the current message in Compose mode.

Set, get, and remove custom internet headers example


The following example shows how to set, get, and remove custom internet headers.

JavaScript

// Set custom internet headers.


function setCustomHeaders() {
Office.context.mailbox.item.internetHeaders.setAsync(
{ "preferred-fruit": "orange", "preferred-vegetable": "broccoli", "best-
vegetable": "spinach" },
setCallback
);
}

function setCallback(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Successfully set headers");
} else {
console.log("Error setting headers: " +
JSON.stringify(asyncResult.error));
}
}

// Get custom internet headers.


function getSelectedCustomHeaders() {
Office.context.mailbox.item.internetHeaders.getAsync(
["preferred-fruit", "preferred-vegetable", "best-vegetable",
"nonexistent-header"],
getCallback
);
}

function getCallback(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Selected headers: " + JSON.stringify(asyncResult.value));
} else {
console.log("Error getting selected headers: " +
JSON.stringify(asyncResult.error));
}
}

// Remove custom internet headers.


function removeSelectedCustomHeaders() {
Office.context.mailbox.item.internetHeaders.removeAsync(
["best-vegetable", "nonexistent-header"],
removeCallback);
}

function removeCallback(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Successfully removed selected headers");
} else {
console.log("Error removing selected headers: " +
JSON.stringify(asyncResult.error));
}
}

setCustomHeaders();
getSelectedCustomHeaders();
removeSelectedCustomHeaders();
getSelectedCustomHeaders();

/* Sample output:
Successfully set headers
Selected headers: {"best-vegetable":"spinach","preferred-
fruit":"orange","preferred-vegetable":"broccoli"}
Successfully removed selected headers
Selected headers: {"preferred-fruit":"orange","preferred-
vegetable":"broccoli"}
*/

Get internet headers while reading a message


Call item.getAllInternetHeadersAsync to get internet headers on the current message in
Read mode.
Get sender preferences from current MIME headers
example
Building on the example from the previous section, the following code shows how to
get the sender's preferences from the current email's MIME headers.

JavaScript

Office.context.mailbox.item.getAllInternetHeadersAsync(getCallback);

function getCallback(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Sender's preferred fruit: " +
asyncResult.value.match(/preferred-fruit:.*/gim)[0].slice(17));
console.log("Sender's preferred vegetable: " +
asyncResult.value.match(/preferred-vegetable:.*/gim)[0].slice(21));
} else {
console.log("Error getting preferences from header: " +
JSON.stringify(asyncResult.error));
}
}

/* Sample output:
Sender's preferred fruit: orange
Sender's preferred vegetable: broccoli
*/

) Important

This sample works for simple cases. For more complex information retrieval (for
example, multi-instance headers or folded values as described in RFC 2822 ), try
using an appropriate MIME-parsing library.

Recommended practices
Currently, internet headers are a finite resource on a user's mailbox. When the quota is
exhausted, you can't create any more internet headers on that mailbox, which can result
in unexpected behavior from clients that rely on this to function.

Apply the following guidelines when you create internet headers in your add-in.

Create the minimum number of headers required. The header quota is based on
the total size of headers applied to a message. In Exchange Online, the header limit
is capped at 256 KB, while in an Exchange on-premises environment, the limit is
determined by your organization's administrator. For further information on
header limits, see Exchange Online message limits and Exchange Server message
limits.
Name headers so that you can reuse and update their values later. As such, avoid
naming headers in a variable manner (for example, based on user input,
timestamp, etc.).

See also
Get and set add-in metadata for an Outlook add-in
Get and set recurrence
Article • 03/28/2023

Sometimes you need to create and update a recurring appointment, such as a weekly
status meeting for a team project or a yearly birthday reminder. You can use the Office
JavaScript API to manage the recurrence patterns of an appointment series in your add-
in.

7 Note

Support for this feature was introduced in requirement set 1.7. See clients and
platforms that support this requirement set.

Available recurrence patterns


To configure the recurrence pattern, you need to combine the recurrence type and its
applicable recurrence properties (if any).

Table 1. Recurrence types and their applicable properties

Recurrence Valid Usage


type recurrence
properties

daily - interval An appointment occurs every interval days. Example: An


appointment occurs every 2 days.

weekday None. An appointment occurs every weekday.

monthly - interval - An appointment occurs on day dayOfMonth every interval


- dayOfMonth months. Example: An appointment occurs on day 5 every 4
- dayOfWeek months.
- weekNumber
- An appointment occurs on the weekNumber dayOfWeek every
interval months. Example: An appointment occurs on the third
Thursday every 2 months.

weekly - interval An appointment occurs on days every interval weeks. Example: An


- days appointment occurs on Tuesday and Thursday every 2 weeks.
Recurrence Valid Usage
type recurrence
properties

yearly - interval - An appointment occurs on day dayOfMonth of month every


- dayOfMonth interval years. Example: An appointment occurs on day 7 of
- dayOfWeek September every 4 years.
- weekNumber
- month - An appointment occurs on the weekNumber dayOfWeek of
month every interval years. Example: An appointment occurs on
the first Thursday of September every 2 years.

7 Note

You can also use the firstDayOfWeek property with the weekly recurrence type.
The specified day will start the list of days displayed in the recurrence dialog.

Access recurrence
How you access the recurrence pattern and what you can do with it depends on if you're
the appointment organizer or an attendee.

Table 2. Applicable appointment states

Appointment state Is recurrence editable? Is recurrence


viewable?

Appointment organizer - compose Yes (setAsync) Yes (getAsync)


series

Appointment organizer - compose No ( setAsync returns an Yes (getAsync)


instance error)

Appointment attendee - read series No ( setAsync not available) Yes (item.recurrence)

Appointment attendee - read instance No ( setAsync not available) Yes (item.recurrence)

Meeting request - read series No ( setAsync not available) Yes (item.recurrence)

Meeting request - read instance No ( setAsync not available) Yes (item.recurrence)

Set recurrence as the organizer


Along with the recurrence pattern, you also need to determine the start and end dates
and times of your appointment series. The SeriesTime object is used to manage that
information.

The appointment organizer can specify the recurrence pattern for an appointment series
in compose mode only. In the following example, the appointment series is set to occur
from 10:30 AM to 11:00 AM PST every Tuesday and Thursday during the period
November 2, 2019 to December 2, 2019.

JavaScript

const seriesTimeObject = new Office.SeriesTime();


seriesTimeObject.setStartDate(2019,10,2);
seriesTimeObject.setEndDate(2019,11,2);
seriesTimeObject.setStartTime(10,30);
seriesTimeObject.setDuration(30);

const pattern = {
"seriesTime": seriesTimeObject,
"recurrenceType": "weekly",
"recurrenceProperties": {"interval": 1, "days": ["tue", "thu"]},
"recurrenceTimeZone": {"name": "Pacific Standard Time"}};

Office.context.mailbox.item.recurrence.setAsync(pattern, callback);

function callback(asyncResult)
{
console.log(JSON.stringify(asyncResult));
}

Change recurrence as the organizer


In the following example, in compose mode, the appointment organizer gets the
recurrence object of an appointment series given the series or an instance of that series,
then sets a new recurrence duration.

JavaScript

Office.context.mailbox.item.recurrence.getAsync(callback);

function callback(asyncResult) {
const recurrencePattern = asyncResult.value;
recurrencePattern.seriesTime.setDuration(60);
Office.context.mailbox.item.recurrence.setAsync(recurrencePattern,
(asyncResult) => {
if (asyncResult.status !== Office.AsyncResultStatus.Succeeded) {
console.log("failed");
return;
}

console.log("success");
});
}

Get recurrence

Get recurrence as the organizer


In the following example, in compose mode, the appointment organizer gets the
recurrence object of an appointment series given the series or an instance of that series.

JavaScript

Office.context.mailbox.item.recurrence.getAsync(callback);

function callback(asyncResult){
const context = asyncResult.context;
const recurrence = asyncResult.value;

if (recurrence == null) {
console.log("Non-recurring meeting");
} else {
console.log(JSON.stringify(recurrence));
}
}

The following example shows the results of the getAsync call that retrieves the
recurrence for a series.

7 Note

In this example, seriesTimeObject is a placeholder for the JSON representing the


recurrence.seriesTime property. You should use the SeriesTime methods to get
the recurrence date and time properties.

JSON

{
"recurrenceType": "weekly",
"recurrenceProperties": {
"interval": 1,
"days": ["tue","thu"],
"firstDayOfWeek": "sun"},
"seriesTime": {seriesTimeObject},
"recurrenceTimeZone": {
"name": "Pacific Standard Time",
"offset": -480}}

Get recurrence as an attendee


In the following example, an appointment attendee can get the recurrence object of an
appointment series given the series, an instance of that series, or a meeting request.

JavaScript

outputRecurrence(Office.context.mailbox.item);

function outputRecurrence(item) {
const recurrence = item.recurrence;
const seriesId = item.seriesId;

if (recurrence == null) {
console.log("Non-recurring item");
} else {
console.log(JSON.stringify(recurrence));
}
}

The following example shows the value of the item.recurrence property for an
appointment series.

7 Note

In this example, seriesTimeObject is a placeholder for the JSON representing the


recurrence.seriesTime property. You should use the SeriesTime methods to get

the recurrence date and time properties.

JSON

{
"recurrenceType": "weekly",
"recurrenceProperties": {
"interval": 1,
"days": ["tue","thu"],
"firstDayOfWeek": "sun"},
"seriesTime": {seriesTimeObject},
"recurrenceTimeZone": {
"name": "Pacific Standard Time",
"offset": -480}}
Get the recurrence details
After you've retrieved the recurrence object (either from the getAsync callback or from
item.recurrence ), you can get specific properties of the recurrence. For example, you
can get the start and end dates and times of the series by using methods on the
recurrence.seriesTime property.

JavaScript

// Get series date and time info


const seriesTime = recurrence.seriesTime;
const startTime = recurrence.seriesTime.getStartTime();
const endTime = recurrence.seriesTime.getEndTime();
const startDate = recurrence.seriesTime.getStartDate();
const endDate = recurrence.seriesTime.getEndDate();
const duration = recurrence.seriesTime.getDuration();

// Get series time zone


const timeZone = recurrence.recurrenceTimeZone;

// Get recurrence properties


const recurrenceProperties = recurrence.recurrenceProperties;

// Get recurrence type


const recurrenceType = recurrence.recurrenceType;

See also
RecurrenceChanged event
Recurrence object
SeriesTime object
Get and set add-in metadata for an
Outlook add-in
Article • 03/28/2023

You can manage custom data in your Outlook add-in by using either of the following:

Roaming settings, which manage custom data for a user's mailbox.


Custom properties, which manage custom data for an item in a user's mailbox.

Both of these give access to custom data that's only accessible by your Outlook add-in,
but each method stores the data separately from the other. That is, the data stored
through roaming settings isn't accessible by custom properties, and vice versa. Roaming
settings are stored on the user's mailbox while custom properties are stored on a
message or appointment. Stored data is accessible in subsequent Outlook sessions on
all the form factors that the add-in supports.

Custom data per mailbox: roaming settings


You can specify data specific to a user's Exchange mailbox using the RoamingSettings
object. Examples of such data include the user's personal data and preferences. Your
mail add-in can access roaming settings when it roams on any device it's designed to
run on (desktop, tablet, or smartphone).

Changes to this data are stored on an in-memory copy of those settings for the current
Outlook session. You should explicitly save all the roaming settings after updating them
so that they'll be available the next time the user opens your add-in, on the same or any
other supported device.

Roaming settings format


The data in a RoamingSettings object is stored as a serialized JavaScript Object
Notation (JSON) string.

The following is an example of the structure, assuming there are three defined roaming
settings named add-in_setting_name_0 , add-in_setting_name_1 , and add-
in_setting_name_2 .

JSON

{
"add-in_setting_name_0": "add-in_setting_value_0",
"add-in_setting_name_1": "add-in_setting_value_1",
"add-in_setting_name_2": "add-in_setting_value_2"
}

Loading roaming settings


A mail add-in typically loads roaming settings in the Office.initialize event handler. The
following JavaScript code example shows how to load existing roaming settings and get
the values of two settings, customerName and customerBalance.

JavaScript

let _mailbox;
let _settings;
let _customerName;
let _customerBalance;

// The initialize function is required for all add-ins.


Office.initialize = function () {
// Initialize instance variables to access API objects.
_mailbox = Office.context.mailbox;
_settings = Office.context.roamingSettings;
_customerName = _settings.get("customerName");
_customerBalance = _settings.get("customerBalance");
}

Creating or assigning a roaming setting


Continuing with the preceding example, the following JavaScript function,
setAddInSetting , shows how to use the RoamingSettings.set method to set a setting
named cookie with today's date, and persist the data by using the
RoamingSettings.saveAsync method to save all the roaming settings to the user's
mailbox.

The set method creates the setting if the setting doesn't already exist, and assigns the
setting to the specified value. The saveAsync method saves roaming settings
asynchronously. This code sample passes a callback function,
saveMyAddInSettingsCallback , to saveAsync . When the asynchronous call finishes,

saveMyAddInSettingsCallback is called by using one parameter, asyncResult. This


parameter is an AsyncResult object that contains the result of and any details about the
asynchronous call. You can use the optional userContext parameter to pass any state
information from the asynchronous call to the callback function.

JavaScript
// Set a roaming setting.
function setAddInSetting() {
_settings.set("cookie", Date());
// Save roaming settings to the mailbox, so that they'll be available in
the next session.
_settings.saveAsync(saveMyAddInSettingsCallback);
}

// Callback function after saving custom roaming settings.


function saveMyAddInSettingsCallback(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
// Handle the failure.
}
}

Removing a roaming setting


Also extending the preceding examples, the following JavaScript function,
removeAddInSetting , shows how to use the RoamingSettings.remove method to remove

the cookie setting and save all the roaming settings to the mailbox.

JavaScript

// Remove an add-in setting.


function removeAddInSetting()
{
_settings.remove("cookie");
// Save changes to the roaming settings for the mailbox, so that they'll
be available in the next session.
_settings.saveAsync(saveMyAddInSettingsCallback);
}

Custom data per item in a mailbox: custom


properties
You can specify data specific to an item in the user's mailbox using the
CustomProperties object. For example, your mail add-in could categorize certain
messages and note the category using a custom property messageCategory . Or, if your
mail add-in creates appointments from meeting suggestions in a message, you can use
a custom property to track each of these appointments. This ensures that if the user
opens the message again, your mail add-in doesn't offer to create the appointment a
second time.
Similar to roaming settings, changes to custom properties are stored on in-memory
copies of the properties for the current Outlook session. To make sure these custom
properties will be available in the next session, use CustomProperties.saveAsync.

These add-in-specific, item-specific custom properties can only be accessed by using


the CustomProperties object. These properties are different from the custom, MAPI-
based UserProperties in the Outlook object model, and extended properties in Exchange
Web Services (EWS). You can't directly access CustomProperties by using the Outlook
object model, EWS, or REST. To learn how to access CustomProperties using EWS or
REST, see the section Get custom properties using EWS or REST.

7 Note

Custom properties are only available to the add-in that created them and only
through the mail item in which they were saved. Because of this, properties set
while in compose mode aren't transmitted to recipients of the mail item. When a
message or appointment with custom properties is sent, its properties can be
accessed from the item in the Sent Items folder. To allow recipients to receive the
custom data your add-in sets, consider using InternetHeaders instead.

Using custom properties


Before you can use custom properties, you must load them by calling the
loadCustomPropertiesAsync method. After you've created the property bag, you can use
the set and get methods to add and retrieve custom properties. You must use the
saveAsync method to save any changes that you make to the property bag.

7 Note

When using custom properties in Outlook add-ins, keep in mind that:

Outlook on Mac doesn't cache custom properties. If the user's network goes
down, add-ins in Outlook on Mac wouldn't be able to access their custom
properties.
In Outlook on Windows, custom properties saved while in compose mode
only persist after the item being composed is closed or after
Office.context.mailbox.item.saveAsync is called.

Custom properties example


The following example shows a simplified set of functions and methods for an Outlook
add-in that uses custom properties. You can use this example as a starting point for your
add-in that uses custom properties.

This example includes the following functions and methods.

Office.initialize -- Initializes the add-in and loads the custom property bag from the
Exchange server.

customPropsCallback -- Gets the custom property bag that's returned from the
server and saves it locally for later use.

updateProperty -- Sets or updates a specific property, and then saves the change
to the local property bag.

removeProperty -- Removes a specific property from the property bag, and then
saves these changes.

JavaScript

let _mailbox;
let _customProps;

// The initialize function is required for all add-ins.


Office.initialize = function () {
_mailbox = Office.context.mailbox;
_mailbox.item.loadCustomPropertiesAsync(customPropsCallback);
}

// Callback function from loading custom properties.


function customPropsCallback(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
// Handle the failure.
}
else {
// Successfully loaded custom properties,
// can get them from the asyncResult argument.
_customProps = asyncResult.value;
}
}

// Get individual custom property.


function getProperty() {
const myProp = _customProps.get("myProp");
}

// Set individual custom property.


function updateProperty(name, value) {
_customProps.set(name, value);
// Save all custom properties to the mail item.
_customProps.saveAsync(saveCallback);
}

// Remove a custom property.


function removeProperty(name) {
_customProps.remove(name);
// Save all custom properties to the mail item.
_customProps.saveAsync(saveCallback);
}

// Callback function from saving custom properties.


function saveCallback() {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
// Handle the failure.
}
}

Get custom properties using EWS or REST


To get CustomProperties using EWS or REST, you should first determine the name of its
MAPI-based extended property. You can then get that property in the same way you
would get any MAPI-based extended property.

How custom properties are stored on an item

Custom properties set by an add-in aren't equivalent to normal MAPI-based properties.


Add-in APIs serialize all your add-in's CustomProperties as a JSON payload and then
save them in a single MAPI-based extended property whose name is cecp-<app-guid>
( <app-guid> is your add-in's ID) and property set GUID is {00020329-0000-0000-C000-
000000000046} . (For more information about this object, see MS-OXCEXT 2.2.5 Mail App

Custom Properties.) You can then use EWS or REST to get this MAPI-based property.

Get custom properties using EWS

Your mail add-in can get the CustomProperties MAPI-based extended property by using
the EWS GetItem operation. Access GetItem on the server side by using a callback
token, or on the client side by using the mailbox.makeEwsRequestAsync method. In the
GetItem request, specify the CustomProperties MAPI-based property in its property set
using the details provided in the preceding section How custom properties are stored
on an item.

The following example shows how to get an item and its custom properties.

) Important
In the following example, replace <app-guid> with your add-in's ID.

TypeScript

let request_str =
'<?xml version="1.0" encoding="utf-8"?>' +
'<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' +

'xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"' +

'xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"' +
'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'
+
'<soap:Header xmlns:wsse="http://docs.oasis-
open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"' +
'xmlns:wsa="http://www.w3.org/2005/08/addressing">' +
'<t:RequestServerVersion Version="Exchange2010_SP1"/>' +
'</soap:Header>' +
'<soap:Body>' +
'<m:GetItem>' +
'<m:ItemShape>' +
'<t:BaseShape>AllProperties</t:BaseShape>' +
'<t:IncludeMimeContent>true</t:IncludeMimeContent>' +
'<t:AdditionalProperties>' +
'<t:ExtendedFieldURI ' +
'DistinguishedPropertySetId="PublicStrings" ' +
'PropertyName="cecp-<app-guid>"' +
'PropertyType="String" ' +
'/>' +
'</t:AdditionalProperties>' +
'</m:ItemShape>' +
'<m:ItemIds>' +
'<t:ItemId Id="' +
Office.context.mailbox.item.itemId +
'"/>' +
'</m:ItemIds>' +
'</m:GetItem>' +
'</soap:Body>' +
'</soap:Envelope>';

Office.context.mailbox.makeEwsRequestAsync(
request_str,
function(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log(asyncResult.value);
}
else {
console.log(JSON.stringify(asyncResult));
}
}
);
You can also get more custom properties if you specify them in the request string as
other ExtendedFieldURI elements.

Get custom properties using REST

In your add-in, you can construct your REST query against messages and events to get
the ones that already have custom properties. In your query, you should include the
CustomProperties MAPI-based property and its property set using the details provided
in the section How custom properties are stored on an item.

The following example shows how to get all events that have any custom properties set
by your add-in and ensure that the response includes the value of the property so you
can apply further filtering logic.

) Important

In the following example, replace <app-guid> with your add-in's ID.

HTTP

GET https://outlook.office.com/api/v2.0/Me/Events?
$filter=SingleValueExtendedProperties/Any
(ep: ep/PropertyId eq 'String {00020329-0000-0000-C000-000000000046}
Name cecp-<app-guid>' and ep/Value ne null)
&$expand=SingleValueExtendedProperties($filter=PropertyId eq 'String
{00020329-0000-0000-C000-000000000046} Name cecp-<app-guid>')

For other examples that use REST to get single-value MAPI-based extended properties,
see Get singleValueExtendedProperty.

The following example shows how to get an item and its custom properties. In the
callback function for the done method, item.SingleValueExtendedProperties contains a
list of the requested custom properties.

) Important

In the following example, replace <app-guid> with your add-in's ID.

TypeScript

Office.context.mailbox.getCallbackTokenAsync(
{
isRest: true
},
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded
&& asyncResult.value !== "") {
let item_rest_id = Office.context.mailbox.convertToRestId(
Office.context.mailbox.item.itemId,
Office.MailboxEnums.RestVersion.v2_0);
let rest_url = Office.context.mailbox.restUrl +
"/v2.0/me/messages('" +
item_rest_id +
"')";
rest_url += "?
$expand=SingleValueExtendedProperties($filter=PropertyId eq 'String
{00020329-0000-0000-C000-000000000046} Name cecp-<app-guid>')";

let auth_token = asyncResult.value;


$.ajax(
{
url: rest_url,
dataType: 'json',
headers:
{
"Authorization":"Bearer " + auth_token
}
}
).done(
function (item) {
console.log(JSON.stringify(item));
}
).fail(
function (error) {
console.log(JSON.stringify(error));
}
);
} else {
console.log(JSON.stringify(asyncResult));
}
}
);

Platform behavior in messages


The following table summarizes saved custom properties behavior in messages for
various Outlook clients.

Scenario Outlook on Windows Outlook on the Outlook on


web Mac

New compose null null null

Reply, reply all null null null


Scenario Outlook on Windows Outlook on the Outlook on
web Mac

Forward Loads parent's properties null null

Sent item from new null null null


compose

Sent item from reply or null null null


reply all

Sent item from forward Removes parent's properties if null null


not saved

To handle the situation in Outlook on Windows:

1. Check for existing properties on initializing your add-in, and keep them or clear
them as needed.
2. When setting custom properties, include an additional property to indicate
whether the custom properties were added in read mode. This will help you
differentiate if the property was created in compose mode or inherited from the
parent.
3. To check if the user is forwarding or replying to a message, you can use
item.getComposeTypeAsync (available from requirement set 1.10).

See also
MAPI Property Overview
Outlook Properties Overview
Call Outlook REST APIs from an Outlook add-in
Call web services from an Outlook add-in
Properties and extended properties in EWS in Exchange
Property sets and response shapes in EWS in Exchange
Get and set internet headers on a message in an Outlook add-in
Create Outlook add-ins for read forms
Article • 04/11/2023

Read add-ins are Outlook add-ins that are activated in the Reading Pane or read
inspector in Outlook. Unlike compose add-ins (Outlook add-ins that are activated when
a user is creating a message or appointment), read add-ins are available when users:

View an email message, meeting request, meeting response, or meeting


cancellation.

7 Note

Outlook doesn't activate add-ins in read form for certain types of messages,
including items that are attachments to another message, items in the
Outlook Drafts folder, or items that are encrypted or protected in other ways.

View a meeting item in which the user is an attendee.

In each of these read scenarios, Outlook activates add-ins when their activation
conditions are fulfilled, and users can choose and open activated add-ins in the add-in
bar in the Reading Pane or read inspector. The following figure shows the Bing Maps
add-in activated and opened as the user is reading a message that contains a
geographic address.

The add-in pane showing the Bing Maps add-in in action for the selected Outlook
message that contains an address
Types of add-ins available in read mode
Read add-ins can be any combination of the following types.

Add-in commands
Contextual Outlook add-ins

API features available to read add-ins


For activating add-ins in read forms, see Table 1 in Specify activation rules in a
manifest.
Use regular expression activation rules to show an Outlook add-in
Match strings in an Outlook item as well-known entities
Extract entity strings from an Outlook item
Get attachments of an Outlook item from the server

See also
Write your first Outlook add-in
Use regular expression activation rules
to show an Outlook add-in
Article • 05/20/2023

You can specify regular expression rules to have a contextual add-in activated when a
match is found in specific fields of the message. Contextual add-ins activate only in read
mode. Outlook doesn't activate contextual add-ins when the user is composing an item.
There are also other scenarios where Outlook doesn't activate add-ins, for example,
digitally signed items. For more information, see Activation rules for Outlook add-ins.

7 Note

Contextual Outlook add-ins aren't supported when the add-in uses a Unified
manifest for Microsoft 365 (preview).

You can specify a regular expression as part of an ItemHasRegularExpressionMatch rule


or ItemHasKnownEntity rule in the add-in XML manifest. The rules are specified in a
DetectedEntity extension point.

Outlook evaluates regular expressions based on the rules for the JavaScript interpreter
used by the browser or webview control on the client computer. For brevity hereafter,
this article uses "browser" to refer to "browser or webview control". Outlook supports
the same list of special characters that all XML processors also support. The following
table lists these special characters. You can use these characters in a regular expression
by specifying the escape sequence of the corresponding character, as described in the
following table.

Character Description Escape sequence to use

" Double quotation mark &quot;

& Ampersand &amp;

' Apostrophe &apos;

< Less-than sign &lt;

> Greater-than sign &gt;

ItemHasRegularExpressionMatch rule
An ItemHasRegularExpressionMatch rule is useful in controlling activation of an add-in
based on specific values of a supported property. The ItemHasRegularExpressionMatch
rule has the following attributes.

Attribute Description
name

RegExName Specifies the name of the regular expression so that you can refer to the
expression in the code for your add-in.

RegExValue Specifies the regular expression that will be evaluated to determine whether the
add-in should be shown.

PropertyName Specifies the name of the property that the regular expression will be evaluated
against. The allowed values are BodyAsHTML , BodyAsPlaintext , SenderSMTPAddress ,
and Subject .

If you specify BodyAsHTML , Outlook only applies the regular expression if the item
body is HTML. Otherwise, Outlook returns no matches for that regular expression.

If you specify BodyAsPlaintext , Outlook always applies the regular expression on


the item body.

Important: If you need to specify the Highlight attribute for the <Rule> element,
you must set the PropertyName attribute to BodyAsPlaintext .

IgnoreCase Specifies whether to ignore case when matching the regular expression specified
by RegExName .

Highlight Specifies how the client should highlight matching text. This element can only be
applied to Rule elements within ExtensionPoint elements. Can be one of the
following: all or none . If not specified, the default value is all .

Important: To specify the Highlight attribute in the <Rule> element, you must
set the PropertyName attribute to BodyAsPlaintext .

Best practices for using regular expressions in rules


Pay special attention to the following when you use regular expressions.

If you specify an ItemHasRegularExpressionMatch rule on the body of an item, the


regular expression should further filter the body and shouldn't attempt to return
the entire body of the item. Using a regular expression such as .* to attempt to
obtain the entire body of an item doesn't always return the expected results.

The plain text body returned on one browser can be different in subtle ways on
another. If you use an ItemHasRegularExpressionMatch rule with BodyAsPlaintext
as the PropertyName attribute, test your regular expression on all the browsers that
your add-in supports.

Because different browsers use different ways to obtain the text body of a selected
item, you should make sure that your regular expression supports the subtle
differences that can be returned as part of the body text. For example, some
browsers such as Internet Explorer 9 uses the innerText property of the DOM, and
others such as Firefox uses the .textContent() method to obtain the text body of
an item. Also, different browsers may return line breaks differently: a line break is
\r\n on Internet Explorer, and \n on Firefox and Chrome. For more information,
se W3C DOM Compatibility - HTML .

The HTML body of an item is slightly different between an Outlook rich client, and
Outlook on the web or Outlook on mobile devices. Define your regular expressions
carefully.

Depending on the Outlook client, type of device, or property that a regular


expression is being applied on, there are other best practices and limits for each of
the clients that you should be aware of when designing regular expressions as
activation rules. See Limits for activation and JavaScript API for Outlook add-ins for
details.

Examples
The following ItemHasRegularExpressionMatch rule activates the add-in whenever the
sender's SMTP email address matches @contoso , regardless of uppercase or lowercase
characters.

XML

<Rule xsi:type="ItemHasRegularExpressionMatch"
RegExName="addressMatches"
RegExValue="@[cC][oO][nN][tT][oO][sS][oO]"
PropertyName="SenderSMTPAddress"
/>

The following is another way to specify the same regular expression using the
IgnoreCase attribute.

XML

<Rule xsi:type="ItemHasRegularExpressionMatch"
RegExName="addressMatches"
RegExValue="@contoso"
PropertyName="SenderSMTPAddress"
IgnoreCase="true"
/>

The following ItemHasRegularExpressionMatch rule activates the add-in whenever a stock


symbol is included in the body of the current item.

XML

<Rule xsi:type="ItemHasRegularExpressionMatch"
PropertyName="BodyAsPlaintext"
RegExName="TickerSymbols"
RegExValue="\b(NYSE|NASDAQ|AMEX):\s*[A-Za-z]+\b"/>

ItemHasKnownEntity rule
An ItemHasKnownEntity rule activates an add-in based on the existence of an entity in
the subject or body of the selected item. The EntityType type defines the supported
entities. Applying a regular expression on an ItemHasKnownEntity rule provides the
convenience where activation is based on a subset of values for an entity (for example, a
specific set of URLs, or telephone numbers with a certain area code).

7 Note

Outlook can only extract entity strings in English regardless of the default locale
specified in the manifest. Only messages support the MeetingSuggestion entity
type; appointments don't support this. You can't extract entities from items in the
Sent Items folder, nor can you use an ItemHasKnownEntity rule to activate an add-
in for items in the Sent Items folder.

The ItemHasKnownEntity rule supports the attributes in the following table. Note that
while specifying a regular expression is optional in an ItemHasKnownEntity rule, if you
choose to use a regular expression as an entity filter, you must specify both the
RegExFilter and FilterName attributes.

Attribute Description
name

EntityType Specifies the type of entity that must be found for the rule to evaluate to true . Use
multiple rules to specify multiple types of entities.
Attribute Description
name

RegExFilter Specifies a regular expression that further filters instances of the entity specified by
EntityType .

FilterName Specifies the name of the regular expression specified by RegExFilter , so that it is
subsequently possible to refer to it by code.

IgnoreCase Specifies whether to ignore case when matching the regular expression specified
by RegExFilter .

Examples
The following ItemHasKnownEntity rule activates the add-in whenever there is a URL in
the subject or body of the current item, and the URL contains the string youtube ,
regardless of the case of the string.

XML

<Rule xsi:type="ItemHasKnownEntity"
EntityType="Url"
RegExFilter="youtube"
FilterName="youtube"
IgnoreCase="true"/>

Using regular expression results in code


You can obtain matches to a regular expression by using the following methods on the
current item.

getRegExMatches returns matches in the current item for all regular expressions
specified in ItemHasRegularExpressionMatch and ItemHasKnownEntity rules of the
add-in.

getRegExMatchesByName returns matches in the current item for the identified


regular expression specified in an ItemHasRegularExpressionMatch rule of the add-
in.

getFilteredEntitiesByName returns entire instances of entities that contain matches


for the identified regular expression specified in an ItemHasKnownEntity rule of the
add-in.
When the regular expressions are evaluated, the matches are returned to your add-in in
an array object. For getRegExMatches , that object has the identifier of the name of the
regular expression.

7 Note

Outlook doesn't return matches in any particular order in the array. Also, you
shouldn't assume that matches are returned in the same order in this array even
when you run the same add-in on each of these clients on the same item in the
same mailbox.

Examples
The following is an example of a rule collection that contains an
ItemHasRegularExpressionMatch rule with a regular expression named videoURL .

XML

<Rule xsi:type="RuleCollection" Mode="And">


<Rule xsi:type="ItemIs" ItemType="Message"/>
<Rule xsi:type="ItemHasRegularExpressionMatch" RegExName="videoURL"
RegExValue="http://www\.youtube\.com/watch\?v=[a-zA-Z0-9_-]{11}"
PropertyName="BodyAsPlaintext"/>
</Rule>

The following example uses getRegExMatches of the current item to set a variable
videos to the results of the preceding ItemHasRegularExpressionMatch rule.

JavaScript

const videos = Office.context.mailbox.item.getRegExMatches().videoURL;

Multiple matches are stored as array elements in that object. The following code
example shows how to iterate over the matches for a regular expression named reg1 to
build a string to display as HTML.

JavaScript

function initDialer()
{
let myEntities;
let myString;
let myCell;
myEntities = Office.context.mailbox.item.getRegExMatches();
myString = "";
myCell = document.getElementById('dialerholder');
// Loop over the myEntities collection.
for (let i in myEntities.reg1) {
myString += "<p><a href='callto:tel:" + myEntities.reg1[i] + "'>" +
myEntities.reg1[i] + "</a></p>";
}

myCell.innerHTML = myString;
}

The following is an example of an ItemHasKnownEntity rule that specifies the


MeetingSuggestion entity and a regular expression named CampSuggestion . Outlook

activates the add-in if it detects that the currently selected item contains a meeting
suggestion, and the subject or body contains the term WonderCamp .

XML

<Rule xsi:type="ItemHasKnownEntity"
EntityType="MeetingSuggestion"
RegExFilter="WonderCamp"
FilterName="CampSuggestion"
IgnoreCase="false"/>

The following code example uses getFilteredEntitiesByName on the current item to set
a variable suggestions to an array of detected meeting suggestions for the preceding
ItemHasKnownEntity rule.

JavaScript

const suggestions =
Office.context.mailbox.item.getFilteredEntitiesByName("CampSuggestion");

See also
Outlook add-in: Contoso Order Number - A sample contextual add-in that
activates based on a regular expression match.
Create Outlook add-ins for read forms
Activation rules for Outlook add-ins
Limits for activation and JavaScript API for Outlook add-ins
Match strings in an Outlook item as well-known entities
Best practices for regular expressions in the .NET framework
Match strings in an Outlook item as
well-known entities
Article • 03/21/2023

Before sending a message or meeting request item, Exchange Server parses the
contents of the item, identifies and stamps certain strings in the subject and body that
resemble entities well-known to Exchange, for example, email addresses, phone
numbers, and URLs. Messages and meeting requests are delivered by Exchange Server
in an Outlook Inbox with well-known entities stamped.

Using the Office JavaScript API, you can get these strings that match specific well-known
entities for further processing. You can also specify a well-known entity in a rule in the
add-in manifest so that Outlook can activate your add-in when the user is viewing an
item that contains matches for that entity. You can then extract and take action on
matches for the entity.

7 Note

Outlook Add-in features that depend on activation rules aren't supported when the
add-in uses a Unified manifest for Microsoft 365 (preview).

Being able to identify or extract such instances from a selected message or appointment
is convenient. For example, you can build a reverse phone look-up service as an Outlook
add-in. The add-in can extract strings in the item subject or body that resemble a phone
number, do a reverse lookup, and display the registered owner of each phone number.

This topic introduces these well-known entities, shows examples of activation rules
based on well-known entities, and how to extract entity matches independently of
having used entities in activation rules.

Support for well-known entities


Exchange Server stamps well-known entities in a message or meeting request item after
the sender sends the item and before Exchange delivers the item to the recipient.
Therefore, only items that have gone through transport in Exchange are stamped, and
Outlook can activate add-ins based on these stamps when the user is viewing such
items. On the contrary, when the user is composing an item or viewing an item that is in
the Sent Items folder, because the item has not gone through transport, Outlook cannot
activate add-ins based on well-known entities.
Similarly, you cannot extract well-known entities in items that are being composed or in
the Sent Items folder, as these items have not gone through transport and are not
stamped. For additional information about the kinds of items that support activation,
see Activation rules for Outlook add-ins.

The following table lists the entities that Exchange Server and Outlook support and
recognize (hence the name "well-known entities"), and the object type of an instance of
each entity. The natural language recognition of a string as one of these entities is
based on a learning model that has been trained on a large amount of data. Therefore,
the recognition is non-deterministic. See Tips for using well-known entities for more
information about conditions for recognition.

Table 1. Supported entities and their types

Entity type Conditions for recognition Object type

Address United States street addresses; for example: 1234 JavaScript String
Main Street, Redmond, WA 07722. Generally, for an object
address to be recognized, it should follow the
structure of a United States postal address, with
most of the elements of a street number, street
name, city, state, and zip code present. The address
can be specified in one or multiple lines.

Contact A reference to a person's information as recognized Contact object


in natural language. The recognition of a contact
depends on the context. For example, a signature at
the end of a message, or a person's name appearing
in the vicinity of some of the following information: a
phone number, address, email address, and URL.

EmailAddress SMTP email addresses. JavaScript String


object

MeetingSuggestion A reference to an event or meeting. For example, MeetingSuggestion


Exchange would recognize the following text as a object
meeting suggestion: Let's meet tomorrow for lunch.

PhoneNumber United States telephone numbers; for example: (235) PhoneNumber


555-0110 object

TaskSuggestion Actionable sentences in an email. For example: TaskSuggestion


Please update the spreadsheet. object
Entity type Conditions for recognition Object type

Url A web address that explicitly specifies the network JavaScript String
location and identifier for a web resource. Exchange object
Server does not require the access protocol in the
web address, and does not recognize URLs that are
embedded in link text as instances of the Url entity.
Exchange Server can match the following examples:
www.youtube.com/user/officevideos
https://www.youtube.com/user/officevideos

The following figure describes how Exchange Server and Outlook support well-known
entities for add-ins, and what add-ins can do with well-known entities. See Retrieving
entities in your add-in and Activating an add-in based on the existence of an entity for
more details on how to use these entities.

How Exchange Server, Outlook, and add-ins support well-known entities


Permissions to extract entities
To extract entities in your JavaScript code or to have your add-in activated based on the
existence of certain well-known entities, make sure you have requested the appropriate
permissions in the add-in manifest.

Specifying the default restricted permission allows your add-in to extract the Address ,
MeetingSuggestion , or TaskSuggestion entity. To extract any of the other entities, specify

read item, read/write item, or read/write mailbox permission in the manifest.

The following example requests the read item permission in the manifest.

XML

<Permissions>ReadItem</Permissions>

To learn more about Outlook add-in permissions, see Understanding Outlook add-in
permissions.

Retrieving entities in your add-in


As long as the subject or body of the item that is being viewed by the user contains
strings that Exchange and Outlook can recognize as well-known entities, these instances
are available to add-ins. They are available even if an add-in is not activated based on
well-known entities. With the appropriate permission, you can use the getEntities or
getEntitiesByType method to retrieve well-known entities that are present in the

current message or appointment.

The getEntities method returns an array of Entities objects that contains all the well-
known entities in the item.

If you're interested in a particular type of entities, use the getEntitiesByType method


which returns an array of only the entities you want. The EntityType enumeration
represents all the types of well-known entities you can extract.

After calling getEntities , you can then use the corresponding property of the Entities
object to obtain an array of instances of a type of entity. Depending on the type of
entity, the instances in the array can be just strings, or can map to specific objects.

As an example seen in the earlier figure, to get addresses in the item, access the array
returned by getEntities().addresses[] . The Entities.addresses property returns an
array of strings that Outlook recognizes as postal addresses. Similarly, the
Entities.contacts property returns an array of Contact objects that Outlook recognizes
as contact information. Tables 1 lists the object type of an instance of each supported
entity.

The following example shows how to retrieve any addresses found in a message.

JavaScript

// Get the address entities from the item.


const entities = Office.context.mailbox.item.getEntities();
// Check to make sure that address entities are present.
if (null != entities && null != entities.addresses && undefined !=
entities.addresses) {
//Addresses are present, so use them here.
}

Activating an add-in based on the existence of


an entity
Another way to use well-known entities is to have Outlook activate your add-in based
on the existence of one or more types of entities in the subject or body of the currently
viewed item. You can do so by specifying an ItemHasKnownEntity rule in the add-in
manifest. The EntityType simple type represents the different types of well-known
entities supported by ItemHasKnownEntity rules. After your add-in is activated, you can
also retrieve the instances of such entities for your purposes, as described in the
previous section Retrieving entities in your add-in.

You can optionally apply a regular expression in an ItemHasKnownEntity rule, so as to


further filter instances of an entity and have Outlook activate an add-in only on a subset
of the instances of the entity. For example, you can specify a filter for the street address
entity in a message that contains a Washington state zip code beginning with "98". To
apply a filter on the entity instances, use the RegExFilter and FilterName attributes in
the Rule element of the ItemHasKnownEntity type.

Similar to other activation rules, you can specify multiple rules to form a rule collection
for your add-in. The following example applies an "AND" operation on 2 rules: an
ItemIs rule and an ItemHasKnownEntity rule. This rule collection activates the add-in
whenever the current item is a message and Outlook recognizes an address in the
subject or body of that item.

XML

<Rule xsi:type="RuleCollection" Mode="And">


<Rule xsi:type="ItemIs" ItemType="Message" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="Address" />
</Rule>

The following example uses getEntitiesByType of the current item to set a variable
addresses to the results of the preceding rule collection.

JavaScript

const addresses =
Office.context.mailbox.item.getEntitiesByType(Office.MailboxEnums.EntityType
.Address);

The following ItemHasKnownEntity rule example activates the add-in whenever there is a
URL in the subject or body of the current item, and the URL contains the string
"youtube", regardless of the case of the string.

XML

<Rule xsi:type="ItemHasKnownEntity"
EntityType="Url"
RegExFilter="youtube"
FilterName="youtube"
IgnoreCase="true"/>

The following example uses getFilteredEntitiesByName(name) of the current item to set


a variable videos to get an array of results that match the regular expression in the
preceding ItemHasKnownEntity rule.

JavaScript

const videos =
Office.context.mailbox.item.getFilteredEntitiesByName(youtube);

Tips for using well-known entities


There are a few facts and limits you should be aware of if you use well-known entities in
your add-in. The following applies as long as your add-in is activated when the user is
reading an item which contains matches of well-known entities, regardless of whether
you use an ItemHasKnownEntity rule.

You can extract strings that are well-known entities only if the strings are in
English.
You can extract well-known entities from the first 2,000 characters in the item
body, but not beyond that limit. This size limit helps balance the need for
functionality and performance, so that Exchange Server and Outlook are not
bogged down by parsing and identifying instances of well-known entities in large
messages and appointments. Note that this limit is independent of whether the
add-in specifies an ItemHasKnownEntity rule. If the add-in does use such a rule,
note also the rule processing limit in item 2 below for the Outlook rich clients.

You can extract entities from appointments that are meetings organized by
someone other than the mailbox owner. You cannot extract entities from calendar
items that are not meetings, or meetings organized by the mailbox owner.

You can extract entities of the MeetingSuggestion type from only messages but not
appointments.

You can extract URLs that exist explicitly in the item body, but not URLs that are
embedded in hyperlinked text in HTML item body. Consider using an
ItemHasRegularExpressionMatch rule instead to get both explicit and embedded

URLs. Specify BodyAsHTML as the PropertyName, and a regular expression that


matches URLs as the RegExValue.

You cannot extract entities from items in the Sent Items folder.

In addition, the following applies if you use an ItemHasKnownEntity rule, and may affect
the scenarios where you'd otherwise expect your add-in to be activated.

When using the ItemHasKnownEntity rule, expect Outlook to match entity strings in
only English regardless of the default locale specified in the manifest.

When your add-in is running on an Outlook rich client, expect Outlook to apply
the ItemHasKnownEntity rule to the first megabyte of the item body and not to the
rest of the body over that limit.

You cannot use an ItemHasKnownEntity rule to activate an add-in for items in the
Sent Items folder.

See also
Create Outlook add-ins for read forms
Extract entity strings from an Outlook item
Activation rules for Outlook add-ins
Use regular expression activation rules to show an Outlook add-in
Understanding Outlook add-in permissions
Extract entity strings from an Outlook
item
Article • 03/21/2023

This article describes how to create a Display entities Outlook add-in that extracts string
instances of supported well-known entities in the subject and body of the selected
Outlook item. This item can be an appointment, email message, or meeting request,
response, or cancellation.

7 Note

The Outlook Add-in feature described in this article uses activation rules, which
aren't supported in add-ins that use a Unified manifest for Microsoft 365
(preview).

The supported entities include:

Address: A United States postal address, that has at least a subset of the elements
of a street number, street name, city, state, and zip code.

Contact: A person's contact information, in the context of other entities such as an


address or business name.

Email address: An SMTP email address.

Meeting suggestion: A meeting suggestion, such as a reference to an event. Note


that only messages but not appointments support extracting meeting suggestions.

Phone number: A North American phone number.

Task suggestion: A task suggestion, typically expressed in an actionable phrase.

URL

Most of these entities rely on natural language recognition, which is based on machine
learning of large amounts of data. This recognition is nondeterministic and sometimes
depends on the context in the Outlook item.

Outlook activates the entities add-in whenever the user selects an appointment, email
message, or meeting request, response, or cancellation for viewing. During initialization,
the sample entities add-in reads all instances of the supported entities from the current
item.
The add-in provides buttons for the user to choose a type of entity. When the user
selects an entity, the add-in displays instances of the selected entity in the add-in pane.
The following sections list the XML manifest, and HTML and JavaScript files of the
entities add-in, and highlight the code that supports the respective entity extraction.

XML manifest
The entities add-in has two activation rules joined by a logical OR operation.

XML

<!-- Activate the add-in if the current item in Outlook is an email or


appointment item. -->
<Rule xsi:type="RuleCollection" Mode="Or">
<Rule xsi:type="ItemIs" ItemType="Message"/>
<Rule xsi:type="ItemIs" ItemType="Appointment"/>
</Rule>

These rules specify that Outlook should activate this add-in when the currently selected
item in the Reading Pane or read inspector is an appointment or message (including an
email message, or meeting request, response, or cancellation).

The following is the manifest of the entities add-in. It uses version 1.1 of the schema for
Office Add-ins manifests.

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:type="MailApp">
<Id>6880A140-1C4F-11E1-BDDB-0800200C9A68</Id>
<Version>1.0</Version>
<ProviderName>Microsoft</ProviderName>
<DefaultLocale>EN-US</DefaultLocale>
<DisplayName DefaultValue="Display entities"/>
<Description DefaultValue=
"Display known entities on the selected item."/>
<Hosts>
<Host Name="Mailbox" />
</Hosts>
<Requirements>
<Sets DefaultMinVersion="1.1">
<Set Name="Mailbox" />
</Sets>
</Requirements>
<FormSettings>
<Form xsi:type="ItemRead">
<DesktopSettings>
<!-- Change the following line to specify the web -->
<!-- server where the HTML file is hosted. -->
<SourceLocation DefaultValue=
"http://webserver/default_entities/default_entities.html"/>
<RequestedHeight>350</RequestedHeight>
</DesktopSettings>
</Form>
</FormSettings>
<Permissions>ReadItem</Permissions>
<!-- Activate the add-in if the current item in Outlook is -->
<!-- an email or appointment item. -->
<Rule xsi:type="RuleCollection" Mode="Or">
<Rule xsi:type="ItemIs" ItemType="Message"/>
<Rule xsi:type="ItemIs" ItemType="Appointment"/>
</Rule>
<DisableEntityHighlighting>false</DisableEntityHighlighting>
</OfficeApp>

HTML implementation
The HTML file of the entities add-in specifies buttons for the user to select each type of
entity, and another button to clear displayed instances of an entity. It includes a
JavaScript file, default_entities.js, which is described in the next section under JavaScript
implementation. The JavaScript file includes the event handlers for each of the buttons.

Note that all Outlook add-ins must include office.js. The HTML file that follows includes
version 1.1 of office.js on the content delivery network (CDN).

HTML

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" >
<title>standard_item_properties</title>
<link rel="stylesheet" type="text/css" media="all"
href="default_entities.css" />
<script type="text/javascript" src="MicrosoftAjax.js"></script>
<!-- Use the CDN reference to Office.js. -->
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/Office.js"
type="text/javascript"></script>
<script type="text/javascript" src="default_entities.js"></script>
</head>

<body>
<div id="container">
<div id="button">
<input type="button" value="clear"
onclick="myClearEntitiesBox();">
<input type="button" value="Get Addresses"
onclick="myGetAddresses();">
<input type="button" value="Get Contact Information"
onclick="myGetContacts();">
<input type="button" value="Get Email Addresses"
onclick="myGetEmailAddresses();">
<input type="button" value="Get Meeting Suggestions"
onclick="myGetMeetingSuggestions();">
<input type="button" value="Get Phone Numbers"
onclick="myGetPhoneNumbers();">
<input type="button" value="Get Task Suggestions"
onclick="myGetTaskSuggestions();">
<input type="button" value="Get URLs"
onclick="myGetUrls();">
</div>
<div id="entities_box"></div>
</div>
</body>
</html>

Style sheet
The entities add-in uses an optional CSS file, default_entities.css, to specify the layout of
the output. The following is a listing of the CSS file.

CSS

{
color: #FFFFFF;
margin: 0px;
padding: 0px;
font-family: Arial, Sans-serif;
}
html
{
scrollbar-base-color: #FFFFFF;
scrollbar-arrow-color: #ABABAB;
scrollbar-lightshadow-color: #ABABAB;
scrollbar-highlight-color: #ABABAB;
scrollbar-darkshadow-color: #FFFFFF;
scrollbar-track-color: #FFFFFF;
}
body
{
background: #4E9258;
}
input
{
color: #000000;
padding: 5px;
}
span
{
color: #FFFF00;
}
div#container
{
height: 100%;
padding: 2px;
overflow: auto;
}
div#container td
{
border-bottom: 1px solid #CCCCCC;
}
td.property-name
{
padding: 0px 5px 0px 0px;
border-right: 1px solid #CCCCCC;
}
div#meeting_suggestions
{
border-top: 1px solid #CCCCCC;
}

JavaScript implementation
The remaining sections describe how this sample (default_entities.js file) extracts well-
known entities from the subject and body of the message or appointment that the user
is viewing.

Extracting entities upon initialization


Upon the Office.initialize event, the entities add-in calls the getEntities method of the
current item. The getEntities method returns the global variable _MyEntities an array
of instances of supported entities. The following is the related JavaScript code.

JavaScript

// Global variables
let _Item;
let _MyEntities;

// The initialize function is required for all add-ins.


Office.initialize = function () {
const _mailbox = Office.context.mailbox;
// Obtains the current item.
_Item = _mailbox.item;
// Reads all instances of supported entities from the subject
// and body of the current item.
_MyEntities = _Item.getEntities();

// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
});
}

Extracting addresses
When the user clicks the Get Addresses button, the myGetAddresses event handler
obtains an array of addresses from the addresses property of the _MyEntities object, if
any address was extracted. Each extracted address is stored as a string in the array.
myGetAddresses forms a local HTML string in htmlText to display the list of extracted

addresses. The following is the related JavaScript code.

JavaScript

// Gets instances of the Address entity on the item.


function myGetAddresses()
{
let htmlText = "";

// Gets an array of postal addresses. Each address is a string.


const addressesArray = _MyEntities.addresses;
for (let i = 0; i < addressesArray.length; i++)
{
htmlText += "Address : <span>" + addressesArray[i] + "</span><br/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

Extracting contact information


When the user clicks the Get Contact Information button, the myGetContacts event
handler obtains an array of contacts together with their information from the contacts
property of the _MyEntities object, if any was extracted. Each extracted contact is
stored as a Contact object in the array. myGetContacts obtains further data about each
contact. Note that the context determines whether Outlook can extract a contact from
an item—a signature at the end of an email message, or at least some of the following
information would have to exist in the vicinity of the contact.
The string representing the contact's name from the Contact.personName
property.

The string representing the company name associated with the contact from the
Contact.businessName property.

The array of telephone numbers associated with the contact from the
Contact.phoneNumbers property. Each telephone number is represented by a
PhoneNumber object.

For each PhoneNumber member in the telephone numbers array, the string
representing the telephone number from the PhoneNumber.phoneString property.

The array of URLs associated with the contact from the Contact.urls property. Each
URL is represented as a string in an array member.

The array of email addresses associated with the contact from the
Contact.emailAddresses property. Each email address is represented as a string in
an array member.

The array of postal addresses associated with the contact from the
Contact.addresses property. Each postal address is represented as a string in an
array member.

myGetContacts forms a local HTML string in htmlText to display the data for each

contact. The following is the related JavaScript code.

JavaScript

// Gets instances of the Contact entity on the item.


function myGetContacts()
{
let htmlText = "";

// Gets an array of contacts and their information.


const contactsArray = _MyEntities.contacts;
for (let i = 0; i < contactsArray.length; i++)
{
// Gets the name of the person. The name is a string.
htmlText += "Name : <span>" + contactsArray[i].personName +
"</span><br/>";

// Gets the company name associated with the contact.


htmlText += "Business : <span>" +
contactsArray[i].businessName + "</span><br/>";

// Gets an array of phone numbers associated with the


// contact. Each phone number is represented by a
// PhoneNumber object.
let phoneNumbersArray = contactsArray[i].phoneNumbers;
for (let j = 0; j < phoneNumbersArray.length; j++)
{
htmlText += "PhoneString : <span>" +
phoneNumbersArray[j].phoneString + "</span><br/>";
htmlText += "OriginalPhoneString : <span>" +
phoneNumbersArray[j].originalPhoneString +
"</span><br/>";
}

// Gets the URLs associated with the contact.


let urlsArray = contactsArray[i].urls;
for (let j = 0; j < urlsArray.length; j++)
{
htmlText += "Url : <span>" + urlsArray[j] +
"</span><br/>";
}

// Gets the email addresses of the contact.


let emailAddressesArray = contactsArray[i].emailAddresses;
for (let j = 0; j < emailAddressesArray.length; j++)
{
htmlText += "E-mail Address : <span>" +
emailAddressesArray[j] + "</span><br/>";
}

// Gets postal addresses of the contact.


let addressesArray = contactsArray[i].addresses;
for (let j = 0; j < addressesArray.length; j++)
{
htmlText += "Address : <span>" + addressesArray[j] +
"</span><br/>";
}

htmlText += "<hr/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

Extracting email addresses


When the user clicks the Get Email Addresses button, the myGetEmailAddresses event
handler obtains an array of SMTP email addresses from the emailAddresses property of
the _MyEntities object, if any was extracted. Each extracted email address is stored as a
string in the array. myGetEmailAddresses forms a local HTML string in htmlText to
display the list of extracted email addresses. The following is the related JavaScript code.

JavaScript
// Gets instances of the EmailAddress entity on the item.
function myGetEmailAddresses() {
let htmlText = "";

// Gets an array of email addresses. Each email address is a


// string.
const emailAddressesArray = _MyEntities.emailAddresses;
for (let i = 0; i < emailAddressesArray.length; i++) {
htmlText += "E-mail Address : <span>" + emailAddressesArray[i] + "
</span><br/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

Extracting meeting suggestions


When the user clicks the Get Meeting Suggestions button, the
myGetMeetingSuggestions event handler obtains an array of meeting suggestions from
the meetingSuggestions property of the _MyEntities object, if any was extracted.

7 Note

Only messages but not appointments support the MeetingSuggestion entity type.

Each extracted meeting suggestion is stored as a MeetingSuggestion object in the array.


myGetMeetingSuggestions obtains further data about each meeting suggestion:

The string that was identified as a meeting suggestion from the


MeetingSuggestion.meetingString property.

The array of meeting attendees from the MeetingSuggestion.attendees property.


Each attendee is represented by an EmailUser object.

For each attendee, the name from the EmailUser.displayName property.

For each attendee, the SMTP address from the EmailUser.emailAddress property.

The string representing the location of the meeting suggestion from the
MeetingSuggestion.location property.

The string representing the subject of the meeting suggestion from the
MeetingSuggestion.subject property.
The string representing the start time of the meeting suggestion from the
MeetingSuggestion.start property.

The string representing the end time of the meeting suggestion from the
MeetingSuggestion.end property.

myGetMeetingSuggestions forms a local HTML string in htmlText to display the data for

each of the meeting suggestions. The following is the related JavaScript code.

JavaScript

// Gets instances of the MeetingSuggestion entity on the


// message item.
function myGetMeetingSuggestions() {
let htmlText = "";

// Gets an array of MeetingSuggestion objects, each array


// element containing an instance of a meeting suggestion
// entity from the current item.
const meetingsArray = _MyEntities.meetingSuggestions;

// Iterates through each instance of a meeting suggestion.


for (let i = 0; i < meetingsArray.length; i++) {
// Gets the string that was identified as a meeting suggestion.
htmlText += "MeetingString : <span>" +
meetingsArray[i].meetingString + "</span><br/>";

// Gets an array of attendees for that instance of a


// meeting suggestion. Each attendee is represented
// by an EmailUser object.
let attendeesArray = meetingsArray[i].attendees;
for (let j = 0; j < attendeesArray.length; j++) {
htmlText += "Attendee : ( ";

// Gets the displayName property of the attendee.


htmlText += "displayName = <span>" +
attendeesArray[j].displayName + "</span> , ";

// Gets the emailAddress property of each attendee.


// This is the SMTP address of the attendee.
htmlText += "emailAddress = <span>" +
attendeesArray[j].emailAddress + "</span>";

htmlText += " )<br/>";


}

// Gets the location of the meeting suggestion.


htmlText += "Location : <span>" + meetingsArray[i].location + "
</span><br/>";

// Gets the subject of the meeting suggestion.


htmlText += "Subject : <span>" + meetingsArray[i].subject + "</span>
<br/>";

// Gets the start time of the meeting suggestion.


htmlText += "Start time : <span>" + meetingsArray[i].start + "
</span><br/>";

// Gets the end time of the meeting suggestion.


htmlText += "End time : <span>" + meetingsArray[i].end + "</span>
<br/>";

htmlText += "<hr/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

Extracting phone numbers


When the user clicks the Get Phone Numbers button, the myGetPhoneNumbers event
handler obtains an array of phone numbers from the phoneNumbers property of the
_MyEntities object, if any was extracted. Each extracted phone number is stored as a

PhoneNumber object in the array. myGetPhoneNumbers obtains further data about each
phone number:

The string representing the kind of phone number, for example, home phone
number, from the PhoneNumber.type property.

The string representing the actual phone number from the


PhoneNumber.phoneString property.

The string that was originally identified as the phone number from the
PhoneNumber.originalPhoneString property.

myGetPhoneNumbers forms a local HTML string in htmlText to display the data for each of
the phone numbers. The following is the related JavaScript code.

JavaScript

// Gets instances of the phone number entity on the item.


function myGetPhoneNumbers()
{
let htmlText = "";

// Gets an array of phone numbers.


// Each phone number is a PhoneNumber object.
const phoneNumbersArray = _MyEntities.phoneNumbers;
for (let i = 0; i < phoneNumbersArray.length; i++)
{
htmlText += "Phone Number : ( ";
// Gets the type of phone number, for example, home, office.
htmlText += "type = <span>" + phoneNumbersArray[i].type +
"</span> , ";

// Gets the actual phone number represented by a string.


htmlText += "phone string = <span>" +
phoneNumbersArray[i].phoneString + "</span> , ";

// Gets the original text that was identified in the item


// as a phone number.
htmlText += "original phone string = <span>" +
phoneNumbersArray[i].originalPhoneString + "</span>";

htmlText += " )<br/>";


}

document.getElementById("entities_box").innerHTML = htmlText;
}

Extracting task suggestions


When the user clicks the Get Task Suggestions button, the myGetTaskSuggestions event
handler obtains an array of task suggestions from the taskSuggestions property of the
_MyEntities object, if any was extracted. Each extracted task suggestion is stored as a

TaskSuggestion object in the array. myGetTaskSuggestions obtains further data about


each task suggestion:

The string that was originally identified a task suggestion from the
TaskSuggestion.taskString property.

The array of task assignees from the TaskSuggestion.assignees property. Each


assignee is represented by an EmailUser object.

For each assignee, the name from the EmailUser.displayName property.

For each assignee, the SMTP address from the EmailUser.emailAddress property.

myGetTaskSuggestions forms a local HTML string in htmlText to display the data for
each task suggestion. The following is the related JavaScript code.

JavaScript

// Gets instances of the task suggestion entity on the item.


function myGetTaskSuggestions()
{
let htmlText = "";
// Gets an array of TaskSuggestion objects, each array element
// containing an instance of a task suggestion entity from
// the current item.
const tasksArray = _MyEntities.taskSuggestions;

// Iterates through each instance of a task suggestion.


for (let i = 0; i < tasksArray.length; i++)
{
// Gets the string that was identified as a task suggestion.
htmlText += "TaskString : <span>" +
tasksArray[i].taskString + "</span><br/>";

// Gets an array of assignees for that instance of a task


// suggestion. Each assignee is represented by an
// EmailUser object.
let assigneesArray = tasksArray[i].assignees;
for (let j = 0; j < assigneesArray.length; j++)
{
htmlText += "Assignee : ( ";
// Gets the displayName property of the assignee.
htmlText += "displayName = <span>" +
assigneesArray[j].displayName +
"</span> , ";

// Gets the emailAddress property of each assignee.


// This is the SMTP address of the assignee.
htmlText += "emailAddress = <span>" +
assigneesArray[j].emailAddress +
"</span>";

htmlText += " )<br/>";


}

htmlText += "<hr/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

Extracting URLs
When the user clicks the Get URLs button, the myGetUrls event handler obtains an array
of URLs from the urls property of the _MyEntities object, if any was extracted. Each
extracted URL is stored as a string in the array. myGetUrls forms a local HTML string in
htmlText to display the list of extracted URLs.

JavaScript

// Gets instances of the URL entity on the item.


function myGetUrls()
{
let htmlText = "";

// Gets an array of URLs. Each URL is a string.


const urlArray = _MyEntities.urls;
for (let i = 0; i < urlArray.length; i++)
{
htmlText += "Url : <span>" + urlArray[i] + "</span><br/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

Clearing displayed entity strings


Lastly, the entities add-in specifies a myClearEntitiesBox event handler which clears any
displayed strings. The following is the related code.

JavaScript

// Clears the div with id="entities_box".


function myClearEntitiesBox()
{
document.getElementById("entities_box").innerHTML = "";
}

JavaScript listing
The following is the complete listing of the JavaScript implementation.

JavaScript

// Global variables
let _Item;
let _MyEntities;

// Initializes the add-in.


Office.initialize = function () {
const _mailbox = Office.context.mailbox;
// Obtains the current item.
_Item = _mailbox.item;
// Reads all instances of supported entities from the subject
// and body of the current item.
_MyEntities = _Item.getEntities();

// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
});
}

// Clears the div with id="entities_box".


function myClearEntitiesBox()
{
document.getElementById("entities_box").innerHTML = "";
}

// Gets instances of the Address entity on the item.


function myGetAddresses()
{
let htmlText = "";

// Gets an array of postal addresses. Each address is a string.


const addressesArray = _MyEntities.addresses;
for (let i = 0; i < addressesArray.length; i++)
{
htmlText += "Address : <span>" + addressesArray[i] +
"</span><br/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

// Gets instances of the EmailAddress entity on the item.


function myGetEmailAddresses()
{
let htmlText = "";

// Gets an array of email addresses. Each email address is a


// string.
const emailAddressesArray = _MyEntities.emailAddresses;
for (let i = 0; i < emailAddressesArray.length; i++)
{
htmlText += "E-mail Address : <span>" +
emailAddressesArray[i] + "</span><br/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

// Gets instances of the MeetingSuggestion entity on the


// message item.
function myGetMeetingSuggestions()
{
let htmlText = "";

// Gets an array of MeetingSuggestion objects, each array


// element containing an instance of a meeting suggestion
// entity from the current item.
const meetingsArray = _MyEntities.meetingSuggestions;

// Iterates through each instance of a meeting suggestion.


for (let i = 0; i < meetingsArray.length; i++)
{
// Gets the string that was identified as a meeting
// suggestion.
htmlText += "MeetingString : <span>" +
meetingsArray[i].meetingString + "</span><br/>";

// Gets an array of attendees for that instance of a


// meeting suggestion.
// Each attendee is represented by an EmailUser object.
let attendeesArray = meetingsArray[i].attendees;
for (let j = 0; j < attendeesArray.length; j++)
{
htmlText += "Attendee : ( ";
// Gets the displayName property of the attendee.
htmlText += "displayName = <span>" +
attendeesArray[j].displayName +
"</span> , ";

// Gets the emailAddress property of each attendee.


// This is the SMTP address of the attendee.
htmlText += "emailAddress = <span>" +
attendeesArray[j].emailAddress +
"</span>";

htmlText += " )<br/>";


}

// Gets the location of the meeting suggestion.


htmlText += "Location : <span>" +
meetingsArray[i].location + "</span><br/>";

// Gets the subject of the meeting suggestion.


htmlText += "Subject : <span>" +
meetingsArray[i].subject + "</span><br/>";

// Gets the start time of the meeting suggestion.


htmlText += "Start time : <span>" +
meetingsArray[i].start + "</span><br/>";

// Gets the end time of the meeting suggestion.


htmlText += "End time : <span>" +
meetingsArray[i].end + "</span><br/>";

htmlText += "<hr/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

// Gets instances of the phone number entity on the item.


function myGetPhoneNumbers()
{
let htmlText = "";

// Gets an array of phone numbers.


// Each phone number is a PhoneNumber object.
const phoneNumbersArray = _MyEntities.phoneNumbers;
for (let i = 0; i < phoneNumbersArray.length; i++)
{
htmlText += "Phone Number : ( ";
// Gets the type of phone number, for example, home, office.
htmlText += "type = <span>" + phoneNumbersArray[i].type +
"</span> , ";

// Gets the actual phone number represented by a string.


htmlText += "phone string = <span>" +
phoneNumbersArray[i].phoneString + "</span> , ";

// Gets the original text that was identified in the item


// as a phone number.
htmlText += "original phone string = <span>" +
phoneNumbersArray[i].originalPhoneString + "</span>";

htmlText += " )<br/>";


}

document.getElementById("entities_box").innerHTML = htmlText;
}

// Gets instances of the task suggestion entity on the item.


function myGetTaskSuggestions()
{
let htmlText = "";

// Gets an array of TaskSuggestion objects, each array element


// containing an instance of a task suggestion entity from the
// current item.
const tasksArray = _MyEntities.taskSuggestions;

// Iterates through each instance of a task suggestion.


for (let i = 0; i < tasksArray.length; i++)
{
// Gets the string that was identified as a task suggestion.
htmlText += "TaskString : <span>" +
tasksArray[i].taskString + "</span><br/>";

// Gets an array of assignees for that instance of a task


// suggestion. Each assignee is represented by an
// EmailUser object.
let assigneesArray = tasksArray[i].assignees;
for (let j = 0; j < assigneesArray.length; j++)
{
htmlText += "Assignee : ( ";
// Gets the displayName property of the assignee.
htmlText += "displayName = <span>" +
assigneesArray[j].displayName +
"</span> , ";

// Gets the emailAddress property of each assignee.


// This is the SMTP address of the assignee.
htmlText += "emailAddress = <span>" +
assigneesArray[j].emailAddress +
"</span>";

htmlText += " )<br/>";


}

htmlText += "<hr/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

// Gets instances of the URL entity on the item.


function myGetUrls()
{
let htmlText = "";

// Gets an array of URLs. Each URL is a string.


const urlArray = _MyEntities.urls;
for (let i = 0; i < urlArray.length; i++)
{
htmlText += "Url : <span>" + urlArray[i] + "</span><br/>";
}

document.getElementById("entities_box").innerHTML = htmlText;
}

See also
Create Outlook add-ins for read forms
Match strings in an Outlook item as well-known entities
item.getEntities method
Get attachments of an Outlook item
from the server
Article • 08/07/2023

You can get the attachments of an Outlook item in a couple of ways, but which option
you use depends on your scenario.

1. Send the attachment information to your remote service.

Your add-in can use the attachments API to send information about the
attachments to the remote service. The service can then contact the Exchange
server directly to retrieve the attachments.

2. Use the getAttachmentContentAsync API, available from requirement set 1.8.


Supported formats: AttachmentContentFormat.

This API may be handy if Microsoft Graph or EWS is unavailable (for example, due
to the admin configuration of your Exchange server), or your add-in wants to use
the base64 content directly in HTML or JavaScript. Also, the
getAttachmentContentAsync API is available in compose scenarios where the

attachment may not have synced to Exchange yet; see Manage an item's
attachments in a compose form in Outlook to learn more.

This article elaborates on the first option. To send attachment information to the remote
service, use the following properties and method.

Office.context.mailbox.ewsUrl property – Provides the URL of Exchange Web


Services (EWS) on the Exchange server that hosts the mailbox. Your service uses
this URL to call the ExchangeService.GetAttachments method, or the
GetAttachment EWS operation.

Office.context.mailbox.item.attachments property – Gets an array of


AttachmentDetails objects, one for each attachment to the item.

Office.context.mailbox.getCallbackTokenAsync method – Makes an asynchronous


call to the Exchange server that hosts the mailbox to get a callback token that the
server sends back to the Exchange server to authenticate a request for an
attachment.

Using the attachments API


To use the attachments API to get attachments from an Exchange mailbox, perform the
following steps.

1. Show the add-in when the user is viewing a message or appointment that contains
an attachment.

2. Get the callback token from the Exchange server.

3. Send the callback token and attachment information to the remote service.

4. Get the attachments from the Exchange server by using the


ExchangeService.GetAttachments method or the GetAttachment operation.

Each of these steps is covered in detail in the following sections using code from the
Outlook-Add-in-JavaScript-GetAttachments sample.

7 Note

The code in these examples has been shortened to emphasize the attachment
information. The sample contains additional code for authenticating the add-in
with the remote server and managing the state of the request.

Get a callback token


The Office.context.mailbox object provides the getCallbackTokenAsync method to get a
token that the remote server can use to authenticate with the Exchange server. The
following code shows a function in an add-in that starts the asynchronous request to
get the callback token, and the callback function that gets the response. The callback
token is stored in the service request object that is defined in the next section.

JavaScript

function getAttachmentToken() {
if (serviceRequest.attachmentToken == "") {

Office.context.mailbox.getCallbackTokenAsync(attachmentTokenCallback);
}
}

function attachmentTokenCallback(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
// Cache the result from the server.
serviceRequest.attachmentToken = asyncResult.value;
serviceRequest.state = 3;
testAttachments();
} else {
showToast("Error", "Couldn't get callback token: " +
asyncResult.error.message);
}
}

Send attachment information to the remote


service
The remote service that your add-in calls defines the specifics of how you should send
the attachment information to the service. In this example, the remote service is a Web
API application created by using Visual Studio. The remote service expects the
attachment information in a JSON object. The following code initializes an object that
contains the attachment information.

JavaScript

// Initialize a context object for the add-in.


// Set the fields that are used on the request
// object to default values.
const serviceRequest = {
attachmentToken: '',
ewsUrl : Office.context.mailbox.ewsUrl,
attachments : []
};

The Office.context.mailbox.item.attachments property contains a collection of


AttachmentDetails objects, one for each attachment to the item. In most cases, the add-

in can pass just the attachment ID property of an AttachmentDetails object to the


remote service. If the remote service needs more details about the attachment, you can
pass all or part of the AttachmentDetails object. The following code defines a method
that puts the entire AttachmentDetails array in the serviceRequest object and sends a
request to the remote service.

JavaScript

function makeServiceRequest() {
// Format the attachment details for sending.
for (let i = 0; i < mailbox.item.attachments.length; i++) {
serviceRequest.attachments[i] =
JSON.parse(JSON.stringify(mailbox.item.attachments[i]));
}

$.ajax({
url: '../../api/Default',
type: 'POST',
data: JSON.stringify(serviceRequest),
contentType: 'application/json;charset=utf-8'
}).done(function (response) {
if (!response.isError) {
const names = "<h2>Attachments processed using " +
serviceRequest.service +
": " +
response.attachmentsProcessed +
"</h2>";
for (let i = 0; i < response.attachmentNames.length; i++) {
names += response.attachmentNames[i] + "<br />";
}
document.getElementById("names").innerHTML = names;
} else {
app.showNotification("Runtime error", response.message);
}
}).fail(function (status) {

}).always(function () {
$('.disable-while-sending').prop('disabled', false);
})
}

Get the attachments from the Exchange server


Your remote service can use either the GetAttachments EWS Managed API method or
the GetAttachment EWS operation to retrieve attachments from the server. The service
application needs two objects to deserialize the JSON string into .NET Framework
objects that can be used on the server. The following code shows the definitions of the
deserialization objects.

C#

namespace AttachmentsSample
{
public class AttachmentSampleServiceRequest
{
public string attachmentToken { get; set; }
public string ewsUrl { get; set; }
public string service { get; set; }
public AttachmentDetails [] attachments { get; set; }
}

public class AttachmentDetails


{
public string attachmentType { get; set; }
public string contentType { get; set; }
public string id { get; set; }
public bool isInline { get; set; }
public string name { get; set; }
public int size { get; set; }
}
}

Use the EWS Managed API to get the attachments


If you use the EWS Managed API in your remote service, you can use the
GetAttachments method, which will construct, send, and receive an EWS SOAP request
to get the attachments. We recommend that you use the EWS Managed API because it
requires fewer lines of code and provides a more intuitive interface for making calls to
EWS. The following code makes one request to retrieve all the attachments, and returns
the count and names of the attachments processed.

C#

private AttachmentSampleServiceResponse
GetAtttachmentsFromExchangeServerUsingEWSManagedApi(AttachmentSampleServiceR
equest request)
{
var attachmentsProcessedCount = 0;
var attachmentNames = new List<string>();

// Create an ExchangeService object, set the credentials and the EWS URL.
ExchangeService service = new ExchangeService();
service.Credentials = new OAuthCredentials(request.attachmentToken);
service.Url = new Uri(request.ewsUrl);

var attachmentIds = new List<string>();

foreach (AttachmentDetails attachment in request.attachments)


{
attachmentIds.Add(attachment.id);
}

// Call the GetAttachments method to retrieve the attachments on the


message.
// This method results in a GetAttachments EWS SOAP request and response
// from the Exchange server.
var getAttachmentsResponse =
service.GetAttachments(attachmentIds.ToArray(),
null,
new
PropertySet(BasePropertySet.FirstClassProperties,
ItemSchema.MimeContent));

if (getAttachmentsResponse.OverallResult == ServiceResult.Success)
{
foreach (var attachmentResponse in getAttachmentsResponse)
{
attachmentNames.Add(attachmentResponse.Attachment.Name);

// Write the content of each attachment to a stream.


if (attachmentResponse.Attachment is FileAttachment)
{
FileAttachment fileAttachment = attachmentResponse.Attachment as
FileAttachment;
Stream s = new MemoryStream(fileAttachment.Content);
// Process the contents of the attachment here.
}

if (attachmentResponse.Attachment is ItemAttachment)
{
ItemAttachment itemAttachment = attachmentResponse.Attachment as
ItemAttachment;
Stream s = new
MemoryStream(itemAttachment.Item.MimeContent.Content);
// Process the contents of the attachment here.
}

attachmentsProcessedCount++;
}
}

// Return the names and number of attachments processed for display


// in the add-in UI.
var response = new AttachmentSampleServiceResponse();
response.attachmentNames = attachmentNames.ToArray();
response.attachmentsProcessed = attachmentsProcessedCount;

return response;
}

Use EWS to get the attachments


If you use EWS in your remote service, you need to construct a GetAttachment SOAP
request to get the attachments from the Exchange server. The following code returns a
string that provides the SOAP request. The remote service uses the String.Format
method to insert the attachment ID for an attachment into the string.

C#

private const string GetAttachmentSoapRequest =


@"<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope xmlns:xsi=""https://www.w3.org/2001/XMLSchema-instance""
xmlns:xsd=""https://www.w3.org/2001/XMLSchema""
xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">
<soap:Header>
<t:RequestServerVersion Version=""Exchange2016"" />
</soap:Header>
<soap:Body>
<GetAttachment
xmlns=""http://schemas.microsoft.com/exchange/services/2006/messages""
xmlns:t=""http://schemas.microsoft.com/exchange/services/2006/types"">
<AttachmentShape/>
<AttachmentIds>
<t:AttachmentId Id=""{0}""/>
</AttachmentIds>
</GetAttachment>
</soap:Body>
</soap:Envelope>";

Finally, the following method does the work of using an EWS GetAttachment request to
get the attachments from the Exchange server. This implementation makes an individual
request for each attachment, and returns the count of attachments processed. Each
response is processed in a separate ProcessXmlResponse method, defined next.

C#

private AttachmentSampleServiceResponse
GetAttachmentsFromExchangeServerUsingEWS(AttachmentSampleServiceRequest
request)
{
var attachmentsProcessedCount = 0;
var attachmentNames = new List<string>();

foreach (var attachment in request.attachments)


{
// Prepare a web request object.
HttpWebRequest webRequest = WebRequest.CreateHttp(request.ewsUrl);
webRequest.Headers.Add("Authorization",
string.Format("Bearer {0}", request.attachmentToken));
webRequest.PreAuthenticate = true;
webRequest.AllowAutoRedirect = false;
webRequest.Method = "POST";
webRequest.ContentType = "text/xml; charset=utf-8";

// Construct the SOAP message for the GetAttachment operation.


byte[] bodyBytes = Encoding.UTF8.GetBytes(
string.Format(GetAttachmentSoapRequest, attachment.id));
webRequest.ContentLength = bodyBytes.Length;

Stream requestStream = webRequest.GetRequestStream();


requestStream.Write(bodyBytes, 0, bodyBytes.Length);
requestStream.Close();

// Make the request to the Exchange server and get the response.
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();

// If the response is okay, create an XML document from the response


// and process the request.
if (webResponse.StatusCode == HttpStatusCode.OK)
{
var responseStream = webResponse.GetResponseStream();

var responseEnvelope = XElement.Load(responseStream);

// After creating a memory stream containing the contents of the


// attachment, this method writes the XML document to the trace
output.
// Your service would perform it's processing here.
if (responseEnvelope != null)
{
var processResult = ProcessXmlResponse(responseEnvelope);
attachmentNames.Add(string.Format("{0} {1}", attachment.name,
processResult));

// Close the response stream.


responseStream.Close();
webResponse.Close();

}
// If the response is not OK, return an error message for the
// attachment.
else
{
var errorString = string.Format("Attachment \"{0}\" could not be
processed. " +
"Error message: {1}.", attachment.name,
webResponse.StatusDescription);
attachmentNames.Add(errorString);
}
attachmentsProcessedCount++;
}

// Return the names and number of attachments processed for display


// in the add-in UI.
var response = new AttachmentSampleServiceResponse();
response.attachmentNames = attachmentNames.ToArray();
response.attachmentsProcessed = attachmentsProcessedCount;

return response;
}

Each response from the GetAttachment operation is sent to the ProcessXmlResponse


method. This method checks the response for errors. If it doesn't find any errors, it
processes file attachments and item attachments. The ProcessXmlResponse method
performs the bulk of the work to process the attachment.

C#
// This method processes the response from the Exchange server.
// In your application the bulk of the processing occurs here.
private string ProcessXmlResponse(XElement responseEnvelope)
{
// First, check the response for web service errors.
var errorCodes = from errorCode in responseEnvelope.Descendants
("
{http://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode")
select errorCode;
// Return the first error code found.
foreach (var errorCode in errorCodes)
{
if (errorCode.Value != "NoError")
{
return string.Format("Could not process result. Error: {0}",
errorCode.Value);
}
}

// No errors found, proceed with processing the content.


// First, get and process file attachments.
var fileAttachments = from fileAttachment in responseEnvelope.Descendants
("
{http://schemas.microsoft.com/exchange/services/2006/types}FileAttachment")
select fileAttachment;
foreach(var fileAttachment in fileAttachments)
{
var fileContent = fileAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}Content");
var fileData = System.Convert.FromBase64String(fileContent.Value);
var s = new MemoryStream(fileData);
// Process the file attachment here.
}

// Second, get and process item attachments.


var itemAttachments = from itemAttachment in responseEnvelope.Descendants
("
{http://schemas.microsoft.com/exchange/services/2006/types}ItemAttachment")
select itemAttachment;
foreach(var itemAttachment in itemAttachments)
{
var message = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}Message");
if (message != null)
{
// Process a message here.
break;
}
var calendarItem = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}CalendarItem");
if (calendarItem != null)
{
// Process calendar item here.
break;
}
var contact = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}Contact");
if (contact != null)
{
// Process contact here.
break;
}
var task = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}Tontact");
if (task != null)
{
// Process task here.
break;
}
var meetingMessage = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}MeetingMessage");
if (meetingMessage != null)
{
// Process meeting message here.
break;
}
var meetingRequest = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}MeetingRequest");
if (meetingRequest != null)
{
// Process meeting request here.
break;
}
var meetingResponse = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}MeetingResponse")
;
if (meetingResponse != null)
{
// Process meeting response here.
break;
}
var meetingCancellation = itemAttachment.Element("
{http://schemas.microsoft.com/exchange/services/2006/types}MeetingCancellati
on");
if (meetingCancellation != null)
{
// Process meeting cancellation here.
break;
}
}

return string.Empty;
}

See also
Create Outlook add-ins for read forms
Explore the EWS Managed API, EWS, and web services in Exchange
Get started with EWS Managed API client applications
Outlook Add-in SSO
Create Outlook add-ins for compose
forms
Article • 05/20/2023

You can create compose add-ins, which are Outlook add-ins activated in compose
forms. In contrast with read add-ins (Outlook add-ins that are activated in read mode
when a user is viewing a message or appointment), compose add-ins are available in the
following user scenarios.

Composing a new message, meeting request, or appointment in a compose form.

Viewing or editing an existing appointment, or meeting item in which the user is


the organizer.

Composing an inline response message or replying to a message in a separate


compose form.

Editing a response (Accept, Tentative, or Decline) to a meeting request or meeting


item.

Proposing a new time for a meeting item.

Forwarding or replying to a meeting request or meeting item.

In each of these scenarios, any add-in command buttons defined by the add-in are
shown in compose form.

Types of add-ins available in compose mode


Compose add-ins are implemented as add-in commands. To activate add-ins for
composing email or meeting responses, add-ins include a
MessageComposeCommandSurface extension point element in the manifest. To activate
add-ins for composing or editing appointments or meetings where the user is the
organizer, add-ins include a AppointmentOrganizerCommandSurface extension point
element. For more information on manifests, see Office add-in manifests.
7 Note

Add-ins developed for servers or clients that don't support add-in commands use
activation rules in a Rule element contained in the OfficeApp element. Unless the
add-in is being specifically developed for older clients and servers, new add-ins
should use add-in commands.

Add-ins that use activation rules aren't supported in an add-in that uses a Unified
manifest for Microsoft 365 (preview).

API features available to compose add-ins


Add and remove attachments to an item in a compose form in Outlook
Get and set item data in a compose form in Outlook
Get, set, or add recipients when composing an appointment or message in
Outlook
Get or set the subject when composing an appointment or message in Outlook
Insert data in the body when composing an appointment or message in Outlook
Get or set the location when composing an appointment in Outlook
Get or set the time when composing an appointment in Outlook
Manage the sensitivity label of your message or appointment in compose mode
Manage the delivery date and time of a message

See also
Get Started with Outlook add-ins for Office
Manage an item's attachments in a
compose form in Outlook
Article • 03/28/2023

The Office JavaScript API provides several APIs you can use to manage an item's
attachments when the user is composing.

Attach a file or Outlook item


You can attach a file or Outlook item to a compose form by using the method that's
appropriate for the type of attachment.

addFileAttachmentAsync: Attach a file.


addFileAttachmentFromBase64Async: Attach a file using its Base64-encoded string.
addItemAttachmentAsync: Attach an Outlook item.

These are asynchronous methods, which means execution can go on without waiting for
the action to complete. Depending on the original location and size of the attachment
being added, the asynchronous call may take a while to complete.

If there are tasks that depend on the action to complete, you should carry out those
tasks in a callback function. This callback function is optional and is invoked when the
attachment upload has completed. The callback function takes an AsyncResult object as
an output parameter that provides any status, error, and returned value from adding the
attachment. If the callback requires any extra parameters, you can specify them in the
optional options.asyncContext parameter. options.asyncContext can be of any type
that your callback function expects.

For example, you can define options.asyncContext as a JSON object that contains one
or more key-value pairs. You can find more examples about passing optional parameters
to asynchronous methods in the Office Add-ins platform in Asynchronous programming
in Office Add-ins. The following example shows how to use the asyncContext parameter
to pass two arguments to a callback function.

JavaScript

const options = { asyncContext: { var1: 1, var2: 2}};

Office.context.mailbox.item.addFileAttachmentAsync('https://contoso.com/rtm/
icon.png', 'icon.png', options, callback);
You can check for success or error of an asynchronous method call in the callback
function using the status and error properties of the AsyncResult object. If the
attaching completes successfully, you can use the AsyncResult.value property to get
the attachment ID. The attachment ID is an integer which you can subsequently use to
remove the attachment.

7 Note

The attachment ID is valid only within the same session and isn't guaranteed to
map to the same attachment across sessions. Examples of when a session is over
include when the user closes the add-in, or if the user starts composing in an inline
form and subsequently pops out the inline form to continue in a separate window.

 Tip

There are limits to the files or Outlook items you can attach to a mail item, such as
the number of attachments and their size. For further guidance, see Limits for
JavaScript API.

Attach a file
You can attach a file to a message or appointment in a compose form by using the
addFileAttachmentAsync method and specifying the URI of the file. You can also use the
addFileAttachmentFromBase64Async method, specifying the Base64-encoded string as

input. If the file is protected, you can include an appropriate identity or authentication
token as a URI query string parameter. Exchange will make a call to the URI to get the
attachment, and the web service which protects the file will need to use the token as a
means of authentication.

The following JavaScript example is a compose add-in that attaches a file, picture.png,
from a web server to the message or appointment being composed. The callback
function takes asyncResult as a parameter, checks for the result status, and gets the
attachment ID if the method succeeds.

JavaScript

Office.initialize = function () {
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
// Add the specified file attachment to the item
// being composed.
// When the attachment finishes uploading, the
// callback function is invoked and gets the attachment ID.
// You can optionally pass any object that you would
// access in the callback function as an argument to
// the asyncContext parameter.
Office.context.mailbox.item.addFileAttachmentAsync(
`https://webserver/picture.png`,
'picture.png',
{ asyncContext: null },
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed){
write(asyncResult.error.message);
} else {
// Get the ID of the attached file.
const attachmentID = asyncResult.value;
write('ID of added attachment: ' + attachmentID);
}
});
});
}

// Writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

To add an inline base64 image to the body of a message or appointment being


composed, you must first get the current item body using the
Office.context.mailbox.item.body.getAsync method before inserting the image using
the addFileAttachmentFromBase64Async method. Otherwise, the image will not render in
the body once it's inserted. For guidance, see the following JavaScript example, which
adds an inline Base64 image to the beginning of an item body.

JavaScript

const mailItem = Office.context.mailbox.item;


const base64String =

"iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAAXNSR0IArs4c6QAAAARnQU1BAAC
xjwv8YQUAAAAnUExURQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN0S+bU
AAAAMdFJOUwAQIDBAUI+fr7/P7yEupu8AAAAJcEhZcwAADsMAAA7DAcdvqGQAAAF8SURBVGhD7df
LdoMwDEVR6Cspzf9/b20QYOthS5Zn0Z2kVdY6O2WULrFYLBaLxd5ur4mDZD14b8ogWS/dtxV+dmx
9ysA2QUj9TQRWv5D7HyKwuIW9n0vc8tkpHP0W4BOg3wQ8wtlvA+PC1e8Ao8Ld7wFjQtHvAiNC2e8
DdqHqKwCrUPc1gE1AfRVgEXBfB+gF0lcCWoH2tYBOYPpqQCNwfT3QF9i+AegJfN8CtAWhbwJagtS
3AbIg9o2AJMh9M5C+SVGBvx6zAfmT0r+Bv8JMwP4kyFPir+cswF5KL3WLv14zAFBCLf56Tw9cpar
FX4upgaJUtPhrOS1QlY5W+vWTXrGgBFB/b72ev3/0igUdQPppP/nfowfKUUEFcP207y/yxKmgAYQ
+PywoAFOfCH3A2MdCFzD3kdADBvq10AGG+pXQBgb7pdAEhvuF0AIc/VtoAK7+JciAs38KIuDugyA
C/v4hiMCE/i7IwLRBsh68N2WQjMVisVgs9i5bln8LGScNcCrONQAAAABJRU5ErkJggg==";

// Get the current body of the message or appointment.


mailItem.body.getAsync(Office.CoercionType.Html, (bodyResult) => {
if (bodyResult.status === Office.AsyncResultStatus.Succeeded) {
// Insert the Base64 image to the beginning of the body.
const options = { isInline: true, asyncContext: bodyResult.value };
mailItem.addFileAttachmentFromBase64Async(base64String, "sample.png",
options, (attachResult) => {
if (attachResult.status === Office.AsyncResultStatus.Succeeded) {
let body = attachResult.asyncContext;
body = body.replace("<p class=MsoNormal>", `<p class=MsoNormal><img
src="cid:sample.png">`);
mailItem.body.setAsync(body, { coercionType:
Office.CoercionType.Html }, (setResult) => {
if (setResult.status === Office.AsyncResultStatus.Succeeded) {
console.log("Inline Base64 image added to the body.");
} else {
console.log(setResult.error.message);
}
});
} else {
console.log(attachResult.error.message);
}
});
} else {
console.log(bodyResult.error.message);
}
});

Attach an Outlook item


You can attach an Outlook item (for example, email, calendar, or contact item) to a
message or appointment in a compose form by specifying the Exchange Web Services
(EWS) ID of the item and using the addItemAttachmentAsync method. You can get the
EWS ID of an email, calendar, contact, or task item in the user's mailbox by using the
mailbox.makeEwsRequestAsync method and accessing the EWS operation FindItem. The
item.itemId property also provides the EWS ID of an existing item in a read form.

The following JavaScript function, addItemAttachment , extends the first example above,
and adds an item as an attachment to the email or appointment that is being
composed. The function takes as an argument the EWS ID of the item that is to be
attached. If attaching succeeds, it gets the attachment ID for further processing,
including removing that attachment in the same session.

JavaScript

// Adds the specified item as an attachment to the composed item.


// ID is the EWS ID of the item to be attached.
function addItemAttachment(itemId) {
// When the attachment finishes uploading, the
// callback function is invoked. Here, the callback
// function uses only asyncResult as a parameter,
// and if the attaching succeeds, gets the attachment ID.
// You can optionally pass any other object you wish to
// access in the callback function as an argument to
// the asyncContext parameter.
Office.context.mailbox.item.addItemAttachmentAsync(
itemId,
'Welcome email',
{ asyncContext: null },
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed){
write(asyncResult.error.message);
} else {
const attachmentID = asyncResult.value;
write('ID of added attachment: ' + attachmentID);
}
});
}

7 Note

You can use a compose add-in to attach an instance of a recurring appointment in


Outlook on the web or on mobile devices. However, in a supporting Outlook
desktop client, attempting to attach an instance would result in attaching the
recurring series (the parent appointment).

Get attachments
APIs to get attachments in compose mode are available from requirement set 1.8.

getAttachmentsAsync
getAttachmentContentAsync

You can use the getAttachmentsAsync method to get the attachments of the message
or appointment being composed.

To get an attachment's content, you can use the getAttachmentContentAsync method.


The supported formats are listed in the AttachmentContentFormat enum.

You should provide a callback function to check for the status and any error by using the
AsyncResult output parameter object. You can also pass any additional parameters to

the callback function by using the optional asyncContext parameter.


The following JavaScript example gets the attachments and allows you to set up distinct
handling for each supported attachment format.

JavaScript

const item = Office.context.mailbox.item;


const options = {asyncContext: {currentItem: item}};
item.getAttachmentsAsync(options, callback);

function callback(result) {
if (result.value.length > 0) {
for (let i = 0 ; i < result.value.length ; i++) {

result.asyncContext.currentItem.getAttachmentContentAsync(result.value[i].id
, handleAttachmentsCallback);
}
}
}

function handleAttachmentsCallback(result) {
// Parse string to be a url, an .eml file, a Base64-encoded string, or an
.icalendar file.
switch (result.value.format) {
case Office.MailboxEnums.AttachmentContentFormat.Base64:
// Handle file attachment.
break;
case Office.MailboxEnums.AttachmentContentFormat.Eml:
// Handle email item attachment.
break;
case Office.MailboxEnums.AttachmentContentFormat.ICalendar:
// Handle .icalender attachment.
break;
case Office.MailboxEnums.AttachmentContentFormat.Url:
// Handle cloud attachment.
break;
default:
// Handle attachment formats that are not supported.
}
}

Remove an attachment
You can remove a file or item attachment from a message or appointment item in a
compose form by specifying the corresponding attachment ID when using the
removeAttachmentAsync method.

) Important
If you're using requirement set 1.7 or earlier, you should only remove attachments
that the same add-in has added in the same session.

Similar to the addFileAttachmentAsync , addItemAttachmentAsync , and


getAttachmentsAsync methods, removeAttachmentAsync is an asynchronous method. You
should provide a callback function to check for the status and any error by using the
AsyncResult output parameter object. You can also pass any additional parameters to

the callback function by using the optional asyncContext parameter.

The following JavaScript function, removeAttachment , continues to extend the examples


above, and removes the specified attachment from the email or appointment that is
being composed. The function takes as an argument the ID of the attachment to be
removed. You can obtain the ID of an attachment after a successful
addFileAttachmentAsync , addFileAttachmentFromBase64Async , or addItemAttachmentAsync
method call, and use it in a subsequent removeAttachmentAsync method call. You can
also call getAttachmentsAsync (introduced in requirement set 1.8) to get the attachments
and their IDs for that add-in session.

JavaScript

// Removes the specified attachment from the composed item.


function removeAttachment(attachmentId) {
// When the attachment is removed, the callback function is invoked.
// Here, the callback function uses an asyncResult parameter and
// gets the ID of the removed attachment if the removal succeeds.
// You can optionally pass any object you wish to access in the
// callback function as an argument to the asyncContext parameter.
Office.context.mailbox.item.removeAttachmentAsync(
attachmentId,
{ asyncContext: null },
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
} else {
write('Removed attachment with the ID: ' +
asyncResult.value);
}
});
}

See also
Create Outlook add-ins for compose forms
Asynchronous programming in Office Add-ins
Limits for activation and JavaScript API for Outlook add-ins
Get and set item data in a compose
form in Outlook
Article • 10/04/2022

Learn how to get or set various properties of an item in an Outlook add-in in a compose
scenario, including its recipients, subject, body, and appointment location and time.

Getting and setting item properties for a


compose add-in
In a compose form, you can get most of the properties that are exposed on the same
kind of item as in a read form (such as attendees, recipients, subject, and body), and you
can get a few extra properties that are relevant in only a compose form but not a read
form (body, bcc).

For most of these properties, because it's possible that an Outlook add-in and the user
can be modifying the same property in the user interface at the same time, the methods
to get and set them are asynchronous. Table 1 lists the item-level properties and
corresponding asynchronous methods to get and set them in a compose form. The
item.itemType and item.conversationId properties are exceptions because users cannot
modify them. You can programmatically get them the same way in a compose form as in
a read form, directly from the parent object.

Other than accessing item properties in the Office JavaScript API, you can access item-
level properties using Exchange Web Services (EWS). With the read/write mailbox
permission, you can use the mailbox.makeEwsRequestAsync method to access EWS
operations, GetItem and UpdateItem, to get and set more properties of an item or items
in the user's mailbox.

The makeEwsRequestAsync method is available in both compose and read forms. For
more information about the read/write mailbox permission, and accessing EWS through
the Office Add-ins platform, see Understanding Outlook add-in permissions and Call
web services from an Outlook add-in.

Table 1. Asynchronous methods to get or set item properties in a compose form

Property Property Asynchronous Asynchronous methods to set


type method to get

bcc Recipients Recipients.getAsync Recipients.addAsync, Recipients.setAsync


Property Property Asynchronous Asynchronous methods to set
type method to get

body Body Body.getAsync Body.prependAsync, Body.setAsync,


Body.setSelectedDataAsync

cc Recipients Recipients.getAsync Recipients.addAsync Recipients.setAsync

end Time Time.getAsync Time.setAsync

location Location Location.getAsync Location.setAsync

optionalAttendees Recipients Recipients.getAsync Recipients.addAsync Recipients.setAsync

requiredAttendees Recipients Recipients.getAsync Recipients.addAsync Recipients.setAsync

start Time Time.getAsync Time.setAsync

subject Subject Subject.getAsync Subject.setAsync

to Recipients Recipients.getAsync Recipients.addAsync Recipients.setAsync

See also
Create Outlook add-ins for compose forms
Understanding Outlook add-in permissions
Call web services from an Outlook add-in
Get and set Outlook item data in read or compose forms
Get, set, or add recipients when
composing an appointment or message
in Outlook
Article • 08/15/2023

The Office JavaScript API provides asynchronous methods (Recipients.getAsync,


Recipients.setAsync, or Recipients.addAsync) to respectively get, set, or add recipients to
a compose form of an appointment or message. These asynchronous methods are
available to only compose add-ins. To use these methods, make sure you have set up
the add-in manifest appropriately for Outlook to activate the add-in in compose forms,
as described in Create Outlook add-ins for compose forms.

Some of the properties that represent recipients in an appointment or message are


available for read access in a compose form and in a read form. These properties include
optionalAttendees and requiredAttendees for appointments, and cc, and to for
messages.

In a read form, you can access the property directly from the parent object, such as:

JavaScript

Office.context.mailbox.item.cc;

But in a compose form, because both the user and your add-in can be inserting or
changing a recipient at the same time, you must use the asynchronous method
getAsync to get these properties, as in the following example.

JavaScript

Office.context.mailbox.item.cc.getAsync(callback);

These properties are available for write access in only compose forms, and not read
forms.

As with most asynchronous methods in the JavaScript API for Office, getAsync ,
setAsync , and addAsync take optional input parameters. For more information on how

to specify these optional input parameters, see "Passing optional parameters to


asynchronous methods" in Asynchronous programming in Office Add-ins.
Get recipients
This section shows a code sample that gets the recipients of the appointment or
message that is being composed, and displays the email addresses of the recipients.

In the Office JavaScript API, because the properties that represent the recipients of an
appointment ( optionalAttendees and requiredAttendees ) are different from those of a
message (bcc, cc , and to ), you should first use the item.itemType property to identify
whether the item being composed is an appointment or message. In compose mode, all
these properties of appointments and messages are Recipients objects, so you can then
call the asynchronous method, Recipients.getAsync , to get the corresponding
recipients.

To use getAsync , provide a callback function to check for the status, results, and any
error returned by the asynchronous getAsync call. The callback function returns an
asyncResult output parameter. Use its status and error properties to check for the
status and any error messages of the asynchronous call, and its value property to get
the actual recipients. Recipients are represented as an array of EmailAddressDetails
objects. You can also provide additional information to the callback function using the
optional asyncContext parameter in the getAsync call.

Note that because the getAsync method is asynchronous, if there are subsequent
actions that depend on successfully getting the recipients, you should organize your
code to start such actions only in the corresponding callback function when the
asynchronous call has successfully completed.

) Important

The getAsync method only returns recipients resolved by the Outlook client. A
resolved recipient has the following characteristics.

If the recipient has a saved entry in the sender's address book, Outlook
resolves the email address to the recipient's saved display name.
A Teams meeting status icon appears before the recipient's name or email
address.
A semicolon appears after the recipient's name or email address.
The recipient's name or email address is underlined or enclosed in a box.

To resolve an email address once it's added to a mail item, the sender must use the
Tab key or select a suggested contact or email address from the auto-complete list.
In Outlook on the web and on Windows, if a user creates a new message by
selecting a contact's email address link from a contact or profile card, they must
first resolve the email address so that it can be included in the results of the
getAsync call.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
getAllRecipients();
}
});

// Gets the email addresses of all the recipients of the item being
composed.
function getAllRecipients() {
let toRecipients, ccRecipients, bccRecipients;

// Verify if the mail item is an appointment or message.


if (item.itemType === Office.MailboxEnums.ItemType.Appointment) {
toRecipients = item.requiredAttendees;
ccRecipients = item.optionalAttendees;
}
else {
toRecipients = item.to;
ccRecipients = item.cc;
bccRecipients = item.bcc;
}

// Get the recipients from the To or Required field of the item being
composed.
toRecipients.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
return;
}

// Display the email addresses of the recipients or attendees.


write(`Recipients in the To or Required field:
${displayAddresses(asyncResult.value)}`);
});

// Get the recipients from the Cc or Optional field of the item being
composed.
ccRecipients.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
return;
}

// Display the email addresses of the recipients or attendees.


write(`Recipients in the Cc or Optional field:
${displayAddresses(asyncResult.value)}`);
});

// Get the recipients from the Bcc field of the message being composed,
if applicable.
if (bccRecipients.length > 0) {
bccRecipients.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
return;
}

// Display the email addresses of the recipients.


write(`Recipients in the Bcc field:
${displayAddresses(asyncResult.value)}`);
});
} else {
write("Recipients in the Bcc field: None");
}
}

// Displays the email address of each recipient.


function displayAddresses (recipients) {
for (let i = 0; i < recipients.length; i++) {
write(recipients[i].emailAddress);
}
}

// Writes to a div with id="message" on the page.


function write(message) {
document.getElementById("message").innerText += message;
}

Set recipients
This section shows a code sample that sets the recipients of the appointment or
message that is being composed by the user. Setting recipients overwrites any existing
recipients. This example first verifies if the mail item is an appointment or message, so
that it can call the asynchronous method, Recipients.setAsync , on the appropriate
properties that represent recipients of the appointment or message.

When calling setAsync , provide an array as the input argument for the recipients
parameter, in one of the following formats.

An array of strings that are SMTP addresses.


An array of dictionaries, each containing a display name and email address, as
shown in the following code sample.
An array of EmailAddressDetails objects, similar to the one returned by the
getAsync method.

You can optionally provide a callback function as an input argument to the setAsync
method, to make sure any code that depends on successfully setting the recipients
would execute only when that happens. If you implement a callback function, use the
status and error properties of the asyncResult output parameter to check the status

and any error messages of the asynchronous call. To provide additional information to
the callback function, use the optional asyncContext parameter in the setAsync call.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
setRecipients();
}
});

// Sets the recipients of the item being composed.


function setRecipients() {
let toRecipients, ccRecipients, bccRecipients;

// Verify if the mail item is an appointment or message.


if (item.itemType === Office.MailboxEnums.ItemType.Appointment) {
toRecipients = item.requiredAttendees;
ccRecipients = item.optionalAttendees;
}
else {
toRecipients = item.to;
ccRecipients = item.cc;
bccRecipients = item.bcc;
}

// Set the recipients in the To or Required field of the item being


composed.
toRecipients.setAsync(
[{
"displayName": "Graham Durkin",
"emailAddress": "graham@contoso.com"
},
{
"displayName": "Donnie Weinberg",
"emailAddress": "donnie@contoso.com"
}],
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("Successfully set the recipients in the To or


Required field.");
// Run additional operations appropriate to your scenario.
});

// Set the recipients in the Cc or Optional field of the item being


composed.
ccRecipients.setAsync(
[{
"displayName": "Perry Horning",
"emailAddress": "perry@contoso.com"
},
{
"displayName": "Guy Montenegro",
"emailAddress": "guy@contoso.com"
}],
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("Successfully set the recipients in the Cc or


Optional field.");
// Run additional operations appropriate to your scenario.
});

// Set the recipients in the Bcc field of the message being composed.
if (bccRecipients) {
bccRecipients.setAsync(
[{
"displayName": "Lewis Cate",
"emailAddress": "lewis@contoso.com"
},
{
"displayName": "Francisco Stitt",
"emailAddress": "francisco@contoso.com"
}],
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed)
{
console.log(asyncResult.error.message);
return;
}

console.log("Successfully set the recipients in the Bcc


field.");
// Run additional operations appropriate to your scenario.
});
}
}

Add recipients
If you don't want to overwrite any existing recipients in an appointment or message,
instead of using Recipients.setAsync , use the Recipients.addAsync asynchronous
method to append recipients. addAsync works similarly as setAsync in that it requires a
recipients input argument. You can optionally provide a callback function, and any

arguments for the callback using the asyncContext parameter. Then, check the status,
result, and any error of the asynchronous addAsync call using the asyncResult output
parameter of the callback function. The following example checks if the item being
composed is an appointment, then appends two required attendees to it.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
addAttendees();
}
});

// Adds the specified recipients as required attendees of the appointment.


function addAttendees() {
if (item.itemType === Office.MailboxEnums.ItemType.Appointment) {
item.requiredAttendees.addAsync(
[{
"displayName": "Kristie Jensen",
"emailAddress": "kristie@contoso.com"
},
{
"displayName": "Pansy Valenzuela",
"emailAddress": "pansy@contoso.com"
}],
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("Successfully added the required attendees.");


// Run additional operations appropriate to your scenario.
});
}
}

See also
Get and set item data in a compose form in Outlook
Get and set Outlook item data in read or compose forms
Create Outlook add-ins for compose forms
Asynchronous programming in Office Add-ins
Get or set the subject when composing an appointment or message in Outlook
Insert data in the body when composing an appointment or message in Outlook
Get or set the location when composing an appointment in Outlook
Get or set the time when composing an appointment in Outlook
Insert data in the body when composing
an appointment or message in Outlook
Article • 08/15/2023

Use the asynchronous methods (Body.getAsync, Body.getTypeAsync,


Body.prependAsync, Body.setAsync and Body.setSelectedDataAsync) to get the body
type and insert data in the body of an appointment or message being composed. These
asynchronous methods are only available to compose add-ins. To use these methods,
make sure you have set up the add-in manifest appropriately so that Outlook activates
your add-in in compose forms, as described in Create Outlook add-ins for compose
forms.

In Outlook, a user can create a message in text, HTML, or Rich Text Format (RTF), and
can create an appointment in HTML format. Before inserting data, you must first verify
the supported item format by calling getTypeAsync , as you may need to take additional
steps. The value that getTypeAsync returns depends on the original item format, as well
as the support of the device operating system and application to edit in HTML format.
Once you've verified the item format, set the coercionType parameter of prependAsync
or setSelectedDataAsync accordingly to insert the data, as shown in the following table.
If you don't specify an argument, prependAsync and setSelectedDataAsync assume the
data to insert is in text format.

Data to insert Item format returned by getTypeAsync coercionType to use

Text Text1 Text

HTML Text1 Text2

Text HTML Text/HTML

HTML HTML HTML

7 Note

1 On tablets and smartphones, getTypeAsync returns "Text" if the operating system


or application doesn't support editing an item, which was originally created in
HTML, in HTML format.

2
If your data to insert is HTML and getTypeAsync returns a text type for the current
mail item, you must reorganize your data as text and set coercionType to
Office.CoercionType.Text . If you simply insert the HTML data into a text-formatted
item, the application displays the HTML tags as text. If you attempt to insert the
HTML data and set coercionType to Office.CoercionType.Html , you'll get an error.

In addition to the coercionType parameter, as with most asynchronous methods in the


Office JavaScript API, getTypeAsync , prependAsync , and setSelectedDataAsync take other
optional input parameters. For more information on how to specify these optional input
parameters, see "Passing optional parameters to asynchronous methods" in
Asynchronous programming in Office Add-ins.

Insert data at the current cursor position


This section shows a code sample that uses getTypeAsync to verify the body type of the
item that is being composed, and then uses setSelectedDataAsync to insert data at the
current cursor location.

You must pass a data string as an input parameter to setSelectedDataAsync . Depending


on the type of the item body, you can specify this data string in text or HTML format
accordingly. As mentioned earlier, you can optionally specify the type of the data to be
inserted in the coercionType parameter. To get the status and results of
setSelectedDataAsync , pass a callback function and optional input parameters to the

method, then extract the needed information from the asyncResult output parameter of
the callback. If the method succeeds, you can get the type of the item body from the
asyncResult.value property, which is either "text" or "html".

If the user hasn't placed the cursor in the item body, setSelectedDataAsync inserts the
data at the top of the body. If the user has selected text in the item body,
setSelectedDataAsync replaces the selected text with the data you specify. Note that

setSelectedDataAsync can fail if the user simultaneously changes the cursor position

while composing the item. The maximum number of characters you can insert at one
time is 1,000,000 characters.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
setItemBody();
}
});
// Inserts data at the current cursor position.
function setItemBody() {
// Identify the body type of the mail item.
item.body.getTypeAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

// Insert data of the appropriate type into the body.


if (asyncResult.value === Office.CoercionType.Html) {
// Insert HTML into the body.
item.body.setSelectedDataAsync(
"<b> Kindly note we now open 7 days a week.</b>",
{ coercionType: Office.CoercionType.Html, asyncContext: {
optionalVariable1: 1, optionalVariable2: 2 } },
(asyncResult) => {
if (asyncResult.status ===
Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

/*
Run additional operations appropriate to your scenario
and
use the optionalVariable1 and optionalVariable2 values
as needed.
*/
});
}
else {
// Insert plain text into the body.
item.body.setSelectedDataAsync(
"Kindly note we now open 7 days a week.",
{ coercionType: Office.CoercionType.Text, asyncContext: {
optionalVariable1: 1, optionalVariable2: 2 } },
(asyncResult) => {
if (asyncResult.status ===
Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

/*
Run additional operations appropriate to your scenario
and
use the optionalVariable1 and optionalVariable2 values
as needed.
*/
});
}
});
}
Insert data at the beginning of the item body
Alternatively, you can use prependAsync to insert data at the beginning of the item body
and disregard the current cursor location. Other than the point of insertion,
prependAsync and setSelectedDataAsync behave in similar ways. You must first check the

type of the message body to avoid prepending HTML data to a message in text format.
Then, pass the data string to be prepended in either text or HTML format to
prependAsync . The maximum number of characters you can prepend at one time is

1,000,000 characters.

The following JavaScript code first calls getTypeAsync to verify the type of the item body.
Then, depending on the type, it inserts the data as HTML or text to the top of the body.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
prependItemBody();
}
});

// Prepends data to the body of the item being composed.


function prependItemBody() {
// Identify the body type of the mail item.
item.body.getTypeAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

// Prepend data of the appropriate type to the body.


if (asyncResult.value === Office.CoercionType.Html) {
// Prepend HTML to the body.
item.body.prependAsync(
'<b>Greetings!</b>',
{ coercionType: Office.CoercionType.Html, asyncContext: {
optionalVariable1: 1, optionalVariable2: 2 } },
(asyncResult) => {
if (asyncResult.status ===
Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

/*
Run additional operations appropriate to your scenario
and
use the optionalVariable1 and optionalVariable2 values
as needed.
*/
});
}
else {
// Prepend plain text to the body.
item.body.prependAsync(
'Greetings!',
{ coercionType: Office.CoercionType.Text, asyncContext: {
optionalVariable1: 1, optionalVariable2: 2 } },
(asyncResult) => {
if (asyncResult.status ===
Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

/*
Run additional operations appropriate to your scenario
and
use the optionalVariable1 and optionalVariable2 values
as needed.
*/
});
}
});
}

See also
Get and set item data in a compose form in Outlook
Get and set Outlook item data in read or compose forms
Create Outlook add-ins for compose forms
Asynchronous programming in Office Add-ins
Get, set, or add recipients when composing an appointment or message in
Outlook
Get or set the subject when composing an appointment or message in Outlook
Get or set the location when composing an appointment in Outlook
Get or set the time when composing an appointment in Outlook
Get or set the subject when composing
an appointment or message in Outlook
Article • 08/15/2023

The Office JavaScript API provides asynchronous methods (subject.getAsync and


subject.setAsync) to get and set the subject of an appointment or message that the user
is composing. These asynchronous methods are available only to compose add-ins. To
use these methods, make sure you have set up the add-in XML manifest appropriately
for Outlook to activate the add-in in compose forms.

The subject property is available for read access in both compose and read forms of
appointments and messages. In a read form, access the property directly from the
parent object, as in:

JavaScript

Office.context.mailbox.item.subject;

But in a compose form, because both the user and your add-in can be inserting or
changing the subject at the same time, you must use the getAsync method to get the
subject asynchronously.

JavaScript

Office.context.mailbox.item.subject.getAsync(callback);

The subject property is available for write access in only compose forms and not in
read forms.

 Tip

To temporarily set the content displayed in the subject of a message in read mode,
use Office.context.mailbox.item.display.subject (preview).

As with most asynchronous methods in the Office JavaScript API, getAsync and
setAsync take optional input parameters. For more information on how to specify these

optional input parameters, see "Passing optional parameters to asynchronous methods"


in Asynchronous programming in Office Add-ins.
Get the subject
This section shows a code sample that gets the subject of the appointment or message
that the user is composing, and displays the subject.

To use item.subject.getAsync , provide a callback function that checks for the status and
result of the asynchronous call. You can provide any necessary arguments to the
callback function through the optional asyncContext parameter. To obtain the status,
results, and any error from the callback function, use the asyncResult output parameter
of the callback. If the asynchronous call is successful, use the AsyncResult.value property
to get the subject as a plain text string.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
getSubject();
}
});

// Gets the subject of the item that the user is composing.


function getSubject() {
item.subject.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
return;
}

// Display the subject on the page.


write(`The subject is: ${asyncResult.value}`);
});
}

// Writes to a div with id="message" on the page.


function write(message) {
document.getElementById("message").innerText += message;
}

Set the subject


This section shows a code sample that sets the subject of the appointment or message
that the user is composing.
To use item.subject.setAsync , specify a string of up to 255 characters in the data
parameter. Optionally, you can provide a callback function and any arguments for the
callback function in the asyncContext parameter. Check the callback status, result, and
any error message in the asyncResult output parameter of the callback. If the
asynchronous call is successful, setAsync inserts the specified subject string as plain
text, overwriting any existing subject for that item.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
setSubject();
}
});

// Sets the subject of the item that the user is composing.


function setSubject() {
// Customize the subject with today's date.
const today = new Date();
const subject = `Summary for ${today.toLocaleDateString()}`;

item.subject.setAsync(
subject,
{ asyncContext: { optionalVariable1: 1, optionalVariable2: 2 } },
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
return;
}

/*
The subject was successfully set.
Run additional operations appropriate to your scenario and
use the optionalVariable1 and optionalVariable2 values as
needed.
*/
});
}

// Writes to a div with id="message" on the page.


function write(message) {
document.getElementById("message").innerText += message;
}

See also
Get and set item data in a compose form in Outlook
Get and set Outlook item data in read or compose forms
Create Outlook add-ins for compose forms
Asynchronous programming in Office Add-ins
Get, set, or add recipients when composing an appointment or message in
Outlook
Insert data in the body when composing an appointment or message in Outlook
Get or set the location when composing an appointment in Outlook
Get or set the time when composing an appointment in Outlook
Get or set the time when composing an
appointment in Outlook
Article • 08/15/2023

The Office JavaScript API provides asynchronous methods (Time.getAsync and


Time.setAsync) to get and set the start or end time of an appointment being composed.
These asynchronous methods are available only to compose add-ins. To use these
methods, make sure you have set up the XML manifest of the add-in appropriately for
Outlook to activate the add-in in compose forms, as described in Create Outlook add-
ins for compose forms.

The start and end properties are available for appointments in both compose and read
forms. In a read form, you can access the properties directly from the parent object, as
in:

JavaScript

Office.context.mailbox.item.start;
Office.context.mailbox.item.end;

But in a compose form, because both the user and your add-in can be inserting or
changing the time at the same time, you must use the getAsync asynchronous method
to get the start or end time.

JavaScript

Office.context.mailbox.item.start.getAsync(callback);
Office.context.mailbox.item.end.getAsync(callback);

As with most asynchronous methods in the Office JavaScript API, getAsync and
setAsync take optional input parameters. For more information on how to specify these

optional input parameters, see "Passing optional parameters to asynchronous methods"


in Asynchronous programming in Office Add-ins.

Get the start or end time


This section shows a code sample that gets the start time of the appointment being
composed and displays the time. You can use the same code, but replace the start
property with the end property to get the end time.
To use the item.start.getAsync or item.end.getAsync methods, provide a callback
function that checks the status and result of the asynchronous call. Obtain the status,
results, and any error using the asyncResult output parameter of the callback. If the
asynchronous call is successful, use the asyncResult.value property to get the start time
as a Date object in UTC format. To provide any necessary arguments to the callback
function, use the asyncContext optional parameter of the getAsync call.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
getStartTime();
}
});

// Gets the start time of the appointment being composed.


function getStartTime() {
item.start.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
return;
}

// Display the start time in UTC format on the page.


write(`The start time in UTC is: ${asyncResult.value.toString()}`);
// Convert the start time to local time and display it on the page.
write(`The start time in local time is:
${asyncResult.value.toLocaleString()}`);
});
}

// Writes to a div with id="message" on the page.


function write(message) {
document.getElementById("message").innerText += message;
}

Set the start or end time


This section shows a code sample that sets the start time of an appointment being
composed. You can use the same code, but replace the start property with the end
property to set the end time. Note that changes to the start or end properties may
affect other properties of the appointment being composed.
If the appointment being composed already has an existing start time, setting the
start time subsequently adjusts the end time to maintain any previous duration of
the appointment.
If the appointment being composed already has an existing end time, setting the
end time subsequently adjusts both the duration and end time.
If the appointment has been set as an all-day event, setting the start time adjusts
the end time to 24 hours later, and clears the checkbox for the all-day event in the
appointment.

To use item.start.setAsync or item.end.setAsync , specify a UTC-formatted Date object


in the dateTime parameter. If you get a date based on an input by the user in the client,
you can use mailbox.convertToUtcClientTime to convert the value to a Date object in
the UTC format. If you provide an optional callback function, include the asyncContext
parameter and add any arguments to it. Additionally, check the status, result, and any
error message through the asyncResult output parameter of the callback. If the
asynchronous call is successful, setAsync inserts the specified start or end time string as
plain text, overwriting any existing start or end time for that item.

7 Note

In Outlook on Windows, the setAsync method can't be used to change the start or
end time of a recurring appointment.

JavaScript

let item;

// Confirms that the Office.js library is loaded.


Office.onReady((info) => {
if (info.host === Office.HostType.Outlook) {
item = Office.context.mailbox.item;
setStartTime();
}
});

// Sets the start time of the appointment being composed.


function setStartTime() {
// Get the current date and time, then add two days to the date.
const startDate = new Date();
startDate.setDate(startDate.getDate() + 2);

item.start.setAsync(
startDate,
{ asyncContext: { optionalVariable1: 1, optionalVariable2: 2 } },
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("Successfully set the start time.");


/*
Run additional operations appropriate to your scenario and
use the optionalVariable1 and optionalVariable2 values as
needed.
*/
});
}

See also
Get and set item data in a compose form in Outlook
Get and set Outlook item data in read or compose forms
Create Outlook add-ins for compose forms
Asynchronous programming in Office Add-ins
Get, set, or add recipients when composing an appointment or message in
Outlook
Get or set the subject when composing an appointment or message in Outlook
Insert data in the body when composing an appointment or message in Outlook
Get or set the location when composing an appointment in Outlook
Get or set the location when composing
an appointment in Outlook
Article • 03/21/2023

The Office JavaScript API provides properties and methods to manage the location of an
appointment that the user is composing. Currently, there are two properties that
provide an appointment's location:

item.location: Basic API that allows you to get and set the location.
item.enhancedLocation: Enhanced API that allows you to get and set the location,
and includes specifying the location type. The type is LocationType.Custom if you
set the location using item.location .

The following table lists the location APIs and the modes (i.e., Compose or Read) where
they are available.

API Applicable appointment modes

item.location Attendee/Read

item.location.getAsync Organizer/Compose

item.location.setAsync Organizer/Compose

item.enhancedLocation.getAsync Organizer/Compose,
Attendee/Read

item.enhancedLocation.addAsync Organizer/Compose

item.enhancedLocation.removeAsync Organizer/Compose

To use the methods that are available only to compose add-ins, configure the add-in
XML manifest to activate the add-in in Organizer/Compose mode. See Create Outlook
add-ins for compose forms for more details. Activation rules aren't supported in add-ins
that use a Unified manifest for Microsoft 365 (preview).

Use the enhancedLocation API


You can use the enhancedLocation API to get and set an appointment's location. The
location field supports multiple locations and, for each location, you can set the display
name, type, and conference room email address (if applicable). See LocationType for
supported location types.
Add location
The following example shows how to add a location by calling addAsync on
mailbox.item.enhancedLocation.

JavaScript

let item;
const locations = [
{
"id": "Contoso",
"type": Office.MailboxEnums.LocationType.Custom
}
];

Office.initialize = function () {
item = Office.context.mailbox.item;
// Check for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
// Add to the location of the item being composed.
item.enhancedLocation.addAsync(locations);
});
}

Get location
The following example shows how to get the location by calling getAsync on
mailbox.item.enhancedLocation.

JavaScript

let item;

Office.initialize = function () {
item = Office.context.mailbox.item;
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
// Get the location of the item being composed.
item.enhancedLocation.getAsync(callbackFunction);
});
}

function callbackFunction(asyncResult) {
asyncResult.value.forEach(function (place) {
console.log("Display name: " + place.displayName);
console.log("Type: " + place.locationIdentifier.type);
if (place.locationIdentifier.type ===
Office.MailboxEnums.LocationType.Room) {
console.log("Email address: " + place.emailAddress);
}
});
}

7 Note

Personal contact groups added as appointment locations aren't returned by the


enhancedLocation.getAsync method.

Remove location
The following example shows how to remove the location by calling removeAsync on
mailbox.item.enhancedLocation.

JavaScript

let item;

Office.initialize = function () {
item = Office.context.mailbox.item;
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
// Get the location of the item being composed.
item.enhancedLocation.getAsync(callbackFunction);
});
}

function callbackFunction(asyncResult) {
asyncResult.value.forEach(function (currentValue) {
// Remove each location from the item being composed.

item.enhancedLocation.removeAsync([currentValue.locationIdentifier]);
});
}

Use the location API


You can use the location API to get and set an appointment's location.

Get the location


This section shows a code sample that gets the location of the appointment that the
user is composing, and displays the location.

To use item.location.getAsync , provide a callback function that checks for the status
and result of the asynchronous call. You can provide any necessary arguments to the
callback function through the asyncContext optional parameter. You can obtain status,
results, and any error using the output parameter asyncResult of the callback. If the
asynchronous call is successful, you can get the location as a string using the
AsyncResult.value property.

JavaScript

let item;

Office.initialize = function () {
item = Office.context.mailbox.item;
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
// Get the location of the item being composed.
getLocation();
});
}

// Get the location of the item that the user is composing.


function getLocation() {
item.location.getAsync(
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed){
write(asyncResult.error.message);
}
else {
// Successfully got the location, display it.
write ('The location is: ' + asyncResult.value);
}
});
}

// Write to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

Set the location


This section shows a code sample that sets the location of the appointment that the
user is composing.
To use item.location.setAsync , specify a string of up to 255 characters in the data
parameter. Optionally, you can provide a callback function and any arguments for the
callback function in the asyncContext parameter. You should check the status, result,
and any error message in the asyncResult output parameter of the callback. If the
asynchronous call is successful, setAsync inserts the specified location string as plain
text, overwriting any existing location for that item.

7 Note

You can set multiple locations by using a semi-colon as the separator (e.g.,
'Conference room A; Conference room B').

JavaScript

let item;

Office.initialize = function () {
item = Office.context.mailbox.item;
// Check for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
// Set the location of the item being composed.
setLocation();
});
}

// Set the location of the item that the user is composing.


function setLocation() {
item.location.setAsync(
'Conference room A',
{ asyncContext: { var1: 1, var2: 2 } },
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed){
write(asyncResult.error.message);
}
else {
// Successfully set the location.
// Do whatever is appropriate for your scenario,
// using the arguments var1 and var2 as applicable.
}
});
}

// Write to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}
See also
Create your first Outlook add-in
Asynchronous programming in Office Add-ins
Manage the sensitivity label of your
message or appointment in compose
mode
Article • 05/20/2023

Collaboration in the workplace not only occurs within the organization, but extends to
external partners as well. With information being shared beyond an organization's
network, it's important to establish measures to prevent data loss and enforce
compliance policies. Microsoft Purview Information Protection helps you implement
solutions to classify and protect sensitive information. The use of sensitivity labels in
Outlook is a capability you can configure to protect your data.

You can use the Office JavaScript API to implement sensitivity label solutions in your
Outlook add-in projects and support the following scenarios.

Automatically apply sensitivity labels to certain messages and appointments while


they're being composed, so that users can focus on their work.
Restrict additional actions if a certain sensitivity label is applied to a message or
appointment, such as preventing users from adding external recipients to a
message.
Add a header or footer to a message or appointment based on its sensitivity label
to comply with business and legal policies.

7 Note

Support for the sensitivity label feature was introduced in requirement set 1.13. For
information about client support for this feature, see Supported clients and
platforms.

Prerequisites
To implement the sensitivity label feature in your add-in, you must have a Microsoft 365
E5 subscription. For access to a free developer sandbox that includes a renewable E5
subscription, join the Microsoft 365 Developer Program.

Preview the sensitivity label feature in Outlook on Mac


To preview the sensitivity label feature in Outlook on Mac, install Version 16.71.312.0 or
later. Then, join the Microsoft 365 Insider program and select the Beta Channel
option to access Office beta builds.

Supported clients and platforms


The following table lists client-server combinations that support the use of the
sensitivity label feature in Outlook add-ins. Excluded combinations aren't supported.

Client Exchange Online

Windows Supported
Version 2304 (Build 16327.20248) or later

Mac Supported
Version 16.71.312.0 or later (preview)

Web browser (modern UI) Supported

iOS Not applicable

Android Not applicable

Configure the manifest

7 Note

The sensitivity label feature isn't yet supported for the Unified manifest for
Microsoft 365 (preview).

To use the sensitivity feature in your Outlook add-in project, you must set the
<Permissions> element of the XML manifest to ReadWriteItem.

XML

<Permissions>ReadWriteItem</Permissions>

If your add-in will detect and handle the OnSensitivityLabelChanged event, additional
manifest configurations are required to enable the event-based activation feature. To
learn more, see Detect sensitivity label changes with the OnSensitivityLabelChanged
event.
Verify the status of the catalog of sensitivity
labels
Sensitivity labels and policies are configured by an organization's administrator through
the Microsoft Purview compliance portal. For guidance on how to configure sensitivity
labels in your tenant, see Create and configure sensitivity labels and their policies.

Before you can get or set the sensitivity label on a message or appointment, you must
first ensure that the catalog of sensitivity labels is enabled on the mailbox where the
add-in is installed. To check the status of the catalog of sensitivity labels, call
context.sensitivityLabelsCatalog.getIsEnabledAsync in compose mode.

JavaScript

// Check whether the catalog of sensitivity labels is enabled.


Office.context.sensitivityLabelsCatalog.getIsEnabledAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log(asyncResult.value);
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});

Identify available sensitivity labels


If you want to determine the sensitivity labels available for use on a message or
appointment in compose mode, use context.sensitivityLabelsCatalog.getAsync. The
available labels are returned in the form of SensitivityLabelDetails objects, which provide
the following details.

The name of the label.


The unique identifier (GUID) of the label.
A description of the label.
The color assigned to the label.
The configured sublabels, if any.

The following example shows how to identify the sensitivity labels available in the
catalog.

JavaScript

// It's recommended to check the status of the catalog of sensitivity labels


before
// calling other sensitivity label methods.
Office.context.sensitivityLabelsCatalog.getIsEnabledAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded &&
asyncResult.value == true) {
// Identify available sensitivity labels in the catalog.
Office.context.sensitivityLabelsCatalog.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
const catalog = asyncResult.value;
console.log("Sensitivity Labels Catalog:");
catalog.forEach((sensitivityLabel) => {
console.log(`Name: ${sensitivityLabel.name}`);
console.log(`ID: ${sensitivityLabel.id}`);
console.log(`Tooltip: ${sensitivityLabel.tooltip}`);
console.log(`Color: ${sensitivityLabel.color}`);
console.log(`Sublabels:
${JSON.stringify(sensitivityLabel.children)}`);
});
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});

Get the sensitivity label of a message or


appointment
To get the sensitivity label currently applied to a message or appointment in compose
mode, call item.sensitivityLabel.getAsync as shown in the following example. This returns
the GUID of the sensitivity label.

JavaScript

// It's recommended to check the status of the catalog of sensitivity labels


before
// calling other sensitivity label methods.
Office.context.sensitivityLabelsCatalog.getIsEnabledAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded &&
asyncResult.value == true) {
// Get the current sensitivity label of a message or appointment.
Office.context.mailbox.item.sensitivityLabel.getAsync((asyncResult)
=> {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
console.log(asyncResult.value);
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});

Set the sensitivity label on a message or


appointment
You can set only one sensitivity label on a message or appointment in compose mode.
Before you set the label, call context.sensitivityLabelsCatalog.getAsync. This ensures that
the label you want to apply is available for use. It also helps you identify a label's GUID,
which you'll need to apply the label to the mail item. After you confirm the label's
availability, pass its GUID as a parameter to item.sensitivityLabel.setAsync, as shown in
the following example.

JavaScript

// It's recommended to check the status of the catalog of sensitivity labels


before
// calling other sensitivity label methods.
Office.context.sensitivityLabelsCatalog.getIsEnabledAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded &&
asyncResult.value == true) {
// Identify available sensitivity labels in the catalog.
Office.context.sensitivityLabelsCatalog.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
const catalog = asyncResult.value;
if (catalog.length > 0) {
// Get the GUID of the sensitivity label.
var id = catalog[0].id;
// Set the mail item's sensitivity label using the
label's GUID.

Office.context.mailbox.item.sensitivityLabel.setAsync(id, (asyncResult) => {


if (asyncResult.status ===
Office.AsyncResultStatus.Succeeded) {
console.log(asyncResult.status);
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});
} else {
console.log("Catalog list is empty");
}
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});

Instead of using the GUID to set the sensitivity label, you can pass the
SensitivityLabelDetails object retrieved from the catalog call, as shown in the following
example.

JavaScript

// It's recommended to check the status of the catalog of sensitivity labels


before
// calling other sensitivity label methods.
Office.context.sensitivityLabelsCatalog.getIsEnabledAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded &&
asyncResult.value == true) {
// Identify available sensitivity labels in the catalog.
Office.context.sensitivityLabelsCatalog.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
const catalog = asyncResult.value;
if (catalog.length > 0) {
// Set the mail item's sensitivity label using the
SensitivityLabelDetails object.

Office.context.mailbox.item.sensitivityLabel.setAsync(catalog[0],
(asyncResult) => {
if (asyncResult.status ===
Office.AsyncResultStatus.Succeeded) {
console.log(asyncResult.status);
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});
} else {
console.log("Catalog list is empty");
}
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});
} else {
console.log("Action failed with error: " +
asyncResult.error.message);
}
});

Detect sensitivity label changes with the


OnSensitivityLabelChanged event
Take extra measures to protect your data by using the OnSensitivityLabelChanged event.
This event enables your add-in to complete tasks in response to sensitivity label
changes on a message or appointment. For example, you can prevent users from
downgrading the sensitivity label of a mail item if it contains certain attachments.

The OnSensitivityLabelChanged event is available through the event-based activation


feature. To learn how to configure, debug, and deploy an event-based add-in that uses
this event, see Configure your Outlook add-in for event-based activation.

7 Note

The OnSensitivityLabelChanged event isn't yet supported for the Unified manifest
for Microsoft 365 (preview).

See also
Learn about sensitivity labels
Get started with sensitivity labels
Create and configure sensitivity labels and their policies
Configure your Outlook add-in for event-based activation
Office Add-ins code sample: Verify the sensitivity label of a message
Manage the delivery date and time of a
message
Article • 06/08/2023

The Outlook client gives you the option to delay the delivery of a message, but requires
you to keep Outlook and your device running to send it at the specified time. With the
Office JavaScript API, you can now implement an Outlook add-in that sends scheduled
messages even with your Outlook client closed or with your device turned off. This
capability provides your users with the convenience to schedule email marketing
campaigns or time a message to be delivered during a colleague or customer's business
hours.

7 Note

Support for this feature was introduced in requirement set 1.13. See clients and
platforms that support this requirement set.

Preview the delay delivery API in Outlook on


Mac
To preview the delay delivery API in Outlook on Mac, install Version 16.71.222.0 or later.

Configure the manifest


To schedule the delivery of a message, your add-in must be able to activate in message
compose mode. This is defined through the MessageComposeCommandSurface
extension point in an XML manifest or the mailCompose "contexts" property in a
Unified manifest for Microsoft 365 (preview).

For further guidance on how to configure an Outlook add-in manifest, see Office add-in
manifests.

Access the delivery property of a message


The item.delayDeliveryTime property returns a DelayDeliveryTime object that provides
you with methods to get or set the delivery date and time of a message.
Get the delivery date and time of a message
To get the delivery date and time of a message in compose mode, call
item.delayDeliveryTime.getAsync as shown in the following example. If a delivery date
hasn't been set on a message yet, the call returns 0 . Otherwise, it returns a JavaScript
Date object .

JavaScript

// Gets the delivery date and time of a message.


Office.context.mailbox.item.delayDeliveryTime.getAsync((asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

const deliveryDate = asyncResult.value;


if (deliveryDate === 0) {
console.log("Your message will be delivered immediately when you select
Send.");
} else {
const date = new Date(deliveryDate);
console.log(`Message delivery date and time: ${date.toString()}`);
}
});

Set the delivery date and time of a message


To delay the delivery of a message, pass a JavaScript Date object as a parameter to
item.delayDeliveryTime.setAsync method, as shown in the following example.

JavaScript

// Delays the delivery time by five minutes from the current time.
const currentTime = new Date().getTime();
const milliseconds = 5 * 60 * 1000;
const timeDelay = new Date(currentTime + milliseconds);
Office.context.mailbox.item.delayDeliveryTime.setAsync(timeDelay,
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
console.log(asyncResult.error.message);
return;
}

console.log("Message delivery has been scheduled.");


});
Feature behavior and limitations
When you schedule the delivery of a message using the
item.delayDeliveryTime.setAsync method, the delay is processed on the server. This

allows the message to be sent even if the Outlook client isn’t running. However, because
of this, the message doesn't appear in the Outbox folder, so you won't be able to edit
the message or cancel its delivery after selecting Send. You'll be able to review the
message from the Sent Items folder once the message is sent.

This behavior differs from a message scheduled using the native Delay Delivery option
in the Outlook client, which processes the delay client-side. A message scheduled using
this option appears in the Outbox folder and is only delivered if the Outlook client from
which it was sent is running at the specified delivery time.

Try sample snippets in Script Lab


Get the Script Lab for Outlook add-in and try out the "Get and set message delivery
(Message Compose)" sample snippet. To learn more about Script Lab, see Explore Office
JavaScript API using Script Lab.

See also
Create Outlook add-ins for compose forms
Privacy, permissions, and security for
Outlook add-ins
Article • 07/17/2023

End users, developers, and administrators can use the tiered permission levels of the
security model for Outlook add-ins to control privacy and performance.

This article describes the possible permissions that Outlook add-ins can request, and
examines the security model from the following perspectives.

AppSource: Add-in integrity

End-users: Privacy and performance concerns

Developers: Permissions choices and resource usage limits

Administrators: Privileges to set performance thresholds

Permissions model
Because customers' perception of add-in security can affect add-in adoption, Outlook
add-in security relies on a tiered permissions model. An Outlook add-in would disclose
the level of permissions it needs, identifying the possible access and actions that the
add-in can make on the customer's mailbox data.

There are four levels of permissions.

Permission XML manifest unified manifest for Summary description


level name Microsoft 365 name
canonical
name

restricted Restricted MailboxItem.Restricted.User Allows use of entities, but not


regular expressions.

read item ReadItem MailboxItem.Read.User In addition to what is allowed


in restricted, it allows:

regular expressions
Outlook add-in API read
access
getting the item
properties and the
callback token
Permission XML manifest unified manifest for Summary description
level name Microsoft 365 name
canonical
name

writing custom
properties

read/write ReadWriteItem MailboxItem.ReadWrite.User In addition to what is allowed


item in read item, it allows:

full Outlook add-in API


access except
makeEwsRequestAsync
setting the item
properties

read/write ReadWriteMailbox Mailbox.ReadWrite.User In addition to what is allowed


mailbox in read/write item, it allows:

creating, reading, writing


items and folders
sending items
calling
makeEwsRequestAsync

Permissions are declared in the manifest. The markup varies depending on the type of
manifest.

XML manifest: Use the <Permissions> element.


Unified manifest for Microsoft 365 (preview): Use the "name" property of an
object in the "authorization.permissions.resourceSpecific" array.

7 Note

There is a supplementary permission needed for add-ins that use the append-
on-send feature. With the XML manifest, you specify the permission in the
ExtendedPermissions element. For details, see Implement append-on-send
in your Outlook add-in. With the unified manifest (preview), you specify this
permission with the name Mailbox.AppendOnSend.User in an additional
object in the "authorization.permissions.resourceSpecific" array.
There is a supplementary permission needed for add-ins that use shared
folders. With the XML manifest, you specify the permission by setting the
SupportsSharedFolders element to true . For details, see Enable shared
folders and shared mailbox scenarios in an Outlook add-in. With the unified
manifest (preview), you specify this permission with the name
Mailbox.SharedFolder in an additional object in the
"authorization.permissions.resourceSpecific" array.

The four levels of permissions are cumulative: the read/write mailbox permission
includes the permissions of read/write item, read item and restricted, read/write item
includes read item and restricted, and the read item permission includes restricted.

The following figure shows the four levels of permissions and describes the capabilities
offered to the end user, developer, and administrator by each tier. For more information
about these permissions, see End users: privacy and performance concerns, Developers:
permission choices and resource usage limits, and Understanding Outlook add-in
permissions.

Relating the four-tier permission model to the end user, developer, and administrator
AppSource: Add-in integrity
AppSource hosts add-ins that can be installed by end users and administrators.
AppSource enforces the following measures to maintain the integrity of these Outlook
add-ins.

Requires the host server of an add-in to always use Secure Socket Layer (SSL) to
communicate.

Requires a developer to provide proof of identity, a contractual agreement, and a


compliant privacy policy to submit add-ins.

Archives add-ins in read-only mode.

Supports a user-review system for available add-ins to promote a self-policing


community.
Optional connected experiences
End users and IT admins can turn off optional connected experiences in Office desktop
and mobile clients. For Outlook add-ins, the impact of disabling the Optional connected
experiences setting depends on the client, but it usually means that user-installed add-
ins and access to the AppSource aren't allowed. Add-ins deployed by an organization's
IT admin through Centralized Deployment will still be available.

Client Behavior when optional connected experiences is disabled

- Windows1 The Get Add-ins or All Apps2 button isn't displayed, so users aren't able to manage
- Mac their add-ins or access AppSource.

- Android The Get Add-ins dialog shows only admin-deployed add-ins.


- iOS

Web Availability of add-ins and access to AppSource are unaffected, so users can
browser continue to manage their add-ins including admin-deployed ones.

7 Note

1
On Windows, support for this experience is available from Version 2008 (Build
13127.20296). For more details on your client version, see the update history page
for Microsoft 365 and how to find your Office client version and update
channel .

2
Starting in Outlook on Windows Version 2303 (Build 16215.10000), the All Apps
button is used to manage add-ins and access AppSource.

For general add-in behavior, see Privacy and security for Office Add-ins.

End users: Privacy and performance concerns


The security model addresses security, privacy, and performance concerns of end users
in the following ways.

End user's messages that are protected by Outlook's Information Rights


Management (IRM) don't interact with Outlook add-ins on non-Windows clients.

) Important
Add-ins activate on digitally signed messages in Outlook on Windows
associated with a Microsoft 365 subscription. This support was introduced
with Version 1711 (Build 8711.1000).

Starting with Outlook Version 2009 (Build 13229.10000) on Windows, add-ins


can now activate on IRM-protected items. To turn on this capability, a tenant
administrator must enable the OBJMODEL usage right by setting the Allow
programmatic access custom policy option in Office. For further guidance,
see Usage rights and descriptions.

Before installing an add-in from AppSource, end users can see the access and
actions that the add-in can make on their data and must explicitly confirm to
proceed. No Outlook add-in is automatically pushed onto a client computer
without manual validation by the user or administrator.

Granting the restricted permission allows the Outlook add-in to have limited
access on only the current item. Granting the read item permission allows the
Outlook add-in to access personal identifiable information, such as sender and
recipient names and email addresses, on only the current item.

An end user can install an Outlook add-in for only himself or herself. Outlook add-
ins that affect an organization are installed by an administrator.

End users can install Outlook add-ins that enable context-sensitive scenarios that
are compelling to users while minimizing the users' security risks.

Manifest files of installed Outlook add-ins are secured in the user's email account.

Data communicated with servers hosting Office Add-ins is always encrypted


according to the Secure Socket Layer (SSL) protocol.

Applicable to only the Outlook rich clients: The Outlook rich clients monitor the
performance of installed Outlook add-ins, exercise governance control, and disable
those Outlook add-ins that exceed limits in the following areas.

Response time to activate

Number of failures to activate or reactivate

Memory usage

CPU usage
Governance deters denial-of-service attacks and maintains add-in performance at
a reasonable level. The Business Bar alerts end users about Outlook add-ins that
the Outlook rich client has disabled based on such governance control.

At any time, end users can verify the permissions requested by installed Outlook
add-ins, and disable or subsequently enable any Outlook add-in in the Exchange
Admin Center.

Developers: Permission choices and resource


usage limits
The security model provides developers granular levels of permissions to choose from,
and strict performance guidelines to observe.

Tiered permissions increases transparency


Developers should follow the tiered permissions model to provide transparency and
alleviate users' concern about what add-ins can do to their data and mailbox, indirectly
promoting add-in adoption.

Developers request an appropriate level of permission for an Outlook add-in,


based on how the Outlook add-in should be activated, and its need to read or
write certain properties of an item, or to create and send an item.

As noted above, developers request permission in the manifest.

The following example requests the read item permission in the XML manifest.

XML

<Permissions>ReadItem</Permissions>

The following example requests the read item permission in the Unified manifest
for Microsoft 365 (preview).

JSON

"authorization": {
"permissions": {
"resourceSpecific": [
...
{
"name": "MailboxItem.Read.User",
"type": "Delegated"
},
]
}
},

Developers can request the restricted permission if the Outlook add-in activates
on a specific type of Outlook item (appointment or message), or on specific
extracted entities (phone number, address, URL) being present in the item's subject
or body. For example, the following rule activates the Outlook add-in if one or
more of three entities - phone number, postal address, or URL - are found in the
subject or body of the current message.

7 Note

Activation rules, as seen in this example, aren't supported in add-ins that use the
Unified manifest for Microsoft 365 (preview).

XML

<Permissions>Restricted</Permissions>
<Rule xsi:type="RuleCollection" Mode="And">
<Rule xsi:type="ItemIs" FormType="Read" ItemType="Message" />
<Rule xsi:type="RuleCollection" Mode="Or">
<Rule xsi:type="ItemHasKnownEntity" EntityType="PhoneNumber" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="Address" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="Url" />
</Rule>
</Rule>

Developers should request the read item permission if the Outlook add-in needs
to read properties of the current item other than the default extracted entities, or
write custom properties set by the add-in on the current item, but doesn't require
reading or writing to other items, or creating or sending a message in the user's
mailbox. For example, a developer should request read item permission if an
Outlook add-in needs to look for an entity like a meeting suggestion, task
suggestion, email address, or contact name in the item's subject or body, or uses a
regular expression to activate.

Developers should request the read/write item permission if the Outlook add-in
needs to write to properties of the composed item, such as recipient names, email
addresses, body, and subject, or needs to add or remove item attachments.

Developers request the read/write mailbox permission only if the Outlook add-in
needs to do one or more of the following actions by using the
mailbox.makeEWSRequestAsync method.
Read or write to properties of items in the mailbox.
Create, read, write, or send items in the mailbox.
Create, read, or write to folders in the mailbox.

Resource usage tuning


Developers should be aware of resource usage limits for activation, incorporate
performance tuning in their development workflow, so as to reduce the chance of a
poorly performing add-in denying service of the host. Developers should follow the
guidelines in designing activation rules as described in Limits for activation and
JavaScript API for Outlook add-ins. If an Outlook add-in is intended to run on an
Outlook rich client, then developers should verify that the add-in performs within the
resource usage limits.

Other measures to promote user security


Developers should be aware of and plan for the following as well.

Developers can't use ActiveX controls in add-ins because they're not supported.

Developers should do the following when submitting an Outlook add-in to


AppSource.

Produce an Extended Validation (EV) SSL certificate as a proof of identity.

Host the add-in they are submitting on a web server that supports SSL.

Produce a compliant privacy policy.

Be ready to sign a contractual agreement upon submitting the add-in.

Administrators: Privileges
The security model provides the following rights and responsibilities to administrators.

Can prevent end users from installing any Outlook add-in, including add-ins from
AppSource.

Can disable or enable any Outlook add-in on the Exchange Admin Center.

Applicable to only Outlook on Windows: Can override performance threshold


settings by GPO registry settings.
See also
Privacy and security for Office Add-ins
Privacy controls for Microsoft 365 Apps
Outlook add-in APIs
Limits for activation and JavaScript API for Outlook add-ins
Understanding Outlook add-in
permissions
Article • 05/18/2023

Outlook add-ins specify the required permission level in their manifest. There are four
available levels.

Permission XML manifest unified manifest for Summary description


level name Microsoft 365 name
canonical
name

restricted Restricted MailboxItem.Restricted.User Allows use of entities, but not


regular expressions.

read item ReadItem MailboxItem.Read.User In addition to what is allowed


in restricted, it allows:

regular expressions
Outlook add-in API read
access
getting the item
properties and the
callback token
writing custom properties

read/write ReadWriteItem MailboxItem.ReadWrite.User In addition to what is allowed


item in read item, it allows:

full Outlook add-in API


access except
makeEwsRequestAsync
setting the item
properties

read/write ReadWriteMailbox Mailbox.ReadWrite.User In addition to what is allowed


mailbox in read/write item, it allows:

creating, reading, writing


items and folders
sending items
calling
makeEwsRequestAsync
Permissions are declared in the manifest. The markup varies depending on the type of
manifest.

XML manifest: Use the <Permissions> element.


Unified manifest for Microsoft 365 (preview): Use the "name" property of an
object in the "authorization.permissions.resourceSpecific" array.

7 Note

There is a supplementary permission needed for add-ins that use the append-
on-send feature. With the XML manifest, you specify the permission in the
ExtendedPermissions element. For details, see Implement append-on-send
in your Outlook add-in. With the unified manifest (preview), you specify this
permission with the name Mailbox.AppendOnSend.User in an additional
object in the "authorization.permissions.resourceSpecific" array.
There is a supplementary permission needed for add-ins that use shared
folders. With the XML manifest, you specify the permission by setting the
SupportsSharedFolders element to true . For details, see Enable shared
folders and shared mailbox scenarios in an Outlook add-in. With the unified
manifest (preview), you specify this permission with the name
Mailbox.SharedFolder in an additional object in the
"authorization.permissions.resourceSpecific" array.

The four levels of permissions are cumulative: the read/write mailbox permission
includes the permissions of read/write item, read item and restricted, read/write item
includes read item and restricted, and the read item permission includes restricted.

You can see the permissions requested by a mail add-in before installing it from
AppSource . You can also see the required permissions of installed add-ins in the
Exchange Admin Center.

restricted permission
The restricted permission is the most basic level of permission. Outlook assigns this
permission to a mail add-in by default if the add-in doesn't request a specific permission
in its manifest.

Can do
Get only specific entities (phone number, address, URL) from the item's subject or
body.

Specify an ItemIs activation rule that requires the current item in a read or
compose form to be a specific item type, or ItemHasKnownEntity rule that matches
any of a smaller subset of supported well-known entities (phone number, address,
URL) in the selected item.

7 Note

Outlook Add-in features that depend on activation rules aren't supported


when the add-in uses a Unified manifest for Microsoft 365 (preview).

Access any properties and methods that do not pertain to specific information
about the user or item (see the next section for the list of members that do).

Can't do
Use an ItemHasKnownEntity rule on the contact, email address, meeting
suggestion, or task suggestion entity.

Use the ItemHasAttachment or ItemHasRegularExpressionMatch rule.

Access the members in the following list that pertain to the information of the user
or item. Attempting to access members in this list will return null and result in an
error message which states that Outlook requires the mail add-in to have elevated
permission.
item.addFileAttachmentAsync
item.addItemAttachmentAsync
item.attachments
item.bcc
item.body
item.cc
item.from
item.getRegExMatches
item.getRegExMatchesByName
item.optionalAttendees
item.organizer
item.removeAttachmentAsync
item.requiredAttendees
item.sender
item.to
mailbox.getCallbackTokenAsync
mailbox.getUserIdentityTokenAsync
mailbox.makeEwsRequestAsync
mailbox.userProfile
Body and all its child members
Location and all its child members
Recipients and all its child members
Subject and all its child members
Time and all its child members

read item permission


The read item permission is the next level of permission in the permissions model.

Can do
Read all the properties of the current item in a read or compose form, for example,
item.to in a read form and item.to.getAsync in a compose form.

Get a callback token to get item attachments or the full item with Exchange Web
Services (EWS) or Outlook REST APIs.

Write custom properties set by the add-in on that item.

Get all existing well-known entities, not just a subset, from the item's subject or
body.

Use all the well-known entities in ItemHasKnownEntity rules, or regular expressions


in ItemHasRegularExpressionMatch rules. The following example follows schema
v1.1. It shows a rule that activates the add-in if one or more of the well-known
entities are found in the subject or body of the selected message.

7 Note

Outlook Add-in features that depend on activation rules aren't supported


when the add-in uses a Unified manifest for Microsoft 365 (preview).

XML

<Permissions>ReadItem</Permissions>
<Rule xsi:type="RuleCollection" Mode="And">
<Rule xsi:type="ItemIs" FormType = "Read" ItemType="Message" />
<Rule xsi:type="RuleCollection" Mode="Or">
<Rule xsi:type="ItemHasKnownEntity"
EntityType="PhoneNumber" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="Address" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="Url" />
<Rule xsi:type="ItemHasKnownEntity"
EntityType="MeetingSuggestion" />
<Rule xsi:type="ItemHasKnownEntity"
EntityType="TaskSuggestion" />
<Rule xsi:type="ItemHasKnownEntity"
EntityType="EmailAddress" />
<Rule xsi:type="ItemHasKnownEntity" EntityType="Contact" />
</Rule>

Can't do
Use the token provided by mailbox.getCallbackTokenAsync to:
Update or delete the current item using the Outlook REST API or access any
other items in the user's mailbox.
Get the current calendar event item using the Outlook REST API.

Use any of the following APIs.


mailbox.makeEwsRequestAsync
item.addFileAttachmentAsync
item.addItemAttachmentAsync
item.bcc.addAsync
item.bcc.setAsync
item.body.prependAsync
item.body.setAsync
item.body.setSelectedDataAsync
item.cc.addAsync
item.cc.setAsync
item.end.setAsync
item.location.setAsync
item.optionalAttendees.addAsync
item.optionalAttendees.setAsync
item.removeAttachmentAsync
item.requiredAttendees.addAsync
item.requiredAttendees.setAsync
item.start.setAsync
item.subject.setAsync
item.to.addAsync
item.to.setAsync
read/write item permission
Specify read/write item permission in the manifest to request this permission. Mail add-
ins activated in compose forms that use write methods (Message.to.addAsync or
Message.to.setAsync) must use at least this level of permission.

Can do
Read and write all item-level properties of the item that is being viewed or
composed in Outlook.

Add or remove attachments of that item.

Use all other members of the Office JavaScript API that are applicable to mail add-
ins, except Mailbox.makeEWSRequestAsync.

Can't do
Use the token provided by mailbox.getCallbackTokenAsync to:
Update or delete the current item using the Outlook REST API or access any
other items in the user's mailbox.
Get the current calendar event item using the Outlook REST API.

Use mailbox.makeEWSRequestAsync.

read/write mailbox permission


The read/write mailbox permission is the highest level of permission.

In addition to what the read/write item permission supports, the token provided by
mailbox.getCallbackTokenAsync provides access to use Exchange Web Services (EWS)
operations or Outlook REST APIs to do the following:

Read and write all properties of any item in the user's mailbox.
Create, read, and write to any folder or item in that mailbox.
Send an item from that mailbox

Through mailbox.makeEWSRequestAsync, you can access the following EWS


operations.

CopyItem
CreateFolder
CreateItem
FindConversation
FindFolder
FindItem
GetConversationItems
GetFolder
GetItem
MarkAsJunk
MoveItem
SendItem
UpdateFolder
UpdateItem

Attempting to use an unsupported operation will result in an error response.

See also
Privacy, permissions, and security for Outlook add-ins
Match strings in an Outlook item as well-known entities
Authentication options in Outlook add-
ins
Article • 08/14/2023

Your Outlook add-in can access information from anywhere on the Internet, whether
from the server that hosts the add-in, from your internal network, or from somewhere
else in the cloud. If that information is protected, your add-in needs a way to
authenticate your user. Outlook add-ins provide a number of different methods to
authenticate, depending on your specific scenario.

Single sign-on access token


Single sign-on access tokens provide a seamless way for your add-in to authenticate
and obtain access tokens to call the Microsoft Graph API. This capability reduces friction
since the user is not required to enter their credentials.

7 Note

The Single Sign-on API is currently supported for Word, Excel, Outlook, and
PowerPoint. For more information about where the Single Sign-on API is currently
supported, see IdentityAPI requirement sets. If you're working with an Outlook
add-in, be sure to enable Modern Authentication for the Microsoft 365 tenancy. For
information about how to do this, see Enable or disable modern authentication
for Outlook in Exchange Online.

Consider using SSO access tokens if your add-in:

Is used primarily by Microsoft 365 users


Needs access to:
Microsoft services that are exposed as part of Microsoft Graph
A non-Microsoft service that you control

The SSO authentication method uses the OAuth2 On-Behalf-Of flow provided by Azure
Active Directory. It requires that the add-in register in the Application Registration
Portal and specify any required Microsoft Graph scopes in its manifest.

7 Note
If the add-in is using the Unified manifest for Microsoft 365 (preview), there is
some manifest configuration, but Microsoft Graph scopes aren't specified. SSO-
enabled add-ins that use the unified manifest can be sideloaded, but can't be
deployed in any other way at this time.

Using this method, your add-in can obtain an access token scoped to your server back-
end API. The add-in uses this as a bearer token in the Authorization header to
authenticate a call back to your API. At that point your server can:

Complete the On-Behalf-Of flow to obtain an access token scoped to the


Microsoft Graph API
Use the identity information in the token to establish the user's identity and
authenticate to your own back-end services

For a more detailed overview, see the full overview of the SSO authentication method.

For details on using the SSO token in an Outlook add-in, see Authenticate a user with an
single-sign-on token in an Outlook add-in.

For a sample add-in that uses the SSO token, see Outlook Add-in SSO .

Exchange user identity token


Exchange user identity tokens provide a way for your add-in to establish the identity of
the user. By verifying the user's identity, you can then perform a one-time
authentication into your back-end system, then accept the user identity token as an
authorization for future requests. Use the Exchange user identity token:

When the add-in is used primarily by Exchange on-premises users.


When the add-in needs access to a non-Microsoft service that you control.
As a fallback authentication when the add-in is running on a version of Office that
doesn't support SSO.

Your add-in can call getUserIdentityTokenAsync to get Exchange user identity tokens.
For details on using these tokens, see Authenticate a user with an identity token for
Exchange.

Access tokens obtained via OAuth2 flows


Add-ins can also access services from Microsoft and others that support OAuth2 for
authorization. Consider using OAuth2 tokens if your add-in:
Needs access to a service outside of your control.

Using this method, your add-in prompts the user to sign-in to the service either by
using the displayDialogAsync method to initialize the OAuth2 flow.

Callback tokens
Callback tokens provide access to the user's mailbox from your server back-end, either
using Exchange Web Services (EWS), or the Outlook REST API. Consider using callback
tokens if your add-in:

Needs access to the user's mailbox from your server back-end.

Add-ins obtain callback tokens using one of the getCallbackTokenAsync methods. The
level of access is controlled by the permissions specified in the add-in manifest.
Authenticate a user with a single-sign-
on token in an Outlook add-in
Article • 08/14/2023

Single sign-on (SSO) provides a seamless way for your add-in to authenticate users (and
optionally to obtain access tokens to call the Microsoft Graph API).

Using this method, your add-in can obtain an access token scoped to your server back-
end API. The add-in uses this as a bearer token in the Authorization header to
authenticate a call back to your API. Optionally, you can also have your server-side code.

Complete the On-Behalf-Of flow to obtain an access token scoped to the


Microsoft Graph API
Use the identity information in the token to establish the user's identity and
authenticate to your own back-end services

For an overview of SSO in Office Add-ins, see Enable single sign-on for Office Add-ins
and Authorize to Microsoft Graph in your Office Add-in.

Enable modern authentication in your


Microsoft 365 tenancy
To use SSO with an Outlook add-in, you must enable Modern Authentication for the
Microsoft 365 tenancy. For information about how to do this, see Enable or disable
modern authentication for Outlook in Exchange Online.

Register your add-in


To use SSO, your Outlook add-in will need to have a server-side web API that is
registered with Azure Active Directory (AAD) v2.0. For more information, see Register an
Office Add-in that uses SSO with the Azure AD v2.0 endpoint.

Provide consent when sideloading an add-in


When you are developing an add-in, you will have to provide consent in advance. For
more information, see Grant administrator consent to the add-in.

Update the add-in manifest


The next step to enable SSO in the add-in is to add some information to the manifest
from the add-in's Microsoft identity platform registration. The markup varies depending
on the type of manifest.

XML manifest: Add a WebApplicationInfo element at the end of the


VersionOverridesV1_1 VersionOverrides element. Then, add its required child

elements. For detailed information about the markup, see Configure the add-in.

Unified manifest for Microsoft 365 (preview): Add a "webApplicationInfo"


property to the root { ... } object in the manifest. Give this object a child "id"
property set to the application ID of the add-in's web app as it was generated in
the Azure portal when you registered the add-in. (See the section Register your
add-in earlier in this article.) Also give it a child "resource" property that is set to
the same Application ID URI that you set when you registered the add-in. This URI
should have the form api://<fully-qualified-domain-name>/<application-id> . The
following is an example.

JSON

"webApplicationInfo": {
"id": "a661fed9-f33d-4e95-b6cf-624a34a2f51d",
"resource": "api://addin.contoso.com/a661fed9-f33d-4e95-b6cf-
624a34a2f51d"
},

7 Note

SSO-enabled add-ins that use the unified manifest can be sideloaded, but
can't be deployed in any other way at this time.

Get the SSO token


The add-in gets an SSO token with client-side script. For more information, see Add
client-side code.

Use the SSO token at the back-end


In most scenarios, there would be little point to obtaining the access token, if your add-
in does not pass it on to a server-side and use it there. For details on what your server-
side could and should do, see Add server-side code.
) Important

When using the SSO token as an identity in an Outlook add-in, we recommend that
you also use the Exchange identity token as an alternate identity. Users of your
add-in may use multiple clients, and some may not support providing an SSO
token. By using the Exchange identity token as an alternate, you can avoid having
to prompt these users for credentials multiple times. For more information, see
Scenario: Implement single sign-on to your service in an Outlook add-in.

SSO for event-based activation


There are additional steps to take if your add-in uses event-based activation. For more
information, see Enable single sign-on (SSO) or cross-origin resource sharing (CORS) in
your event-based Outlook add-in.

See also
getAccessToken
For a sample Outlook add-in that uses the SSO token to access the Microsoft
Graph API, see Outlook Add-in SSO .
SSO API reference
IdentityAPI requirement set
Enable single sign-on (SSO) or cross-origin resource sharing (CORS) in your event-
based Outlook add-in
Enable single sign-on (SSO) or cross-
origin resource sharing (CORS) in your
event-based Outlook add-in
Article • 07/07/2023

When an Outlook add-in uses event-based activation, the events run in a separate
runtime. To enable single sign-on (SSO) in your event-based add-in or allow it to
request external data through cross-origin resource sharing (CORS), you must configure
a well-known URI. Through this resource, Office will be able to identify the add-ins,
including their JavaScript files, that support SSO or CORS requests.

7 Note

The steps in this article only apply when running your Outlook add-in on Windows.
This is because Outlook on Windows uses a JavaScript file, while Outlook on Mac
and on the web use an HTML file that references the same JavaScript file. To learn
more about event-based activation in Outlook add-ins, see Configure your
Outlook add-in for event-based activation.

List allowed add-ins in a well-known URI


To list which add-ins are allowed to work with SSO or CORS, create a JSON file that
identifies each JavaScript file for each add-in. Then, host that JSON file at a well-known
URI. A well-known URI allows the specification of all hosted JS files that are authorized
to obtain tokens for the current web origin. This ensures that the owner of the origin has
full control over which hosted JavaScript files are meant to be used in an add-in and
which ones are not, preventing any security vulnerabilities around impersonation, for
example.

The following example shows how to enable SSO or CORS for two add-ins (a main
version and beta version). You can list as many add-ins as necessary depending on how
many you provide from your web server.

JSON

{
"allowed":
[
"https://addin.contoso.com:8000/main/js/autorun.js",
"https://addin.contoso.com:8000/beta/js/autorun.js"
]
}

Host the JSON file under a location named .well-known in the URI at the root of the
origin. For example, if the origin is https://addin.contoso.com:8000/ , then the well-
known URI is https://addin.contoso.com:8000/.well-known/microsoft-officeaddins-
allowed.json .

The origin refers to a pattern of scheme + subdomain + domain + port. The name of
the location must be .well-known , and the name of the resource file must be
microsoft-officeaddins-allowed.json . This file must contain a JSON object with an

attribute named allowed whose value is an array of all JavaScript files authorized for
SSO for their respective add-ins.

After you configure the well-known URI, if your add-in implements SSO, you can then
call the getAccessToken() API to get an access token with the user's identity.

) Important

While OfficeRuntime.auth.getAccessToken and Office.auth.getAccessToken


perform the same functionality of retrieving an access token, we recommend
calling OfficeRuntime.auth.getAccessToken in your event-based add-in. This API is
supported in all Outlook client versions that support event-based activation and
SSO. On the other hand, Office.auth.getAccessToken is only supported in Outlook
on Windows starting from Version 2111 (Build 14701.20000).

See also
Authenticate a user with a single-sign-on token in an Outlook add-in
Configure your Outlook add-in for event-based activation
Authenticate a user with an identity
token for Exchange
Article • 03/28/2023

Exchange user identity tokens provide a way for your add-in to uniquely identify an add-
in user. By establishing the user's identity, you can implement a single sign-on (SSO)
authentication scheme for your back-end service that enables customers who are using
Outlook add-ins to connect to your service without signing in. See Exchange user
identity token for more about when to use this token type. In this article, we'll take a
look at a simplistic method of using the Exchange identity token to authenticate a user
to your back-end.

) Important

This is just a simple example of an SSO implementation. As always, when you're


dealing with identity and authentication, you have to make sure that your code
meets the security requirements of your organization.

Send the ID token with each request


The first step is for your add-in to obtain the Exchange user identity token from the
server by calling getUserIdentityTokenAsync. Then the add-in sends this token with
every request it makes to your back-end. This could be in a header, or as part of the
request body.

Validate the token


The back-end MUST validate the token before accepting it. This is an important step to
ensure that the token was issued by the user's Exchange server. For information on
validating Exchange user identity tokens, see Validate an Exchange identity token.

Once validated and decoded, the payload of the token looks something like the
following:

JSON

{
"aud" : "https://mailhost.contoso.com/IdentityTest.html",
"iss" : "00000002-0000-0ff1-ce00-000000000000@mailhost.contoso.com",
"nbf" : "1505749527",
"exp" : "1505778327",
"appctxsender":"00000002-0000-0ff1-ce00-
000000000000@mailhost.context.com",
"isbrowserhostedapp":"true",
"appctx" : {
"msexchuid" : "53e925fa-76ba-45e1-be0f-4ef08b59d389",
"version" : "ExIdTok.V1",
"amurl" :
"https://mailhost.contoso.com:443/autodiscover/metadata/json/1"
}
}

Map the token to a user in your backend


Your back-end service can calculate a unique user ID from the token and map it to a
user in your internal user system. For example, if you use a database to store users, you
could add this unique ID to the user's record in your database.

Generate a unique ID
Use a combination of the msexchuid and amurl properties. For example, you could
concatenate the two values together and generate a base 64-encoded string. This value
can be reliably generated from the token every time, so you can map an Exchange user
identity token back to the user in your system.

Check the user


With the unique ID generated, the next step is to check for a user in your system with
that associated ID.

If the user is found, the back-end treats the request as authenticated, and allows
the request to proceed.

If the user is not found, then the back-end returns an error indicating that the user
needs to sign in. The add-in then prompts the user to sign in to the back-end
using your existing authentication method. Once the user is authenticated, the
Exchange user identity token is submitted with the user authentication details. The
back-end can then update the user's record in your system with the unique ID.
Validate an Exchange identity token
Article • 03/28/2023

Your Outlook add-in can send you an Exchange user identity token, but before you trust
the request you must validate the token to ensure that it came from the Exchange server
that you expect. Exchange user identity tokens are JSON Web Tokens (JWT). The steps
required to validate a JWT are described in RFC 7519 JSON Web Token (JWT) .

We suggest that you use a four-step process to validate the identity token and obtain
the user's unique identifier. First, extract the JSON Web Token (JWT) from a base64 URL-
encoded string. Second, make sure that the token is well-formed, that it is for your
Outlook add-in, that it has not expired, and that you can extract a valid URL for the
authentication metadata document. Next, retrieve the authentication metadata
document from the Exchange server and validate the signature attached to the identity
token. Finally, compute a unique identifier for the user by concatenating the user's
Exchange ID with the URL of the authentication metadata document.

Extract the JSON Web Token


The token returned from getUserIdentityTokenAsync is an encoded string representation
of the token. In this form, per RFC 7519, all JWTs have three parts, separated by a period.
The format is as follows.

JSON

{header}.{payload}.{signature}

The header and payload should be base64-decoded to obtain a JSON representation of


each part. The signature should be base64-decoded to obtain a byte array containing
the binary signature.

For more information about the contents of the token, see Inside the Exchange identity
token.

After you have the three decoded components, you can proceed with validating the
content of the token.

Validate token contents


To validate the token contents, you should check the following:
Check the header and verify that the:
typ claim is set to JWT .
alg claim is set to RS256 .

x5t claim is present.

Check the payload and verify that the:


amurl claim inside the appctx is set to the location of an authorized token

signing key manifest file. For example, the expected amurl value for Microsoft
365 is https://outlook.office365.com:443/autodiscover/metadata/json/1 . See
the next section Verify the domain for additional information.
Current time is between the times specified in the nbf and exp claims. The nbf
claim specifies the earliest time that the token is considered valid, and the exp
claim specifies the expiration time for the token. It is recommended to allow for
some variation in clock settings between servers.
aud claim is the expected URL for your add-in.
version claim inside the appctx claim is set to ExIdTok.V1 .

Verify the domain


When implementing the verification logic described in the previous section, you must
also require that the domain of the amurl claim matches the Autodiscover domain for
the user. To do so, you'll need to use or implement Autodiscover for Exchange.

For Exchange Online, confirm that the amurl is a well-known domain


(https://outlook.office365.com:443/autodiscover/metadata/json/1 ), or belongs
to a geo-specific or specialty cloud (Office 365 URLs and IP address ranges).

If your add-in service has a preexisting configuration with the user's tenant, then
you can establish if this amurl is trusted.

For an Exchange hybrid deployment, use OAuth-based Autodiscover to verify the


domain expected for the user. However, while the user will need to authenticate as
part of the Autodiscover flow, your add-in should never collect the user's
credentials and do basic authentication.

If your add-in can't verify the amurl using any of these options, you may choose to have
your add-in shut down gracefully with an appropriate notification to the user if
authentication is necessary for the add-in's workflow.

Validate the identity token signature


After you know that the JWT contains the required claims, you can proceed with
validating the token signature.

Retrieve the public signing key


The first step is to retrieve the public key that corresponds to the certificate that the
Exchange server used to sign the token. The key is found in the authentication metadata
document. This document is a JSON file hosted at the URL specified in the amurl claim.

The authentication metadata document uses the following format.

JSON

{
"id": "_70b34511-d105-4e2b-9675-39f53305bb01",
"version": "1.0",
"name": "Exchange",
"realm": "*",
"serviceName": "00000002-0000-0ff1-ce00-000000000000",
"issuer": "00000002-0000-0ff1-ce00-000000000000@*",
"allowedAudiences": [
"00000002-0000-0ff1-ce00-000000000000@*"
],
"keys": [
{
"usage": "signing",
"keyinfo": {
"x5t": "enh9BJrVPU5ijV1qjZjV-fL2bco"
},
"keyvalue": {
"type": "x509Certificate",
"value": "MIIHNTCC..."
}
}
],
"endpoints": [
{
"location":
"https://by2pr06mb2229.namprd06.prod.outlook.com:444/autodiscover/metadata/j
son/1",
"protocol": "OAuth2",
"usage": "metadata"
}
]
}

The available signing keys are in the keys array. Select the correct key by ensuring that
the x5t value in the keyinfo property matches the x5t value in the header of the
token. The public key is inside the value property in the keyvalue property, stored as a
base64-encoded byte array.

After you have the correct public key, verify the signature. The signed data is the first
two parts of the encoded token, separated by a period:

JSON

{header}.{payload}

Compute the unique ID for an Exchange


account
Create a unique identifier for an Exchange account by concatenating the authentication
metadata document URL with the Exchange identifier for the account. When you have
this unique identifier, use it to create a single sign-on (SSO) system for your Outlook
add-in web service. For details about using the unique identifier for SSO, see
Authenticate a user with an identity token for Exchange.

Use a library to validate the token


There are a number of libraries that can do general JWT parsing and validation.
Microsoft provides the System.IdentityModel.Tokens.Jwt library that can be used to
validate Exchange user identity tokens.

) Important

We no longer recommend the Exchange Web Services Managed API because the
Microsoft.Exchange.WebServices.Auth.dll, though still available, is now obsolete
and relies on unsupported libraries like Microsoft.IdentityModel.Extensions.dll.

System.IdentityModel.Tokens.Jwt
The System.IdentityModels.Tokens.Jwt library can parse the token and also perform
the validation, though you will need to parse the appctx claim yourself and retrieve the
public signing key.

C#
// Load the encoded token
string encodedToken = "...";
JwtSecurityToken jwt = new JwtSecurityToken(encodedToken);

// Parse the appctx claim to get the auth metadata url


string authMetadataUrl = string.Empty;
var appctx = jwt.Claims.FirstOrDefault(claim => claim.Type == "appctx");
if (appctx != null)
{
var AppContext = JsonConvert.DeserializeObject<ExchangeAppContext>
(appctx.Value);

// Token version check


if (string.Compare(AppContext.Version, "ExIdTok.V1",
StringComparison.InvariantCulture) != 0) {
// Fail validation
}

authMetadataUrl = AppContext.MetadataUrl;
}

// Use System.IdentityModel.Tokens.Jwt library to validate standard parts


JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
TokenValidationParameters tvp = new TokenValidationParameters();

tvp.ValidateIssuer = false;
tvp.ValidateAudience = true;
tvp.ValidAudience = "{URL to add-in}";
tvp.ValidateIssuerSigningKey = true;
// GetSigningKeys downloads the auth metadata doc and
// returns a List<SecurityKey>
tvp.IssuerSigningKeys = GetSigningKeys(authMetadataUrl);
tvp.ValidateLifetime = true;

try
{
var claimsPrincipal = tokenHandler.ValidateToken(encodedToken, tvp, out
SecurityToken validatedToken);

// If no exception, all standard checks passed


}
catch (SecurityTokenValidationException ex)
{
// Validation failed
}

The ExchangeAppContext class is defined as follows:

C#
using Newtonsoft.Json;

/// <summary>
/// Representation of the appctx claim in an Exchange user identity token.
/// </summary>
public class ExchangeAppContext
{
/// <summary>
/// The Exchange identifier for the user
/// </summary>
[JsonProperty("msexchuid")]
public string ExchangeUid { get; set; }

/// <summary>
/// The token version
/// </summary>
public string Version { get; set; }

/// <summary>
/// The URL to download authentication metadata
/// </summary>
[JsonProperty("amurl")]
public string MetadataUrl { get; set; }
}

For an example that uses this library to validate Exchange tokens and has an
implementation of GetSigningKeys , see Outlook-Add-In-Token-Viewer .

See also
Outlook-Add-In-Token-Viewer
Outlook-Add-in-JavaScript-ValidateIdentityToken
Inside the Exchange identity token
Article • 05/20/2023

The Exchange user identity token returned by the getUserIdentityTokenAsync method


provides a way for your add-in code to include the user's identity with calls to your
back-end service. This article will discuss the format and contents of the token.

An Exchange user identity token is a base-64 URL-encoded string that is signed by the
Exchange server that sent it. The token is not encrypted, and the public key that you use
to validate the signature is stored on the Exchange server that issued the token. The
token has three parts: a header, a payload, and a signature. In the token string, the parts
are separated by a period character ( . ) to make it easy for you to split the token.

Exchange uses a the JSON Web Token (JWT) format for the identity token. For
information about JWT tokens, see RFC 7519 JSON Web Token (JWT) .

Identity token header


The header provides information about the format and signature information of the
token. The following example shows what the header of the token looks like.

JSON

{
"typ": "JWT",
"alg": "RS256",
"x5t": "Un6V7lYN-rMgaCoFSTO5z707X-4"
}

The following table describes the parts of the token header.

Claim Value Description

typ JWT Identifies the token as a JSON Web Token. All identity tokens provided by
Exchange server are JWT tokens.

alg RS256 The hashing algorithm that is used to create the signature. All tokens
provided by Exchange server use the RSASSA-PKCS1-v1_5 with SHA-256
hash algorithm.

x5t Certificate The X.509 thumbprint of the token.


thumbprint
Identity token payload
The payload contains the authentication claims that identify the email account and
identify the Exchange server that sent the token. The following example shows what the
payload section looks like.

JSON

{
"aud": "https://mailhost.contoso.com/IdentityTest.html",
"iss": "00000002-0000-0ff1-ce00-000000000000@mailhost.contoso.com",
"nbf": "1331579055",
"exp": "1331607855",
"appctxsender": "00000002-0000-0ff1-ce00-
000000000000@mailhost.context.com",
"isbrowserhostedapp": "true",
"appctx": {
"msexchuid": "53e925fa-76ba-45e1-be0f-
4ef08b59d389@mailhost.contoso.com",
"version": "ExIdTok.V1",
"amurl": "https://mailhost.contoso.com:443/autodiscover/metadata/json/1"
}
}

The following table lists the parts of the identity token payload.

Claim Description

aud The URL of the add-in that requested the token. A token is only valid if it is
sent from the add-in that is running in the client's webview control. The
URL of the add-in is specified in the manifest. The markup depends on the
type of manifest.

XML manifest: If the add-in uses the Office Add-ins manifests schema v1.1,
this URL is the URL specified in the first <SourceLocation> element, under
the form type ItemRead or ItemEdit , whichever occurs first as part of the
FormSettings element in the add-in manifest.

Unified manifest for Microsoft 365 (preview): The URL is specified in the
"extensions.audienceClaimUrl" property.

iss A unique identifier for the Exchange server that issued the token. All tokens
issued by this Exchange server will have the same identifier.

nbf The date and time that the token is valid starting from. The value is the
number of seconds since January 1, 1970.
Claim Description

exp The date and time that the token is valid until. The value is the number of
seconds since January 1, 1970.

appctxsender A unique identifier for the Exchange server that sent the application
context.

isbrowserhostedapp Indicates whether the add-in is hosted in a browser.

appctx The application context for the token.

The information in the appctx claim provides you with the unique identifier for the
account and the location of the public key used to sign the token. The following table
lists the parts of the appctx claim.

Application Description
context
property

msexchuid A unique identifier associated with the email account and the Exchange server.

version The version number of the token. For all tokens provided by Exchange, the
value is ExIdTok.V1 .

amurl The URL of the authentication metadata document that contains the public
key of the X.509 certificate that was used to sign the token.

For more information about how to use the authentication metadata


document, see Validate an Exchange identity token.

Identity token signature


The signature is created by hashing the header and payload sections with the algorithm
specified in the header and using the self-signed X509 certificate located on the server
at the location specified in the payload. Your web service can validate this signature to
help make sure that the identity token comes from the server that you expect to send it.

See also
For an example that parses the Exchange user identity token, see Outlook-Add-In-
Token-Viewer .
Scenario: Implement single sign-on to
your service in an Outlook add-in
Article • 08/14/2023

In this article we'll explore a recommended method of using the single sign-on access
token and the Exchange identity token together to provide a single-sign on
implementation to your own backend service. By using both tokens together, you can
take advantage of the benefits of the SSO access token when it is available, while
ensuring that your add-in will work when it is not, such as when the user switches to a
client that does not support them, or if the user's mailbox is on an on-premises
Exchange server.

7 Note

The Single Sign-on API is currently supported for Word, Excel, Outlook, and
PowerPoint. For more information about where the Single Sign-on API is currently
supported, see IdentityAPI requirement sets. If you're working with an Outlook
add-in, be sure to enable Modern Authentication for the Microsoft 365 tenancy. For
information about how to do this, see Enable or disable modern authentication
for Outlook in Exchange Online.

Why use the SSO access token?


The Exchange identity token is available in all requirement sets of the add-in APIs, so it
may be tempting to just rely on this token and ignore the SSO token altogether.
However, the SSO token offers some advantages over the Exchange identity token
which make it the recommended method to use when it is available.

The SSO token uses a standard OpenID format and is issued by Azure. This greatly
simplifies the process of validating these tokens. In comparison, Exchange identity
tokens use a custom format based on the JSON Web Token standard, requiring
custom work to validate the token.
The SSO token can be used by your backend to retrieve an access token for
Microsoft Graph without the user having to do any additional sign in action.
The SSO token provides richer identity information, such as the user's display
name.

Add-in scenario
For the purposes of this example, consider an add-in that consists of both the add-in UI
and scripts (HTML + JavaScript) and a backend Web API that is called by the add-in. The
backend Web API makes calls both to the Microsoft Graph API and the Contoso Data
API, a fictional API created by a third party. Like the Microsoft Graph API, the Contoso
Data API requires OAuth authentication. The requirement is that the backend Web API
should be able to call both APIs without having to prompt the user for their credentials
every time an access token expires.

To do this, the backend API creates a secure database of users. Each user will get an
entry in the database where the backend can store long-lived refresh tokens for both
the Microsoft Graph API and the Contoso Data API. The following JSON markup
represents a user's entry in the database.

JSON

{
"userDisplayName": "...",
"ssoId": "...",
"exchangeId": "...",
"graphRefreshToken": "...",
"contosoRefreshToken": "..."
}

The add-in includes either the SSO access token (if it is available) or the Exchange
identity token (if the SSO token is not available) with every call it makes to the backend
Web API.

Add-in startup
1. When the add-in starts, it sends a request to the backend Web API to determine if
the user is registered (i.e. has an associated record in the user database) and that
the API has refresh tokens for both Graph and Contoso. In this call, the add-in
includes both the SSO token (if available) and the identity token.

2. The Web API uses the methods in Authenticate a user with an single-sign-on token
in an Outlook add-in and Authenticate a user with an identity token for Exchange
to validate and generate a unique identifier from both tokens.

3. If an SSO token was provided, the Web API then queries the user database for an
entry that has an ssoId value that matches the unique identifier generated from
the SSO token.

If an entry did not exist, continue to the next step.


If an entry exists, proceed to step 5.
4. The Web API queries the database for an entry that has an exchangeId value that
matches the unique identifier generated from the Exchange identity token.

If an entry exists and an SSO token was provided, update the user's record in
the database to set the ssoId value to the unique identifier generated from
the SSO token and proceed to step 5.
If an entry exists and no SSO token was provided, proceed to step 5.
If no entry exists, create a new entry. Set ssoId to the unique identifier
generated from the SSO token (if available), and set exchangeId to the unique
identifier generated from the Exchange identity token.

5. Check for a valid refresh token in the user's graphRefreshToken value.

If the value is missing or invalid and an SSO token was provided, use the
OAuth2 On-Behalf-Of flow to obtain an access token and refresh token for
Graph. Save the refresh token in the graphRefreshToken value for the user.

6. Check for valid refresh tokens in both graphRefreshToken and


contosoRefreshToken .

If both values are valid, respond to the add-in to indicate that the user is
already registered and configured.
If either value is invalid, respond to the add-in to indicate that user setup is
required, along with which services (Graph or Contoso) need to be
configured.

7. The add-in checks the response.

If the user is already registered and configured, the add-in continues with
normal operation.
If user setup is required, the add-in enters "setup" mode and prompts the
user to authorize the add-in.

Authorize the backend Web API


The procedure for authorizing the backend Web API to call the Microsoft Graph API and
Contoso Data API should ideally only have to happen once, to minimize having to
prompt the user for sign-in.

Based on the response from the backend Web API, the add-in may need to authorize
the user for the Microsoft Graph API, the Contoso Data API, or both. Since both APIs use
OAuth2 authentication, the method is similar for both.
1. The add-in notifies the user that it needs them to authorize their use of the API
and asks them to click a link or button to start the process.

2. Once the flow completes, the add-in sends the refresh token to the backend Web
API and includes the SSO token (if available) or the Exchange identity token.

3. The backend Web API locates the user in the database and updates the
appropriate refresh token.

4. The add-in continues with normal operation.

Normal operation
Whenever the add-in calls the backend Web API, it includes either the SSO token or the
Exchange identity token. The backend Web API locates the user by this token, then uses
the stored refresh tokens to obtain access tokens for the Microsoft Graph API and the
Contoso Data API. As long as the refresh tokens are valid, the user will not have to sign
in again.
Deploy and install Outlook add-ins for
testing
Article • 03/28/2023

As part of the process of developing an Outlook add-in, you'll probably find yourself
iteratively deploying and installing the add-in for testing, which involves the following
steps.

1. Creating a manifest file that describes the add-in.


2. Deploying the add-in UI file(s) to a web server.
3. Installing the add-in in your mailbox.
4. Testing the add-in, making appropriate changes to the UI or manifest files, and
repeating steps 2 and 3 to test the changes.

7 Note

Custom panes have been deprecated so please ensure that you're using a
supported add-in extension point.

Create a manifest file for the add-in


Each add-in is described by a manifest, a document that gives the server information
about the add-in, provides descriptive information about the add-in for the user, and
identifies the location of the add-in UI HTML file. You can store the manifest in a local
folder or server, as long as the location is accessible by the Exchange server of the
mailbox that you're testing with. We'll assume that you store your manifest in a local
folder. For information about how to create a manifest file, see Office add-in manifests.

Deploy an add-in to a web server


You can use HTML and JavaScript to create the add-in. The resulting source files are
stored on a web server that can be accessed by the Exchange server that hosts the add-
in. After initially deploying the source files for the add-in, you can update the add-in UI
and behavior by replacing the HTML files or JavaScript files stored on the web server
with a new version of the HTML file.

Install the add-in


After preparing the add-in manifest file and deploying the add-in UI to a web server
that can be accessed, you can sideload the add-in for a mailbox on an Exchange server
by using an Outlook client, or install the add-in by running remote Windows PowerShell
cmdlets.

Sideload the add-in


You can install an add-in if your mailbox is on Exchange. Sideloading add-ins requires at
minimum the My Custom Apps role for your Exchange Server. In order to test your add-
in, or install add-ins in general by specifying a URL or file name for the add-in manifest,
you should request your Exchange administrator to provide the necessary permissions.

The Exchange administrator can run the following PowerShell cmdlet to assign a single
user the necessary permissions. In this example, wendyri is the user's email alias.

PowerShell

New-ManagementRoleAssignment -Role "My Custom Apps" -User "wendyri"

If necessary, the administrator can run the following cmdlet to assign multiple users the
similar necessary permissions.

PowerShell

$users = Get-Mailbox *$users | ForEach-Object { New-ManagementRoleAssignment


-Role "My Custom Apps" -User $_.Alias}

For more information about the My Custom Apps role, see My Custom Apps role.

Using Microsoft 365 or Visual Studio to develop add-ins assigns you the organization
administrator role which allows you to install add-ins by file or URL in the EAC, or by
Powershell cmdlets.

Install an add-in by using remote PowerShell


After you create a remote Windows PowerShell session on your Exchange server, you
can install an Outlook add-in by using the New-App cmdlet with the following PowerShell
command.

PowerShell

New-App -URL:"http://<fully-qualified URL">


The fully qualified URL is the location of the add-in manifest file that you prepared for
your add-in.

Use the following additional PowerShell cmdlets to manage the add-ins for a mailbox.

Get-App - Lists the add-ins that are enabled for a mailbox.


Set-App - Enables or disables a add-in on a mailbox.

Remove-App - Removes a previously installed add-in from an Exchange server.

Client versions
Deciding what versions of the Outlook client to test depends on your development
requirements.

If you're developing an add-in for private use, or only for members of your
organization, then it is important to test the versions of Outlook that your
company uses. Keep in mind that some users may use Outlook on the web, so
testing your company's standard browser versions is also important.

If you're developing an add-in to list in AppSource , you must test the required
versions as specified in the Commercial marketplace certification policies 1120.3.
This includes:
The latest version of Outlook on Windows and the version prior to the latest.
The latest version of Outlook on Mac.
The latest version of Outlook on iOS and Android (if your add-in supports
mobile form factor).
The browser versions specified in the Commercial marketplace validation policy
1120.3.

7 Note

If your add-in does not support one of the above clients due to requesting an API
requirement set that the client does not support, that client would be removed
from the list of required clients.

Outlook on the web and Exchange server


versions
Consumer and Microsoft 365 account users see the modern UI version when they access
Outlook on the web and no longer see the classic version which has been deprecated.
However, on-premises Exchange servers continue to support classic Outlook on the
web. Therefore, during the validation process, your submission may receive a warning
that the add-in is not compatible with classic Outlook on the web. In that case, you
should consider testing your add-in in an on-premises Exchange environment. This
warning won't block your submission to AppSource but your customers may experience
a sub-optimal experience if they use Outlook on the web in an on-premises Exchange
environment.

To mitigate this, we recommend you test your add-in in Outlook on the web connected
to your own private on-premises Exchange environment. For more information, see
guidance on how to Establish an Exchange 2016 or Exchange 2019 test environment and
how to manage Outlook on the web in Exchange Server.

Alternatively, you can opt to pay for and use a service that hosts and manages on-
premises Exchange servers. A couple of options are:

Rackspace
Hostway

Furthermore, if you don't want your add-ins to be available for users who are connected
to on-premises Exchange, you can set the requirement set in the add-in manifest to be
1.6 or higher. Such add-ins will not be tested or validated on the classic Outlook on the
web UI.

See also
Troubleshoot user errors with Office Add-ins
Sideload Outlook add-ins for testing
Article • 08/15/2023

Sideload your Outlook add-in for testing without having to first put it in an add-in
catalog.

) Important

If your Outlook add-in supports mobile, sideload the manifest using the
instructions in this article for your Outlook client on the web, on Windows, or on
Mac, then follow the guidance in Testing your add-ins on mobile.

Sideload automatically
If you created your Outlook add-in using the Yeoman generator for Office Add-ins,
sideloading is best done through the command line on Windows. This takes advantage
of our tooling and allows you to sideload across all of your supported devices in one
command.

1. On Windows, open a command prompt and navigate to the root directory of your
Yeoman generated add-in project. Run the command npm start .

2. Your Outlook add-in will automatically sideload to Outlook on your desktop


computer. You'll see a dialog appear, stating there is an attempt to sideload the
add-in, listing the name and the location of the manifest file. Select OK, which will
register the manifest.

) Important

If the manifest contains an error or the path to the manifest is invalid, you'll
receive an error message.

3. If your manifest contains no errors and the path is valid, your add-in will now be
sideloaded and available on both your desktop and in Outlook on the web. It will
also be installed across all your supported devices.

Sideload manually
Though we strongly recommend sideloading automatically through the command line
as covered in the previous section, you can also manually sideload an Outlook add-in
based on the Outlook client. Select the tab for your preferred Outlook client.

Windows

Outlook 2016 or later


1. Open Outlook 2016 or later on Windows.

2. Depending on your Outlook version, select Get Add-ins or All Apps from the
ribbon.

7 Note

Starting in Outlook on Windows Version 2303 (Build 16215.10000), the All


Apps button is used to manage your add-ins and access AppSource.

) Important

If you don't see Get Add-ins or All Apps in your version of Outlook, do
one of the following:

If you configured your ribbon layout to Simplified Ribbon, select the


ellipsis button ( ... ) from the ribbon. Then, depending on your
Outlook version, select Get Add-ins or All Apps.
Select File > Info > Manage Add-ins. This opens the Add-Ins for
Outlook dialog in Outlook on the web. To learn more about the web
experience, select the Web tab in Sideload manually.

3. In the flyout that appears, select Get Add-ins.

) Important

If you see the Add Apps option instead of Get Add-ins in your version of
Outlook, you must manually sideload your add-in through Outlook on
the web.

To access the Add-Ins for Outlook dialog in Outlook on the web, do one
of the following:

In Outlook on Windows, select File > Info > Manage Add-ins.

From your preferred browser, go to https://aka.ms/olksideload .

Outlook on the web opens in your preferred browser. When the Add-Ins
for Outlook dialog appears, follow the succeeding steps to sideload your
add-in.

Note that due to caching, it may take up to 24 hours for the sideloaded
add-in to appear in your Outlook client on Windows.

4. If there are tabs near the top of the dialog that opens, ensure that the Add-ins
tab is selected. Then, choose My add-ins.
5. Locate the Custom add-ins section at the bottom of the dialog. Select the
Add a custom add-in link, and then select Add from File.

6. Locate the manifest file for your custom add-in and install it. Accept all
prompts during the installation.

Outlook 2013
1. Open Outlook 2013 on Windows.

2. Select File > Info > Manage Add-ins. Outlook will open the web version in a
browser.

3. Depending on your version of Outlook on the web, follow the steps in the
Web tab of Sideload manually.

Remove a sideloaded add-in


On all versions of Outlook, the key to removing a sideloaded add-in is the Add-Ins for
Outlook dialog, which lists your installed add-ins. To access the dialog on your Outlook
client, use the steps listed for manual sideloading in the previous section of this article.

To remove a sideloaded add-in from Outlook, in the Add-Ins for Outlook dialog,
navigate to the Custom add-ins section. Choose the ellipsis ( ... ) for the add-in, then
choose Remove.

See also
Add-ins for Outlook on mobile devices
Compare Outlook add-in support in
Outlook on Mac with other Outlook
clients
Article • 10/11/2022

You can create and run an Outlook add-in the same way in Outlook on Mac as in the
other clients, including Outlook on the web, Windows, iOS, and Android, without
customizing the JavaScript for each client. The same calls from the add-in to the Office
JavaScript API generally work the same way, except for the areas described in the
following table.

For more information, see Deploy and install Outlook add-ins for testing.

For information about new UI support, see Add-in support in Outlook on new Mac UI.

Area Outlook on the web, Windows, and mobile Outlook on Mac


devices

Supported All APIs in Office.js. All APIs in Office.js.


versions of
office.js NOTE: In Outlook on Mac, only build
16.35.308 or later supports saving a
meeting. Otherwise, the saveAsync
method fails when called from a
meeting in compose mode. See
Cannot save a meeting as a draft in
Outlook for Mac by using Office JS
API for a workaround.

Instances of a Can get the item ID and other Can get the item ID and other
recurring properties of a master appointment or properties of the master
appointment appointment instance of a recurring appointment, but not those of
series series. an instance of a recurring
Can use series.
mailbox.displayAppointmentForm to Can display the master
display an instance or the master of a appointment of a recurring
recurring series. series. Without the item ID,
cannot display an instance of a
recurring series.

Recipient type of Can use EmailAddressDetails.recipientType to EmailAddressDetails.recipientType


an appointment identify the recipient type of an attendee. returns undefined for appointment
attendee attendees.
Area Outlook on the web, Windows, and mobile Outlook on Mac
devices

Version string of The format of the version string returned by An example of the version string
the client diagnostics.hostVersion depends on the returned by Diagnostics.hostVersion
application actual type of client. For example: on Outlook on Mac: 15.0 (140325)

Outlook on Windows: 15.0.4454.1002


Outlook on the web: 15.0.918.2

Custom If the network goes down, an add-in can still Because Outlook on Mac does not
properties of an access cached custom properties. cache custom properties, if the
item network goes down, add-ins would
not be able to access them.

Attachment The content type and attachment names in an A JSON example of


details AttachmentDetails object depend on the type AttachmentDetails.contentType :
of client: "contentType" "image/png"
AttachmentDetails.name always
A JSON example of
includes a filename extension.
AttachmentDetails.contentType :
Attachments that are mail items
"contentType": "image/x-png" .
have a .eml extension, and
AttachmentDetails.name does not
appointments have a .ics
contain any filename extension. As an extension. As an example, if an
example, if the attachment is a attachment is an email with the
message that has the subject "RE: subject "RE: Summer activity",
Summer activity", the JSON object that the JSON object that represents
represents the attachment name would the attachment name would be
be "name": "RE: Summer activity" . "name": "RE: Summer
activity.eml" .

NOTE: If a file is
programmatically attached (e.g
through an add-in) without an
extension then the
AttachmentDetails.name will not
contain the extension as part of
filename.

String As an example: Thu Mar 13 2014 14:09:11 As an example: Thu Mar 13 2014
representing the GMT+0800 (China Standard Time) 14:09:11 GMT+0800 (CST)
time zone in the
dateTimeCreated
and
dateTimeModified
properties
Area Outlook on the web, Windows, and mobile Outlook on Mac
devices

Time accuracy of If an add-in uses the following code, the The accuracy is up to only a second.
dateTimeCreated accuracy is up to a millisecond.
and JSON.stringify(Office.context.mailbox.item,
dateTimeModified null, 4);

Add-in support in Outlook on new Mac UI


Outlook add-ins are now supported in the new Mac UI (available from Outlook version
16.38.506). For requirement sets currently supported in the new Mac UI, see Outlook API
requirement set client support.

To learn more about the new Mac UI, see The new Outlook for Mac .

You can determine which UI version you're on, as follows:

Classic UI

New UI
Tips for handling date values in Outlook add-ins
Article • 07/11/2022

The Office JavaScript API uses the JavaScript Date object for most of the storage and retrieval of dates and times.

That Date object provides methods such as getUTCDate , getUTCHour , getUTCMinutes , and toUTCString , which return the
requested date or time value according to Universal Coordinated Time (UTC) time.

The Date object also provides other methods such as getDate , getHour , getMinutes , and toString , which return the
requested date or time according to "local time".

The concept of "local time" is largely determined by the browser and operating system on the client computer. For instance, on
most browsers running on a Windows-based client computer, a JavaScript call to getDate , returns a date based on the time zone
set in Windows on the client computer.

The following example creates a Date object myLocalDate in local time, and calls toUTCString to convert that date to a date string
in UTC.

JavaScript

// Create and get the current date represented


// in the client computer time zone.
const myLocalDate = new Date ();

// Convert the Date value in the client computer time zone


// to a date string in UTC, and display the string.
document.write ("The current UTC time is " +
myLocalDate.toUTCString());

While you can use the JavaScript Date object to get a date or time value based on UTC or the client computer time zone, the Date
object is limited in one respect - it does not provide methods to return a date or time value for any other specific time zone. For
example, if your client computer is set to be on Eastern Standard Time (EST), there is no Date method that allows you to get the
hour value other than in EST or UTC, such as Pacific Standard Time (PST).

Date-related features for Outlook add-ins


The aforementioned JavaScript limitation has an implication for you, when you use the Office JavaScript API to handle date or time
values in Outlook add-ins that run in an Outlook rich client, and in Outlook on the web or mobile devices.

Time zones for Outlook clients


For clarity, let's define the time zones in question.

Time zone Description

Client computer This is set on the operating system of the client computer. Most browsers use the client computer time zone to display date or
time zone time values of the JavaScript Date object.

An Outlook rich client uses this time zone to display date or time values in the user interface.

For example, on a client computer running Windows, Outlook uses the time zone set on Windows as the local time zone. On
the Mac, if the user changes the time zone on the client computer, Outlook on Mac would prompt the user to update the time
zone in Outlook as well.

Exchange Admin The user sets this time zone value (and the preferred language) when he or she logs on to Outlook on the web or mobile
Center (EAC) devices the first time.
time zone
Outlook on the web and mobile devices use this time zone to display date or time values in the user interface.

Because an Outlook rich client uses the client computer time zone, and the user interface of Outlook on the web and mobile
devices uses the EAC time zone, the local time for the same add-in installed for the same mailbox can be different when running in
an Outlook rich client and in Outlook on the web or mobile devices. As an Outlook add-in developer, you should appropriately
input and output date values so that those values are always consistent with the time zone that the user expects on the
corresponding client.

Date-related API
The following are the properties and methods in the Office JavaScript API that support date-related features.

API member Time zone representation Example in an Outlook Example in Outlook on


rich client the web or mobile
devices

Office.context.mailbox.userProfile.timeZone In an Outlook rich client, this property EST PST


returns the client computer time zone.
In Outlook on the web and mobile
devices, this property returns the EAC
time zone.

Office.context.mailbox.item.dateTimeCreated and Each of these properties returns a If the item is created at 9 If the item creation time is
Office.context.mailbox.item.dateTimeModified JavaScript Date object. This Date AM UTC: 9 AM UTC:
value is UTC-correct, as shown in the
following example - myUTCDate has Office.mailbox.item. Office.mailbox.item.
the same value in an Outlook rich dateTimeCreated.getHours dateTimeCreated.getHours
client, Outlook on the web and mobile returns 4am EST. returns 4am EST.
devices.
If the item is modified at If the item is modified at
const myDate = 11 AM UTC: 11 AM UTC:
Office.mailbox.item.dateTimeCreated;
const myUTCDate = Office.mailbox.item. Office.mailbox.item.

myDate.getUTCDate; dateTimeModified.getHours dateTimeModified.getHours


returns 6am EST. returns 6am EST.
However, calling myDate.getDate
returns a date value in the client Notice that if you want to
computer time zone, which is display the creation or
consistent with the time zone used to modification time in the
display date times values in the user interface, you would
Outlook rich client interface, but may want to first convert the
be different from the EAC time zone time to PST to be
that Outlook on the web and mobile consistent with the rest of
devices use in its user interface. the user interface.

Office.context.mailbox.displayNewAppointmentForm Each of the Start and End parameters If the start and end times If the start and end times
requires a JavaScript Date object. The for the appointment form for the appointment form
arguments should be UTC-correct are 9 AM UTC and 11 AM are 9 AM UTC and 11 AM
regardless of the time zone used in UTC, then you should UTC, then you should
the user interface of an Outlook rich ensure that the start and ensure that the start and
client, or Outlook on the web or end arguments are UTC- end arguments are UTC-
mobile devices. correct, which means: correct, which means:

start.getUTCHours start.getUTCHours
returns 9am UTC returns 9am UTC
end.getUTCHours end.getUTCHours
returns 11am UTC returns 11am UTC

Helper methods for date-related scenarios


As described in the preceding sections, because the "local time" for a user in Outlook on the web or mobile devices can be
different on an Outlook rich client, but the JavaScript Date object supports converting to only the client computer time zone or
UTC, the Office JavaScript API provides two helper methods: Office.context.mailbox.convertToLocalClientTime and
Office.context.mailbox.convertToUtcClientTime.

These helper methods take care of any need to handle date or time differently for the following two date-related scenarios, in an
Outlook rich client, Outlook on the web and mobile devices, thus reinforcing "write-once" for different clients of your add-in.

Scenario A: Displaying item creation or modified time


If you are displaying the item creation time ( Item.dateTimeCreated ) or modification time ( Item.dateTimeModified in the user
interface, first use convertToLocalClientTime to convert the Date object provided by these properties to obtain a dictionary
representation in the appropriate local time. Then display the parts of the dictionary date. The following is an example of this
scenario.

JavaScript

// This date is UTC-correct.


const myDate = Office.context.mailbox.item.dateTimeCreated;

// Call helper method to get date in dictionary format,


// represented in the appropriate local time.
// In an Outlook rich client, this is dictionary format
// in client computer time zone.
// In Outlook on the web or mobile devices, this dictionary
// format is in EAC time zone.
const myLocalDictionaryDate = Office.context.mailbox.convertToLocalClientTime(myDate);

// Display different parts of the dictionary date.


document.write ("The item was created at " + myLocalDictionaryDate["hours"] +
":" + myLocalDictionaryDate["minutes"]);)

Note that convertToLocalClientTime takes care of the difference between an Outlook rich client, and Outlook on the web or
mobile devices:

If convertToLocalClientTime detects the current application is a rich client, the method converts the Date representation to a
dictionary representation in the same client computer time zone, consistent with the rest of the rich client user interface.

If convertToLocalClientTime detects the current application is Outlook on the web or mobile devices, the method converts
the UTC-correct Date representation to a dictionary format in the EAC time zone, consistent with the rest of the Outlook on
the web or mobile devices user interface.

Scenario B: Displaying start and end dates in a new appointment form


If you are obtaining as input different parts of a date-time value represented in the local time, and would like to provide this
dictionary input value as a start or end time in an appointment form, first use the convertToUtcClientTime helper method to
convert the dictionary value to a UTC-correct Date object.

In the following example, assume myLocalDictionaryStartDate and myLocalDictionaryEndDate are date-time values in dictionary
format that you have obtained from the user. These values are based on the local time, dependent on the client platform.

JavaScript

const myUTCCorrectStartDate = Office.context.mailbox.convertToUtcClientTime(myLocalDictionaryStartDate);


const myUTCCorrectEndDate = Office.context.mailbox.convertToUtcClientTime(myLocalDictionaryEndDate);

The resultant values, myUTCCorrectStartDate and myUTCCorrectEndDate , are UTC-correct. Then pass these Date objects as
arguments for the Start and End parameters of the Mailbox.displayNewAppointmentForm method to display the new appointment
form.

Note that convertToUtcClientTime takes care of the difference between an Outlook rich client, and Outlook on the web or mobile
devices:

If convertToUtcClientTime detects the current application is an Outlook rich client, the method simply converts the dictionary
representation to a Date object. This Date object is UTC-correct, as expected by displayNewAppointmentForm .

If convertToUtcClientTime detects the current application is Outlook on the web or mobile devices, the method converts the
dictionary format of the date and time values expressed in the EAC time zone to a Date object. This Date object is UTC-
correct, as expected by displayNewAppointmentForm .

See also
Deploy and install Outlook add-ins for testing
Limits for activation and JavaScript API
for Outlook add-ins
Article • 07/05/2023

To provide a satisfactory experience for users of Outlook add-ins, you should be aware of
certain activation and API usage guidelines, and implement your add-ins to stay within
these limits. These guidelines exist so that an individual add-in cannot require Exchange
Server or Outlook to spend an unusually long period of time to process its activation rules
or calls to the Office JavaScript API, affecting the overall user experience for Outlook and
other add-ins. These limits apply to designing activation rules in the add-in manifest, and
using custom properties, roaming settings, recipients, Exchange Web Services (EWS)
requests and responses, and asynchronous calls.

7 Note

If your add-in runs on an Outlook rich client, you must also verify that the add-in
performs within certain runtime resource usage limits.

Limits on where add-ins activate


To learn more about where add-ins do and don't activate, refer to the Mailbox items
available to add-ins section of the Outlook add-ins overview page.

Limits for activation rules

7 Note

Outlook Add-in features that depend on activation rules aren't supported when the
add-in uses a Unified manifest for Microsoft 365 (preview).

Follow these guidelines when designing activation rules for Outlook add-ins:

Limit the size of the manifest to 256 KB. You can't install the Outlook add-in for an
Exchange mailbox if you exceed that limit.

Specify up to 15 activation rules for the add-in. You can't install the add-in if you
exceed that limit.
If you use an ItemHasKnownEntity rule on the body of the selected item, expect an
Outlook rich client to apply the rule against only the first 1 MB of the body and not
to the rest of the body over that limit. Your add-in wouldn't activate if matches exist
only after the first MB of the body. If you expect that to be a likely scenario, re-
design your conditions for activation.

If you use regular expressions in ItemHasKnownEntity or


ItemHasRegularExpressionMatch rules, be aware of the following limits and
guidelines that generally apply to any Outlook application, and those described in
tables 1, 2 and 3 that differ depending on the application.
Specify up to only five regular expressions in activation rules in an add-in. You
can't install an add-in if you exceed that limit.
Specify regular expressions such that the results you anticipate are returned by
the getRegExMatches method call within the first 50 matches.
Important: Text is highlighted based on strings that result from matching the
regular expression. However, the highlighted occurrences may not exactly match
what should result from actual regular expression assertions like negative look-
ahead (?!text) , look-behind (?<=text) , and negative look-behind (?<!text) .
For example, if you use the regular expression under(?!score) on "Like under,
under score, and underscore", the string "under" is highlighted for all occurrences
instead of just the first two.

Table 1 lists the limits and describes the differences in the support for regular expressions
between an Outlook rich client and Outlook on the web or mobile devices. The support is
independent of any specific type of device and item body.

Table 1. General differences in the support for regular expressions

Outlook rich client Outlook on the web or mobile devices

Uses the C++ regular expression engine Uses regular expression evaluation that is part
provided as part of the Visual Studio standard of JavaScript, is provided by the browser, and
template library. This engine complies with supports a superset of ECMAScript 5.
ECMAScript 5 standards.
Outlook rich client Outlook on the web or mobile devices

Because of the different regex engines, expect a You should test each regex thoroughly on each
regex that includes a custom character class Outlook client, and if a regex returns different
based on predefined character classes to return results, rewrite the regex.
different results in an Outlook rich client than in
Outlook on the web or mobile devices.

As an example, the regex [\s\S]{0,100} matches


any number, between 0 and 100, of single
characters that is a whitespace or a non-
whitespace. This regex returns different results in
an Outlook rich client than Outlook on the web
and mobile devices.

You should rewrite the regex as (\s\|\S){0,100}


as a workaround. This workaround regex
matches any number, between 0 and 100, of
white space or non-white space.

You should test each regex thoroughly on each


Outlook client, and if a regex returns different
results, rewrite the regex.

By default, limits the evaluation of all regular Don't support the same resource monitoring or
expressions for an add-in to 1 second. Exceeding registry settings as in an Outlook rich client.
this limit causes reevaluation of up to 3 times. However, add-ins with regular expressions that
Beyond the reevaluation limit, an Outlook rich require excessive amount of evaluation time on
client disables the add-in from running for the an Outlook rich client are disabled for the same
same mailbox in any of the Outlook clients. mailbox on all the Outlook clients.

Administrators can override these evaluation


limits by using the
OutlookActivationAlertThreshold and
OutlookActivationManagerRetryLimit registry
keys.

Table 2 lists the limits and describes the differences in the portion of the item body that
the each of the Outlook applies a regular expression. Some of these limits depend on the
type of device and item body, if the regular expression is applied on the item body.

Table 2. Limits on the size of the item body evaluated

Outlook rich client Outlook on mobile Outlook on


devices the web
Outlook rich client Outlook on mobile Outlook on
devices the web

Form Any supported device. Android smartphones, Any


factor iPad, or iPhone. supported
device other
than Android
smartphones,
iPad, and
iPhone.

Plain Applies the regex on the first 1 MB of the Activates the add-in only Activates the
text data of the body, but not on the rest of the if the body < 16,000 add-in only if
item body over that limit. characters. the body <
body 500,000
characters.

HTML Applies the regex on the first 512 KB of the Applies the regex on the Activates the
item data of the body, but not on the rest of the first 64,000 characters add-in only if
body body over that limit. (The actual number of (including HTML tag the body <
characters depends on the encoding which characters), but not on 500,000
can range from 1 to 4 bytes per character.) the rest of the body over characters.
that limit.

Table 3 lists the limits and describes the differences in the matches that each Outlook
client returns after evaluating a regular expression. The support is independent of any
specific type of device, but may depend on the type of item body, if the regular
expression is applied on the item body.

Table 3. Limits on the matches returned

Outlook rich client Outlook on the web or


mobile devices

Order of Assume getRegExMatches returns matches for the same Assume getRegExMatches
returned regular expression applied on the same item to be returns matches in
matches different in an Outlook rich client than in Outlook on the different order in an
web or mobile devices. Outlook rich client than in
Outlook on the web or
mobile devices.

Plain getRegExMatches returns any matches that are up to 1,536 getRegExMatches returns
text (1.5 KB) characters, for a maximum of 50 matches. any matches that are up
item to 3,072 (3 KB) characters,
body Note: getRegExMatches doesn't return matches in any for a maximum of 50
specific order in the returned array. In general, assume the matches.
order of matches in an Outlook rich client for the same
regular expression applied on the same item is different
from that in Outlook on the web and mobile devices.
Outlook rich client Outlook on the web or
mobile devices

HTML getRegExMatches returns any matches that are up to 3,072 getRegExMatches returns
item (3 KB) characters, for a maximum of 50 matches. any matches that are up
body to 3,072 (3 KB) characters,
Note: getRegExMatches does not return matches in any for a maximum of 50
specific order in the returned array. In general, assume the matches.
order of matches in an Outlook rich client for the same
regular expression applied on the same item is different
from that in Outlook on the web and mobile devices.

Limits for JavaScript API


Aside from the preceding guidelines for activation rules, each Outlook client enforces
certain limits in the JavaScript object model, as described in Table 4.

Table 4. Limits to get or set certain data using the Office JavaScript API

Feature Limit Related API Description

Custom 2,500 CustomProperties object Limit for all custom


properties characters properties for an
Item.loadCustomPropertiesAsync method appointment or message
item. All the Outlook clients
return an error if the total
size of all custom properties
of an add-in exceeds this
limit.

Roaming 32 KB number RoamingSettings object Limit for all roaming settings


settings of characters for the add-in. All the
Context.roamingSettings property Outlook clients return an
error if your settings exceed
this limit.
Feature Limit Related API Description

Internet 256 KB per InternetHeaders.setAsync method The total size limit of headers
headers message in that can be applied to a
Exchange message.
Online

Header size
limit
determined
by the
organization's
administrators
in Exchange
on-premises

Extract well- 2,000 number Item.getEntities method Limit for Exchange Server to
known of characters extract well-known entities
entities Item.getEntitiesByType method on the item body. Exchange
Server ignores entities
Item.getFilteredEntitiesByName method beyond that limit. Note that
this limit is independent of
whether the add-in uses an
ItemHasKnownEntity rule.

Exchange 1 MB number Mailbox.makeEwsRequestAsync method Limit for a request or


Web Services of characters response to a
Mailbox.makeEwsRequestAsync
call.

Item multi- 100 messages Mailbox.getSelectedItemsAsync method The maximum number of


select selected messages on which
an Outlook add-in can
activate.

Recipients Varies across Item.requiredAttendees property Limit for the recipients


Outlook specified in each property or
clients Item.optionalAttendees property method. To learn about the
recipient limits for a specific
Item.to property Outlook client, see the API
documentation for the
Item.cc property property or method.

Recipients.addAsync method

Recipients.getAsync method

Recipients.setAsync method
Feature Limit Related API Description

Display name 255 EmailAddressDetails.displayName Limit for the length of a


characters property display name in an
appointment or message.
Recipients object

Item.requiredAttendees property

Item.optionalAttendees property

Item.to property

Item.cc property

Set the 255 Mailbox.displayNewAppointmentForm Limit for the subject in the


subject characters method new appointment form, or
for setting the subject of an
Subject.setAsync method appointment or message.

Set the 255 Location.setAsync method Limit for setting the location
location characters of an appointment or
meeting request.

Body in a new 32 KB number Mailbox.displayNewAppointmentForm Limit for the body in a new


appointment of characters method appointment form.
form

Display the 32 KB number Mailbox.displayAppointmentForm For Outlook on the web and


body of an of characters method mobile devices: limit for the
existing item body in an existing
Mailbox.displayMessageForm method appointment or message
form.

Set the body 1 MB number Body.prependAsync method Limit for setting the body of
of characters an appointment or message
Body.setAsync item.

Body.setSelectedDataAsync method

Set the 30,000 Body.setSignatureAsync method Limit for the length of a


signature characters signature in an appointment
or message.
Feature Limit Related API Description

Number of 499 files on Item.addFileAttachmentAsync method Limit for the number of files
attachments Outlook on that can be attached to an
the web and item for sending. Outlook on
mobile the web and mobile devices
devices generally limit attaching up
to 499 files, through the user
interface and
addFileAttachmentAsync . An
Outlook rich client does not
specifically limit the number
of file attachments. However,
all Outlook clients observe
the limit for the size of
attachments that user's
Exchange Server has been
configured with. See the next
row for "Size of
attachments".

Size of Dependent on Item.addFileAttachmentAsync method There is a limit on the size of


attachments Exchange all the attachments for an
Server item, which an administrator
can configure on the
Exchange Server of the user's
mailbox.For an Outlook rich
client, this limits the number
of attachments for an item.
For Outlook on the web and
mobile devices, the lesser of
the two limits - the number
of attachments and the size
of all attachments - restricts
the actual attachments for an
item.

Attachment 255 Item.addFileAttachmentAsync method Limit for the length of the


filename characters filename of an attachment to
be added to an item.

Attachment 2,048 Item.addFileAttachmentAsync method Limit of the URI of the


URI characters filename to be added as an
Item.addFileAttachmentFromBase64Async attachment to an item.
method

Base64- 27,892,122 Item.addFileAttachmentFromBase64Async Limit of the Base64-encoded


encoded characters method string to be added as an
string of an (about 25 MB) attachment to an item.
attachment
Feature Limit Related API Description

Attachment 100 Item.addItemAttachmentAsync method Limit for the length of the ID


ID characters of the attachment to be
Item.removeAttachmentAsync method added to or removed from
an item.

Asynchronous 3 calls Item.addFileAttachmentAsync method For Outlook on the web or


calls mobile devices: limit of the
Item.addItemAttachmentAsync method number of simultaneous
asynchronous calls at any
Item.removeAttachmentAsync method one time, as browsers allow
only a limited number of
Body.getTypeAsync method asynchronous calls to servers.

Body.prependAsync method

Body.setSelectedDataAsync method

CustomProperties.saveAsync method

Item.LoadCustomPropertiesAsync method

Location.getAsync method

Location.setAsync method

Mailbox.getCallbackTokenAsync method

Mailbox.getUserIdentityTokenAsync
method

Mailbox.makeEwsRequestAsync method

Recipients.addAsync method

Recipients.getAsync method

Recipients.setAsync method

RoamingSettings.saveAsync method

Subject.getAsync method

Subject.setAsync method

Time.getAsync method

Time.setAsync method
Feature Limit Related API Description

Append-on- 5,000 Body.appendOnSendAsync method Limit of the content to be


send characters appended to a message or
appointment body on send.

Prepend-on- 5,000 Body.prependOnSendAsync method Limit of the content to be


send characters prepended to a message or
appointment body on send.

See also
Deploy and install Outlook add-ins for testing
Privacy, permissions, and security for Outlook add-ins
Troubleshoot Outlook add-in activation
Article • 03/21/2023

Outlook contextual add-in activation is based on the activation rules in an XML manifest
for the add-in. When conditions for the currently selected item satisfy the activation
rules for the add-in, the application activates and displays the add-in button in the
Outlook UI (add-in selection pane for compose add-ins, add-in bar for read add-ins).
However, if your add-in doesn't activate as you expect, you should look into the
following areas for possible reasons.

7 Note

Outlook Add-in features that depend on activation rules aren't supported when the
add-in uses a Unified manifest for Microsoft 365 (preview).

Is user mailbox on a version of Exchange Server


that is at least Exchange 2013?
First, ensure that the user's email account you're testing with is on a version of Exchange
Server that is at least Exchange 2013. If you are using specific features that are released
after Exchange 2013, make sure the user's account is on the appropriate version of
Exchange.

You can verify the version of Exchange 2013 by using one of the following approaches.

Check with your Exchange Server administrator.

If you are testing the add-in on Outlook on the web or mobile devices, in a script
debugger (for example, the JScript Debugger that comes with Internet Explorer),
look for the src attribute of the script tag that specifies the location from which
scripts are loaded. The path should contain a substring owa/15.0.516.x/owa2/...,
where 15.0.516.x represents the version of the Exchange Server, such as 15.0.516.2.

Alternatively, you can use the Office.context.mailbox.diagnostics.hostVersion


property to verify the version. On Outlook on the web and mobile devices, this
property returns the version of the Exchange Server.

If you can test the add-in on Outlook, you can use the following simple debugging
technique that uses the Outlook object model and Visual Basic Editor.
1. First, verify that macros are enabled for Outlook. Choose File, Options, Trust
Center, Trust Center Settings, Macro Settings. Ensure that Notifications for
all macros is selected in the Trust Center. You should have also selected
Enable Macros during Outlook startup.

2. On the Developer tab of the ribbon, choose Visual Basic.

7 Note

Not seeing the Developer tab? See How to: Show the Developer Tab on
the Ribbon to turn it on.

3. In the Visual Basic Editor, choose View, Immediate Window.

4. Type the following in the Immediate window to display the version of the
Exchange Server. The major version of the returned value must be equal to or
greater than 15.
If there is only one Exchange account in the user's profile:

VB

?Session.ExchangeMailboxServerVersion

If there are multiple Exchange accounts in the same user profile


( emailAddress represents a string that contains the user's primary SMTP
address):

VB

?Session.Accounts.Item(emailAddress).ExchangeMailboxServerVersion

Is the add-in disabled?


Any one of the Outlook rich clients can disable an add-in for performance reasons,
including exceeding usage thresholds for CPU core or memory, tolerance for crashes,
and length of time to process all the regular expressions for an add-in. When this
happens, the Outlook rich client displays a notification that it is disabling the add-in.

7 Note

Only Outlook rich clients monitor resource usage, but disabling an add-in in an
Outlook rich client also disables the add-in in Outlook on the web and mobile
devices.

Check your list of installed add-ins to verify whether an add-in is disabled.

For instructions on how to view your add-ins in Outlook on Windows or on Mac,


see Get an Office Add-in for Outlook .

In Outlook on the web, select Get Add-ins from the ribbon. To learn more, see
Using add-ins in Outlook on the web .

Does the tested item support Outlook add-ins?


Is the selected item delivered by a version of
Exchange Server that is at least Exchange 2013?
If your Outlook add-in is a read add-in and is supposed to be activated when the user is
viewing a message (including email messages, meeting requests, responses, and
cancellations) or appointment, even though these items generally support add-ins, there
are exceptions. Check if the selected item is one of those listed where Outlook add-ins
don't activate.

Also, because appointments are always saved in Rich Text Format, an


ItemHasRegularExpressionMatch rule that specifies a PropertyName value of
BodyAsHTML wouldn't activate an add-in on an appointment or message that's saved
in plain text or Rich Text Format.

Even if a mail item is not one of the above types, if the item wasn't delivered by a
version of Exchange Server that's at least Exchange 2013, known entities and properties,
such as sender's SMTP address, wouldn't be identified on the item. Any activation rules
that rely on these entities or properties wouldn't be satisfied, and the add-in wouldn't
be activated.

In Outlook on clients besides Windows, if your add-in activates when the user is
composing a message or meeting request, make sure the item isn't protected by
Information Rights Management (IRM).

) Important

Add-ins activate on digitally signed messages in Outlook on Windows


associated with a Microsoft 365 subscription. This support was introduced
with Version 1711 (Build 8711.1000).
Starting with Outlook Version 2009 (Build 13229.10000) on Windows, add-ins
can now activate on IRM-protected items. To turn on this capability, a tenant
administrator must enable the OBJMODEL usage right by setting the Allow
programmatic access custom policy option in Office. For further guidance,
see Usage rights and descriptions.

Is the add-in manifest installed properly, and


does Outlook have a cached copy?
This scenario applies to only Outlook on Windows. Normally, when you install an
Outlook add-in for a mailbox, the Exchange Server copies the add-in manifest from the
location you indicate to the mailbox on that Exchange Server. Every time Outlook starts,
it reads all the manifests installed for that mailbox into a temporary cache at the
following location.

text

%LocalAppData%\Microsoft\Office\16.0\WEF

For example, for the user John, the cache might be at


C:\Users\john\AppData\Local\Microsoft\Office\16.0\WEF.

) Important

For Outlook 2013 on Windows, use 15.0 instead of 16.0 so the location would be:

text

%LocalAppData%\Microsoft\Office\15.0\WEF

If an add-in does not activate for any items, the manifest might not have been installed
properly on the Exchange Server, or Outlook has not read the manifest properly on
startup. Using the Exchange Admin Center, ensure that the add-in is installed and
enabled for your mailbox, and reboot the Exchange Server, if necessary.

Figure 1 shows a summary of the steps to verify whether Outlook has a valid version of
the manifest.
Figure 1. Flow chart of the steps to verify whether Outlook properly cached the
manifest

The following procedure describes the details.

1. If you have modified the manifest while Outlook is open, and you are not using
Visual Studio 2012 or a later version of Visual Studio to develop the add-in, you
should uninstall the add-in and reinstall it using the Exchange Admin Center.

2. Restart Outlook and test whether Outlook now activates the add-in.

3. If Outlook doesn't activate the add-in, check whether Outlook has a properly
cached copy of the manifest for the add-in. Look under the following path.

text
%LocalAppData%\Microsoft\Office\16.0\WEF

You can find the manifest in the following subfolder.

text

\<insert your guid>\<insert base 64 hash>\Manifests\


<ManifestID>_<ManifestVersion>

7 Note

The following is an example of a path to a manifest installed for a mailbox for


the user John.

text

C:\Users\john\appdata\Local\Microsoft\Office\16.0\WEF\{8D8445A4-
80E4-4D6B-B7AC-
D4E6AF594E73}\GoRshCWa7vW8+jhKmyiDhA==\Manifests\b3d7d9d5-6f57-
437d-9830-94e2aaccef16_1.2

Verify whether the manifest of the add-in you're testing is among the cached
manifests.

4. If the manifest is in the cache, skip the rest of this section and consider the other
possible reasons following this section.

5. If the manifest is not in the cache, check whether Outlook indeed successfully read
the manifest from the Exchange Server. To do that, use the Windows Event Viewer:

a. Under Windows Logs, choose Application.

b. Look for a reasonably recent event for which the Event ID equals 63, which
represents Outlook downloading a manifest from an Exchange Server.

c. If Outlook successfully read a manifest, the logged event should have the
following description.

text

The Exchange web service request GetAppManifests succeeded.


Then skip the rest of this section and consider the other possible reasons
following this section.

6. If you don't see a successful event, close Outlook, and delete all the manifests in
the following path.

text

%LocalAppData%\Microsoft\Office\16.0\WEF\<insert your guid>\<insert


base 64 hash>\Manifests\

Start Outlook and test whether Outlook now activates the add-in.

7. If Outlook doesn't activate the add-in, go back to Step 3 to verify again whether
Outlook has properly read the manifest.

Is the add-in manifest valid?


See Validate and troubleshoot issues with your manifest to debug add-in manifest
issues.

Are you using the appropriate activation rules?


Starting in version 1.1 of the Office Add-ins manifests schema, you can create add-ins
that are activated when the user is in a compose form (compose add-ins) or in a read
form (read add-ins). Make sure you specify the appropriate activation rules for each type
of form that your add-in is supposed to activate in. For example, you can activate
compose add-ins using only ItemIs rules with the FormType attribute set to Edit or
ReadOrEdit, and you cannot use any of the other types of rules, such as
ItemHasKnownEntity and ItemHasRegularExpressionMatch rules for compose add-ins.
For more information, see Activation rules for Outlook add-ins.

If you use a regular expression, is it properly


specified?
Because regular expressions in activation rules are part of the XML manifest file for a
read add-in, if a regular expression uses certain characters, be sure to follow the
corresponding escape sequence that XML processors support. Table 1 lists these special
characters.

Table 1. Escape sequences for regular expressions


Character Description Escape sequence to use

" Double quotation mark &quot;

& Ampersand &amp;

' Apostrophe &apos;

< Less-than sign &lt;

> Greater-than sign &gt;

If you use a regular expression, is the read add-


in activating in Outlook on the web or mobile
devices, but not in any of the Outlook rich
clients?
Outlook rich clients use a regular expression engine that's different from the one used
by Outlook on the web and mobile devices. Outlook rich clients use the C++ regular
expression engine provided as part of the Visual Studio standard template library. This
engine complies with ECMAScript 5 standards. Outlook on the web and mobile devices
use regular expression evaluation that is part of JavaScript, is provided by the browser,
and supports a superset of ECMAScript 5.

While in most cases, these Outlook clients find the same matches for the same regular
expression in an activation rule, there are exceptions. For instance, if the regex includes a
custom character class based on predefined character classes, an Outlook rich client
may return results different from Outlook on the web and mobile devices. As an
example, character classes that contain shorthand character classes [\d\w] within them
would return different results. In this case, to avoid different results on different
applications, use (\d|\w) instead.

Test your regular expression thoroughly. If it returns different results, rewrite the regular
expression for compatibility with both engines. To verify evaluation results on an
Outlook rich client, write a small C++ program that applies the regular expression
against a sample of the text you are trying to match. Running on Visual Studio, the C++
test program would use the standard template library, simulating the behavior of the
Outlook rich client when running the same regular expression. To verify evaluation
results on Outlook on the web or mobile devices, use your favorite JavaScript regular
expression tester.
If you use an ItemIs, ItemHasAttachment, or
ItemHasRegularExpressionMatch rule, have you
verified the related item property?
If you use an ItemHasRegularExpressionMatch activation rule, verify whether the value
of the PropertyName attribute is what you expect for the selected item. The following
are some tips to debug the corresponding properties.

If the selected item is a message and you specify BodyAsHTML in the


PropertyName attribute, open the message, and then choose View Source to
verify the message body in the HTML representation of that item.

If the selected item is an appointment, or if the activation rule specifies


BodyAsPlaintext in the PropertyName, you can use the Outlook object model and
the Visual Basic Editor in Outlook on Windows:

1. Ensure that macros are enabled and the Developer tab is displayed on the
ribbon for Outlook.

2. In the Visual Basic Editor, choose View, Immediate Window.

3. Type the following to display various properties depending on the scenario.


The HTML body of the message or appointment item selected in the
Outlook explorer:

VB

?ActiveExplorer.Selection.Item(1).HTMLBody

The plain text body of the message or appointment item selected in the
Outlook explorer:

VB

?ActiveExplorer.Selection.Item(1).Body

The HTML body of the message or appointment item opened in the


current Outlook inspector:

VB

?ActiveInspector.CurrentItem.HTMLBody
The plain text body of the message or appointment item opened in the
current Outlook inspector:

VB

?ActiveInspector.CurrentItem.Body

If the ItemHasRegularExpressionMatch activation rule specifies Subject or


SenderSMTPAddress, or if you use an ItemIs or ItemHasAttachment rule, and you are
familiar with or would like to use MAPI, you can use MFCMAPI to verify the value in
Table 2 that your rule relies on.

Table 2. Activation rules and corresponding MAPI properties

Type of rule Verify this MAPI property

ItemHasRegularExpressionMatch rule with PidTagSubject


Subject

ItemHasRegularExpressionMatch rule with PidTagSenderSmtpAddress and


SenderSMTPAddress PidTagSentRepresentingSmtpAddress

ItemIs PidTagMessageClass

ItemHasAttachment PidTagHasAttachments

After verifying the property value, you can then use a regular expression evaluation tool
to test whether the regular expression finds a match in that value.

Does Outlook apply all the regular expressions


to the portion of the item body as you expect?
This section applies to all activation rules that use regular expressions -- particularly
those that are applied to the item body, which may be large in size and take longer to
evaluate for matches. You should be aware that even if the item property that an
activation rule depends on has the value you expect, Outlook may not be able to
evaluate all the regular expressions on the entire value of the item property. To provide
reasonable performance and to control excessive resource usage by a read add-in,
Outlook observes the following limits on processing regular expressions in activation
rules at run time.

The size of the item body evaluated -- There are limits to the portion of an item
body on which Outlook evaluates a regular expression. These limits depend on the
Outlook client, form factor, and format of the item body. See the details in Table 2
in Limits for activation and JavaScript API for Outlook add-ins.

Number of regular expression matches -- The Outlook rich clients, and Outlook on
the web and mobile devices each returns a maximum of 50 regular expression
matches. These matches are unique, and duplicate matches do not count against
this limit. Do not assume any order to the returned matches, and do not assume
the order in an Outlook rich client is the same as that in Outlook on the web and
mobile devices. If you expect many matches to regular expressions in your
activation rules, and you're missing a match, you may be exceeding this limit.

Length of a regular expression match -- There are limits to the length of a regular
expression match that the Outlook application would return. Outlook does not
include any match above the limit and does not display any warning message. You
can run your regular expression using other regex evaluation tools or a stand-
alone C++ test program to verify whether you have a match that exceeds such
limits. Table 3 summarizes the limits. For more information, see Table 3 in Limits for
activation and JavaScript API for Outlook add-ins.

Table 3. Length limits for a regular expression match

Limit on length of a regex Outlook rich Outlook on the web or mobile


match clients devices

Item body is plain text 1.5 KB 3 KB

Item body is HTML 3 KB 3 KB

Time spent on evaluating all regular expressions of a read add-in for an Outlook
rich client: By default, for each read add-in, Outlook must finish evaluating all the
regular expressions in its activation rules within 1 second. Otherwise Outlook
retries up to three times and disables the add-in if Outlook cannot complete the
evaluation. Outlook displays a message in the notification bar that the add-in has
been disabled. The amount of time available for your regular expression can be
modified by setting a group policy or a registry key.

7 Note

If the Outlook rich client disables a read add-in, the read add-in is not
available for use for the same mailbox on the Outlook rich client, and Outlook
on the web and mobile devices.
See also
Deploy and install Outlook add-ins for testing
Activation rules for Outlook add-ins
Use regular expression activation rules to show an Outlook add-in
Limits for activation and JavaScript API for Outlook add-ins
Validate and troubleshoot issues with your manifest
Debug function commands in Outlook
add-ins
Article • 07/05/2023

7 Note

The technique in this article can be used only on a Windows development


computer. If you're developing on a Mac, see Debug function commands.

This article describes how to use the Office Add-in Debugger Extension in Visual Studio
Code to debug function commands. Function commands are initiated through an add-in
command button on the ribbon. For more information about add-in commands, see
Add-in commands.

This article assumes that you already have an add-in project that you'd like to debug. To
create an add-in with a function command to practice debugging, follow the steps in
Tutorial: Build a message compose Outlook add-in.

Mark your add-in for debugging


If you used the Yeoman generator for Office Add-ins to create your add-in project, skip
to the Configure and run the debugger section later in this article. When you run npm
start to build your add-in and start the local server, the command also sets the

UseDirectDebugger value of the

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\Developer\[Add-in ID] registry


key to mark your add-in for debugging.

Otherwise, if you used another tool to create your add-in, perform the following steps.

1. Navigate to the
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\Developer\[Add-in ID]

registry key. Replace [Add-in ID] with the <Id> from your add-in's manifest.

7 Note

If the Developer key (folder) doesn't already exist under


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\ , complete the

following steps to create it.


a. Right-click the WEF key (folder) and select New > Key.
b. Name the new key Developer.

2. Set the key's UseDirectDebugger value to 1 .

Configure and run the debugger


Now that you've enabled debugging on your add-in, you're ready to configure and run
the debugger. For instructions on how to do this, select one of the following options
that applies to your webview control. For information about how to determine what
webview control is used on your development computer, see Browsers and webview
controls used by Office Add-ins.

If your add-in runs in the embedded webview control from Edge Legacy
(EdgeHTML), see Microsoft Office Add-in Debugger Extension for Visual Studio
Code.

If your add-in runs in the embedded webview control from Microsoft Edge
Chromium (WebView2), see Debug add-ins on Windows using Visual Studio Code
and Microsoft Edge WebView2 (Chromium-based).

See also
Add-in commands
Overview of debugging Office Add-ins
Debug your event-based Outlook add-in
Debug your event-based Outlook add-in
Article • 06/29/2023

This article discusses the key debugging stages to enable and set breakpoints in your
code as you implement event-based activation in your add-in. The event-based
activation feature was introduced in requirement set 1.10, with additional events now
available in subsequent requirement sets. For more information, see Supported events.
Before you proceed, review the event-based troubleshooting guide for additional
guidance.

To begin debugging, select the tab for your applicable client.

Windows

If you used the Yeoman generator for Office Add-ins to create your add-in project
(for example, by doing the event-based activation walkthrough), follow the Created
with Yeoman generator option throughout this article. Otherwise, follow the Other
steps. Visual Studio Code should be at least version 1.56.1.

Mark your add-in for debugging and set the


debugger port
1. Set the registry key
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Wef\Developer\[Add-in

ID]\UseDirectDebugger . Replace [Add-in ID] with your add-in's ID from the

manifest.

XML manifest: Use the value of the <Id> element child of the root
<OfficeApp> element.
Unified manifest for Microsoft 365 (preview): Use the value of the "id"
property of the root anonymous { ... } object.

7 Note

If the Developer key (folder) doesn't already exist under


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\ , complete the
following steps to create it.
a. Right-click the WEF key (folder) and select New > Key.
b. Name the new key Developer.
Created with Yeoman generator: In a command line window, navigate to the
root of your add-in folder then run the following command.

command line

npm start

In addition to building the code and starting the local server, this command
sets the UseDirectDebugger registry key for this add-in to 1 .

Other: Add the UseDirectDebugger registry key to


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\WEF\Developer\[Add-in

ID]\ . Replace [Add-in ID] with your add-in's ID from the manifest. Set the

registry key to 1 .

2. In the registry key


HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Wef\Developer\[Add-in
ID] , where [Add-in ID] is your add-in's ID from the manifest, create a new

DWORD value with the following configuration.

Value name: DebuggerPort


Value data (hexadecimal): 00002407

This sets the debugger port to 9223 .

3. Start Outlook or restart it if it's already open.

4. Perform the action to initiate the event you're developing for, such as creating
a new message to initiate the OnNewMessageCompose event. The Debug Event-
based handler dialog should appear. Do not interact with the dialog yet.
Configure Visual Studio Code

Created with Yeoman generator


1. Back in the command line window, run the following to open your add-in
project in Visual Studio Code.

command line

code .

2. In Visual Studio Code, open the ./.vscode/launch.json file and add the
following excerpt to your list of configurations. Save your changes.

JSON

{
"name": "Direct Debugging",
"type": "node",
"request": "attach",
"port": 9223,
"timeout": 600000,
"trace": true
}

Other
1. Create a new folder called Debugging (perhaps in your Desktop folder).

2. Open Visual Studio Code.

3. Go to File > Open Folder, navigate to the folder you just created, then choose
Select Folder.

4. On the Activity Bar, select Run and Debug (Ctrl+Shift+D).


5. Select the create a launch.json file link.

6. In the Select Environment dropdown, select Edge: Launch to create a


launch.json file.

7. Add the following excerpt to your list of configurations. Save your changes.

JSON

{
"name": "Direct Debugging",
"type": "node",
"request": "attach",
"port": 9223,
"timeout": 600000,
"trace": true
}

Attach the debugger


The bundle.js file of an add-in contains the JavaScript code of your add-in. It's
created when Outlook on Windows is opened. When Outlook starts, the bundle.js
file of each installed add-in is cached in the Wef folder of your machine.
1. To find the add-in's bundle.js file, navigate to the following folder in File
Explorer. Replace text enclosed in [] with your applicable Outlook and add-in
information.

text

%LOCALAPPDATA%\Microsoft\Office\16.0\Wef\{[Outlook profile GUID]}\


[Outlook mail account encoding]\Javascript\[Add-in ID]_[Add-in
Version]_[locale]

 Tip

If the bundle.js file doesn't appear in the Wef folder, try the following:

If your add-in is installed or sideloaded, restart Outlook.


Remove your add-in from Outlook, then sideload it again.

2. Open bundle.js in Visual Studio Code.

3. Place breakpoints in bundle.js where you want the debugger to stop.

4. In the DEBUG dropdown, select Direct Debugging, then select the Start
Debugging icon.

Run the debugger


1. After confirming that the debugger is attached, return to Outlook, and in the
Debug Event-based handler dialog, choose OK .

2. You can now hit your breakpoints in Visual Studio Code, enabling you to
debug your event-based activation code.
Stop the debugger
To stop debugging the rest of the current Outlook on Windows session, in the
Debug Event-based handler dialog, choose Cancel. To re-enable debugging, restart
Outlook.

To prevent the Debug Event-based handler dialog from popping up and stop
debugging for subsequent Outlook sessions, delete the associated registry key,
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\16.0\Wef\Developer\[Add-in

ID]\UseDirectDebugger , or set its value to 0 .

See also
Configure your Outlook add-in for event-based activation
Event-based activation troubleshooting guide
Debug your add-in with runtime logging
PowerPoint add-ins documentation
With PowerPoint add-ins, you can use familiar web technologies such as HTML, CSS, and
JavaScript to build a solution that can run in PowerPoint across multiple platforms,
including on the web, Windows, Mac, and iPad. Learn how to build, test, debug, and
publish PowerPoint add-ins.

About PowerPoint add-ins

e OVERVIEW

What are PowerPoint add-ins?

JavaScript API for PowerPoint

f QUICKSTART

Build your first PowerPoint add-in

Explore Office JavaScript API using Script Lab

c HOW-TO GUIDE

Test and debug a PowerPoint add-ins

Deploy and publish a PowerPoint add-ins

Key Office Add-ins concepts

e OVERVIEW

Office Add-ins platform overview

b GET STARTED

Core concepts for Office Add-ins

Design Office Add-ins

Develop Office Add-ins

Resources
i REFERENCE

Ask questions

Request features

Report issues

Office Add-ins additional resources


PowerPoint add-ins
Article • 03/28/2023

You can use PowerPoint add-ins to build engaging solutions for your users'
presentations across platforms including Windows, iPad, Mac, and in a browser. You can
create two types of PowerPoint add-ins:

Use content add-ins to add dynamic HTML5 content to your presentations. For
example, see the LucidChart Diagrams for PowerPoint add-in, which you can use
to inject an interactive diagram from LucidChart into your deck.

Use task pane add-ins to bring in reference information or insert data into the
presentation via a service. For example, see the Pexels - Free Stock Photos add-
in, which you can use to add professional photos to your presentation.

PowerPoint add-in scenarios


The code examples in this article demonstrate some basic tasks for developing add-ins
for PowerPoint. Please note the following:

To display information, these examples use the app.showNotification function,


which is included in the Visual Studio Office Add-ins project templates. If you
aren't using Visual Studio to develop your add-in, you'll need replace the
showNotification function with your own code.

Several of these examples also use a Globals object that is declared beyond the
scope of these functions as: var Globals = {activeViewHandler:0,
firstSlideId:0};

To use these examples, your add-in project must reference Office.js v1.1 library or
later.

Detect the presentation's active view and


handle the ActiveViewChanged event
If you are building a content add-in, you will need to get the presentation's active view
and handle the ActiveViewChanged event, as part of your Office.Initialize handler.

7 Note
In PowerPoint on the web, the Document.ActiveViewChanged event will never fire
as Slide Show mode is treated as a new session. In this case, the add-in must fetch
the active view on load, as shown in the following code sample.

In the following code sample:

The getActiveFileView function calls the Document.getActiveViewAsync method


to return whether the presentation's current view is "edit" (any of the views in
which you can edit slides, such as Normal or Outline View) or "read" (Slide Show
or Reading View).

The registerActiveViewChanged function calls the addHandlerAsync method to


register a handler for the Document.ActiveViewChanged event.

JavaScript

//general Office.initialize function. Fires on load of the add-in.


Office.initialize = function(){

//Gets whether the current view is edit or read.


const currentView = getActiveFileView();

//register for the active view changed handler


registerActiveViewChanged();

//render the content based off of the currentView


//....
}

function getActiveFileView()
{
Office.context.document.getActiveViewAsync(function (asyncResult) {
if (asyncResult.status == "failed") {
app.showNotification("Action failed with error: " +
asyncResult.error.message);
}
else {
app.showNotification(asyncResult.value);
}
});

function registerActiveViewChanged() {
Globals.activeViewHandler = function (args) {
app.showNotification(JSON.stringify(args));
}

Office.context.document.addHandlerAsync(Office.EventType.ActiveViewChanged,
Globals.activeViewHandler,
function (asyncResult) {
if (asyncResult.status == "failed") {
app.showNotification("Action failed with error: " +
asyncResult.error.message);
}
else {
app.showNotification(asyncResult.status);
}
});
}

Navigate to a particular slide in the


presentation
In the following code sample, the getSelectedRange function calls the
Document.getSelectedDataAsync method to get the JSON object returned by
asyncResult.value , which contains an array named slides . The slides array contains

the ids, titles, and indexes of selected range of slides (or of the current slide, if multiple
slides are not selected). It also saves the id of the first slide in the selected range to a
global variable.

JavaScript

function getSelectedRange() {
// Get the id, title, and index of the current slide (or selected
slides) and store the first slide id */
Globals.firstSlideId = 0;

Office.context.document.getSelectedDataAsync(Office.CoercionType.SlideRange,
function (asyncResult) {
if (asyncResult.status == "failed") {
app.showNotification("Action failed with error: " +
asyncResult.error.message);
}
else {
Globals.firstSlideId = asyncResult.value.slides[0].id;
app.showNotification(JSON.stringify(asyncResult.value));
}
});
}

In the following code sample, the goToFirstSlide function calls the


Document.goToByIdAsync method to navigate to the first slide that was identified by
the getSelectedRange function shown previously.
JavaScript

function goToFirstSlide() {
Office.context.document.goToByIdAsync(Globals.firstSlideId,
Office.GoToType.Slide, function (asyncResult) {
if (asyncResult.status == "failed") {
app.showNotification("Action failed with error: " +
asyncResult.error.message);
}
else {
app.showNotification("Navigation successful");
}
});
}

Navigate between slides in the presentation


In the following code sample, the goToSlideByIndex function calls the
Document.goToByIdAsync method to navigate to the next slide in the presentation.

JavaScript

function goToSlideByIndex() {
const goToFirst = Office.Index.First;
const goToLast = Office.Index.Last;
const goToPrevious = Office.Index.Previous;
const goToNext = Office.Index.Next;

Office.context.document.goToByIdAsync(goToNext, Office.GoToType.Index,
function (asyncResult) {
if (asyncResult.status == "failed") {
app.showNotification("Action failed with error: " +
asyncResult.error.message);
}
else {
app.showNotification("Navigation successful");
}
});
}

Get the URL of the presentation


In the following code sample, the getFileUrl function calls the
Document.getFileProperties method to get the URL of the presentation file.

JavaScript
function getFileUrl() {
//Get the URL of the current file.
Office.context.document.getFilePropertiesAsync(function (asyncResult) {
const fileUrl = asyncResult.value.url;
if (fileUrl == "") {
app.showNotification("The file hasn't been saved yet. Save the
file and try again");
}
else {
app.showNotification(fileUrl);
}
});
}

Create a presentation
Your add-in can create a new presentation, separate from the PowerPoint instance in
which the add-in is currently running. The PowerPoint namespace has the
createPresentation method for this purpose. When this method is called, the new

presentation is immediately opened and displayed in a new instance of PowerPoint. Your


add-in remains open and running with the previous presentation.

JavaScript

PowerPoint.createPresentation();

The createPresentation method can also create a copy of an existing presentation. The
method accepts a base64-encoded string representation of an .pptx file as an optional
parameter. The resulting presentation will be a copy of that file, assuming the string
argument is a valid .pptx file. The FileReader class can be used to convert a file into
the required base64-encoded string, as demonstrated in the following example.

JavaScript

const myFile = document.getElementById("file");


const reader = new FileReader();

reader.onload = function (event) {


// strip off the metadata before the base64-encoded string
const startIndex = reader.result.toString().indexOf("base64,");
const copyBase64 = reader.result.toString().substr(startIndex + 7);

PowerPoint.createPresentation(copyBase64);
};
// read in the file as a data URL so we can parse the base64-encoded string
reader.readAsDataURL(myFile.files[0]);

See also
Developing Office Add-ins
Learn about the Microsoft 365 Developer Program
PowerPoint Code Samples
How to save add-in state and settings per document for content and task pane
add-ins
Read and write data to the active selection in a document or spreadsheet
Get the whole document from an add-in for PowerPoint or Word
Use document themes in your PowerPoint add-ins
Build your first PowerPoint task pane
add-in
Article • 01/10/2023

In this article, you'll walk through the process of building a PowerPoint task pane add-in.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Create the add-in project
Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? PowerPoint

After you complete the wizard, the generator creates the project and installs
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a basic task pane add-in. If you'd like to explore the components of your
add-in project, open the project in your code editor and review the files listed
below. When you're ready to try out your add-in, proceed to the next section.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in. To learn more about the manifest.xml file, see
Office Add-ins XML manifest.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and the Office client
application.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your
add-in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're
developing. If you're prompted to install a certificate after you run one of
the following commands, accept the prompt to install the certificate that
the Yeoman generator provides. You may also have to run your command
prompt or terminal as an administrator for the changes to be made.

 Tip
If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server

To test your add-in in PowerPoint, run the following command in the


root directory of your project. This starts the local web server (if it's not
already running) and opens PowerPoint with your add-in loaded.

command line

npm start

To test your add-in in PowerPoint on a browser, run the following


command in the root directory of your project. When you run this
command, the local web server starts. Replace "{url}" with the URL of a
PowerPoint document on your OneDrive or a SharePoint library to which
you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single


quotation marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798Bpuhwl
uxCMfF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document


https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp

npm run start:web -- --document https://contoso-my.sharepoint-


df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP
If your add-in doesn't sideload in the document, manually sideload it by
following the instructions in Manually sideload add-ins to Office on the
web.

3. In PowerPoint, insert a new blank slide, choose the Home tab, and then
choose the Show Taskpane button on the ribbon to open the add-in task
pane.

4. At the bottom of the task pane, choose the Run link to insert the text "Hello
World" into the current slide.

Next steps
Congratulations, you've successfully created a PowerPoint task pane add-in! Next,
learn more about the capabilities of a PowerPoint add-in and build a more complex
add-in by following along with the PowerPoint add-in tutorial.

See also
Office Add-ins platform overview
Develop Office Add-ins
Using Visual Studio Code to publish
Tutorial: Create a PowerPoint task pane
add-in
Article • 04/04/2023

In this tutorial, you'll use Visual Studio to create an PowerPoint task pane add-in that:

" Adds the Bing photo of the day to a slide


" Adds text to a slide
" Gets slide metadata
" Navigates between slides

Prerequisites
Visual Studio 2019, version 16.10.3 or earlier, or Visual Studio 2022 , with the
Office/SharePoint development workload installed.

) Important

Some versions of Visual Studio 2019 after 16.10.3 have a bug that prevents
this tutorial from being completed. Use an earlier version of Visual Studio
2019 or use Visual Studio 2022.

7 Note

If you've previously installed Visual Studio, use the Visual Studio Installer to
ensure that the Office/SharePoint development workload is installed.

Office connected to a Microsoft 365 subscription (including Office on the web).

7 Note

If you don't already have Office, you can join the Microsoft 365 developer
program to get a free, 90-day renewable Microsoft 365 subscription to use
during development.

Create your add-in project


Complete the following steps to create a PowerPoint add-in project using Visual Studio.

1. Choose Create a new project.

2. Using the search box, enter add-in. Choose PowerPoint Web Add-in, then select
Next.

3. Name the project HelloWorld , and select Create.

4. In the Create Office Add-in dialog window, choose Add new functionalities to
PowerPoint, and then choose Finish to create the project.

5. Visual Studio creates a solution and its two projects appear in Solution Explorer.
The Home.html file opens in Visual Studio.

6. The following NuGet packages must be installed. Install them using the NuGet
Package Manager in Visual Studio. See Visual Studio help for instructions. The
second of these may be installed automatically when you install the first.

Microsoft.AspNet.WebApi.WebHost
Microsoft.AspNet.WebApi.Core

) Important

When you're using the NuGet Package Manager to install these packages, do
not install the recommended update to jQuery. The jQuery version installed
with your Visual Studio solution matches the jQuery call within the solution
files.

Explore the Visual Studio solution


When you've completed the wizard, Visual Studio creates a solution that contains two
projects.

Project Description

Add-in Contains only an XML manifest file, which contains all the settings that describe
project your add-in. These settings help the Office application determine when your add-
in should be activated and where the add-in should appear. Visual Studio
generates the contents of this file for you so that you can run the project and use
your add-in immediately. You change these settings any time by modifying the
XML file.

Web Contains the content pages of your add-in, including all the files and file
application references that you need to develop Office-aware HTML and JavaScript pages.
project While you develop your add-in, Visual Studio hosts the web application on your
local IIS server. When you're ready to publish the add-in, you'll need to deploy
this web application project to a web server.

Update code
Edit the add-in code as follows to create the framework that you'll use to implement
add-in functionality in subsequent steps of this tutorial.

1. Home.html specifies the HTML that will be rendered in the add-in's task pane. In
Home.html, find the div with id="content-main" , replace that entire div with the
following markup, and save the file.

HTML

<!-- TODO2: Create the content-header div. -->


<div id="content-main">
<div class="padding">
<!-- TODO1: Create the insert-image button. -->
<!-- TODO3: Create the insert-text button. -->
<!-- TODO4: Create the get-slide-metadata button. -->
<!-- TODO5: Create the go-to-slide buttons. -->
</div>
</div>
2. Open the file Home.js in the root of the web application project. This file specifies
the script for the add-in. Replace the entire contents with the following code and
save the file.

JavaScript

(function () {
"use strict";

let messageBanner;

Office.onReady(function () {
$(document).ready(function () {
// Initialize the FabricUI notification mechanism and hide
it
const element = document.querySelector('.MessageBanner');
messageBanner = new components.MessageBanner(element);
messageBanner.hideBanner();

// TODO1: Assign event handler for insert-image button.


// TODO4: Assign event handler for insert-text button.
// TODO6: Assign event handler for get-slide-metadata
button.
// TODO8: Assign event handlers for the four navigation
buttons.
});
});

// TODO2: Define the insertImage function.

// TODO3: Define the insertImageFromBase64String function.

// TODO5: Define the insertText function.

// TODO7: Define the getSlideMetadata function.

// TODO9: Define the navigation functions.

// Helper function for displaying notifications


function showNotification(header, content) {
$("#notification-header").text(header);
$("#notification-body").text(content);
messageBanner.showBanner();
messageBanner.toggleExpansion();
}
})();

Insert an image
Complete the following steps to add code that retrieves the Bing photo of the day
and inserts that image into a slide.

1. Using Solution Explorer, add a new folder named Controllers to the


HelloWorldWeb project.

2. Right-click the Controllers folder and select Add > New Scaffolded Item....

3. In the Add Scaffold dialog window, select Web API 2 Controller - Empty and
choose the Add button.

4. In the Add Controller dialog window, enter PhotoController as the controller


name and choose the Add button. Visual Studio creates and opens the
PhotoController.cs file.

7 Note

The scaffolding process does not complete properly on some versions of


Visual Studio 2019 after version 16.10.3. Visual Studio 2022 is not affected.

5. Replace the entire contents of the PhotoController.cs file with the following code
that calls the Bing service to retrieve the photo of the day as a Base64 encoded
string. When you use the Office JavaScript API to insert an image into a document,
the image data must be specified as a Base64 encoded string.

C#

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Web.Http;
using System.Xml;
namespace HelloWorldWeb.Controllers
{
public class PhotoController : ApiController
{
public string Get()
{
string url = "http://www.bing.com/HPImageArchive.aspx?
format=xml&idx=0&n=1";

// Create the request.


HttpWebRequest request =
(HttpWebRequest)WebRequest.Create(url);
WebResponse response = request.GetResponse();

using (Stream responseStream =


response.GetResponseStream())
{
// Process the result.
StreamReader reader = new StreamReader(responseStream,
Encoding.UTF8);
string result = reader.ReadToEnd();

// Parse the xml response and to get the URL.


XmlDocument doc = new XmlDocument();
doc.LoadXml(result);
string photoURL = "http://bing.com" +
doc.SelectSingleNode("/images/image/url").InnerText;

// Fetch the photo and return it as a Base64 encoded


string.
return getPhotoFromURL(photoURL);
}
}

private string getPhotoFromURL(string imageURL)


{
var webClient = new WebClient();
byte[] imageBytes = webClient.DownloadData(imageURL);
return Convert.ToBase64String(imageBytes);
}
}
}

6. In the Home.html file, replace TODO1 with the following markup. This markup
defines the Insert Image button that will appear within the add-in's task pane.

HTML

<button class="Button Button--primary" id="insert-image">


<span class="Button-icon"><i class="ms-Icon ms-Icon--plus"></i>
</span>
<span class="Button-label">Insert Image</span>
<span class="Button-description">Gets the photo of the day that
shows on the Bing home page and adds it to the slide.</span>
</button>

7. In the Home.js file, replace TODO1 with the following code to assign the event
handler for the Insert Image button.

JavaScript

$('#insert-image').click(insertImage);

8. In the Home.js file, replace TODO2 with the following code to define the
insertImage function. This function fetches the image from the Bing web service

and then calls the insertImageFromBase64String function to insert that image into
the document.

JavaScript

function insertImage() {
// Get image from from web service (as a Base64 encoded string).
$.ajax({
url: "/api/Photo/", success: function (result) {
insertImageFromBase64String(result);
}, error: function (xhr, status, error) {
showNotification("Error", "Oops, something went wrong.");
}
});
}

9. In the Home.js file, replace TODO3 with the following code to define the
insertImageFromBase64String function. This function uses the Office JavaScript API

to insert the image into the document. Note:

The coercionType option that's specified as the second parameter of the


setSelectedDataAsync request indicates the type of data being inserted.

The asyncResult object encapsulates the result of the setSelectedDataAsync


request, including status and error information if the request failed.

JavaScript

function insertImageFromBase64String(image) {
// Call Office.js to insert the image into the document.
Office.context.document.setSelectedDataAsync(image, {
coercionType: Office.CoercionType.Image
},
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed)
{
showNotification("Error", asyncResult.error.message);
}
});
}

Test the add-in


1. Using Visual Studio, test the newly created PowerPoint add-in by pressing F5 or
choosing the Start button to launch PowerPoint with the Show Taskpane add-in
button displayed on the ribbon. The add-in will be hosted locally on IIS.

2. In PowerPoint, select the Show Taskpane button on the ribbon to open the add-in
task pane.

3. In the task pane, choose the Insert Image button to add the Bing photo of the day
to the current slide.
4. In Visual Studio, stop the add-in by pressing Shift + F5 or choosing the Stop
button. PowerPoint will automatically close when the add-in is stopped.

Customize User Interface (UI) elements


Complete the following steps to add markup that customizes the task pane UI.

1. In the Home.html file, replace TODO2 with the following markup to add a header
section and title to the task pane. Note:

The styles that begin with ms- are defined by Fabric Core in Office Add-ins, a
JavaScript front-end framework for building user experiences for Office. The
Home.html file includes a reference to the Fabric Core stylesheet.

HTML

<div id="content-header">
<div class="ms-Grid ms-bgColor-neutralPrimary">
<div class="ms-Grid-row">
<div class="padding ms-Grid-col ms-u-sm12 ms-u-md12 ms-u-
lg12"> <div class="ms-font-xl ms-fontColor-white ms-fontWeight-
semibold">My PowerPoint add-in</div></div>
</div>
</div>
</div>

2. In the Home.html file, find the div with class="footer" and delete that entire div
to remove the footer section from the task pane.

Test the add-in


1. Using Visual Studio, test the PowerPoint add-in by pressing F5 or choosing the
Start button to launch PowerPoint with the Show Taskpane add-in button
displayed on the ribbon. The add-in will be hosted locally on IIS.

2. In PowerPoint, select the Show Taskpane button on the ribbon to open the add-in
task pane.

3. Notice that the task pane now contains a header section and title, and no longer
contains a footer section.
4. In Visual Studio, stop the add-in by pressing Shift + F5 or choosing the Stop
button. PowerPoint will automatically close when the add-in is stopped.

Insert text
Complete the following steps to add code that inserts text into the title slide which
contains the Bing photo of the day.

1. In the Home.html file, replace TODO3 with the following markup. This markup
defines the Insert Text button that will appear within the add-in's task pane.

HTML

<br /><br />


<button class="Button Button--primary" id="insert-text">
<span class="Button-icon"><i class="ms-Icon ms-Icon--plus"></i>
</span>
<span class="Button-label">Insert Text</span>
<span class="Button-description">Inserts text into the slide.
</span>
</button>

2. In the Home.js file, replace TODO4 with the following code to assign the event
handler for the Insert Text button.

JavaScript

$('#insert-text').click(insertText);

3. In the Home.js file, replace TODO5 with the following code to define the insertText
function. This function inserts text into the current slide.

JavaScript

function insertText() {
Office.context.document.setSelectedDataAsync('Hello World!',
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed)
{
showNotification("Error", asyncResult.error.message);
}
});
}

Test the add-in


1. Using Visual Studio, test the add-in by pressing F5 or choosing the Start button to
launch PowerPoint with the Show Taskpane add-in button displayed on the ribbon.
The add-in will be hosted locally on IIS.

2. In PowerPoint, select the Show Taskpane button on the ribbon to open the add-in
task pane.
3. In the task pane, choose the Insert Image button to add the Bing photo of the day
to the current slide and choose a design for the slide that contains a text box for
the title.

4. Put your cursor in the text box on the title slide and then in the task pane, choose
the Insert Text button to add text to the slide.
5. In Visual Studio, stop the add-in by pressing Shift + F5 or choosing the Stop
button. PowerPoint will automatically close when the add-in is stopped.

Get slide metadata


Complete the following steps to add code that retrieves metadata for the selected slide.

1. In the Home.html file, replace TODO4 with the following markup. This markup
defines the Get Slide Metadata button that will appear within the add-in's task
pane.

HTML

<br /><br />


<button class="Button Button--primary" id="get-slide-metadata">
<span class="Button-icon"><i class="ms-Icon ms-Icon--plus"></i>
</span>
<span class="Button-label">Get Slide Metadata</span>
<span class="Button-description">Gets metadata for the selected
slide(s).</span>
</button>

2. In the Home.js file, replace TODO6 with the following code to assign the event
handler for the Get Slide Metadata button.

JavaScript

$('#get-slide-metadata').click(getSlideMetadata);

3. In the Home.js file, replace TODO7 with the following code to define the
getSlideMetadata function. This function retrieves metadata for the selected

slide(s) and writes it to a popup dialog window within the add-in task pane.

JavaScript
function getSlideMetadata() {

Office.context.document.getSelectedDataAsync(Office.CoercionType.SlideR
ange,
function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed)
{
showNotification("Error", asyncResult.error.message);
} else {
showNotification("Metadata for selected slide(s):",
JSON.stringify(asyncResult.value), null, 2);
}
}
);
}

Test the add-in


1. Using Visual Studio, test the add-in by pressing F5 or choosing the Start button to
launch PowerPoint with the Show Taskpane add-in button displayed on the ribbon.
The add-in will be hosted locally on IIS.

2. In PowerPoint, select the Show Taskpane button on the ribbon to open the add-in
task pane.

3. In the task pane, choose the Get Slide Metadata button to get the metadata for
the selected slide. The slide metadata is written to the popup dialog window at the
bottom of the task pane. In this case, the slides array within the JSON metadata
contains one object that specifies the id , title , and index of the selected slide. If
multiple slides had been selected when you retrieved slide metadata, the slides
array within the JSON metadata would contain one object for each selected slide.

4. In Visual Studio, stop the add-in by pressing Shift + F5 or choosing the Stop
button. PowerPoint will automatically close when the add-in is stopped.

Navigate between slides


Complete the following steps to add code that navigates between the slides of a
document.

1. In the Home.html file, replace TODO5 with the following markup. This markup
defines the four navigation buttons that will appear within the add-in's task pane.

HTML

<br /><br />


<button class="Button Button--primary" id="go-to-first-slide">
<span class="Button-icon"><i class="ms-Icon ms-Icon--plus"></i>
</span>
<span class="Button-label">Go to First Slide</span>
<span class="Button-description">Go to the first slide.</span>
</button>
<br /><br />
<button class="Button Button--primary" id="go-to-next-slide">
<span class="Button-icon"><i class="ms-Icon ms-Icon--plus"></i>
</span>
<span class="Button-label">Go to Next Slide</span>
<span class="Button-description">Go to the next slide.</span>
</button>
<br /><br />
<button class="Button Button--primary" id="go-to-previous-slide">
<span class="Button-icon"><i class="ms-Icon ms-Icon--plus"></i>
</span>
<span class="Button-label">Go to Previous Slide</span>
<span class="Button-description">Go to the previous slide.</span>
</button>
<br /><br />
<button class="Button Button--primary" id="go-to-last-slide">
<span class="Button-icon"><i class="ms-Icon ms-Icon--plus"></i>
</span>
<span class="Button-label">Go to Last Slide</span>
<span class="Button-description">Go to the last slide.</span>
</button>

2. In the Home.js file, replace TODO8 with the following code to assign the event
handlers for the four navigation buttons.

JavaScript

$('#go-to-first-slide').click(goToFirstSlide);
$('#go-to-next-slide').click(goToNextSlide);
$('#go-to-previous-slide').click(goToPreviousSlide);
$('#go-to-last-slide').click(goToLastSlide);

3. In the Home.js file, replace TODO9 with the following code to define the navigation
functions. Each of these functions uses the goToByIdAsync method to select a slide
based upon its position in the document (first, last, previous, and next).

JavaScript

function goToFirstSlide() {
Office.context.document.goToByIdAsync(Office.Index.First,
Office.GoToType.Index,
function (asyncResult) {
if (asyncResult.status == "failed") {
showNotification("Error", asyncResult.error.message);
}
});
}
function goToLastSlide() {
Office.context.document.goToByIdAsync(Office.Index.Last,
Office.GoToType.Index,
function (asyncResult) {
if (asyncResult.status == "failed") {
showNotification("Error", asyncResult.error.message);
}
});
}

function goToPreviousSlide() {
Office.context.document.goToByIdAsync(Office.Index.Previous,
Office.GoToType.Index,
function (asyncResult) {
if (asyncResult.status == "failed") {
showNotification("Error", asyncResult.error.message);
}
});
}

function goToNextSlide() {
Office.context.document.goToByIdAsync(Office.Index.Next,
Office.GoToType.Index,
function (asyncResult) {
if (asyncResult.status == "failed") {
showNotification("Error", asyncResult.error.message);
}
});
}

Test the add-in


1. Using Visual Studio, test the add-in by pressing F5 or choosing the Start button to
launch PowerPoint with the Show Taskpane add-in button displayed on the ribbon.
The add-in will be hosted locally on IIS.

2. In PowerPoint, select the Show Taskpane button on the ribbon to open the add-in
task pane.
3. Use the New Slide button on the ribbon of the Home tab to add two new slides to
the document.

4. In the task pane, choose the Go to First Slide button. The first slide in the
document is selected and displayed.

5. In the task pane, choose the Go to Next Slide button. The next slide in the
document is selected and displayed.
6. In the task pane, choose the Go to Previous Slide button. The previous slide in the
document is selected and displayed.

7. In the task pane, choose the Go to Last Slide button. The last slide in the
document is selected and displayed.
8. In Visual Studio, stop the add-in by pressing Shift + F5 or choosing the Stop
button. PowerPoint will automatically close when the add-in is stopped.

Next steps
In this tutorial, you've created a PowerPoint add-in that inserts an image, inserts text,
gets slide metadata, and navigates between slides. To learn more about building
PowerPoint add-ins, continue to the following article.

PowerPoint add-ins overview

See also
Office Add-ins platform overview
Develop Office Add-ins
JavaScript API for PowerPoint
Article • 05/02/2023

A PowerPoint add-in interacts with objects in PowerPoint by using the Office JavaScript
API, which includes two JavaScript object models:

PowerPoint JavaScript API: The PowerPoint JavaScript API provides strongly-typed


objects that you can use to access objects in PowerPoint.

Common APIs: Introduced with Office 2013, the Common API can be used to
access features such as UI, dialogs, and client settings that are common across
multiple types of Office applications.

Learn programming concepts


See PowerPoint add-ins overview for information about important programming
concepts.

Learn about API capabilities


For hands-on experience using the Common API to interact with content in PowerPoint,
complete the PowerPoint add-in tutorial.

For detailed information about the PowerPoint JavaScript API object model, see the
PowerPoint JavaScript API reference documentation.

Try out code samples in Script Lab


Use Script Lab to get started quickly with a collection of built-in samples that show how
to complete tasks with the API. You can run the samples in Script Lab to instantly see the
result in the task pane or document, examine the samples to learn how the API works,
and even use samples to prototype your own add-in.

See also
PowerPoint add-ins documentation
PowerPoint add-ins overview
PowerPoint JavaScript API reference
Office client application and platform availability for Office Add-ins
API Reference documentation
powerpoint package
Reference

Classes
PowerPoint.Application

PowerPoint.Presentation

PowerPoint.RequestContext The RequestContext object facilitates requests to the PowerPoint


application. Since the Office add-in and the PowerPoint
application run in two different processes, the request context is
required to get access to the PowerPoint object model from the
add-in.

Interfaces
PowerPoint.Interfaces.Bullet An interface describing the data returned by calling
FormatData bulletFormat.toJSON() .

PowerPoint.Interfaces.Bullet An interface for updating data on the BulletFormat object, for


FormatUpdateData use in bulletFormat.set({ ... }) .

PowerPoint.Interfaces. Provides ways to load properties of only a subset of members of


CollectionLoadOptions a collection.

PowerPoint.Interfaces. An interface describing the data returned by calling


ParagraphFormatData paragraphFormat.toJSON() .

PowerPoint.Interfaces. An interface for updating data on the ParagraphFormat object,


ParagraphFormatUpdateData for use in paragraphFormat.set({ ... }) .

PowerPoint.Interfaces. An interface describing the data returned by calling


PresentationData presentation.toJSON() .

PowerPoint.Interfaces.PresentationLoadOptions

PowerPoint.Interfaces.Shape An interface describing the data returned by calling


CollectionData shapeCollection.toJSON() .

PowerPoint.Interfaces.Shape An interface for updating data on the ShapeCollection object, for


CollectionUpdateData use in shapeCollection.set({ ... }) .

PowerPoint.Interfaces.Shape An interface describing the data returned by calling


Data shape.toJSON() .
PowerPoint.Interfaces.Shape An interface describing the data returned by calling
FillData shapeFill.toJSON() .

PowerPoint.Interfaces.Shape An interface for updating data on the ShapeFill object, for use in
FillUpdateData shapeFill.set({ ... }) .

PowerPoint.Interfaces.Shape An interface describing the data returned by calling


FontData shapeFont.toJSON() .

PowerPoint.Interfaces.Shape An interface for updating data on the ShapeFont object, for use
FontUpdateData in shapeFont.set({ ... }) .

PowerPoint.Interfaces.Shape An interface describing the data returned by calling


LineFormatData shapeLineFormat.toJSON() .

PowerPoint.Interfaces.Shape An interface for updating data on the ShapeLineFormat object,


LineFormatUpdateData for use in shapeLineFormat.set({ ... }) .

PowerPoint.Interfaces.Shape An interface describing the data returned by calling


ScopedCollectionData shapeScopedCollection.toJSON() .

PowerPoint.Interfaces.Shape An interface for updating data on the ShapeScopedCollection


ScopedCollectionUpdateData object, for use in shapeScopedCollection.set({ ... }) .

PowerPoint.Interfaces.Shape An interface for updating data on the Shape object, for use in
UpdateData shape.set({ ... }) .

PowerPoint.Interfaces.Slide An interface describing the data returned by calling


CollectionData slideCollection.toJSON() .

PowerPoint.Interfaces.Slide An interface for updating data on the SlideCollection object, for


CollectionUpdateData use in slideCollection.set({ ... }) .

PowerPoint.Interfaces.Slide An interface describing the data returned by calling


Data slide.toJSON() .

PowerPoint.Interfaces.Slide An interface describing the data returned by calling


LayoutCollectionData slideLayoutCollection.toJSON() .

PowerPoint.Interfaces.Slide An interface for updating data on the SlideLayoutCollection


LayoutCollectionUpdateData object, for use in slideLayoutCollection.set({ ... }) .

PowerPoint.Interfaces.Slide An interface describing the data returned by calling


LayoutData slideLayout.toJSON() .

PowerPoint.Interfaces.Slide An interface describing the data returned by calling


MasterCollectionData slideMasterCollection.toJSON() .

PowerPoint.Interfaces.Slide An interface for updating data on the SlideMasterCollection


MasterCollectionUpdateData object, for use in slideMasterCollection.set({ ... }) .
PowerPoint.Interfaces.Slide An interface describing the data returned by calling
MasterData slideMaster.toJSON() .

PowerPoint.Interfaces.Slide An interface describing the data returned by calling


ScopedCollectionData slideScopedCollection.toJSON() .

PowerPoint.Interfaces.Slide An interface for updating data on the SlideScopedCollection


ScopedCollectionUpdateData object, for use in slideScopedCollection.set({ ... }) .

PowerPoint.Interfaces.Tag An interface describing the data returned by calling


CollectionData tagCollection.toJSON() .

PowerPoint.Interfaces.Tag An interface for updating data on the TagCollection object, for


CollectionUpdateData use in tagCollection.set({ ... }) .

PowerPoint.Interfaces.TagData An interface describing the data returned by calling


tag.toJSON() .

PowerPoint.Interfaces.Tag An interface for updating data on the Tag object, for use in
UpdateData tag.set({ ... }) .

PowerPoint.Interfaces.Text An interface describing the data returned by calling


FrameData textFrame.toJSON() .

PowerPoint.Interfaces.Text An interface for updating data on the TextFrame object, for use
FrameUpdateData in textFrame.set({ ... }) .

PowerPoint.Interfaces.Text An interface describing the data returned by calling


RangeData textRange.toJSON() .

PowerPoint.Interfaces.Text An interface for updating data on the TextRange object, for use
RangeUpdateData in textRange.set({ ... }) .

Enums
PowerPoint.ErrorCodes

Functions
PowerPoint.create Creates and opens a new presentation. Optionally, the
Presentation(base64File) presentation can be pre-populated with a base64-encoded .pptx
file.

[ API set: PowerPointApi 1.1 ]

PowerPoint.run(batch) Executes a batch script that performs actions on the PowerPoint


object model, using a new RequestContext. When the promise is
resolved, any tracked objects that were automatically allocated
during execution will be released.

PowerPoint.run(object, batch) Executes a batch script that performs actions on the PowerPoint
object model, using the RequestContext of a previously-created
API object. When the promise is resolved, any tracked objects
that were automatically allocated during execution will be
released.

PowerPoint.run(objects, batch) Executes a batch script that performs actions on the PowerPoint
object model, using the RequestContext of previously-created
API objects.

Function Details

PowerPoint.createPresentation(base64File)
Creates and opens a new presentation. Optionally, the presentation can be pre-
populated with a base64-encoded .pptx file.

[ API set: PowerPointApi 1.1 ]

TypeScript

export function createPresentation(base64File?: string): Promise<void>;

Parameters
base64File string
Optional. The base64-encoded .pptx file. The default value is null.

Returns
Promise<void>

Examples

TypeScript

const myFile = <HTMLInputElement>document.getElementById("file");


const reader = new FileReader();

reader.onload = (event) => {


// Remove the metadata before the base64-encoded string.
const startIndex = reader.result.toString().indexOf("base64,");
const copyBase64 = reader.result.toString().substr(startIndex + 7);

PowerPoint.createPresentation(copyBase64);
};

// Read in the file as a data URL so we can parse the base64-encoded


string.
reader.readAsDataURL(myFile.files[0]);

PowerPoint.run(batch)
Executes a batch script that performs actions on the PowerPoint object model, using
a new RequestContext. When the promise is resolved, any tracked objects that were
automatically allocated during execution will be released.

TypeScript

export function run<T>(batch: (context: PowerPoint.RequestContext) =>


OfficeExtension.IPromise<T>): OfficeExtension.IPromise<T>;

Parameters
batch (context: PowerPoint.RequestContext) => OfficeExtension.IPromise<T>
A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the
PowerPoint application. Since the Office add-in and the PowerPoint application run
in two different processes, the RequestContext is required to get access to the
PowerPoint object model from the add-in.

Returns
OfficeExtension.IPromise<T>

PowerPoint.run(object, batch)
Executes a batch script that performs actions on the PowerPoint object model, using
the RequestContext of a previously-created API object. When the promise is
resolved, any tracked objects that were automatically allocated during execution will
be released.

TypeScript
export function run<T>(object: OfficeExtension.ClientObject, batch:
(context: PowerPoint.RequestContext) => OfficeExtension.IPromise<T>):
OfficeExtension.IPromise<T>;

Parameters
object OfficeExtension.ClientObject
A previously-created API object. The batch will use the same RequestContext as the
passed-in object, which means that any changes applied to the object will be picked
up by "context.sync()".

batch (context: PowerPoint.RequestContext) => OfficeExtension.IPromise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the
PowerPoint application. Since the Office add-in and the PowerPoint application run
in two different processes, the RequestContext is required to get access to the
PowerPoint object model from the add-in.

Returns
OfficeExtension.IPromise<T>

PowerPoint.run(objects, batch)
Executes a batch script that performs actions on the PowerPoint object model, using
the RequestContext of previously-created API objects.

TypeScript

export function run<T>(objects: OfficeExtension.ClientObject[], batch:


(context: PowerPoint.RequestContext) => OfficeExtension.IPromise<T>):
OfficeExtension.IPromise<T>;

Parameters
objects OfficeExtension.ClientObject[]
An array of previously-created API objects. The array will be validated to make sure
that all of the objects share the same context. The batch will use this shared
RequestContext, which means that any changes applied to these objects will be
picked up by "context.sync()".
batch (context: PowerPoint.RequestContext) => OfficeExtension.IPromise<T>
A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the
PowerPoint application. Since the Office add-in and the PowerPoint application run
in two different processes, the RequestContext is required to get access to the
PowerPoint object model from the add-in.

Returns
OfficeExtension.IPromise<T>
Add and delete slides in PowerPoint
Article • 07/21/2022

A PowerPoint add-in can add slides to the presentation and optionally specify which
slide master, and which layout of the master, is used for the new slide. The add-in can
also delete slides.

The APIs for adding slides are primarily used in scenarios where the IDs of the slide
masters and layouts in the presentation are known at coding time or can be found in a
data source at runtime. In such a scenario, either you or the customer must create and
maintain a data source that correlates the selection criterion (such as the names or
images of slide masters and layouts) with the IDs of the slide masters and layouts. The
APIs can also be used in scenarios where the user can insert slides that use the default
slide master and the master's default layout, and in scenarios where the user can select
an existing slide and create a new one with the same slide master and layout (but not
the same content). See Selecting which slide master and layout to use for more
information about this.

Add a slide with SlideCollection.add


Add slides with the SlideCollection.add method. The following is a simple example in
which a slide that uses the presentation's default slide master and the first layout of that
master is added. The method always adds new slides to the end of the presentation. The
following is an example.

JavaScript

async function addSlide() {


await PowerPoint.run(async function(context) {
context.presentation.slides.add();

await context.sync();
});
}

Select which slide master and layout to use


Use the AddSlideOptions parameter to control which slide master is used for the new
slide and which layout within the master is used. The following is an example. About this
code, note:
You can include either or both the properties of the AddSlideOptions object.
If both properties are used, then the specified layout must belong to the specified
master or an error is thrown.
If the masterId property isn't present (or its value is an empty string), then the
default slide master is used and the layoutId must be a layout of that slide master.
The default slide master is the slide master used by the last slide in the
presentation. (In the unusual case where there are currently no slides in the
presentation, then the default slide master is the first slide master in the
presentation.)
If the layoutId property isn't present (or its value is an empty string), then the first
layout of the master that is specified by the masterId is used.
Both properties are strings of one of three possible forms: nnnnnnnnnn#,
#mmmmmmmmm, or nnnnnnnnnn#mmmmmmmmm, where nnnnnnnnnn is the
master's or layout's ID (typically 10 digits) and mmmmmmmmm is the master's or
layout's creation ID (typically 6 - 10 digits). Some examples are
2147483690#2908289500 , 2147483690# , and #2908289500 .

JavaScript

async function addSlide() {


await PowerPoint.run(async function(context) {
context.presentation.slides.add({
slideMasterId: "2147483690#2908289500",
layoutId: "2147483691#2499880"
});

await context.sync();
});
}

There is no practical way that users can discover the ID or creation ID of a slide master
or layout. For this reason, you can really only use the AddSlideOptions parameter when
either you know the IDs at coding time or your add-in can discover them at runtime.
Because users can't be expected to memorize the IDs, you also need a way to enable the
user to select slides, perhaps by name or by an image, and then correlate each title or
image with the slide's ID.

Accordingly, the AddSlideOptions parameter is primarily used in scenarios in which the


add-in is designed to work with a specific set of slide masters and layouts whose IDs are
known. In such a scenario, either you or the customer must create and maintain a data
source that correlates a selection criterion (such as slide master and layout names or
images) with the corresponding IDs or creation IDs.
Have the user choose a matching slide
If your add-in can be used in scenarios where the new slide should use the same
combination of slide master and layout that is used by an existing slide, then your add-
in can (1) prompt the user to select a slide and (2) read the IDs of the slide master and
layout. The following steps show how to read the IDs and add a slide with a matching
master and layout.

1. Create a function to get the index of the selected slide. The following is an
example. About this code, note:

It uses the Office.context.document.getSelectedDataAsync method of the


Common JavaScript APIs.
The call to getSelectedDataAsync is embedded in a Promise-returning
function. For more information about why and how to do this, see Wrap
Common APIs in promise-returning functions.
getSelectedDataAsync returns an array because multiple slides can be
selected. In this scenario, the user has selected just one, so the code gets the
first (0th) slide, which is the only one selected.
The index value of the slide is the 1-based value the user sees beside the
slide in the thumbnails pane.

JavaScript

function getSelectedSlideIndex() {
return new OfficeExtension.Promise<number>(function(resolve,
reject) {

Office.context.document.getSelectedDataAsync(Office.CoercionType.SlideR
ange, function(asyncResult) {
try {
if (asyncResult.status ===
Office.AsyncResultStatus.Failed) {
reject(console.error(asyncResult.error.message));
} else {
resolve(asyncResult.value.slides[0].index);
}
}
catch (error) {
reject(console.log(error));
}
});
});
}

2. Call your new function inside the PowerPoint.run() of the main function that adds
the slide. The following is an example.
JavaScript

async function addSlideWithMatchingLayout() {


await PowerPoint.run(async function(context) {

let selectedSlideIndex = await getSelectedSlideIndex();

// Decrement the index because the value returned by


getSelectedSlideIndex()
// is 1-based, but SlideCollection.getItemAt() is 0-based.
const realSlideIndex = selectedSlideIndex - 1;
const selectedSlide =
context.presentation.slides.getItemAt(realSlideIndex).load("slideMaster
/id, layout/id");

await context.sync();

context.presentation.slides.add({
slideMasterId: selectedSlide.slideMaster.id,
layoutId: selectedSlide.layout.id
});

await context.sync();
});
}

Delete slides
Delete a slide by getting a reference to the Slide object that represents the slide and call
the Slide.delete method. The following is an example in which the 4th slide is deleted.

JavaScript

async function deleteSlide() {


await PowerPoint.run(async function(context) {

// The slide index is zero-based.


const slide = context.presentation.slides.getItemAt(3);
slide.delete();

await context.sync();
});
}
Insert slides in a PowerPoint
presentation
Article • 09/20/2022

A PowerPoint add-in can insert slides from one presentation into the current
presentation by using PowerPoint's application-specific JavaScript library. You can
control whether the inserted slides keep the formatting of the source presentation or
the formatting of the target presentation.

The slide insertion APIs are primarily used in presentation template scenarios: There are
a small number of known presentations which serve as pools of slides that can be
inserted by the add-in. In such a scenario, either you or the customer must create and
maintain a data source that correlates the selection criterion (such as slide titles or
images) with slide IDs. The APIs can also be used in scenarios where the user can insert
slides from any arbitrary presentation, but in that scenario the user is effectively limited
to inserting all the slides from the source presentation. See Selecting which slides to
insert for more information about this.

There are two steps to inserting slides from one presentation into another.

1. Convert the source presentation file (.pptx) into a base64-formatted string.


2. Use the insertSlidesFromBase64 method to insert one or more slides from the
base64 file into the current presentation.

Convert the source presentation to base64


There are many ways to convert a file to base64. Which programming language and
library you use, and whether to convert on the server-side of your add-in or the client-
side is determined by your scenario. Most commonly, you'll do the conversion in
JavaScript on the client-side by using a FileReader object. The following example
shows this practice.

1. Begin by getting a reference to the source PowerPoint file. In this example, we will
use an <input> control of type file to prompt the user to choose a file. Add the
following markup to the add-in page.

HTML

<section>
<p>Select a PowerPoint presentation from which to insert slides</p>
<form>
<input type="file" id="file" />
</form>
</section>

This markup adds the UI in the following screenshot to the page.

7 Note

There are many other ways to get a PowerPoint file. For example, if the file is
stored on OneDrive or SharePoint, you can use Microsoft Graph to download
it. For more information, see Working with files in Microsoft Graph and
Access Files with Microsoft Graph.

2. Add the following code to the add-in's JavaScript to assign a function to the input
control's change event. (You create the storeFileAsBase64 function in the next
step.)

JavaScript

$("#file").change(storeFileAsBase64);

3. Add the following code. Note the following about this code.

The reader.readAsDataURL method converts the file to base64 and stores it in


the reader.result property. When the method completes, it triggers the
onload event handler.
The onload event handler trims metadata off of the encoded file and stores
the encoded string in a global variable.
The base64-encoded string is stored globally because it will be read by
another function that you create in a later step.

JavaScript

let chosenFileBase64;

async function storeFileAsBase64() {


const reader = new FileReader();
reader.onload = async (event) => {
const startIndex = reader.result.toString().indexOf("base64,");
const copyBase64 = reader.result.toString().substr(startIndex +
7);

chosenFileBase64 = copyBase64;
};

const myFile = document.getElementById("file") as HTMLInputElement;


reader.readAsDataURL(myFile.files[0]);
}

Insert slides with insertSlidesFromBase64


Your add-in inserts slides from another PowerPoint presentation into the current
presentation with the Presentation.insertSlidesFromBase64 method. The following is a
simple example in which all of the slides from the source presentation are inserted at
the beginning of the current presentation and the inserted slides keep the formatting of
the source file. Note that chosenFileBase64 is a global variable that holds a base64-
encoded version of a PowerPoint presentation file.

JavaScript

async function insertAllSlides() {


await PowerPoint.run(async function(context) {
context.presentation.insertSlidesFromBase64(chosenFileBase64);
await context.sync();
});
}

You can control some aspects of the insertion result, including where the slides are
inserted and whether they get the source or target formatting , by passing an
InsertSlideOptions object as a second parameter to insertSlidesFromBase64 . The
following is an example. About this code, note:

There are two possible values for the formatting property: "UseDestinationTheme"
and "KeepSourceFormatting". Optionally, you can use the InsertSlideFormatting
enum, (e.g., PowerPoint.InsertSlideFormatting.useDestinationTheme ).
The function will insert the slides from the source presentation immediately after
the slide specified by the targetSlideId property. The value of this property is a
string of one of three possible forms: nnn#, #mmmmmmmmm, or
nnn#mmmmmmmmm, where nnn is the slide's ID (typically 3 digits) and
mmmmmmmmm is the slide's creation ID (typically 9 digits). Some examples are
267#763315295 , 267# , and #763315295 .
JavaScript

async function insertSlidesDestinationFormatting() {


await PowerPoint.run(async function(context) {
context.presentation
.insertSlidesFromBase64(chosenFileBase64,
{
formatting: "UseDestinationTheme",
targetSlideId: "267#"
}
);
await context.sync();
});
}

Of course, you typically won't know at coding time the ID or creation ID of the target
slide. More commonly, an add-in will ask users to select the target slide. The following
steps show how to get the nnn# ID of the currently selected slide and use it as the
target slide.

1. Create a function that gets the ID of the currently selected slide by using the
Office.context.document.getSelectedDataAsync method of the Common JavaScript
APIs. The following is an example. Note that the call to getSelectedDataAsync is
embedded in a Promise-returning function. For more information about why and
how to do this, see Wrap Common-APIs in promise-returning functions.

JavaScript

function getSelectedSlideID() {
return new OfficeExtension.Promise<string>(function (resolve, reject)
{

Office.context.document.getSelectedDataAsync(Office.CoercionType.SlideR
ange, function (asyncResult) {
try {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
reject(console.error(asyncResult.error.message));
} else {
resolve(asyncResult.value.slides[0].id);
}
}
catch (error) {
reject(console.log(error));
}
});
})
}
2. Call your new function inside the PowerPoint.run() of the main function and pass
the ID that it returns (concatenated with the "#" symbol) as the value of the
targetSlideId property of the InsertSlideOptions parameter. The following is an

example.

JavaScript

async function insertAfterSelectedSlide() {


await PowerPoint.run(async function(context) {

const selectedSlideID = await getSelectedSlideID();

context.presentation.insertSlidesFromBase64(chosenFileBase64, {
formatting: "UseDestinationTheme",
targetSlideId: selectedSlideID + "#"
});

await context.sync();
});
}

Selecting which slides to insert


You can also use the InsertSlideOptions parameter to control which slides from the
source presentation are inserted. You do this by assigning an array of the source
presentation's slide IDs to the sourceSlideIds property. The following is an example
that inserts four slides. Note that each string in the array must follow one or another of
the patterns used for the targetSlideId property.

JavaScript

async function insertAfterSelectedSlide() {


await PowerPoint.run(async function(context) {
const selectedSlideID = await getSelectedSlideID();
context.presentation.insertSlidesFromBase64(chosenFileBase64, {
formatting: "UseDestinationTheme",
targetSlideId: selectedSlideID + "#",
sourceSlideIds: ["267#763315295", "256#", "#926310875", "1270#"]
});

await context.sync();
});
}

7 Note
The slides will be inserted in the same relative order in which they appear in the
source presentation, regardless of the order in which they appear in the array.

There is no practical way that users can discover the ID or creation ID of a slide in the
source presentation. For this reason, you can really only use the sourceSlideIds
property when either you know the source IDs at coding time or your add-in can
retrieve them at runtime from some data source. Because users cannot be expected to
memorize slide IDs, you also need a way to enable the user to select slides, perhaps by
title or by an image, and then correlate each title or image with the slide's ID.

Accordingly, the sourceSlideIds property is primarily used in presentation template


scenarios: The add-in is designed to work with a specific set of presentations that serve
as pools of slides that can be inserted. In such a scenario, either you or the customer
must create and maintain a data source that correlates a selection criterion (such as
titles or images) with slide IDs or slide creation IDs that has been constructed from the
set of possible source presentations.
Get the whole document from an add-in
for PowerPoint or Word
Article • 03/21/2023

You can create an Office Add-in to send or publish a Word document or PowerPoint
presentation to a remote location. This article demonstrates how to build a simple task
pane add-in for PowerPoint or Word that gets all of the presentation or document as a
data object and sends that data to a web server via an HTTP request.

Prerequisites for creating an add-in for


PowerPoint or Word
This article assumes that you are using a text editor to create the task pane add-in for
PowerPoint or Word. To create the task pane add-in, you must create the following files.

On a shared network folder or on a web server, you need the following files.

An HTML file (GetDoc_App.html) that contains the user interface plus links to
the JavaScript files (including Office.js and application-specific .js files) and
Cascading Style Sheet (CSS) files.

A JavaScript file (GetDoc_App.js) to contain the programming logic of the add-


in.

A CSS file (Program.css) to contain the styles and formatting for the add-in.

An XML manifest file (GetDoc_App.xml) for the add-in, available on a shared


network folder or add-in catalog. The manifest file must point to the location of
the HTML file mentioned previously.

Alternatively, you can create an add-in for your Office application using one of the
following options. You won't have to create new files as the equivalent of each required
file will be available for you to update. For example, the Yeoman generator options
include ./src/taskpane/taskpane.html, ./src/taskpane/taskpane.js,
./src/taskpane/taskpane.css, and ./manifest.xml.

PowerPoint
Visual Studio
Yeoman generator for Office Add-ins
Word
Visual Studio
Yeoman generator for Office Add-ins

Core concepts to know for creating a task pane add-in


Before you begin creating this add-in for PowerPoint or Word, you should be familiar
with building Office Add-ins and working with HTTP requests. This article doesn't
discuss how to decode Base64-encoded text from an HTTP request on a web server.

Create the manifest for the add-in


The XML manifest file for an Office Add-in provides important information about the
add-in: what applications can host it, the location of the HTML file, the add-in title and
description, and many other characteristics.

1. In a text editor, add the following code to the manifest file.

XML

<?xml version="1.0" encoding="utf-8" ?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<Id>[Replace_With_Your_GUID]</Id>
<Version>1.0</Version>
<ProviderName>[Provider Name]</ProviderName>
<DefaultLocale>EN-US</DefaultLocale>
<DisplayName DefaultValue="Get Doc add-in" />
<Description DefaultValue="My get PowerPoint or Word document add-
in." />
<IconUrl
DefaultValue="http://officeimg.vo.msecnd.net/_layouts/images/general/of
fice_logo.jpg" />
<SupportUrl DefaultValue="[Insert the URL of a page that provides
support information for the app]" />
<Hosts>
<Host Name="Document" />
<Host Name="Presentation" />
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="[Network location of
app]/GetDoc_App.html" />
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
</OfficeApp>

2. Save the file as GetDoc_App.xml using UTF-8 encoding to a network location or to


an add-in catalog.
Create the user interface for the add-in
For the user interface of the add-in, you can use HTML written directly into the
GetDoc_App.html file. The programming logic and functionality of the add-in must be
contained in a JavaScript file (for example, GetDoc_App.js).

Use the following procedure to create a simple user interface for the add-in that
includes a heading and a single button.

1. In a new file in the text editor, add the HTML for your selected Office application.

PowerPoint

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<title>Publish presentation</title>
<link rel="stylesheet" type="text/css" href="Program.css"
/>
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-
1.9.0.min.js" type="text/javascript"></script>
<script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>
<script src="GetDoc_App.js"></script>
</head>
<body>
<form>
<h1>Publish presentation</h1>
<br />
<div><input id='submit' type="button" value="Submit" />
</div>
<br />
<div><h2>Status</h2>
<div id="status"></div>
</div>
</form>
</body>
</html>

2. Save the file as GetDoc_App.html using UTF-8 encoding to a network location or


to a web server.
7 Note

Be sure that the head tags of the add-in contains a script tag with a valid link
to the Office.js file.

3. We'll use some CSS to give the add-in a simple yet modern and professional
appearance. Use the following CSS to define the style of the add-in.

In a new file in the text editor, add the following CSS.

css

body
{
font-family: "Segoe UI Light","Segoe UI",Tahoma,sans-serif;
}
h1,h2
{
text-decoration-color:#4ec724;
}
input [type="submit"], input[type="button"]
{
height:24px;
padding-left:1em;
padding-right:1em;
background-color:white;
border:1px solid grey;
border-color: #dedfe0 #b9b9b9 #b9b9b9 #dedfe0;
cursor:pointer;
}

4. Save the file as Program.css using UTF-8 encoding to the network location or to
the web server where the GetDoc_App.html file is located.

Add the JavaScript to get the document


In the code for the add-in, a handler to the Office.initialize event adds a handler to the
click event of the Submit button on the form and informs the user that the add-in is
ready.

The following code example shows the event handler for the Office.initialize event
along with a helper function, updateStatus , for writing to the status div.

JavaScript
// The initialize or onReady function is required for all add-ins.
Office.initialize = function (reason) {

// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {

// Run sendFile when Submit is clicked.


$('#submit').click(function () {
sendFile();
});

// Update status.
updateStatus("Ready to send file.");
});
}

// Create a function for writing to the status div.


function updateStatus(message) {
var statusInfo = $('#status');
statusInfo[0].innerHTML += message + "<br/>";
}

When you choose the Submit button in the UI, the add-in calls the sendFile function,
which contains a call to the Document.getFileAsync method. The getFileAsync method
uses the asynchronous pattern, similar to other methods in the Office JavaScript API. It
has one required parameter, fileType, and two optional parameters, options and callback.

The fileType parameter expects one of three constants from the FileType enumeration:
Office.FileType.Compressed ("compressed"), Office.FileType.PDF ("pdf"), or

Office.FileType.Text ("text"). The current file type support for each platform is listed

under the Document.getFileType remarks. When you pass in Compressed for the
fileType parameter, the getFileAsync method returns the current document as a
PowerPoint presentation file (*.pptx) or Word document file (*.docx) by creating a
temporary copy of the file on the local computer.

The getFileAsync method returns a reference to the file as a File object. The File
object exposes the following four members.

size property
sliceCount property
getSliceAsync method
closeAsync method

The size property returns the number of bytes in the file. The sliceCount returns the
number of Slice objects (discussed later in this article) in the file.
Use the following code to get the current PowerPoint or Word document as a File
object using the Document.getFileAsync method and then make a call to the locally
defined getSlice function. Note that the File object, a counter variable, and the total
number of slices in the file are passed along in the call to getSlice in an anonymous
object.

JavaScript

// Get all of the content from a PowerPoint or Word document in 100-KB


chunks of text.
function sendFile() {
Office.context.document.getFileAsync("compressed",
{ sliceSize: 100000 },
function (result) {

if (result.status === Office.AsyncResultStatus.Succeeded) {

// Get the File object from the result.


var myFile = result.value;
var state = {
file: myFile,
counter: 0,
sliceCount: myFile.sliceCount
};

updateStatus("Getting file of " + myFile.size + " bytes");


getSlice(state);
} else {
updateStatus(result.status);
}
});
}

The local function getSlice makes a call to the File.getSliceAsync method to retrieve
a slice from the File object. The getSliceAsync method returns a Slice object from
the collection of slices. It has two required parameters, sliceIndex and callback. The
sliceIndex parameter takes an integer as an indexer into the collection of slices. Like
other methods in the Office JavaScript API, the getSliceAsync method also takes a
callback function as a parameter to handle the results from the method call.

The Slice object gives you access to the data contained in the file. Unless otherwise
specified in the options parameter of the getFileAsync method, the Slice object is 4
MB in size. The Slice object exposes three properties: size, data, and index. The size
property gets the size, in bytes, of the slice. The index property gets an integer that
represents the slice's position in the collection of slices.

JavaScript
// Get a slice from the file and then call sendSlice.
function getSlice(state) {
state.file.getSliceAsync(state.counter, function (result) {
if (result.status == Office.AsyncResultStatus.Succeeded) {
updateStatus("Sending piece " + (state.counter + 1) + " of " +
state.sliceCount);
sendSlice(result.value, state);
} else {
updateStatus(result.status);
}
});
}

The Slice.data property returns the raw data of the file as a byte array. If the data is in
text format (that is, XML or plain text), the slice contains the raw text. If you pass in
Office.FileType.Compressed for the fileType parameter of Document.getFileAsync , the
slice contains the binary data of the file as a byte array. In the case of a PowerPoint or
Word file, the slices contain byte arrays.

You must implement your own function (or use an available library) to convert byte array
data to a Base64-encoded string. For information about Base64 encoding with
JavaScript, see Base64 encoding and decoding .

Once you've converted the data to Base64, you can then transmit it to a web server in
several ways, including as the body of an HTTP POST request.

Add the following code to send a slice to a web service.

7 Note

This code sends a PowerPoint or Word file to the web server in multiple slices. The
web server or service must append each individual slice into a single file, and then
save it as a .pptx or .docx file before you can perform any manipulations on it.

JavaScript

function sendSlice(slice, state) {


var data = slice.data;

// If the slice contains data, create an HTTP request.


if (data) {

// Encode the slice data, a byte array, as a Base64 string.


// NOTE: The implementation of myEncodeBase64(input) function isn't
// included with this example. For information about Base64 encoding
with
// JavaScript, see
https://developer.mozilla.org/docs/Web/JavaScript/Base64_encoding_and_decodi
ng.
var fileData = myEncodeBase64(data);

// Create a new HTTP request. You need to send the request


// to a webpage that can receive a post.
var request = new XMLHttpRequest();

// Create a handler function to update the status


// when the request has been sent.
request.onreadystatechange = function () {
if (request.readyState == 4) {

updateStatus("Sent " + slice.size + " bytes.");


state.counter++;

if (state.counter < state.sliceCount) {


getSlice(state);
} else {
closeFile(state);
}
}
}

request.open("POST", "[Your receiving page or service]");


request.setRequestHeader("Slice-Number", slice.index);

// Send the file as the body of an HTTP POST


// request to the web server.
request.send(fileData);
}
}

As the name implies, the File.closeAsync method closes the connection to the
document and frees up resources. Although the Office Add-ins sandbox garbage
collects out-of-scope references to files, it's still a best practice to explicitly close files
once your code is done with them. The closeAsync method has a single parameter,
callback, that specifies the function to call on the completion of the call.

JavaScript

function closeFile(state) {
// Close the file when you're done with it.
state.file.closeAsync(function (result) {

// If the result returns as a success, the


// file has been successfully closed.
if (result.status === Office.AsyncResultStatus.Succeeded) {
updateStatus("File closed.");
} else {
updateStatus("File couldn't be closed.");
}
});
}

The final JavaScript file could look like the following:

JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT license.
* See LICENSE in the project root for license information.
*/

// The initialize or onReady function is required for all add-ins.


Office.initialize = function (reason) {

// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {

// Run sendFile when Submit is clicked.


$('#submit').click(function () {
sendFile();
});

// Update status.
updateStatus("Ready to send file.");
});
}

// Create a function for writing to the status div.


function updateStatus(message) {
var statusInfo = $('#status');
statusInfo[0].innerHTML += message + "<br/>";
}

// Get all of the content from a PowerPoint or Word document in 100-KB


chunks of text.
function sendFile() {
Office.context.document.getFileAsync("compressed",
{ sliceSize: 100000 },
function (result) {

if (result.status === Office.AsyncResultStatus.Succeeded) {

// Get the File object from the result.


var myFile = result.value;
var state = {
file: myFile,
counter: 0,
sliceCount: myFile.sliceCount
};
updateStatus("Getting file of " + myFile.size + " bytes");
getSlice(state);
} else {
updateStatus(result.status);
}
});
}

// Get a slice from the file and then call sendSlice.


function getSlice(state) {
state.file.getSliceAsync(state.counter, function (result) {
if (result.status == Office.AsyncResultStatus.Succeeded) {
updateStatus("Sending piece " + (state.counter + 1) + " of " +
state.sliceCount);
sendSlice(result.value, state);
} else {
updateStatus(result.status);
}
});
}

function sendSlice(slice, state) {


var data = slice.data;

// If the slice contains data, create an HTTP request.


if (data) {

// Encode the slice data, a byte array, as a Base64 string.


// NOTE: The implementation of myEncodeBase64(input) function isn't
// included with this example. For information about Base64 encoding
with
// JavaScript, see
https://developer.mozilla.org/docs/Web/JavaScript/Base64_encoding_and_decodi
ng.
var fileData = myEncodeBase64(data);

// Create a new HTTP request. You need to send the request


// to a webpage that can receive a post.
var request = new XMLHttpRequest();

// Create a handler function to update the status


// when the request has been sent.
request.onreadystatechange = function () {
if (request.readyState == 4) {

updateStatus("Sent " + slice.size + " bytes.");


state.counter++;

if (state.counter < state.sliceCount) {


getSlice(state);
} else {
closeFile(state);
}
}
}
request.open("POST", "[Your receiving page or service]");
request.setRequestHeader("Slice-Number", slice.index);

// Send the file as the body of an HTTP POST


// request to the web server.
request.send(fileData);
}
}

function closeFile(state) {
// Close the file when you're done with it.
state.file.closeAsync(function (result) {

// If the result returns as a success, the


// file has been successfully closed.
if (result.status === Office.AsyncResultStatus.Succeeded) {
updateStatus("File closed.");
} else {
updateStatus("File couldn't be closed.");
}
});
}
Use custom tags for presentations,
slides, and shapes in PowerPoint
Article • 07/21/2022

An add-in can attach custom metadata, in the form of key-value pairs, called "tags", to
presentations, specific slides, and specific shapes on a slide.

There are two main scenarios for using tags:

When applied to a slide or a shape, a tag enables the object to be categorized for
batch processing. For example, suppose a presentation has some slides that should
be included in presentations to the East region but not the West region. Similarly,
there are alternative slides that should be shown only to the West. Your add-in can
create a tag with the key REGION and the value East and apply it to the slides that
should only be used in the East. The tag's value is set to West for the slides that
should only be shown to the West region. Just before a presentation to the East, a
button in the add-in runs code that loops through all the slides checking the value
of the REGION tag. Slides where the region is West are deleted. The user then
closes the add-in and starts the slide show.
When applied to a presentation, a tag is effectively a custom property in the
presentation document (similar to a CustomProperty in Word).

Tag slides and shapes


A tag is a key-value pair, where the value is always of type string and is represented by
a Tag object. Each type of parent object, such as a Presentation, Slide, or Shape object,
has a tags property of type TagsCollection.

Add, update, and delete tags


To add a tag to an object, call the TagCollection.add method of the parent object's tags
property. The following code adds two tags to the first slide of a presentation. About
this code, note:

The first parameter of the add method is the key in the key-value pair.
The second parameter is the value.
The key is in uppercase letters. This isn't strictly mandatory for the add method;
however, the key is always stored by PowerPoint as uppercase, and some tag-
related methods do require that the key be expressed in uppercase, so we
recommend as a best practice that you always use uppercase in your code for a
tag key.

JavaScript

async function addMultipleSlideTags() {


await PowerPoint.run(async function(context) {
const slide = context.presentation.slides.getItemAt(0);
slide.tags.add("OCEAN", "Arctic");
slide.tags.add("PLANET", "Jupiter");

await context.sync();
});
}

The add method is also used to update a tag. The following code changes the value of
the PLANET tag.

JavaScript

async function updateTag() {


await PowerPoint.run(async function(context) {
const slide = context.presentation.slides.getItemAt(0);
slide.tags.add("PLANET", "Mars");

await context.sync();
});
}

To delete a tag, call the delete method on it's parent TagsCollection object and pass
the key of the tag as the parameter. For an example, see Set custom metadata on the
presentation.

Use tags to selectively process slides and shapes


Consider the following scenario: Contoso Consulting has a presentation they show to all
new customers. But some slides should only be shown to customers that have paid for
"premium" status. Before showing the presentation to non-premium customers, they
make a copy of it and delete the slides that only premium customers should see. An
add-in enables Contoso to tag which slides are for premium customers and to delete
these slides when needed. The following list outlines the major coding steps to create
this functionality.

1. Create a function that tags the currently selected slide as intended for Premium
customers. About this code, note:
The getSelectedSlideIndex function is defined in the next step. It returns the
1-based index of the currently selected slide.
The value returned by the getSelectedSlideIndex function has to be
decremented because the SlideCollection.getItemAt method is 0-based.

JavaScript

async function addTagToSelectedSlide() {


await PowerPoint.run(async function(context) {
let selectedSlideIndex = await getSelectedSlideIndex();
selectedSlideIndex = selectedSlideIndex - 1;
const slide =
context.presentation.slides.getItemAt(selectedSlideIndex);
slide.tags.add("CUSTOMER_TYPE", "Premium");

await context.sync();
});
}

2. The following code creates a method to get the index of the selected slide. About
this code, note:

It uses the Office.context.document.getSelectedDataAsync method of the


Common JavaScript APIs.
The call to getSelectedDataAsync is embedded in a promise-returning
function. For more information about why and how to do this, see Wrap
Common APIs in promise-returning functions.
getSelectedDataAsync returns an array because multiple slides can be

selected. In this scenario, the user has selected just one, so the code gets the
first (0th) slide, which is the only one selected.
The index value of the slide is the 1-based value the user sees beside the
slide in the PowerPoint UI thumbnails pane.

JavaScript

function getSelectedSlideIndex() {
return new OfficeExtension.Promise<number>(function(resolve,
reject) {

Office.context.document.getSelectedDataAsync(Office.CoercionType.SlideR
ange, function(asyncResult) {
try {
if (asyncResult.status ===
Office.AsyncResultStatus.Failed) {
reject(console.error(asyncResult.error.message));
} else {
resolve(asyncResult.value.slides[0].index);
}
}
catch (error) {
reject(console.log(error));
}
});
});
}

3. The following code creates a function to delete slides that are tagged for premium
customers. About this code, note:

Because the key and value properties of the tags are going to be read after
the context.sync , they must be loaded first.

JavaScript

async function deleteSlidesByAudience() {


await PowerPoint.run(async function(context) {
const slides = context.presentation.slides;
slides.load("tags/key, tags/value");

await context.sync();

for (let i = 0; i < slides.items.length; i++) {


let currentSlide = slides.items[i];
for (let j = 0; j < currentSlide.tags.items.length; j++) {
let currentTag = currentSlide.tags.items[j];
if (currentTag.key === "CUSTOMER_TYPE" && currentTag.value ===
"Premium") {
currentSlide.delete();
}
}
}

await context.sync();
});
}

Set custom metadata on the presentation


Add-ins can also apply tags to the presentation as a whole. This enables you to use tags
for document-level metadata similar to how the CustomPropertyclass is used in Word.
But unlike the Word CustomProperty class, the value of a PowerPoint tag can only be of
type string .

The following code is an example of adding a tag to a presentation.


JavaScript

async function addPresentationTag() {


await PowerPoint.run(async function (context) {
let presentationTags = context.presentation.tags;
presentationTags.add("SECURITY", "Internal-Audience-Only");

await context.sync();
});
}

The following code is an example of deleting a tag from a presentation. Note that the
key of the tag is passed to the delete method of the parent TagsCollection object.

JavaScript

async function deletePresentationTag() {


await PowerPoint.run(async function (context) {
let presentationTags = context.presentation.tags;
presentationTags.delete("SECURITY");

await context.sync();
});
}
Use document themes in your
PowerPoint add-ins
Article • 03/22/2022

An Office theme consists, in part, of a visually coordinated set of fonts and colors that
you can apply to presentations, documents, worksheets, and emails. To apply or
customize the theme of a presentation in PowerPoint, you use the Themes and Variants
groups on Design tab of the ribbon. PowerPoint assigns a new blank presentation with
the default Office Theme, but you can choose other themes available on the Design tab,
download additional themes from Office.com, or create and customize your own theme.

Using OfficeThemes.css, design add-ins that are coordinated with PowerPoint in two
ways.

In content add-ins for PowerPoint. Use the document theme classes of


OfficeThemes.css to specify fonts and colors that match the theme of the
presentation your content add-in is inserted into - and those fonts and colors will
dynamically update if a user changes or customizes the presentation's theme.

In task pane add-ins for PowerPoint. Use the Office UI theme classes of
OfficeThemes.css to specify the same fonts and background colors used in the UI
so that your task pane add-ins will match the colors of built-in task panes - and
those colors will dynamically update if a user changes the Office UI theme.

Document theme colors


Every Office document theme defines 12 colors. Ten of these colors are available when
you set font, background, and other color settings in a presentation with the color
picker.
To view or customize the full set of 12 theme colors in PowerPoint, in the Variants group
on the Design tab, click the More drop-down - then select Colors > Customize Colors
to display the Create New Theme Colors dialog box.

The first four colors are for text and backgrounds. Text that is created with the light
colors will always be legible over the dark colors, and text that is created with dark
colors will always be legible over the light colors. The next six are accent colors that are
always visible over the four potential background colors. The last two colors are for
hyperlinks and followed hyperlinks.

Document theme fonts


Every Office document theme also defines two fonts -- one for headings and one for
body text. PowerPoint uses these fonts to construct automatic text styles. In addition,
Quick Styles galleries for text and WordArt use these same theme fonts. These two
fonts are available as the first two selections when you select fonts with the font picker.
To view or customize theme fonts in PowerPoint, in the Variants group on the Design
tab, click the More drop-down - then select Fonts > Customize Fonts to display the
Create New Theme Fonts dialog box.

Office UI theme fonts and colors


Office also lets you choose between several predefined themes that specify some of the
colors and fonts used in the UI of all Office applications. To do that, you use the File >
Account > Office Theme drop-down (from any Office application).

OfficeThemes.css includes classes that you can use in your task pane add-ins for
PowerPoint so they will use these same fonts and colors. This lets you design your task
pane add-ins that match the appearance of built-in task panes.

Use OfficeThemes.css
Using the OfficeThemes.css file with your content add-ins for PowerPoint lets you
coordinate the appearance of your add-in with the theme applied to the presentation
it's running with. Using the OfficeThemes.css file with your task pane add-ins for
PowerPoint lets you coordinate the appearance of your add-in with the fonts and colors
of the Office UI.

Add the OfficeThemes.css file to your project


Use the following steps to add and reference the OfficeThemes.css file to your add-in
project.

7 Note

The steps in this procedure only apply to Visual Studio 2015. If you are using Visual
Studio 2019, the OfficeThemes.css file is created automatically for any new
PowerPoint add-in projects that you create.

1. In Solution Explorer, right-click the Content folder in the project_nameWeb


project, choose Add, and then select Style Sheet.

2. Name the new style sheet OfficeThemes.

) Important

The style sheet must be named OfficeThemes, or the feature that dynamically
updates add-in fonts and colors when a user changes the theme won't work.

3. Delete the default body class ( body {} ) in the file, and copy and paste the
following CSS code into the file.

css

/* The following classes describe the common theme information for


office documents */

/* Basic Font and Background Colors for text */


.office-docTheme-primary-fontColor { color:#000000; }
.office-docTheme-primary-bgColor { background-color:#ffffff; }
.office-docTheme-secondary-fontColor { color: #000000; }
.office-docTheme-secondary-bgColor { background-color: #ffffff; }

/* Accent color definitions for fonts */


.office-contentAccent1-color { color:#5b9bd5; }
.office-contentAccent2-color { color:#ed7d31; }
.office-contentAccent3-color { color:#a5a5a5; }
.office-contentAccent4-color { color:#ffc000; }
.office-contentAccent5-color { color:#4472c4; }
.office-contentAccent6-color { color:#70ad47; }

/* Accent color for backgrounds */


.office-contentAccent1-bgColor { background-color:#5b9bd5; }
.office-contentAccent2-bgColor { background-color:#ed7d31; }
.office-contentAccent3-bgColor { background-color:#a5a5a5; }
.office-contentAccent4-bgColor { background-color:#ffc000; }
.office-contentAccent5-bgColor { background-color:#4472c4; }
.office-contentAccent6-bgColor { background-color:#70ad47; }

/* Accent color for borders */


.office-contentAccent1-borderColor { border-color:#5b9bd5; }
.office-contentAccent2-borderColor { border-color:#ed7d31; }
.office-contentAccent3-borderColor { border-color:#a5a5a5; }
.office-contentAccent4-borderColor { border-color:#ffc000; }
.office-contentAccent5-borderColor { border-color:#4472c4; }
.office-contentAccent6-borderColor { border-color:#70ad47; }

/* links */
.office-a { color: #0563c1; }
.office-a:visited { color: #954f72; }

/* Body Fonts */
.office-bodyFont-eastAsian { } /* East Asian name of the Font */
.office-bodyFont-latin { font-family:"Calibri"; } /* Latin name of the
Font */
.office-bodyFont-script { } /* Script name of the Font */
.office-bodyFont-localized { font-family:"Calibri"; } /* Localized name
of the Font. Corresponds to the default font of the culture currently
used in Office.*/

/* Headers Font */
.office-headerFont-eastAsian { }
.office-headerFont-latin { font-family:"Calibri Light"; }
.office-headerFont-script { }
.office-headerFont-localized { font-family:"Calibri Light"; }

/* The following classes define font and background colors for Office
UI themes. These classes should only be used in task pane add-ins */

/* Basic Font and Background Colors for PPT */


.office-officeTheme-primary-fontColor { color:#b83b1d; }
.office-officeTheme-primary-bgColor { background-color:#dedede; }
.office-officeTheme-secondary-fontColor { color:#262626; }
.office-officeTheme-secondary-bgColor { background-color:#ffffff; }

4. If you are using a tool other than Visual Studio to create your add-in, copy the CSS
code from step 3 into a text file, making sure to save the file as OfficeThemes.css.
Reference OfficeThemes.css in your add-in's
HTML pages
To use the OfficeThemes.css file in your add-in project, add a <link> tag that references
the OfficeThemes.css file inside the <head> tag of the web pages (such as an .html, .aspx,
or .php file) that implement the UI of your add-in in this format.

HTML

<link href="<local_path_to_OfficeThemes.css>" rel="stylesheet"


type="text/css" />

To do this in Visual Studio, follow these steps.

1. Choose Create a new project.

2. Using the search box, enter add-in. Choose PowerPoint Web Add-in, then select
Next.

3. Name your project and select Create.

4. In the Create Office Add-in dialog window, choose Add new functionalities to
PowerPoint, and then choose Finish to create the project.

5. Visual Studio creates a solution and its two projects appear in Solution Explorer.
The Home.html file opens in Visual Studio.

6. In the HTML pages that implement the UI of your add-in, such as Home.html in the
default template, add the following <link> tag inside the <head> tag that
references the OfficeThemes.css file.

HTML

<link href="../../Content/OfficeThemes.css" rel="stylesheet"


type="text/css" />

If you are creating your add-in with a tool other than Visual Studio, add a <link> tag
with the same format specifying a relative path to the copy of OfficeThemes.css that will
be deployed with your add-in.

Use OfficeThemes.css document theme classes in your


content add-in's HTML page
The following shows a simple example of HTML in a content add-in that uses the
OfficeTheme.css document theme classes. For details about the OfficeThemes.css classes
that correspond to the 12 colors and 2 fonts used in a document theme, see Theme
classes for content add-ins.

HTML

<body>
<div id="themeSample" class="office-docTheme-primary-fontColor ">
<h1 class="office-headerFont-latin">Hello world!</h1>
<h1 class="office-headerFont-latin office-contentAccent1-
bgColor">Hello world!</h1>
<h1 class="office-headerFont-latin office-contentAccent2-
bgColor">Hello world!</h1>
<h1 class="office-headerFont-latin office-contentAccent3-
bgColor">Hello world!</h1>
<h1 class="office-headerFont-latin office-contentAccent4-
bgColor">Hello world!</h1>
<h1 class="office-headerFont-latin office-contentAccent5-
bgColor">Hello world!</h1>
<h1 class="office-headerFont-latin office-contentAccent6-
bgColor">Hello world!</h1>
<p class="office-bodyFont-latin office-docTheme-secondary-
fontColor">Hello world!</p>
</div>
</body>

At runtime, when inserted into a presentation that uses the default Office Theme, the
content add-in is rendered like this.

If you change the presentation to use another theme or customize the presentation's
theme, the fonts and colors specified with OfficeThemes.css classes will dynamically
update to correspond to the fonts and colors of the presentation's theme. Using the
same HTML example as above, if the presentation the add-in is inserted into uses the
Facet theme, the add-in rendering will look like this.

Use OfficeThemes.css Office UI theme classes in your task


pane add-in's HTML page
In addition to the document theme, users can customize the color scheme of the Office
user interface for all Office applications using the File > Account > Office Theme drop-
down box.

The following shows a simple example of HTML in a task pane add-in that uses
OfficeTheme.css classes to specify font color and background color. For details about
the OfficeThemes.css classes that correspond to fonts and colors of the Office UI theme,
see Theme classes for task pane add-ins.

HTML

<body>
<div id="content-header" class="office-officeTheme-primary-fontColor
office-officeTheme-primary-bgColor">
<div class="padding">
<h1>Welcome</h1>
</div>
</div>
<div id="content-main" class="office-officeTheme-secondary-fontColor
office-officeTheme-secondary-bgColor">
<div class="padding">
<p>Add home screen content here.</p>
<p>For example:</p>
<button id="get-data-from-selection">Get data from
selection</button>
<p><a target="_blank" class="office-a"
href="https://go.microsoft.com/fwlink/?LinkId=276812">Find more samples
online...</a></p>
</div>
</div>
</body>

When running in PowerPoint with File > Account > Office Theme set to White, the task
pane add-in is rendered like this.

If you change OfficeTheme to Dark Gray, the fonts and colors specified with
OfficeThemes.css classes will dynamically update to render like this.

OfficeTheme.css classes
The OfficeThemes.css file contains two sets of classes you can use with your content and
task pane add-ins for PowerPoint.

Theme classes for content add-ins


The OfficeThemes.css file provides classes that correspond to the 2 fonts and 12 colors
used in a document theme. These classes are appropriate to use with content add-ins
for PowerPoint so that your add-in's fonts and colors will be coordinated with the
presentation it's inserted into.

Theme fonts for content add-ins

Class Description

office-bodyFont- East Asian name of the body font.


eastAsian

office-bodyFont- Latin name of the body font. Default "Calabri"


latin

office-bodyFont- Script name of the body font.


script

office-bodyFont- Localized name of the body font. Specifies the default font name according
localized to the culture currently used in Office.

office- East Asian name of the headers font.


headerFont-
eastAsian

office- Latin name of the headers font. Default "Calabri Light"


headerFont-latin

office- Script name of the headers font.


headerFont-script

office- Localized name of the headers font. Specifies the default font name
headerFont- according to the culture currently used in Office.
localized

Theme colors for content add-ins

Class Description

office-docTheme-primary-fontColor Primary font color. Default #000000


Class Description

office-docTheme-primary-bgColor Primary font background color. Default #FFFFFF

office-docTheme-secondary-fontColor Secondary font color. Default #000000

office-docTheme-secondary-bgColor Secondary font background color. Default #FFFFFF

office-contentAccent1-color Font accent color 1. Default #5B9BD5

office-contentAccent2-color Font accent color 2. Default #ED7D31

office-contentAccent3-color Font accent color 3. Default #A5A5A5

office-contentAccent4-color Font accent color 4. Default #FFC000

office-contentAccent5-color Font accent color 5. Default #4472C4

office-contentAccent6-color Font accent color 6. Default #70AD47

office-contentAccent1-bgColor Background accent color 1. Default #5B9BD5

office-contentAccent2-bgColor Background accent color 2. Default #ED7D31

office-contentAccent3-bgColor Background accent color 3. Default #A5A5A5

office-contentAccent4-bgColor Background accent color 4. Default #FFC000

office-contentAccent5-bgColor Background accent color 5. Default #4472C4

office-contentAccent6-bgColor Background accent color 6. Default #70AD47

office-contentAccent1-borderColor Border accent color 1. Default #5B9BD5

office-contentAccent2-borderColor Border accent color 2. Default #ED7D31

office-contentAccent3-borderColor Border accent color 3. Default #A5A5A5

office-contentAccent4-borderColor Border accent color 4. Default #FFC000

office-contentAccent5-borderColor Border accent color 5. Default #4472C4

office-contentAccent6-borderColor Border accent color 6. Default #70AD47

office-a Hyperlink color. Default #0563C1

office-a:visited Followed hyperlink color. Default #954F72

The following screenshot shows examples of all of the theme color classes (except for
the two hyperlink colors) assigned to add-in text when using the default Office theme.
Theme classes for task pane add-ins
The OfficeThemes.css file provides classes that correspond to the 4 colors assigned to
fonts and backgrounds used by the Office application UI theme. These classes are
appropriate to use with task add-ins for PowerPoint so that your add-in's colors will be
coordinated with the other built-in task panes in Office.

Theme font and background colors for task pane add-ins

Class Description

office-officeTheme-primary-fontColor Primary font color. Default #B83B1D

office-officeTheme-primary-bgColor Primary background color. Default #DEDEDE

office-officeTheme-secondary-fontColor Secondary font color. Default #262626

office-officeTheme-secondary-bgColor Secondary background color. Default #FFFFFF

See also
Create content and task pane add-ins for PowerPoint
Work with shapes using the PowerPoint
JavaScript API
Article • 03/16/2023

This article describes how to use geometric shapes, lines, and text boxes in conjunction
with the Shape and ShapeCollection APIs.

Create shapes
Shapes are created through and stored in a slide's shape collection ( slide.shapes ).
ShapeCollection has several .add* methods for this purpose. All shapes have names

and IDs generated for them when they are added to the collection. These are the name
and id properties, respectively. name can be set by your add-in.

Geometric shapes
A geometric shape is created with one of the overloads of
ShapeCollection.addGeometricShape . The first parameter is either a GeometricShapeType

enum or the string equivalent of one of the enum's values. There is an optional second
parameter of type ShapeAddOptions that can specify the initial size of the shape and its
position relative to the top and left sides of the slide, measured in points. Or these
properties can be set after the shape is created.

The following code sample creates a rectangle named "Square" that is positioned 100
points from the top and left sides of the slide. The method returns a Shape object.

JavaScript

// This sample creates a rectangle positioned 100 points from the top and
left sides
// of the slide and is 150x150 points. The shape is put on the first slide.
await PowerPoint.run(async (context) => {
const shapes = context.presentation.slides.getItemAt(0).shapes;
const rectangle =
shapes.addGeometricShape(PowerPoint.GeometricShapeType.rectangle);
rectangle.left = 100;
rectangle.top = 100;
rectangle.height = 150;
rectangle.width = 150;
rectangle.name = "Square";
await context.sync();
});
Lines
A line is created with one of the overloads of ShapeCollection.addLine . The first
parameter is either a ConnectorType enum or the string equivalent of one of the enum's
values to specify how the line contorts between endpoints. There is an optional second
parameter of type ShapeAddOptions that can specify the start and end points of the
line. Or these properties can be set after the shape is created. The method returns a
Shape object.

7 Note

When the shape is a line, the top and left properties of the Shape and
ShapeAddOptions objects specify the starting point of the line relative to the top and
left edges of the slide. The height and width properties specify the endpoint of the
line relative to the start point. So, the end point relative to the top and left edges of
the slide is ( top + height ) by ( left + width ). The unit of measure for all properties
is points and negative values are allowed.

The following code sample creates a straight line on the slide.

JavaScript

// This sample creates a straight line on the first slide.


await PowerPoint.run(async (context) => {
const shapes = context.presentation.slides.getItemAt(0).shapes;
const line = shapes.addLine(PowerPoint.ConnectorType.straight, {left:
200, top: 50, height: 300, width: 150});
line.name = "StraightLine";
await context.sync();
});

Text boxes
A text box is created with the addTextBox method. The first parameter is the text that
should appear in the box initially. There is an optional second parameter of type
ShapeAddOptions that can specify the initial size of the text box and its position relative
to the top and left sides of the slide. Or these properties can be set after the shape is
created.

The following code sample shows how to create a text box on the first slide.

JavaScript
// This sample creates a text box with the text "Hello!" and sizes it
appropriately.
await PowerPoint.run(async (context) => {
const shapes = context.presentation.slides.getItemAt(0).shapes;
const textbox = shapes.addTextBox("Hello!");
textbox.left = 100;
textbox.top = 100;
textbox.height = 300;
textbox.width = 450;
textbox.name = "Textbox";
await context.sync();
});

Move and resize shapes


Shapes sit on top of the slide. Their placement is defined by the left and top
properties. These act as margins from slide's respective edges, measured in points, with
left: 0 and top: 0 being the upper-left corner. The shape size is specified by the

height and width properties. Your code can move or resize the shape by resetting these
properties. (These properties have a slightly different meaning when the shape is a line.
See Lines.)

Text in shapes
Geometric shapes can contain text. Shapes have a textFrame property of type
TextFrame. The TextFrame object manages the text display options (such as margins and
text overflow). TextFrame.textRange is a TextRange object with the text content and font
settings.

The following code sample creates a geometric shape named "Braces" with the text
"Shape text". It also adjusts the shape and text colors, as well as sets the text's vertical
alignment to the center.

JavaScript

// This sample creates a light blue rectangle with braces ("{}") on the left
and right ends
// and adds the purple text "Shape text" to the center.
await PowerPoint.run(async (context) => {
const shapes = context.presentation.slides.getItemAt(0).shapes;
const braces =
shapes.addGeometricShape(PowerPoint.GeometricShapeType.bracePair);
braces.left = 100;
braces.top = 400;
braces.height = 50;
braces.width = 150;
braces.name = "Braces";
braces.fill.setSolidColor("lightblue");
braces.textFrame.textRange.text = "Shape text";
braces.textFrame.textRange.font.color = "purple";
braces.textFrame.verticalAlignment =
PowerPoint.TextVerticalAlignment.middleCentered;
await context.sync();
});

Delete shapes
Shapes are removed from the slide with the Shape object's delete method.

The following code sample shows how to delete shapes.

JavaScript

await PowerPoint.run(async (context) => {


// Delete all shapes from the first slide.
const sheet = context.presentation.slides.getItemAt(0);
const shapes = sheet.shapes;

// Load all the shapes in the collection without loading their


properties.
shapes.load("items/$none");
await context.sync();

shapes.items.forEach(function (shape) {
shape.delete();
});
await context.sync();
});
Project add-ins documentation
With Project add-ins, you can use familiar web technologies such as HTML, CSS, and
JavaScript to build a solution that can run in Project on Windows. Learn how to build,
test, debug, and publish Project add-ins.

About Project add-ins

e OVERVIEW

What are Project add-ins?

JavaScript API for Project

f QUICKSTART

Build your first Project add-in

c HOW-TO GUIDE

Test and debug a Project add-in

Deploy and publish a Project add-in

Key Office Add-ins concepts

e OVERVIEW

Office Add-ins platform overview

b GET STARTED

Core concepts for Office Add-ins

Design Office Add-ins

Develop Office Add-ins

Resources

i REFERENCE
Ask questions

Request features

Report issues

Office Add-ins additional resources


Task pane add-ins for Project
Article • 07/12/2022

Project Standard 2013 and Project Professional 2013 (version 15.1 or higher) both
include support for task pane add-ins. You can run general task pane add-ins that are
developed for Word or Excel. You can also develop custom add-ins that handle selection
events in Project and integrate task, resource, view, and other cell-level data in a project
with SharePoint lists, SharePoint Add-ins, Web Parts, web services, and enterprise
applications.

7 Note

The Project 2013 SDK download includes sample add-ins that show how to use
the add-in object model for Project, and how to use the OData service for reporting
data in Project Server 2013. When you extract and install the SDK, see the
\Samples\Apps\ subdirectory.

For an introduction to Office Add-ins, see Office Add-ins platform overview.

Add-in scenarios for Project


Project managers can use Project task pane add-ins to help with project management
activities. Instead of leaving Project and opening another application to search for
frequently used information, project managers can directly access the information within
Project. The content in a task pane add-in can be context-sensitive, based on the
selected task, resource, view, or other data in a cell in a Gantt chart, task usage view, or
resource usage view.

7 Note

With Project Professional 2013, you can develop task pane add-ins that access
Project on the web, on-premises installations of Project Server 2013, and on-
premises or online SharePoint 2013. Project Standard 2013 does not support direct
integration with Project Server data or SharePoint task lists that are synchronized
with Project Server.

Add-in scenarios for Project include the following:


Project scheduling View data from related projects that can affect scheduling. A
task pane add-in can integrate relevant data from other projects in Project Server
2013. For example, you can view the departmental collection of projects and
milestone dates, or view specified data from other projects that are based on a
selected custom field.

Resource management View the complete resource pool in Project Server 2013 or
a subset based on specified skills, including cost data and resource availability, to
help select appropriate resources.

Statusing and approvals Use a web application in a task pane add-in to update or
view data from an external enterprise resource planning (ERP) application,
timesheet system, or accounting application. Or, create a custom status approval
Web Part that can be used within both Project Web App and Project Professional
2013.

Team communication Communicate with team members and resources directly


from a task pane add-in, within the context of a project. Or, easily maintain a set of
context-sensitive notes for yourself as you work in a project.

Work packages Search for specified kinds of project templates within SharePoint
libraries and online template collections. For example, find templates for
construction projects and add them to your Project template collection.

Related items View metadata, documents, and messages that are related to
specific tasks in a project plan. For example, you can use Project Professional 2013
to manage a project that was imported from a SharePoint task list, and still
synchronize the task list with changes in the project. A task pane add-in can show
additional fields or metadata that Project did not import for tasks in the SharePoint
list.

Use the Project Server object models Use the GUID of a selected task with
methods in the Project Server Interface (PSI) or the client-side object model
(CSOM) of Project Server. For example, the web application for an add-in can read
and update the statusing data of a selected task and resource, or integrate with an
external timesheet application.

Get reporting data Use Representational State Transfer (REST), JavaScript, or LINQ
queries to find related information for a selected task or resource in the OData
service for reporting tables in Project Web App. Queries that use the OData service
can be done with an online or an on-premises installation of Project Server 2013.
For example, see Create a Project add-in that uses REST with an on-premises
Project Server OData service.

Developing Project add-ins


The JavaScript library for Project add-ins includes extensions of the Office namespace
alias that enable developers to access properties of the Project application and tasks,
resources, and views in a project. The JavaScript library extensions in the Project-15.js
file are used in a Project add-in created with Visual Studio 2015. The Office.js,
Office.debug.js, Project-15.js, Project-15.debug.js, and related files are also provided in
the Project 2013 SDK download.

To create an add-in, you can use a simple text editor to create an HTML webpage and
related JavaScript files, CSS files, and REST queries. In addition to an HTML page or a
web application, an add-in requires an XML manifest file for configuration. Project can
use a manifest file that includes a type attribute that is specified as TaskPaneExtension.
The manifest file can be used by multiple Office 2013 client applications, or you can
create a manifest file that is specific for Project 2013. For more information, see the
Development basics section in Office Add-ins platform overview.

When you install the Project 2013 SDK download, the \Samples\Apps\ subdirectory
includes the following sample add-ins.

Bing Search: The BingSearch.xml manifest file points to the Bing search page for
mobile devices. Because the Bing web app already exists on the Internet, the Bing
Search add-in does not use other source code files or the add-in object model for
Project.

Project OM Test: The JSOM_SimpleOMCalls.xml manifest file and the


JSOM_Call.html file are, together, an example that tests the object model and add-
in functionality in Project 2013. The HTML file references the JSOM_Sample.js file,
which has JavaScript functions that use the Office.js file and the Project-15.js file
for the primary functionality. The SDK download includes all of the necessary
source code files and the manifest XML file for the Project OM Test add-in. The
development and installation of the Project OM Test sample is described in Create
your first task pane add-in for Project 2013 by using a text editor.

HelloProject_OData: This is a Visual Studio solution for Project Professional 2013


that summarizes data from the active project, such as cost, work, and percent
complete, and compares that with the average for all published projects in the
Project Web App instance where the active project is stored. The development,
installation, and testing of the sample, which uses the REST protocol with the
ProjectData service in Project Web App, is described in Create a Project add-in that
uses REST with an on-premises Project Server OData service.

Creating an add-in manifest file


The manifest file specifies the URL of the add-in webpage or web application, the kind
of add-in (task pane for Project), optional URLs of content for other languages and
locales, and other properties.

Procedure 1. To create the add-in manifest file for Bing


Search
Create an XML file in a local directory. The XML file includes the <OfficeApp>
element and child elements, which are described in the Office Add-ins XML
manifest. For example, create a file named BingSearch.xml that contains the
following XML.

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<!--IMPORTANT! Id must be unique for each add-in. If you copy this
manifest ensure that you change this id to your own GUID. -->
<Id>01234567-89ab-cedf-0123-456789abcdef</Id>
<Version>15.0</Version>
<ProviderName>Microsoft</ProviderName>
<DefaultLocale>en-us</DefaultLocale>
<DisplayName DefaultValue="Bing Search">
</DisplayName>
<Description DefaultValue="Search selected data on Bing">
</Description>
<IconUrl
DefaultValue="http://officeimg.vo.msecnd.net/_layouts/images/general/of
fice_logo.jpg">
</IconUrl>
<SupportUrl DefaultValue="[Insert the URL of a page that provides
support information for the app]" />
<Capabilities>
<Capability Name="Project"/>
</Capabilities>
<DefaultSettings>
<SourceLocation DefaultValue="http://m.bing.com">
</SourceLocation>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
</OfficeApp>
Following are the required elements in the add-in manifest.
In the <OfficeApp> element, the xsi:type="TaskPaneApp" attribute specifies
that the add-in is a task pane type.
The <Id> element is a UUID and must be unique.
The <Version> element is the version of the add-in. The <ProviderName>
element is the name of the company or developer who provides the add-in. The
<DefaultLocale> element specifies the default locale for the strings in the
manifest.
The <DisplayName> element is the name that shows in the Task Pane Add-in
drop-down list in the VIEW tab of the ribbon in Project 2013. The value can
contain up to 32 characters.
The <Description> element contains the add-in description for the default
locale. The value can contain up to 2000 characters.
The Capabilities element contains one or more Capability child elements that
specify the Office application.
The <DefaultSettings> element includes the <SourceLocation> element, which
specifies the path of an HTML file on a file share or the URL of a webpage that
the add-in uses. A task pane add-in ignores the <RequestedHeight> element
and the <RequestedWidth> element.
The <IconUrl> element is optional. It can be an icon on a file share or the URL
of an icon in a web application.

(Optional) Add <Override> elements that have values for other locales. For
example, the following manifest provides <Override> elements for French values
of <DisplayName>, <Description>, <IconUrl>, and <SourceLocation>.

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<!--IMPORTANT! Id must be unique for each add-in. If you copy this
manifest ensure that you change this id to your own GUID. -->
<Id>01234567-89ab-cedf-0123-456789abcdef</Id>
<Version>15.0</Version>
<ProviderName>Microsoft</ProviderName>
<DefaultLocale>en-us</DefaultLocale>
<DisplayName DefaultValue="Bing Search">
<Override Locale="fr-fr" Value="Bing Search"/>
</DisplayName>
<Description DefaultValue="Search selected data on Bing">
<Override Locale="fr-fr" Value="Search selected data on Bing">
</Override>
</Description>
<IconUrl
DefaultValue="http://officeimg.vo.msecnd.net/_layouts/images/general/of
fice_logo.jpg">
<Override Locale="fr-fr"
Value="http://officeimg.vo.msecnd.net/_layouts/images/general/office_lo
go.jpg"/>
</IconUrl>
<SupportUrl DefaultValue="[Insert the URL of a page that provides
support information for the app]" />
<Capabilities>
<Capability Name="Project"/>
</Capabilities>
<DefaultSettings>
<SourceLocation DefaultValue="http://m.bing.com">
<Override Locale="fr-fr" Value="http://m.bing.com"/>
</SourceLocation>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
</OfficeApp>

Installing Project add-ins


In Project 2013, you can install add-ins as stand-alone solutions on a file share, or in a
private add-in catalog. You can also review and purchase add-ins in AppSource.

There can be multiple add-in manifest XML files and subdirectories in a file share. You
can add or remove manifest directory locations and catalogs by using the Trusted Add-
in Catalogs tab in the Trust Center dialog box in Project 2013. To show an add-in in
Project, the <SourceLocation> element in a manifest must point to an existing website
or HTML source file.

7 Note

If you are developing on a Windows computer, either Internet Explorer or Microsoft


Edge must be installed. For more information see Browsers and webview controls
used by Office Add-ins.

In Procedure 2, the Bing Search add-in is installed on the local computer where Project
2013 is installed. However, because the add-in infrastructure does not directly use local
file paths such as C:\Project\AppManifests , you can create a network share on the local
computer. If you prefer, you can create a file share on a remote computer.

Procedure 2. To install the Bing Search add-in


1. Create a local directory for add-in manifests. For example, create the
C:\Project\AppManifests directory.

2. Share the C:\Project\AppManifests directory asAppManifests, so the network path


to the file share becomes \\ServerName\AppManifests .

3. Copy the BingSearch.xml manifest file to the C:\Project\AppManifests directory.

4. In Project 2013, open the Project Options dialog box, choose Trust Center, and
then choose Trust Center Settings.

5. In the Trust Center dialog box, in the left pane, choose Trusted Add-in Catalogs.

6. In the Trusted Add-in Catalogs pane (see Figure 1), add the
\\ServerName\AppManifests path in the Catalog Url text box, choose Add Catalog,

and then choose OK.

7 Note

Figure 1 shows two file shares and one hypothetical URL for a private catalog
in the Trusted Catalog Address list. Only one file share can be the default file
share and only one catalog URL can be the default catalog. For example, if
you set \\Server2\AppManifests as the default, Project clears the Default
check box for \\ServerName\AppManifests .If you change the default selection,
you can choose Clear to remove installed add-ins, and then restart Project. If
you add an add-in to the default file share or SharePoint catalog while Project
is open, you should restart Project.

Figure 1. Using the Trust Center to add catalogs of add-in manifests


7. On the Project ribbon, choose the Office Add-ins drop-down menu, and then
choose See All. In the Insert Add-in dialog box, choose SHARED FOLDER (see
Figure 2).

Figure 2. Starting an add-in that is on a file share

8. Select the Bing Search add-in, and then choose Insert.

The Bing Search add-in shows in a task pane, as in Figure 3. You can manually
resize the task pane, and use the Bing Search add-in.

Figure 3. Using the Bing Search add-in


Distributing Project add-ins
You can distribute add-ins through a file share, an app catalog in a SharePoint library, or
AppSource. For more information, see Publish your Office Add-in.

See also
Office Add-ins platform overview
Learn about the Microsoft 365 Developer Program
Developing Office Add-ins
Create your first task pane add-in for Project 2013 by using a text editor
Create a Project add-in that uses REST with an on-premises Project Server OData
service
Project 2013 SDK download
Build your first Project task pane add-in
Article • 05/02/2023

In this article, you'll walk through the process of building a Project task pane add-in.

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Project 2016 or later on Windows

Create the add-in


Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note
When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: Javascript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Project

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample code
for a very basic task pane add-in.

The ./manifest.xml file in the root directory of the project defines the settings and
capabilities of the add-in.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to content in
the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code that
facilitates interaction between the task pane and the Office client application. In
this quick start, the code sets the Name field and Notes field of the selected task of
a project.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Start the local web server.

7 Note

Office Add-ins should use HTTPS, not HTTP, even when you are developing. If
you are prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

Run the following command in the root directory of your project. When you run
this command, the local web server will start.

command line

npm run dev-server

3. In Project, create a simple project plan.

4. Load your add-in in Project by following the instructions in Sideload Office Add-ins
on Windows.

5. Select a single task within the project.


6. At the bottom of the task pane, choose the Run link to rename the selected task
and add notes to the selected task.

Next steps
Congratulations, you've successfully created a Project task pane add-in! Next, learn
more about the capabilities of a Project add-in and explore common scenarios.

Project add-ins

See also
Develop Office Add-ins
Core concepts for Office Add-ins
Using Visual Studio Code to publish
JavaScript API for Project
Article • 05/02/2023

Project supports add-ins made with the JavaScript API, but there's currently no
JavaScript API designed specifically for interacting with Project. You can use the
Common API to create Project add-ins. For information about the Common API, see
Office JavaScript API object model.

Learn about API capabilities


For hands-on experience using the Common API to interact with content in Project,
complete the Project add-in quick start.

See also
Project add-ins documentation
Project add-ins overview
API Reference documentation
Office client application and platform availability for Office Add-ins
Create a Project add-in that uses REST
with an on-premises Project Server
OData service
Article • 07/21/2022

This article describes how to build a task pane add-in for Project Professional 2013 that
compares cost and work data in the active project with the averages for all projects in
the current Project Web App instance. The add-in uses REST with the jQuery library to
access the ProjectData OData reporting service in Project Server 2013.

The code in this article is based on a sample developed by Saurabh Sanghvi and Arvind
Iyer, Microsoft Corporation.

Prerequisites
The following are the prerequisites for creating a Project task pane add-in that reads the
ProjectData service of a Project Web App instance in an on-premises installation of
Project Server 2013.

Ensure that you have installed the most recent service packs and Windows updates
on your local development computer. The operating system can be Windows 7,
Windows 8, Windows Server 2008, or Windows Server 2012.

Project Professional 2013 is required to connect with Project Web App. The
development computer must have Project Professional 2013 installed to enable F5
debugging with Visual Studio.

7 Note

Project Standard 2013 can also host task pane add-ins, but cannot sign in to
Project Web App.

Visual Studio 2015 with Office Developer Tools for Visual Studio includes templates
for creating Office and SharePoint Add-ins. Ensure that you have installed the most
recent version of Office Developer Tools; see the Tools section of the Office Add-ins
and SharePoint downloads.

The procedures and code examples in this article access the ProjectData service of
Project Server 2013 in a local domain. The jQuery methods in this article do not
work with Project on the web.

Verify that the ProjectData service is accessible from your development computer.

Procedure 1. Verify that the ProjectData service is


accessible
1. To enable your browser to directly show the XML data from a REST query, turn off
the feed reading view. For information about how to do this in Internet Explorer,
see Procedure 1, step 4 in Query OData feeds for Project reporting data.

2. Query the ProjectData service by using your browser with the following URL:
http://ServerName /ProjectServerName /_api/ProjectData. For example, if the
Project Web App instance is http://MyServer/pwa , the browser shows the following
results.

XML

<?xml version="1.0" encoding="utf-8"?>


<service xml:base="http://myserver/pwa/_api/ProjectData/"
xmlns="https://www.w3.org/2007/app"
xmlns:atom="https://www.w3.org/2005/Atom">
<workspace>
<atom:title>Default</atom:title>
<collection href="Projects">
<atom:title>Projects</atom:title>
</collection>
<collection href="ProjectBaselines">
<atom:title>ProjectBaselines</atom:title>
</collection>
<!-- ... and 33 more collection elements -->
</workspace>
</service>

3. You may have to provide your network credentials to see the results. If the browser
shows "Error 403, Access Denied," either you do not have logon permission for
that Project Web App instance, or there is a network problem that requires
administrative help.

Use Visual Studio to create a task pane add-in


for Project
Office Developer Tools for Visual Studio includes a template for task pane add-ins for
Project 2013. If you create a solution named HelloProjectOData, the solution contains
the following two Visual Studio projects:

The add-in project takes the name of the solution. It includes the XML manifest file
for the add-in and targets the .NET Framework 4.5. Procedure 3 shows the steps to
modify the manifest for the HelloProjectOData add-in.

The web project is named HelloProjectODataWeb. It includes the webpages,


JavaScript files, CSS files, images, references, and configuration files for the web
content in the task pane. The web project targets the .NET Framework 4. Procedure
4 and Procedure 5 show how to modify the files in the web project to create the
functionality of the HelloProjectOData add-in.

Procedure 2. Create the HelloProjectOData add-in for


Project
1. Run Visual Studio 2015 as an administrator, and then select New Project on the
Start page.

2. In the New Project dialog box, expand the Templates, Visual C#, and
Office/SharePoint nodes, and then select Office Add-ins. Select .NET Framework
4.5.2 in the target framework drop-down list at the top of the center pane, and
then select Office Add-in (see the next screenshot).

3. To place both of the Visual Studio projects in the same directory, select Create
directory for solution, and then browse to the location you want.

4. In the Name field, typeHelloProjectOData, and then choose OK.

Figure 1. Create an Office Add-in


5. In the Choose the add-in type dialog box, select Task pane and choose Next (see
the next screenshot).

Figure 2. Choosing the type of add-in to create


6. In the Choose the host applications dialog box, clear all check boxes except the
Project check box (see the next screenshot) and choose Finish.

Figure 3. Choosing the host application

Visual Studio creates the HelloProjectOdata project and the


HelloProjectODataWeb project.

The AddIn folder (see the next screenshot) contains the App.css file for custom CSS
styles. In the Home subfolder , the Home.html file contains references to the CSS files
and the JavaScript files that the add-in uses, and the HTML5 content for the add-in.
Also, the Home.js file is for your custom JavaScript code. The Scripts folder includes the
jQuery library files. The Office subfolder includes the JavaScript libraries such as office.js
and project-15.js, plus the language libraries for standard strings in the Office Add-ins.
In the Content folder, the Office.css file contains the default styles for all of the Office
Add-ins.

Figure 4. View the default web project files in Solution Explorer


The manifest for the HelloProjectOData project is the HelloProjectOData.xml file. You
can optionally modify the manifest to add a description of the add-in, a reference to an
icon, information for additional languages, and other settings. Procedure 3 simply
modifies the add-in display name and description, and adds an icon.

For more information about the manifest, see Office Add-ins manifest and Schema
reference for Office Add-ins manifests.

Procedure 3. Modify the add-in manifest


1. In Visual Studio, open the HelloProjectOData.xml file.

2. The default display name is the name of the Visual Studio project
("HelloProjectOData"). For example, change the default value of the
<DisplayName> element to"Hello ProjectData".
3. The default description is also "HelloProjectOData". For example, change the
default value of the Description element to "Test REST queries of the ProjectData
service".

4. Add an icon to show in the Office Add-ins drop-down list on the PROJECT tab of
the ribbon. You can add an icon file in the Visual Studio solution or use a URL for
an icon.

The following steps show how to add an icon file to the Visual Studio solution.

1. In Solution Explorer, go to the folder named Images.

2. To be displayed in the Office Add-ins drop-down list, the icon must be 32 x 32


pixels. For example, install the Project 2013 SDK, and then choose the Images
folder and add the following file from the SDK:
\Samples\Apps\HelloProjectOData\HelloProjectODataWeb\Images\NewIcon.png

Alternately, use your own 32 x 32 icon; or, copy the following image to a file
named NewIcon.png, and then add that file to the HelloProjectODataWeb\Images
folder.

3. In the HelloProjectOData.xml manifest, add an <IconUrl> element below the


<Description> element, where the value of the icon URL is the relative path to the
32x32 icon file. For example, add the following line: <IconUrl
DefaultValue="~remoteAppUrl/Images/NewIcon.png" /> . The HelloProjectOData.xml

manifest file now contains the following (your <Id> value will be different):

XML

<?xml version="1.0" encoding="UTF-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<!--IMPORTANT! Id must be unique for each add-in. If you copy this
manifest ensure that you change this id to your own GUID. -->
<Id>c512df8d-a1c5-4d74-8a34-d30f6bbcbd82</Id>
<Version>1.0</Version>
<ProviderName> [Provider name]</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="Hello ProjectData" />
<Description DefaultValue="Test REST queries of the ProjectData
service"/>
<IconUrl DefaultValue="~remoteAppUrl/Images/NewIcon.png" />
<SupportUrl DefaultValue="[Insert the URL of a page that provides
support information for the app]" />
<Hosts>
<Host Name="Project" />
</Hosts>
<DefaultSettings>
<SourceLocation
DefaultValue="~remoteAppUrl/AddIn/Home/Home.html" />
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
</OfficeApp>

Create the HTML content for the


HelloProjectOData add-in
The HelloProjectOData add-in is a sample that includes debugging and error output; it
is not intended for production use. Before you start coding the HTML content, design
the UI and user experience for the add-in, and outline the JavaScript functions that
interact with the HTML code. For more information, see Design guidelines for Office
Add-ins.

The task pane shows the add-in display name at the top, which is the value of the
<DisplayName> element in the manifest. The body element in the
HelloProjectOData.html file contains the other UI elements, as follows:

A subtitle indicates the general functionality or type of operation, for example,


ODATA REST QUERY.

The Get ProjectData Endpoint button calls the setOdataUrl function to get the
endpoint of the ProjectData service, and display it in a text box. If Project is not
connected with Project Web App, the add-in calls an error handler to display a
pop-up error message.

The Compare All Projects button is disabled until the add-in gets a valid OData
endpoint. When you select the button, it calls the retrieveOData function, which
uses a REST query to get project cost and work data from the ProjectData service.

A table displays the average values for project cost, actual cost, work, and percent
complete. The table also compares the current active project values with the
average. If the current value is greater than the average for all projects, the value is
displayed as red. If the current value is less than the average, the value is displayed
as green. If the current value is not available, the table displays a blue NA.

The retrieveOData function calls the parseODataResult function, which calculates


and displays values for the table.
7 Note

In this example, cost and work data for the active project are derived from the
published values. If you change values in Project, the ProjectData service does
not have the changes until the project is published.

Procedure 4. Create the HTML content


1. In the head element of the Home.html file, add any additional link elements for
CSS files that your add-in uses. The Visual Studio project template includes a link
for the App.css file that you can use for custom CSS styles.

2. Add any additional script elements for JavaScript libraries that your add-in uses.
The project template includes links for the jQuery- [version].js, office.js, and
MicrosoftAjax.js files in the Scripts folder.

7 Note

Before you deploy the add-in, change the office.js reference and the jQuery
reference to the content delivery network (CDN) reference. The CDN reference
provides the most recent version and better performance.

The HelloProjectOData add-in also uses the SurfaceErrors.js file, which displays
errors in a pop-up message. You can copy the code from the Robust Programming
section of Create your first task pane add-in for Project 2013 by using a text editor,
and then add a SurfaceErrors.js file in the Scripts\Office folder of the
HelloProjectODataWeb project.

Following is the updated HTML code for the head element, with the additional line
for the SurfaceErrors.js file.

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<title>Test ProjectData Service</title>

<link rel="stylesheet" type="text/css" href="../Content/Office.css" />

<!-- Add your CSS styles to the following file. -->


<link rel="stylesheet" type="text/css" href="../Content/App.css" />

<!-- Use the CDN reference to the mini-version of jQuery when deploying
your add-in. -->
<!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-
1.9.0.min.js"></script> -->
<script src="../Scripts/jquery-1.7.1.js"></script>

<!-- Use the CDN reference to office.js when deploying your add-in. -->
<!--<script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js">
</script>-->

<!-- Use the local script references for Office.js to enable offline
debugging -->
<script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script>
<script src="../Scripts/Office/1.0/Office.js"></script>

<!-- Add your JavaScript to the following files. -->


<script src="../Scripts/HelloProjectOData.js"></script>
<script src="../Scripts/SurfaceErrors.js"></script>
</head>
<body>
<!-- See the code in Step 3. -->
</body>
</html>

3. In the body element, delete the existing code from the template, and then add the
code for the user interface. If an element is to be filled with data or manipulated by
a jQuery statement, the element must include a unique id attribute. In the
following code, the id attributes for the button, span, and td (table cell definition)
elements that jQuery functions use are shown in bold font.

The following HTML adds a graphic image, which could be a company logo. You
can use a logo of your choice, or copy the NewLogo.png file from the Project 2013
SDK download, and then use Solution Explorer to add the file to the
HelloProjectODataWeb\Images folder.

HTML

<body>
<div id="SectionContent">
<div id="odataQueries">
ODATA REST QUERY
</div>
<div id="odataInfo">
<button class="button-wide" onclick="setOdataUrl()">Get
ProjectData Endpoint</button>
<br /><br />
<span class="rest" id="projectDataEndPoint">Endpoint of the
<strong>ProjectData</strong> service</span>
<br />
</div>
<div id="compareProjectData">
<button class="button-wide" disabled="disabled"
id="compareProjects"
onclick="retrieveOData()">Compare All Projects</button>
<br />
</div>
</div>
<div id="corpInfo">
<table class="infoTable" aria-readonly="True" style="width:
100%;">
<tr>
<td class="heading_leftCol"></td>
<td class="heading_midCol"><strong>Average</strong>
</td>
<td class="heading_rightCol"><strong>Current</strong>
</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Cost</strong>
</td>
<td class="row_midCol"
id="AverageProjectCost">&amp;nbsp;</td>
<td class="row_rightCol"
id="CurrentProjectCost">&amp;nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Actual
Cost</strong></td>
<td class="row_midCol"
id="AverageProjectActualCost">&amp;nbsp;</td>
<td class="row_rightCol"
id="CurrentProjectActualCost">&amp;nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Work</strong>
</td>
<td class="row_midCol"
id="AverageProjectWork">&amp;nbsp;</td>
<td class="row_rightCol"
id="CurrentProjectWork">&amp;nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project %
Complete</strong></td>
<td class="row_midCol"
id="AverageProjectPercentComplete">&amp;nbsp;</td>
<td class="row_rightCol"
id="CurrentProjectPercentComplete">&amp;nbsp;</td>
</tr>
</table>
</div>
<img alt="Corporation" class="logo" src="../../images/NewLogo.png"
/>
<br />
<textarea id="odataText" rows="12" cols="40"></textarea>
</body>

Create the JavaScript code for the add-in


The template for a Project task pane add-in includes default initialization code that is
designed to demonstrate basic get and set actions for data in a document for a typical
Office 2013 add-in. Because Project 2013 does not support actions that write to the
active project, and the HelloProjectOData add-in does not use the
getSelectedDataAsync method, you can delete the script within the Office.initialize
function, and delete the setData function and getData function in the default
HelloProjectOData.js file.

The JavaScript includes global constants for the REST query and global variables that are
used in several functions. The Get ProjectData Endpoint button calls the setOdataUrl
function, which initializes the global variables and determines whether Project is
connected with Project Web App.

The remainder of the HelloProjectOData.js file includes two functions: the retrieveOData
function is called when the user selects Compare All Projects; and the parseODataResult
function calculates averages and then populates the comparison table with values that
are formatted for color and units.

Procedure 5. Create the JavaScript code


1. Delete all code in the default HelloProjectOData.js file, and then add the global
variables and Office.initialize function. Variable names that are all capitals
imply that they are constants; they are later used with the _pwa variable to create
the REST query in this example.

JavaScript

var PROJDATA = "/_api/ProjectData";


var PROJQUERY = "/Projects?";
var QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative
Work Items'";
var QUERY_SELECT1 = "&amp;$select=ProjectId, ProjectName";
var QUERY_SELECT2 = ", ProjectCost, ProjectWork,
ProjectPercentCompleted, ProjectActualCost";
var _pwa; // URL of Project Web App.
var _projectUid; // GUID of the active project.
var _docUrl; // Path of the project document.
var _odataUrl = ""; // URL of the OData service: http[s]://ServerName
/ProjectServerName /_api/ProjectData

// The initialize function is required for all add-ins.


Office.initialize = function (reason) {
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
});
}

2. Add setOdataUrl and related functions. The setOdataUrl function calls


getProjectGuid and getDocumentUrl to initialize the global variables. In the

getProjectFieldAsync method, the anonymous function for the callback parameter


enables the Compare All Projects button by using the removeAttr method in the
jQuery library, and then displays the URL of the ProjectData service. If Project is
not connected with Project Web App, the function throws an error, which displays
a pop-up error message. The SurfaceErrors.js file includes the throwError function.

7 Note

If you run Visual Studio on the Project Server computer, to use F5 debugging,
uncomment the code after the line that initializes the _pwa global variable. To
enable using the jQuery ajax method when debugging on the Project Server
computer, you must set the localhost value for the PWA URL.If you run Visual
Studio on a remote computer, the localhost URL is not required. Before you
deploy the add-in, comment out that code.

JavaScript

function setOdataUrl() {
Office.context.document.getProjectFieldAsync(
Office.ProjectProjectFields.ProjectServerUrl,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
_pwa = String(asyncResult.value.fieldValue);

// If you debug with Visual Studio on a local Project


Server computer,
// uncomment the following lines to use the localhost
URL.
//var localhost = location.host.split(":", 1);
//var pwaStartPosition = _pwa.lastIndexOf("/");
//var pwaLength = _pwa.length - pwaStartPosition;
//var pwaName = _pwa.substr(pwaStartPosition,
pwaLength);
//_pwa = location.protocol + "//" + localhost +
pwaName;

if (_pwa.substring(0, 4) == "http") {
_odataUrl = _pwa + PROJDATA;
$("#compareProjects").removeAttr("disabled");
getProjectGuid();
}
else {
_odataUrl = "No connection!";
throwError(_odataUrl, "You are not connected to
Project Web App.");
}
getDocumentUrl();
$("#projectDataEndPoint").text(_odataUrl);
}
else {
throwError(asyncResult.error.name,
asyncResult.error.message);
}
}
);
}

// Get the GUID of the active project.


function getProjectGuid() {
Office.context.document.getProjectFieldAsync(
Office.ProjectProjectFields.GUID,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
_projectUid = asyncResult.value.fieldValue;
}
else {
throwError(asyncResult.error.name,
asyncResult.error.message);
}
}
);
}

// Get the path of the project in Project web app, which is in the form
<>\ProjectName .
function getDocumentUrl() {
_docUrl = "Document path:\r\n" + Office.context.document.url;
}

3. Add the retrieveOData function, which concatenates values for the REST query
and then calls the ajax function in jQuery to get the requested data from the
ProjectData service. The support.cors variable enables cross-origin resource
sharing (CORS) with the ajax function. If the support.cors statement is missing or
is set to false , the ajax function returns a No transport error.

7 Note

The following code works with an on-premises installation of Project Server


2013. For Project on the web, you can use OAuth for token-based
authentication. For more information, see Addressing same-origin policy
limitations in Office Add-ins.

In the ajax call, you can use either the headers parameter or the beforeSend
parameter. The complete parameter is an anonymous function so that it is in the
same scope as the variables in retrieveOData . The function for the complete
parameter displays results in the odataText control and also calls the
parseODataResult method to parse and display the JSON response. The error
parameter specifies the named getProjectDataErrorHandler function, which writes
an error message to the odataText control and also uses the throwError function
to display a pop-up message.

JavaScript

// Functions to get and parse the Project Server reporting data./

// Get data about all projects on Project Server,


// by using a REST query with the ajax method in jQuery.
function retrieveOData() {
var restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1
+ QUERY_SELECT2;
var accept = "application/json; odata=verbose";
accept.toLocaleLowerCase();

// Enable cross-origin scripting (required by jQuery 1.5 and


later).
// This does not work with Project on the web.
$.support.cors = true;

$.ajax({
url: restUrl,
type: "GET",
contentType: "application/json",
data: "", // Empty string for the optional data.
//headers: { "Accept": accept },
beforeSend: function (xhr) {
xhr.setRequestHeader("ACCEPT", accept);
},
complete: function (xhr, textStatus) {
// Create a message to display in the text box.
var message = "\r\ntextStatus: " + textStatus +
"\r\nContentType: " + xhr.getResponseHeader("Content-
Type") +
"\r\nStatus: " + xhr.status +
"\r\nResponseText:\r\n" + xhr.responseText;

// xhr.responseText is the result from an XmlHttpRequest,


which
// contains the JSON response from the OData service.
parseODataResult(xhr.responseText, _projectUid);

// Write the document name, response header, status, and


JSON to the odataText control.
$("#odataText").text(_docUrl);
$("#odataText").append("\r\nREST query:\r\n" + restUrl);
$("#odataText").append(message);

if (xhr.status != 200 &amp;&amp; xhr.status != 1223


&amp;&amp; xhr.status != 201) {
$("#odataInfo").append("<div>" + htmlEncode(restUrl) +
"</div>");
}
},
error: getProjectDataErrorHandler
});
}

function getProjectDataErrorHandler(data, errorCode, errorMessage) {


$("#odataText").text("Error code: " + errorCode + "\r\nError
message: \r\n"
+ errorMessage);
throwError(errorCode, errorMessage);
}

4. Add the parseODataResult function, which deserializes and processes the JSON
response from the OData service. The parseODataResult function calculates
average values of the cost and work data to an accuracy of one or two decimal
places, formats values with the correct color and adds a unit ( $, hrs, or %), and
then displays the values in specified table cells.

If the GUID of the active project matches the ProjectId value, the myProjectIndex
variable is set to the project index. If myProjectIndex indicates the active project is
published on Project Server, the parseODataResult method formats and displays
cost and work data for that project. If the active project is not published, values for
the active project are displayed as a blue NA.

JavaScript

// Calculate the average values of actual cost, cost, work, and percent
complete
// for all projects, and compare with the values for the current
project.
function parseODataResult(oDataResult, currentProjectGuid) {
// Deserialize the JSON string into a JavaScript object.
var res =
Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult);
var len = res.d.results.length;
var projActualCost = 0;
var projCost = 0;
var projWork = 0;
var projPercentCompleted = 0;
var myProjectIndex = -1;
for (i = 0; i < len; i++) {
// If the current project GUID matches the GUID from the OData
query,
// store the project index.
if (currentProjectGuid.toLocaleLowerCase() ==
res.d.results[i].ProjectId) {
myProjectIndex = i;
}
projCost += Number(res.d.results[i].ProjectCost);
projWork += Number(res.d.results[i].ProjectWork);
projActualCost += Number(res.d.results[i].ProjectActualCost);
projPercentCompleted +=
Number(res.d.results[i].ProjectPercentCompleted);
}
var avgProjCost = projCost / len;
var avgProjWork = projWork / len;
var avgProjActualCost = projActualCost / len;
var avgProjPercentCompleted = projPercentCompleted / len;

// Round off cost to two decimal places, and round off other values
to one decimal place.
avgProjCost = avgProjCost.toFixed(2);
avgProjWork = avgProjWork.toFixed(1);
avgProjActualCost = avgProjActualCost.toFixed(2);
avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1);

// Display averages in the table, with the correct units.


document.getElementById("AverageProjectCost").innerHTML = "$"
+ avgProjCost;
document.getElementById("AverageProjectActualCost").innerHTML
= "$" + avgProjActualCost;
document.getElementById("AverageProjectWork").innerHTML
= avgProjWork + " hrs";
document.getElementById("AverageProjectPercentComplete").innerHTML
= avgProjPercentCompleted + "%";

// Calculate and display values for the current project.


if (myProjectIndex != -1) {
var myProjCost =
Number(res.d.results[myProjectIndex].ProjectCost);
var myProjWork =
Number(res.d.results[myProjectIndex].ProjectWork);
var myProjActualCost =
Number(res.d.results[myProjectIndex].ProjectActualCost);
var myProjPercentCompleted =
Number(res.d.results[myProjectIndex].ProjectPercentCompleted);

myProjCost = myProjCost.toFixed(2);
myProjWork = myProjWork.toFixed(1);
myProjActualCost = myProjActualCost.toFixed(2);
myProjPercentCompleted = myProjPercentCompleted.toFixed(1);

document.getElementById("CurrentProjectCost").innerHTML = "$" +
myProjCost;

if (Number(myProjCost) <= Number(avgProjCost)) {


document.getElementById("CurrentProjectCost").style.color =
"green"
}
else {
document.getElementById("CurrentProjectCost").style.color =
"red"
}

document.getElementById("CurrentProjectActualCost").innerHTML =
"$" + myProjActualCost;

if (Number(myProjActualCost) <= Number(avgProjActualCost)) {

document.getElementById("CurrentProjectActualCost").style.color =
"green"
}
else {

document.getElementById("CurrentProjectActualCost").style.color = "red"
}

document.getElementById("CurrentProjectWork").innerHTML =
myProjWork + " hrs";

if (Number(myProjWork) <= Number(avgProjWork)) {


document.getElementById("CurrentProjectWork").style.color =
"red"
}
else {
document.getElementById("CurrentProjectWork").style.color =
"green"
}

document.getElementById("CurrentProjectPercentComplete").innerHTML =
myProjPercentCompleted + "%";

if (Number(myProjPercentCompleted) <=
Number(avgProjPercentCompleted)) {

document.getElementById("CurrentProjectPercentComplete").style.color =
"red"
}
else {

document.getElementById("CurrentProjectPercentComplete").style.color =
"green"
}
}
else {
document.getElementById("CurrentProjectCost").innerHTML = "NA";
document.getElementById("CurrentProjectCost").style.color =
"blue"

document.getElementById("CurrentProjectActualCost").innerHTML =
"NA";
document.getElementById("CurrentProjectActualCost").style.color
= "blue"

document.getElementById("CurrentProjectWork").innerHTML = "NA";
document.getElementById("CurrentProjectWork").style.color =
"blue"

document.getElementById("CurrentProjectPercentComplete").innerHTML =
"NA";

document.getElementById("CurrentProjectPercentComplete").style.color =
"blue"
}
}

Test the HelloProjectOData add-in


To test and debug the HelloProjectOData add-in with Visual Studio 2015, Project
Professional 2013 must be installed on the development computer. To enable different
test scenarios, ensure that you can choose whether Project opens for files on the local
computer or connects with Project Web App. For example, do the following steps.

1. On the File tab, choose the Info tab in the Backstage view, and then choose
Manage Accounts.

2. In the Project web app Accounts dialog box, the Available accounts list can have
multiple Project Web App accounts in addition to the local Computer account. In
the When starting section, select Choose an account.

3. Close Project so that Visual Studio can start it for debugging the add-in.

Basic tests should include the following:


Run the add-in from Visual Studio, and then open a published project from Project
Web App that contains cost and work data. Verify that the add-in displays the
ProjectData endpoint and correctly displays the cost and work data in the table.
You can use the output in the odataText control to check the REST query and other
information.

Run the add-in again, where you choose the local computer profile in the Login
dialog box when Project starts. Open a local .mpp file, and then test the add-in.
Verify that the add-in displays an error message when you try to get the
ProjectData endpoint.

Run the add-in again, where you create a project that has tasks with cost and work
data. You can save the project to Project Web App, but don't publish it. Verify that
the add-in displays data from Project Server, but NA for the current project.

Procedure 6. Test the add-in


1. Run Project Professional 2013, connect with Project Web App, and then create a
test project. Assign tasks to local resources or to enterprise resources, set various
values of percent complete on some tasks, and then publish the project. Quit
Project, which enables Visual Studio to start Project for debugging the add-in.

2. In Visual Studio, press F5. Log on to Project Web App, and then open the project
that you created in the previous step. You can open the project in read-only mode
or in edit mode.

3. On the PROJECT tab of the ribbon, in the Office Add-ins drop-down list, select
Hello ProjectData (see Figure 5). The Compare All Projects button should be
disabled.

Figure 5. Start the HelloProjectOData add-in


4. In the Hello ProjectData task pane, select Get ProjectData Endpoint. The
projectDataEndPoint line should show the URL of the ProjectData service, and the
Compare All Projects button should be enabled (see Figure 6).

5. Select Compare All Projects. The add-in may pause while it retrieves data from the
ProjectData service, and then it should display the formatted average and current
values in the table.

Figure 6. View results of the REST query


6. Examine output in the text box. It should show the document path, REST query,
status information, and JSON results from the calls to ajax and parseODataResult .
The output helps to understand, create, and debug code in the parseODataResult
function such as projCost += Number(res.d.results[i].ProjectCost); .

Following is an example of the output with line breaks and spaces added to the
text for clarity, for three projects in a Project Web App instance.

JSON

Document path: <>\WinProj test1

REST query:
http://sphvm-37189/pwa/_api/ProjectData/Projects?$filter=ProjectName ne
'Timesheet Administrative Work Items'
&amp;$select=ProjectId, ProjectName, ProjectCost, ProjectWork,
ProjectPercentCompleted, ProjectActualCost

textStatus: success
ContentType: application/json;odata=verbose;charset=utf-8
Status: 200

ResponseText:
{"d":{"results":[
{"__metadata":
{"id":"http://sphvm-
37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-
00155d157123')",
"uri":"http://sphvm-
37189/pwa/_api/ProjectData/Projects(guid'ce3d0d65-3904-e211-96cd-
00155d157123')",
"type":"ReportingData.Project"},
"ProjectId":"ce3d0d65-3904-e211-96cd-00155d157123",
"ProjectActualCost":"0.000000",
"ProjectCost":"0.000000",
"ProjectName":"Task list created in PWA",
"ProjectPercentCompleted":0,
"ProjectWork":"16.000000"},
{"__metadata":
{"id":"http://sphvm-
37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-
3c075433b7bd')",
"uri":"http://sphvm-
37189/pwa/_api/ProjectData/Projects(guid'c31023fc-1404-e211-86b2-
3c075433b7bd')",
"type":"ReportingData.Project"},
"ProjectId":"c31023fc-1404-e211-86b2-3c075433b7bd",
"ProjectActualCost":"700.000000",
"ProjectCost":"2400.000000",
"ProjectName":"WinProj test 2",
"ProjectPercentCompleted":29,
"ProjectWork":"48.000000"},
{"__metadata":
{"id":"http://sphvm-
37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-
3c075433b7bd')",
"uri":"http://sphvm-
37189/pwa/_api/ProjectData/Projects(guid'dc81fbb2-b801-e211-9d2a-
3c075433b7bd')",
"type":"ReportingData.Project"},
"ProjectId":"dc81fbb2-b801-e211-9d2a-3c075433b7bd",
"ProjectActualCost":"1900.000000",
"ProjectCost":"5200.000000",
"ProjectName":"WinProj test1",
"ProjectPercentCompleted":37,
"ProjectWork":"104.000000"}
]}}
7. Stop debugging (press Shift + F5), and then press F5 again to run a new instance
of Project. In the Login dialog box, choose the local Computer profile, not Project
Web App. Create or open a local project .mpp file, open the Hello ProjectData task
pane, and then select Get ProjectData Endpoint. The add-in should show a No
connection! error (see Figure 7), and the Compare All Projects button should
remain disabled.

Figure 7. Use the add-in without a Project web app connection

8. Stop debugging, and then press F5 again. Log on to Project Web App, and then
create a project that contains cost and work data. You can save the project, but
don't publish it.

In the Hello ProjectData task pane, when you select Compare All Projects, you
should see a blue NA for fields in the Current column (see Figure 8).
Figure 8. Compare an unpublished project with other projects

Even if your add-in is working correctly in the previous tests, there are other tests that
should be run. For example:

Open a project from Project Web App that has no cost or work data for the tasks.
You should see values of zero in the fields in the Current column.

Test a project that has no tasks.

If you modify the add-in and publish it, you should run similar tests again with the
published add-in. For other considerations, see Next steps.

7 Note

There are limits to the amount of data that can be returned in one query of the
ProjectData service; the amount of data varies by entity. For example, the Projects
entity set has a default limit of 100 projects per query, but the Risks entity set has
a default limit of 200. For a production installation, the code in the
HelloProjectOData example should be modified to enable queries of more than
100 projects. For more information, see Next steps and Querying OData feeds for
Project reporting data.

Example code for the HelloProjectOData add-in

HelloProjectOData.html file
The following code is in the Pages\HelloProjectOData.html file of the
HelloProjectODataWeb project.

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<title>Test ProjectData Service</title>

<link rel="stylesheet" type="text/css" href="../Content/Office.css"


/>

<!-- Add your CSS styles to the following file. -->


<link rel="stylesheet" type="text/css" href="../Content/App.css" />

<!-- Use the CDN reference to the mini-version of jQuery when


deploying your add-in. -->
<!--<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-
1.9.0.min.js"></script> -->
<script src="../Scripts/jquery-1.7.1.js"></script>

<!-- Use the CDN reference to Office.js when deploying your add-in -
->
<!--<script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>--
>

<!-- Use the local script references for Office.js to enable offline
debugging -->
<script src="../Scripts/Office/1.0/MicrosoftAjax.js"></script>
<script src="../Scripts/Office/1.0/Office.js"></script>

<!-- Add your JavaScript to the following files. -->


<script src="../Scripts/HelloProjectOData.js"></script>
<script src="../Scripts/SurfaceErrors.js"></script>
</head>
<body>
<div id="SectionContent">
<div id="odataQueries">
ODATA REST QUERY
</div>
<div id="odataInfo">
<button class="button-wide" onclick="setOdataUrl()">Get
ProjectData Endpoint</button>
<br />
<br />
<span class="rest" id="projectDataEndPoint">Endpoint of the
<strong>ProjectData</strong> service</span>
<br />
</div>
<div id="compareProjectData">
<button class="button-wide" disabled="disabled"
id="compareProjects"
onclick="retrieveOData()">
Compare All Projects</button>
<br />
</div>
</div>
<div id="corpInfo">
<table class="infoTable" aria-readonly="True" style="width: 100%;">
<tr>
<td class="heading_leftCol"></td>
<td class="heading_midCol"><strong>Average</strong></td>
<td class="heading_rightCol"><strong>Current</strong></td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Cost</strong></td>
<td class="row_midCol" id="AverageProjectCost">&amp;nbsp;</td>
<td class="row_rightCol" id="CurrentProjectCost">&amp;nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Actual Cost</strong>
</td>
<td class="row_midCol" id="AverageProjectActualCost">&amp;nbsp;
</td>
<td class="row_rightCol"
id="CurrentProjectActualCost">&amp;nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project Work</strong></td>
<td class="row_midCol" id="AverageProjectWork">&amp;nbsp;</td>
<td class="row_rightCol" id="CurrentProjectWork">&amp;nbsp;</td>
</tr>
<tr>
<td class="row_leftCol"><strong>Project % Complete</strong></td>
<td class="row_midCol"
id="AverageProjectPercentComplete">&amp;nbsp;</td>
<td class="row_rightCol"
id="CurrentProjectPercentComplete">&amp;nbsp;</td>
</tr>
</table>
</div>
<img alt="Corporation" class="logo" src="../../images/NewLogo.png"
/>
<br />
<textarea id="odataText" rows="12" cols="40"></textarea>
</body>
</html>

HelloProjectOData.js file
The following code is in the Scripts\Office\HelloProjectOData.js file of the
HelloProjectODataWeb project.

JavaScript

/* File: HelloProjectOData.js
* JavaScript functions for the HelloProjectOData example task pane app.
* October 2, 2012
*/

var PROJDATA = "/_api/ProjectData";


var PROJQUERY = "/Projects?";
var QUERY_FILTER = "$filter=ProjectName ne 'Timesheet Administrative Work
Items'";
var QUERY_SELECT1 = "&amp;$select=ProjectId, ProjectName";
var QUERY_SELECT2 = ", ProjectCost, ProjectWork, ProjectPercentCompleted,
ProjectActualCost";
var _pwa; // URL of Project Web App.
var _projectUid; // GUID of the active project.
var _docUrl; // Path of the project document.
var _odataUrl = ""; // URL of the OData service: http[s]://ServerName
/ProjectServerName /_api/ProjectData

// The initialize function is required for all add-ins.


Office.initialize = function (reason) {
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
});
}

// Set the global variables, enable the Compare All Projects button,
// and display the URL of the ProjectData service.
// Display an error if Project is not connected with Project Web App.
function setOdataUrl() {
Office.context.document.getProjectFieldAsync(
Office.ProjectProjectFields.ProjectServerUrl,
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
_pwa = String(asyncResult.value.fieldValue);
// If you debug with Visual Studio on a local Project Server
computer,
// uncomment the following lines to use the localhost URL.
//var localhost = location.host.split(":", 1);
//var pwaStartPosition = _pwa.lastIndexOf("/");
//var pwaLength = _pwa.length - pwaStartPosition;
//var pwaName = _pwa.substr(pwaStartPosition, pwaLength);
//_pwa = location.protocol + "//" + localhost + pwaName;

if (_pwa.substring(0, 4) == "http") {
_odataUrl = _pwa + PROJDATA;
$("#compareProjects").removeAttr("disabled");
getProjectGuid();
}
else {
_odataUrl = "No connection!";
throwError(_odataUrl, "You are not connected to Project
Web App.");
}
getDocumentUrl();
$("#projectDataEndPoint").text(_odataUrl);
}
else {
throwError(asyncResult.error.name,
asyncResult.error.message);
}
}
);
}

// Get the GUID of the active project.


function getProjectGuid() {
Office.context.document.getProjectFieldAsync(
Office.ProjectProjectFields.GUID,
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
_projectUid = asyncResult.value.fieldValue;
}
else {
throwError(asyncResult.error.name,
asyncResult.error.message);
}
}
);
}

// Get the path of the project in Project web app, which is in the form
<>\ProjectName .
function getDocumentUrl() {
_docUrl = "Document path:\r\n" + Office.context.document.url;
}

// Functions to get and parse the Project Server reporting data./


// Get data about all projects on Project Server,
// by using a REST query with the ajax method in jQuery.
function retrieveOData() {
var restUrl = _odataUrl + PROJQUERY + QUERY_FILTER + QUERY_SELECT1 +
QUERY_SELECT2;
var accept = "application/json; odata=verbose";
accept.toLocaleLowerCase();

// Enable cross-origin scripting (required by jQuery 1.5 and later).


// This does not work with Project on the web.
$.support.cors = true;

$.ajax({
url: restUrl,
type: "GET",
contentType: "application/json",
data: "", // Empty string for the optional data.
//headers: { "Accept": accept },
beforeSend: function (xhr) {
xhr.setRequestHeader("ACCEPT", accept);
},
complete: function (xhr, textStatus) {
// Create a message to display in the text box.
var message = "\r\ntextStatus: " + textStatus +
"\r\nContentType: " + xhr.getResponseHeader("Content-Type")
+
"\r\nStatus: " + xhr.status +
"\r\nResponseText:\r\n" + xhr.responseText;

// xhr.responseText is the result from an XmlHttpRequest, which


// contains the JSON response from the OData service.
parseODataResult(xhr.responseText, _projectUid);

// Write the document name, response header, status, and JSON to


the odataText control.
$("#odataText").text(_docUrl);
$("#odataText").append("\r\nREST query:\r\n" + restUrl);
$("#odataText").append(message);

if (xhr.status != 200 &amp;&amp; xhr.status != 1223 &amp;&amp;


xhr.status != 201) {
$("#odataInfo").append("<div>" + htmlEncode(restUrl) + "
</div>");
}
},
error: getProjectDataErrorHandler
});
}

function getProjectDataErrorHandler(data, errorCode, errorMessage) {


$("#odataText").text("Error code: " + errorCode + "\r\nError message:
\r\n"
+ errorMessage);
throwError(errorCode, errorMessage);
}
// Calculate the average values of actual cost, cost, work, and percent
complete
// for all projects, and compare with the values for the current project.
function parseODataResult(oDataResult, currentProjectGuid) {
// Deserialize the JSON string into a JavaScript object.
var res =
Sys.Serialization.JavaScriptSerializer.deserialize(oDataResult);
var len = res.d.results.length;
var projActualCost = 0;
var projCost = 0;
var projWork = 0;
var projPercentCompleted = 0;
var myProjectIndex = -1;

for (i = 0; i < len; i++) {


// If the current project GUID matches the GUID from the OData
query,
// then store the project index.
if (currentProjectGuid.toLocaleLowerCase() ==
res.d.results[i].ProjectId) {
myProjectIndex = i;
}
projCost += Number(res.d.results[i].ProjectCost);
projWork += Number(res.d.results[i].ProjectWork);
projActualCost += Number(res.d.results[i].ProjectActualCost);
projPercentCompleted +=
Number(res.d.results[i].ProjectPercentCompleted);

}
var avgProjCost = projCost / len;
var avgProjWork = projWork / len;
var avgProjActualCost = projActualCost / len;
var avgProjPercentCompleted = projPercentCompleted / len;

// Round off cost to two decimal places, and round off other values to
one decimal place.
avgProjCost = avgProjCost.toFixed(2);
avgProjWork = avgProjWork.toFixed(1);
avgProjActualCost = avgProjActualCost.toFixed(2);
avgProjPercentCompleted = avgProjPercentCompleted.toFixed(1);

// Display averages in the table, with the correct units.


document.getElementById("AverageProjectCost").innerHTML = "$"
+ avgProjCost;
document.getElementById("AverageProjectActualCost").innerHTML
= "$" + avgProjActualCost;
document.getElementById("AverageProjectWork").innerHTML
= avgProjWork + " hrs";
document.getElementById("AverageProjectPercentComplete").innerHTML
= avgProjPercentCompleted + "%";

// Calculate and display values for the current project.


if (myProjectIndex != -1) {
var myProjCost = Number(res.d.results[myProjectIndex].ProjectCost);
var myProjWork = Number(res.d.results[myProjectIndex].ProjectWork);
var myProjActualCost =
Number(res.d.results[myProjectIndex].ProjectActualCost);
var myProjPercentCompleted =
Number(res.d.results[myProjectIndex].ProjectPercentCompleted);

myProjCost = myProjCost.toFixed(2);
myProjWork = myProjWork.toFixed(1);
myProjActualCost = myProjActualCost.toFixed(2);
myProjPercentCompleted = myProjPercentCompleted.toFixed(1);

document.getElementById("CurrentProjectCost").innerHTML = "$" +
myProjCost;

if (Number(myProjCost) <= Number(avgProjCost)) {


document.getElementById("CurrentProjectCost").style.color =
"green"
}
else {
document.getElementById("CurrentProjectCost").style.color =
"red"
}

document.getElementById("CurrentProjectActualCost").innerHTML = "$"
+ myProjActualCost;

if (Number(myProjActualCost) <= Number(avgProjActualCost)) {


document.getElementById("CurrentProjectActualCost").style.color
= "green"
}
else {
document.getElementById("CurrentProjectActualCost").style.color
= "red"
}

document.getElementById("CurrentProjectWork").innerHTML = myProjWork
+ " hrs";

if (Number(myProjWork) <= Number(avgProjWork)) {


document.getElementById("CurrentProjectWork").style.color =
"red"
}
else {
document.getElementById("CurrentProjectWork").style.color =
"green"
}

document.getElementById("CurrentProjectPercentComplete").innerHTML =
myProjPercentCompleted + "%";

if (Number(myProjPercentCompleted) <=
Number(avgProjPercentCompleted)) {

document.getElementById("CurrentProjectPercentComplete").style.color = "red"
}
else {

document.getElementById("CurrentProjectPercentComplete").style.color =
"green"
}
}
else { // The current project is not published.
document.getElementById("CurrentProjectCost").innerHTML = "NA";
document.getElementById("CurrentProjectCost").style.color = "blue"

document.getElementById("CurrentProjectActualCost").innerHTML =
"NA";
document.getElementById("CurrentProjectActualCost").style.color =
"blue"

document.getElementById("CurrentProjectWork").innerHTML = "NA";
document.getElementById("CurrentProjectWork").style.color = "blue"

document.getElementById("CurrentProjectPercentComplete").innerHTML =
"NA";
document.getElementById("CurrentProjectPercentComplete").style.color
= "blue"
}
}

App.css file
The following code is in the Content\App.css file of the HelloProjectODataWeb project.

css

/*
* File: App.css for the HelloProjectOData app.
* Updated: 10/2/2012
*/

body
{
font-size: 11pt;
}
h1
{
font-size: 22pt;
}
h2
{
font-size: 16pt;
}

/******************************************************************
Code label class
******************************************************************/

.rest
{
font-family: 'Courier New';
font-size: 0.9em;
}

/******************************************************************
Button classes
******************************************************************/

.button-wide {
width: 210px;
margin-top: 2px;
}
.button-narrow
{
width: 80px;
margin-top: 2px;
}

/******************************************************************
Table styles
******************************************************************/

.infoTable
{
text-align: center;
vertical-align: middle
}
.heading_leftCol
{
width: 20px;
height: 20px;
}
.heading_midCol
{
width: 100px;
height: 20px;
font-size: medium;
font-weight: bold;
}
.heading_rightCol
{
width: 101px;
height: 20px;
font-size: medium;
font-weight: bold;
}
.row_leftCol
{
width: 20px;
font-size: small;
font-weight: bold;
}
.row_midCol
{
width: 100px;
}
.row_rightCol
{
width: 101px;
}
.logo
{
width: 135px;
height: 53px;
}

SurfaceErrors.js file
You can copy code for the SurfaceErrors.js file from the Robust Programming section of
Create your first task pane add-in for Project 2013 by using a text editor.

Next steps
If HelloProjectOData were a production add-in to be sold in AppSource or distributed
in a SharePoint app catalog, it would be designed differently. For example, there would
be no debug output in a text box, and probably no button to get the ProjectData
endpoint. You would also have to rewrite the retrieveOData function to handle Project
Web App instances that have more than 100 projects.

The add-in should contain additional error checks, plus logic to catch and explain or
show edge cases. For example, if a Project Web App instance has 1000 projects with an
average duration of five days and average cost of $2400, and the active project is the
only one that has a duration longer than 20 days, the cost and work comparison would
be skewed. That could be shown with a frequency graph. You could add options to
display duration, compare similar length projects, or compare projects from the same or
different departments. Or, add a way for the user to select from a list of fields to display.

For other queries of the ProjectData service, there are limits to the length of the query
string, which affects the number of steps that a query can take from a parent collection
to an object in a child collection. For example, a two-step query of Projects to Tasks to
task item works, but a three-step query such as Projects to Tasks to Assignments to
assignment item may exceed the default maximum URL length. For more information,
see Query OData feeds for Project reporting data.

If you modify the HelloProjectOData add-in for production use, do the following steps.
In the HelloProjectOData.html file, for better performance, change the office.js
reference from the local project to the CDN reference:

HTML

<script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js">
</script>

Rewrite the retrieveOData function to enable queries of more than 100 projects.
For example, you could get the number of projects with a
~/ProjectData/Projects()/$count query, and use the $skip operator and $top

operator in the REST query for project data. Run multiple queries in a loop, and
then average the data from each query. Each query for project data would be of
the form:

~/ProjectData/Projects()?skip= [numSkipped]&amp;$top=100&amp;$filter=
[filter]&amp;$select=[field1,field2, ???????]

For more information, see OData system query options using the REST endpoint.
You can also use the Set-SPProjectOdataConfiguration command in Windows
PowerShell to override the default page size for a query of the Projects entity set
(or any of the 33 entity sets). See ProjectData - Project OData service reference.

To deploy the add-in, see Publish your Office Add-in.

See also
Task pane add-ins for Project
Create your first task pane add-in for Project 2013 by using a text editor
ProjectData - Project OData service reference
Office Add-ins manifest
Publish your Office Add-in
Create your first task pane add-in for
Microsoft Project
Article • 04/11/2023

You can create a task pane add-in for Project Standard 2013, Project Professional 2013,
or later versions using the Yeoman generator for Office Add-ins. This article describes
how to create a simple add-in that uses an XML manifest that points to an HTML file on
a file share. The Project OM Test sample add-in tests some JavaScript functions that use
the object model for add-ins. After you use the Trust Center in Project to register the file
share that contains the manifest file, you can open the task pane add-in from the
Project tab on the ribbon. (The sample code in this article is based on a test application
by Arvind Iyer, Microsoft Corporation.)

Project uses the same add-in manifest schema that other Office clients use, and much of
the same JavaScript API. The complete code for the add-in that is described in this
article is available in the Samples\Apps subdirectory of the Project 2013 SDK download.

The Project OM Test sample add-in can get the GUID of a task and properties of the
application and the active project. If Project Professional 2013 opens a project that is in
a SharePoint library, the add-in can show the URL of the project.

The Project 2013 SDK download includes the complete source code. When you extract
and install the SDK and samples that are in the Project2013SDK.msi file, see the
\Samples\Apps\Copy_to_AppManifests_FileShare subdirectory for the manifest file and

the \Samples\Apps\Copy_to_AppSource_FileShare subdirectory for the source code.

The JSOMCall.html sample uses JavaScript functions in the office.js file and project-15.js
file, which are included. You can use the corresponding debug files (office.debug.js and
project-15.debug.js) to examine the functions.

For an introduction to using JavaScript in Office Add-ins, see Understanding the Office
JavaScript API.

Procedure 1. To create the add-in manifest file


Create an XML file in a local directory. The XML file includes the OfficeApp element and
child elements, which are described in the Office Add-ins XML manifest. For example,
create a file named JSOM_SimpleOMCalls.xml that contains the following XML (change
the GUID value of the Id element).
XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<!--IMPORTANT! Id must be unique for each add-in. If you copy this
manifest ensure that you change this id to your own GUID. -->
<Id>93A26520-9414-492F-994B-4983A1C7A607</Id>
<Version>15.0</Version>
<ProviderName>Microsoft</ProviderName>
<DefaultLocale>en-us</DefaultLocale>
<DisplayName DefaultValue="Project OM Test">
<Override Locale="fr-fr" Value="Le Project OM Test"/>
</DisplayName>
<Description DefaultValue="Test the task pane add-in object model for
Project - English (US)">
<Override Locale="fr-fr" Value="Test the task pane add-in object
model for Project - French (France)"/>
</Description>
<SupportUrl DefaultValue="[Insert the URL of a page that provides
support information for the app]" />
<Hosts>
<Host Name="Project"/>
<Host Name="Workbook"/>
<Host Name="Document"/>
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="\\ServerName\AppSource\JSOMCall.html">
<Override Locale="fr-fr"
Value="\\ServerName\AppSource\JSOMCall.html"/>
</SourceLocation>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
<IconUrl
DefaultValue="http://officeimg.vo.msecnd.net/_layouts/images/general/office_
logo.jpg">
<Override Locale="fr-fr"
Value="http://officeimg.vo.msecnd.net/_layouts/images/general/office_logo.jp
g"/>
</IconUrl>
<AllowSnapshot>true</AllowSnapshot>
</OfficeApp>

For Project, the OfficeApp element must include the xsi:type="TaskPaneApp" attribute
value. The Id element is a GUID. The SourceLocation value must be a file share path or
a SharePoint URL for the add-in HTML source file or the web application that runs in the
task pane. For an explanation of the other elements in manifest file, see Task pane add-
ins for Project.
Procedure 2 shows how to create the HTML file that the JSOM_SimpleOMCalls.xml
manifest specifies for the Project test add-in. Buttons that are specified in the HTML file
call related JavaScript functions. You can add the JavaScript functions within the HTML
file, or put them in a separate .js file.

Procedure 2. To create the source files for the


Project OM Test add-in
1. Create an HTML file with a name that is specified by the SourceLocation element in
the JSOM_SimpleOMCalls.xml manifest.

For example, create theJSOMCall.html file in the C:\Project\AppSource directory.


Although you can use a simple text editor to create the source files, it is easier to
use a tool such as Visual Studio Code, which works with specific document types
(such as HTML and JavaScript) and has other editing aids. If you have not already
done the Bing Search example that is described in Task pane add-ins for Project,
Procedure 3 shows how to create the \\ServerName\AppSource file share that the
manifest specifies.

The JSOMCall.html file uses the common MicrosoftAjax.js file for AJAX functionality
and the Office.js file for the add-in functionality in Office 2013 applications.

HTML

<!DOCTYPE html>
<html>
<head>
<title>Project OM Sample Code</title>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<script type="text/javascript" src="MicrosoftAjax.js"></script>

<!-- Use the CDN reference to office.js when deploying your


add-in. -->
<!-- <script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js">
</script> -->
<script type="text/javascript" src="office.js"></script>
<script type="text/javascript" src="JSOM_Sample.js"></script>
</head>
<body>
<div id="Common_JSOM_API">
OBJECT MODEL TESTS
</div>

<textarea id="text" rows="6" cols="25">This is the text result.


</textarea>
</body>
</html>

The textarea element specifies a text box that shows results of the JavaScript
functions.

7 Note

For the Project OM Test sample to work, copy the following files from the
Project 2013 SDK download to the same directory as the JSOMCall.html file:
Office.js, Project-15.js, and MicrosoftAjax.js.

Step 2 adds the JSOM_Sample.js file for specific functions that the Project OM Test
sample add-in uses. In later steps, you will add other HTML elements for buttons
that call JavaScript functions.

2. Create a JavaScript file named JSOM_Sample.js in the same directory as the


JSOMCall.html file.

The following code gets the application context and document information by
using methods in the Office.js file. The text object is the ID of the textarea
control in the HTML file.

The _projDoc variable is initialized with a ProjectDocument object. The code


includes some simple error handling functions, and the getContextValues function
that gets application context and project document context properties. For more
information about the JavaScript object model for Project, see JavaScript API for
Office.

JavaScript

/*
* JavaScript functions for the Project OM Test example app
* in the Project 2013 SDK.
*/

var _projDoc;
var _app;
var taskGuid;
var resourceGuid;

// The initialize function is required for all add-ins.


Office.initialize = function (reason) {
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
_projDoc = Office.context.document;
_app = Office.context;
});
}

function logError(errorText) {
text.value = "Error in " + errorText;
}

function logEventError(erroneousEvent) {
logError("event " + erroneousEvent);
}

function logMethodError(methodName, errorName, errorMessage) {


logError(methodName + " method.\nError name: " + errorName +
"\nMessage: " + errorMessage);
}

// . . . Add other JavaScript functions here.

function getContextValues() {
getDocumentUrl();
getDocumentMode();
getApplicationContentLanguage();
getApplicationDisplayLanguage();
}

function getDocumentUrl() {
text.value ="Document URL:\n" +_projDoc.url;
}

function getDocumentMode() {
var docMode = _projDoc.mode;
text.value = text.value + "\n\nDocument mode: " + docMode;
}

function getApplicationContentLanguage() {
text.value = text.value + "\nApp language: " +
_app.contentLanguage;
}

function getApplicationDisplayLanguage() {
text.value = text.value + "\nDisplay language: " +
_app.displayLanguage;
}

For information about the functions in the Office.debug.js file, see Office JavaScript
API. For example, the getDocumentUrl function gets the URL or file path of the
open project.

3. Add JavaScript functions that call asynchronous methods in Office.js and Project-
15.js to get selected data:
For example, getSelectedDataAsync is a general method in Office.js that gets
unformatted text for the selected data. For more information, see Document
object.

The getSelectedTaskAsync function in Project-15.js gets the GUID of the


selected task. Similarly, the getSelectedResourceAsync function gets the GUID
of the selected resource. If you call those functions when a task or a resource
is not selected, the functions show an undefined error.

The getTaskAsync function gets the task name and the names of the assigned
resources. If the task is in a synchronized SharePoint task list, getTaskAsync
gets the task ID in the SharePoint list; otherwise, the SharePoint task ID is 0.

7 Note

For demonstration purposes, the example code includes a bug. If


taskGuid is undefined, the getTaskAsync function errors out. If you get a

valid task GUID and then select a different task, the getTaskAsync
function gets data for the most recent task that was operated on by the
getSelectedTaskAsync function.

getTaskFields , getResourceFields , and getProjectFields are local functions


that call getTaskFieldAsync , getResourceFieldAsync , or getProjectFieldAsync
multiple times to get specified fields of a task or a resource. In the project-
15.debug.js file, the ProjectTaskFields enumeration and the
ProjectResourceFields enumeration show which fields are supported.

The getSelectedViewAsync function gets the type of view (defined in the


ProjectViewTypes enumeration in project-15.debug.js) and the name of the

view.

If the project is synchronized with a SharePoint tasks list, the getWSSUrlAsync


function gets the URL and the name of the tasks list. If the project is not
synchronized with a SharePoint tasks list, the getWSSUrlAsync function errors
out.

7 Note

To get the SharePoint URL and name of the tasks list, we recommend
that you use the getProjectFieldAsync method with the WSSUrl and
WSSList constants in the ProjectProjectFields enumeration.

Each of the functions in the following code includes an anonymous function that is
specified by function (asyncResult) , which is a callback that gets the
asynchronous result. Instead of anonymous functions, you could use named
functions, which can help with maintainability of complex add-ins.

JavaScript

// Get the data in the selected cells of the grid in the active view.
function getSelectedDataAsync() {
_projDoc.getSelectedDataAsync(
Office.CoercionType.Text,
{ ValueFormat: "Formatted" },
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded)
text.value = asyncResult.value;
else
logMethodError("getSelectedDataAsync",
asyncResult.error.name,
asyncResult.error.message);
}
);
}

// Get the GUID of the selected task.


function getSelectedTaskAsync() {
_projDoc.getSelectedTaskAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
text.value = asyncResult.value;
taskGuid = asyncResult.value;
}
else {
logMethodError("getSelectedTaskAsync",
asyncResult.error.name,
asyncResult.error.message);
}
});
}

// Get the GUID of the selected resource.


function getSelectedResourceAsync() {
_projDoc.getSelectedResourceAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
text.value = asyncResult.value;
resourceGuid = asyncResult.value;
}
else {
logMethodError("getSelectedResourceAsync",
asyncResult.error.name,
asyncResult.error.message);
}
});
}

// Get data for the specified task.


function getTaskAsync() {
if (taskGuid != undefined) {
_projDoc.getTaskAsync(
taskGuid,
function (asyncResult) {
if (asyncResult.status ===
Office.AsyncResultStatus.Failed) {
logMethodError("getTaskAsync",
asyncResult.error.name,
asyncResult.error.message);
} else {
var taskInfo = asyncResult.value;
var taskOutput = "Task name: " + taskInfo.taskName
+
"\nGUID: " + taskGuid +
"\nWSS Id: " + taskInfo.wssTaskId
+
"\nResourceNames: " +
taskInfo.resourceNames;
text.value = taskOutput;
}
}
);
} else {
text.value = 'Task GUID not valid:\n' + taskGuid;
}
}

// Get additional data for task fields.


function getTaskFields() {
text.value = "";

_projDoc. getTaskFieldAsync(taskGuid,
Office.ProjectTaskFields.Name,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Name: "
+ asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getTaskFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getTaskFieldAsync(taskGuid, Office.ProjectTaskFields.ID,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "ID: "
+ asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getTaskFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getTaskFieldAsync(taskGuid,
Office.ProjectTaskFields.Start,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Start: "
+ asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getTaskFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getTaskFieldAsync(taskGuid,
Office.ProjectTaskFields.Duration,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Duration: "
+ asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getTaskFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getTaskFieldAsync(taskGuid,
Office.ProjectTaskFields.Priority,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Priority: "
+ asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getTaskFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getTaskFieldAsync(taskGuid,
Office.ProjectTaskFields.Notes,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Notes: "
+ asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getTaskFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);
}

// Get data for the specified resource fields.


function getResourceFields() {
text.value = "";

_projDoc.getResourceFieldAsync(resourceGuid,
Office.ProjectResourceFields.Name,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Resource name: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getResourceFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getResourceFieldAsync(resourceGuid,
Office.ProjectResourceFields.Cost,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Cost: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getResourceFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getResourceFieldAsync(resourceGuid,
Office.ProjectResourceFields.StandardRate,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Standard Rate: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getResourceFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getResourceFieldAsync(resourceGuid,
Office.ProjectResourceFields.ActualCost,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Actual Cost: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getResourceFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getResourceFieldAsync(resourceGuid,
Office.ProjectResourceFields.ActualWork,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Actual Work: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getResourceFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);

_projDoc.getResourceFieldAsync(resourceGuid,
Office.ProjectResourceFields.Units,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Units: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getResourceFieldAsync",
asyncResult.error.name,
asyncResult.error.message);
}
}
);
}

// Get the URL and list name of the synchronized SharePoint task list.
// Recommended: use getProjectField instead.
function getWSSUrlAsync() {
_projDoc.getWSSUrlAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
text.value = "SharePoint URL:\n" +
asyncResult.value.serverUrl
+ "\nList name: " + asyncResult.value.listName;
}
else {
logMethodError("getWSSUrlAsync", asyncResult.error.name,
asyncResult.error.message);
}
});
}

// Get the type and name of the selected view.


function getSelectedViewAsync() {
_projDoc.getSelectedViewAsync(function (asyncResult) {
text.value = "View type: " + asyncResult.value.viewType
+ "\nName: " + asyncResult.value.viewName;
});
}

// Get information about the active project.


function getProjectFields() {
text.value = "";

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.GUID,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Project GUID: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.Start,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "\nStart: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.Finish,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "\nFinish: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProject " + errorText);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.CurrencyDigit
s,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "\nCurrency digits: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.CurrencySymbo
l,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "Currency symbol: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.CurrencySymbo
lPosition,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "\nSymbol position: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.ProjectServer
Url,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "\nProject web app URL:\n "
+ asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.WSSUrl,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "\nSharePoint URL:\n " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);

_projDoc.getProjectFieldAsync(Office.ProjectProjectFields.WSSList,
function (asyncResult) {
if (asyncResult.status ==
Office.AsyncResultStatus.Succeeded) {
text.value = text.value + "\nSharePoint list: " +
asyncResult.value.fieldValue + "\n";
}
else {
logMethodError("getProjectFieldAsync",
asyncResult.error.name, asyncResult.error.message);
}
}
);
}

4. Add JavaScript event handler callbacks and functions to register the task selection,
resource selection, and view selection change event handlers and to unregister the
event handlers. The manageEventHandlerAsync function adds or removes the
specified event handler, depending on the operation parameter. The operation can
be addHandlerAsync or removeHandlerAsync .

The manageTaskEventHandler , manageResourceEventHandler , and


manageViewEventHandler functions can add or remove an event handler, as

specified by the docMethod parameter.

JavaScript

// Task selection changed event handler.


function onTaskSelectionChanged(eventArgs) {
text.value = "In task selection change event handler";
}

// Resource selection changed event handler.


function onResourceSelectionChanged(eventArgs) {
text.value = "In Resource selection changed event handler";
}

// View selection changed event handler.


function onViewSelectionChanged(eventArgs) {
text.value = "In View selection changed event handler";
}

// Add or remove the specified event handler.


function manageEventHandlerAsync(eventType, handler, operation,
onComplete) {
_projDoc[operation] //The operation is addHandlerAsync or
removeHandlerAsync.
(
eventType,
handler,
function (asyncResult) {
if (onComplete) {
onComplete(asyncResult, operation);
} else {
var message = "Operation: " + operation;
message = message + "\nStatus: " + asyncResult.status +
"\n";
text.value = message;
}
}
);
}

// Write the asyncResult status from the manageEventHandlerAsync


function (optional).
function onComplete(asyncResult, operation) {
var message = "In onComplete function for " + operation;
message = message + "\nStatus: " + asyncResult.status;
text.value = message;
}

// Add or remove a task selection changed event handler.


function manageTaskEventHandler(docMethod) {
manageEventHandlerAsync(
Office.EventType.TaskSelectionChanged, // The task
selection changed event.
onTaskSelectionChanged, // The event
handler.
docMethod, // The Office.Document method to add
or remove an event handler.
onComplete // Manages the successful asyncResult
data (optional).
);
}

// Add or remove a resource selection changed event handler.


function manageResourceEventHandler(docMethod) {
manageEventHandlerAsync(
Office.EventType.ResourceSelectionChanged, // The resource
selection changed event.
onResourceSelectionChanged, // The event
handler.
docMethod, // The Office.Document method to add
or remove an event handler.
onComplete // Manages the successful asyncResult
data (optional).
);
}

// Add or remove a view selection changed event handler.


function manageViewEventHandler(docMethod) {
manageEventHandlerAsync(
Office.EventType.ViewSelectionChanged, // The view
selection changed event.
onViewSelectionChanged, // The event
handler.
docMethod, // The Office.Document method to add
or remove an event handler.
onComplete // Manages the successful asyncResult
data (optional).
);
}

5. For the body of the HTML document, add buttons that call the JavaScript functions
for testing. For example, in the div element for the common JSOM API, add an
input button that calls the general getSelectedDataAsync function.

HTML

<body>
<div id="Common_JSOM_API">
OBJECT MODEL TESTS
<br /><br />
<strong>General function:</strong>
<br />
<input id="Button5" class="button-wide" type="button"
onclick="getSelectedDataAsync()"
value="getSelectedDataAsync" />
</div>
<!-- more code . . . -->

6. Add a div section with buttons for project-specific task functions and for the
TaskSelectionChanged event.

HTML

<div id="ProjectSpecificTask">
<br />
<strong>Project-specific task methods:</strong><br />
<button class="button-wide"
onclick="getSelectedTaskAsync()">getSelectedTaskAsync</button><br />
<button class="button-wide"
onclick="getTaskAsync()">getTaskAsync</button><br />
<button class="button-wide" onclick="getTaskFields()">Get Task
Fields</button><br />
<button class="button-wide"
onclick="getWSSUrlAsync()">getWSSUrlAsync</button>
<strong>Task selection changed:</strong>
<button class="button-narrow"
onclick="manageTaskEventHandler('addHandlerAsync')">Add</button>
<button class="button-narrow"
onclick="manageTaskEventHandler('removeHandlerAsync')">Remove</button>
</div>

7. Add div sections with buttons for the resource methods and events, view
methods and events, project properties, and context properties

HTML
<div id="ResourceMethods">
<br />
<strong>Resource methods:</strong>
<button class="button-wide"
onclick="getSelectedResourceAsync()">getSelectedResourceAsync</button>
<br />
<button class="button-wide" onclick="getResourceFields()">Get
Resource Fields</button><br />
<strong>Resource selection changed:</strong>
<button class="button-narrow"
onclick="manageResourceEventHandler('addHandlerAsync')">Add</button>
<button class="button-narrow"
onclick="manageResourceEventHandler('removeHandlerAsync')">Remove</butt
on>
</div>
<div id="ViewMethods">
<br />
<strong>View method:</strong>
<button class="button-wide"
onclick="getSelectedViewAsync()">getSelectedViewAsync</button><br />
<strong>View selection changed:</strong>
<button class="button-narrow"
onclick="manageViewEventHandler('addHandlerAsync')">Add</button>
<button class="button-narrow"
onclick="manageViewEventHandler('removeHandlerAsync')">Remove</button>
</div>
<div id="ProjectMethods">
<br />
<strong>Project properties:</strong>
<button class="button-wide" onclick="getProjectFields()">Get Project
Fields</button><br />
</div>
<div id="ContextVariables">
<br />
<strong>Context properties:</strong>
<button class="button-wide" onclick="getContextValues()">Get Context
Values</button>
</div>

8. To format the button elements, add a CSS style element. For example, add the
following as a child of the head element.

HTML

<style type="text/css">
.button-wide
{
width: 210px;
margin-top: 2px;
}
.button-narrow
{
width: 80px;
margin-top: 2px;
}
</style>

Procedure 3 shows how to install and use the Project OM Test add-in features.

Procedure 3. To install and use the Project OM


Test add-in
1. Create a file share for the directory that contains the JSOM_SimpleOMCalls.xml
manifest. You can create the file share on the local computer or on a remote
computer that is accessible on the network. For example, if the manifest is in the
C:\Project\AppManifests directory on the local computer, run the following

command.

Net share AppManifests=C:\Project\AppManifests

2. Create a file share for the directory that contains the HTML and JavaScript files for
the Project OM Test add-in. Ensure the file share path matches the path that is
specified in the JSOM_SimpleOMCalls.xml manifest. For example, if the files are in
the C:\Project\AppSource directory on the local computer, run the following
command.

net share AppSource=C:\Project\AppSource

3. In Project, open the Project Options dialog box, choose Trust Center, and then
choose Trust Center Settings.

The procedure for registering an add-in is also described in Task pane add-ins for
Project, with additional information.

4. In the Trust Center dialog box, in the left pane, choose Trusted Add-in Catalogs.

5. If you have already added the \\ServerName\AppManifests path for the Bing Search
add-in, skip this step. Otherwise, in the Trusted Add-in Catalogs pane, add the
\\ServerName\AppManifests path in the Catalog Url text box, choose Add catalog,
enable the network share as a default source (see Figure 1), and then choose OK.

Figure 1. Adding a network file share for add-in manifests


6. After you add new add-ins, or change the source code, restart Project. On the
PROJECT ribbon, choose the Office Add-ins drop-down menu, and then choose
See All. In the Insert Add-in dialog box, choose SHARED FOLDER (see Figure 2),
select Project OM Test, and then choose Insert. The Project OM Test add-in starts
in a task pane.

Figure 2. Starting the Project OM Test add-in that is on a file share


7. In Project, create and save a simple project that has at least two tasks. For example,
create tasks named T1, T2, and a milestone named M1, and then set the task
durations and predecessors to be similar to those in Figure 3. Choose the PROJECT
tab on the ribbon, select the entire row for task T2, and then choose the
getSelectedDataAsync button in the task pane. Figure 3 shows the data that is
selected in the text box of the Project OM Test add-in.

Figure 3. Using the Project OM Test add-in

8. Select the cell in the Duration column for the first task, and then choose the
getSelectedDataAsync button in the Project OM Test add-in. The
getSelectedDataAsync function sets the text box value to show 2 days .

9. Select the three Duration cells for all three tasks. The getSelectedDataAsync
function returns semicolon-separated text values for cells selected in different
rows, for example, 2 days;4 days;0 days .

The getSelectedDataAsync function returns comma-separated text values for cells


selected within a row. For example in Figure 3, the entire row for task T2 is
selected. When you choose getSelectedDataAsync , the text box shows the
following: ,Auto Scheduled,T2,4 days,Thu 6/14/12,Tue 6/19/12,1,,<NA>

The Indicators column and the Resource Names column are both empty, so the
text array shows empty values for those columns. The <NA> value is for the Add
New Column cell.

10. Select any cell in the row for task T2, or the entire row for task T2, and then choose
getSelectedTaskAsync. The text box shows the task GUID value, for example,
{25D3E03B-9A7D-E111-92FC-00155D3BA208} . Project stores that value in the global

taskGuid variable of the Project OM Test add-in.

11. Select getTaskAsync . If the taskGuid variable contains the GUID for task T2, the
text box displays the task information. The ResourceNames value is empty.

Create two local resources R1 andR2, assign them to task T2 at 50% each, and
choose getTaskAsync again. The results in the text box include the resource
information. If the task is in a synchronized SharePoint task list, the results also
include the SharePoint task ID.

Task name: T2
GUID: {25D3E03B-9A7D-E111-92FC-00155D3BA208}
WSS Id: 0
ResourceNames: R1[50%],R2[50%]

12. Select the Get Task Fields button. The getTaskFields function calls the
getTaskFieldAsync method multiple times for the task name, index, start date,
duration, priority, and task notes.

Name: T2
ID: 2
Start: Thu 6/14/12
Duration: 4d
Priority: 500
Notes: This is a note for task T2. It is only a test note. If it had been a real
note, there would be some real information.

13. Select the getWSSUrlAsync button. If the project is one of the following kinds, the
results show the task list URL and name.

A SharePoint task list that was imported to Project Server.


A SharePoint task list that was imported to Project Professional, and then
saved back in SharePoint (not using Project Server).
7 Note

If Project Professional is installed on a Windows Server computer, to be able


to save the project back to SharePoint, you can use the Server Manager to
add the Desktop Experience feature.

If the project is a local project, or if you use Project Professional to open a project
that is managed by Project Server, the getWSSUrlAsync method shows an
undefined error.

SharePoint URL: http://ServerName


List name: Test task list

14. Select the Add button in the TaskSelectionChanged event section, which calls the
manageTaskEventHandler function to register a task selection changed event and

returns In onComplete function for addHandlerAsync Status: succeeded in the text


box. Select a different task; the text box shows In task selection changed event
handler , which is the output of the callback function for the task selection changed
event. Choose the Remove button to unregister the event handler.

15. To use the resource methods, first select a view such as Resource Sheet, Resource
Usage, or Resource Form, and then select a resource in that view. Choose
getSelectedResourceAsync to initialize the resourceGuid variable, and then
choose Get Resource Fields to call getResourceFieldAsync multiple times for the
resource properties. You can also add or remove the resource selection changed
event handler.

Resource name: R1
Cost: $800.00
Standard Rate: $50.00/h
Actual Cost: $0.00
Actual Work: 0h
Units: 100%

16. Select getSelectedViewAsync to show the type and name of the active view. You
can also add or remove the view selection changed event handler. For example, if
Resource Form is the active view, the getSelectedViewAsync function shows the
following in the text box.

View type: 6
Name: Resource Form
17. Select Get Project Fields to call the getProjectFieldAsync function multiple times
for different properties of the active project. If the project is opened from Project
Web App, the getProjectFieldAsync function can get the URL of the Project Web
App instance.

Project GUID: 9845922E-DAB4-E111-8AF3-00155D3BA208


Start: Tue 6/12/12
Finish: Tue 6/19/12
Currency digits: 2
Currency symbol: $
Symbol position: 0
Project web app URL: http://servername/pwa

18. Select the Get Context Values button get properties of the document and the
application in which the add-in is running, by getting properties of the
Office.Context.document object and the Office.context.application object. For
example, if the Project1.mpp file is on the local computer desktop, the document
URL is C:\Users\UserAlias\Desktop\Project1.mpp . If the .mpp file is in a SharePoint
library, the value is the URL of the document. If you use Project Professional 2013
to open a project named Project1 from Project Web App, the document URL is
<>\Project1 .

Document URL: <>\Project1


Document mode: readWrite
App language: en-US
Display language: en-US

19. You can refresh the add-in after you edit the source code by closing and restarting
Project. In the Project ribbon, the Office Add-ins drop-down list maintains the list
of recently used add-ins.

Example
The Project 2013 SDK download contains the complete code in the JSOMCall.html file,
the JSOM_Sample.js file, and the related Office.js, Office.debug.js, Project-15.js, and
Project-15.debug.js files. Following is the code in the JSOMCall.html file.

HTML

<!DOCTYPE html>
<html>
<head>
<title>Project OM Sample Code</title>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>

<script type="text/javascript" src="MicrosoftAjax.js"></script>

<!-- Use the CDN reference to office.js when deploying your add-in.
-->
<!-- <script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script> -
->
<script type="text/javascript" src="office.js"></script>
<script type="text/javascript" src="JSOM_Sample.js"></script>

<style type="text/css">
.button-wide {
width: 210px;
margin-top: 2px;
}
.button-narrow
{
width: 80px;
margin-top: 2px;
}
</style>
</head>

<body>
<div id="Common_JSOM_API">
OBJECT MODEL TESTS
<br /><br />
<strong>General method:</strong>
<br />
<input id="Button5" class="button-wide" type="button"
onclick="getSelectedDataAsync()"
value="getSelectedDataAsync" />
</div>
<div id="ProjectSpecificTask">
<br />
<strong>Project-specific task methods:</strong><br />
<button class="button-wide"
onclick="getSelectedTaskAsync()">getSelectedTaskAsync</button><br />
<button class="button-wide"
onclick="getTaskAsync()">getTaskAsync</button><br />
<button class="button-wide" onclick="getTaskFields()">Get Task
Fields</button><br />
<button class="button-wide"
onclick="getWSSUrlAsync()">getWSSUrlAsync</button>
<strong>Task selection changed:</strong>
<button class="button-narrow"
onclick="manageTaskEventHandler('addHandlerAsync')">Add</button>
<button class="button-narrow"
onclick="manageTaskEventHandler('removeHandlerAsync')">Remove</button>
</div>
<div id="ResourceMethods">
<br />
<strong>Resource methods:</strong>
<button class="button-wide"
onclick="getSelectedResourceAsync()">getSelectedResourceAsync</button><br />
<button class="button-wide" onclick="getResourceFields()">Get
Resource Fields</button><br />
<strong>Resource selection changed:</strong>
<button class="button-narrow"
onclick="manageResourceEventHandler('addHandlerAsync')">Add</button>
<button class="button-narrow"
onclick="manageResourceEventHandler('removeHandlerAsync')">Remove</button>
</div>
<div id="ViewMethods">
<br />
<strong>View method:</strong>
<button class="button-wide"
onclick="getSelectedViewAsync()">getSelectedViewAsync</button><br />
<strong>View selection changed:</strong>
<button class="button-narrow"
onclick="manageViewEventHandler('addHandlerAsync')">Add</button>
<button class="button-narrow"
onclick="manageViewEventHandler('removeHandlerAsync')">Remove</button>
</div>
<div id="ProjectMethods">
<br />
<strong>Project properties:</strong>
<button class="button-wide" onclick="getProjectFields()">Get
Project Fields</button><br />
</div>
<div id="ContextVariables">
<br />
<strong>Context properties:</strong>
<button class="button-wide" onclick="getContextValues()">Get
Context Values</button>
</div>
<br />
<textarea id="text" rows="10" cols="25">This is the text result.
</textarea>
</body>
</html

Robust programming
The Project OM Test add-in is an example that shows the use of some JavaScript
functions for Project 2013 in the Project-15.js and Office.js files. The example is for
testing only and does not include robust error checks. For example, if you do not select
a resource and run the getSelectedResourceAsync function, the resourceGuid variable is
not initialized, and calls to getResourceFieldAsync return an error. For a production add-
in, you should check for specific errors and ignore the results, hide functionality that
does not apply, or notify the user to choose a view and make a valid selection before
using a function.

For a simple example, the error output in the following code includes th actionMessage
variable that specifies the action to take to avoid an error in the
getSelectedResourceAsync function.

JavaScript

function logError(errorText) {
text.value = "Error in " + errorText;
}

function logMethodError(methodName, errorName, errorMessage, actionMessage)


{
logError(methodName + " method.\nError name: " + errorName
+ "\nMessage: " + errorMessage
+ "\n\nAction: " + actionMessage);
}

// Get the GUID of the selected resource.


function getSelectedResourceAsync() {
_projDoc.getSelectedResourceAsync(function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
text.value = asyncResult.value;
resourceGuid = asyncResult.value;
}
else {
var actionMessage = "Select a resource before running the
getSelectedResourceAsync method.";
logMethodError("getSelectedResourceAsync",
asyncResult.error.name,
asyncResult.error.message, actionMessage);
}
});
}

The HelloProject_OData sample in the Project 2013 SDK download includes the
SurfaceErrors.js file that uses the JQuery library to display a pop-up error message.
Figure 4 shows the error message in a "toast" notification.

The following code in the SurfaceErrors.js file includes th throwError function that
creates a Toast object.

JavaScript

/*
* Show error messages in a "toast" notification.
*/
// Throws a custom defined error.
function throwError(errTitle, errMessage) {
try {
// Define and throw a custom error.
var customError = { name: errTitle, message: errMessage }
throw customError;
}
catch (err) {
// Catch the error and display it to the user.
Toast.showToast(err.name, err.message);
}
}

// Add a dynamically-created div "toast" for displaying errors to the user.


var Toast = {

Toast: "divToast",
Close: "btnClose",
Notice: "lblNotice",
Output: "lblOutput",

// Show the toast with the specified information.


showToast: function (title, message) {

if (document.getElementById(this.Toast) == null) {
this.createToast();
}

document.getElementById(this.Notice).innerText = title;
document.getElementById(this.Output).innerText = message;

$("#" + this.Toast).hide();
$("#" + this.Toast).show("slow");
},

// Create the display for the toast.


createToast: function () {
var divToast;
var lblClose;
var btnClose;
var divOutput;
var lblOutput;
var lblNotice;

// Create the container div.


divToast = document.createElement("div");
var toastStyle = "background-color:rgba(220, 220, 128, 0.80);" +
"position:absolute;" +
"bottom:0px;" +
"width:90%;" +
"text-align:center;" +
"font-size:11pt;";
divToast.setAttribute("style", toastStyle);
divToast.setAttribute("id", this.Toast);
// Create the close button.
lblClose = document.createElement("div");
lblClose.setAttribute("id", this.Close);
var btnStyle = "text-align:right;" +
"padding-right:10px;" +
"font-size:10pt;" +
"cursor:default";
lblClose.setAttribute("style", btnStyle);
lblClose.appendChild(document.createTextNode("CLOSE "));

btnClose = document.createElement("span");
btnClose.setAttribute("style", "cursor:pointer;");
btnClose.setAttribute("onclick", "Toast.close()");
btnClose.innerText = "X";
lblClose.appendChild(btnClose);

// Create the div to contain the toast title and message.


divOutput = document.createElement("div");
divOutput.setAttribute("id", "divOutput");
var outputStyle = "margin-top:0px;";
divOutput.setAttribute("style", outputStyle);

lblNotice = document.createElement("span");
lblNotice.setAttribute("id", this.Notice);
var labelStyle = "font-weight:bold;margin-top:0px;";
lblNotice.setAttribute("style", labelStyle);

lblOutput = document.createElement("span");
lblOutput.setAttribute("id", this.Output);

// Add the child nodes to the toast div.


divOutput.appendChild(lblNotice);
divOutput.appendChild(document.createElement("br"));
divOutput.appendChild(lblOutput);
divToast.appendChild(lblClose);
divToast.appendChild(divOutput);

// Add the toast div to the document body.


document.body.appendChild(divToast);
},

// Close the toast.


close: function () {
$("#" + this.Toast).hide("slow");
}
}

To use the throwError function, include the JQuery library and the SurfaceErrors.js script
in the JSOMCall.html file, and then add a call to throwError in other JavaScript functions
such as logMethodError .

7 Note
Before you deploy the add-in, change the office.js reference and the jQuery
reference to the content delivery network (CDN) reference. The CDN reference
provides the most recent version and better performance.

HTML

<!DOCTYPE html>
<html>
<head>
<title>Project OM Sample Code</title>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

<script type="text/javascript" src="MicrosoftAjax.js"></script>

<!-- Use the CDN reference to Office.js and jQuery when deploying your
add-in. -->
<!-- <script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script> -
->
<script type="text/javascript" src="office.js"></script>
<script type="text/javascript"
src="http://ajax.microsoft.com/ajax/jQuery/jquery-1.9.0.min.js"></script>

<script type="text/javascript" src="JSOM_Sample.js"></script>


<script type="text/javascript" src="SurfaceErrors.js"></script>

<!-- . . . INVALID USE OF SYMBOLS . . . -->


</head>

JavaScript

function logMethodError(methodName, errorName, errorMessage, actionMessage)


{
logError(methodName + " method.\nError name: " + errorName
+ "\nMessage: " + errorMessage
+ "\n\nAction: " + actionMessage);

throwError(methodName + " error", actionMessage);


}

Figure 4. Functions in the SurfaceErrors.js file can show a "toast" notification


See also
Task pane add-ins for Project
Understanding the JavaScript API for add-ins
Office JavaScript API Add-ins
Schema reference for Office Add-ins manifests (v1.1)
Project 2013 SDK download
Visio documentation
You can use the Visio JavaScript APIs to embed Visio diagrams in SharePoint Online.
Learn how to use the Visio JavaScript APIs with Visio on the web to build solutions for
SharePoint Online.

About Visio JavaScript API

e OVERVIEW

What is Visio JavaScript API?

b GET STARTED

Create a script that displays the shape text of the selected shape

i REFERENCE

Explore the Visio JavaScript API

Resources

i REFERENCE

Ask questions

Request features

Report issues

Office Add-ins additional resources


Visio JavaScript API overview
Article • 05/02/2023

You can use the Visio JavaScript APIs to embed Visio diagrams in classic SharePoint
pages in SharePoint Online. (This extensibility feature is not supported in on-premise
SharePoint or on SharePoint Framework pages.)

An embedded Visio diagram is a diagram that is stored in a SharePoint document library


and displayed on a SharePoint page. To embed a Visio diagram, display it in an HTML
<iframe> element. Then you can use Visio JavaScript APIs to programmatically work
with the embedded diagram.

You can use the Visio JavaScript APIs to:

Interact with Visio diagram elements like pages and shapes.


Create visual markup on the Visio diagram canvas.
Write custom handlers for mouse events within the drawing.
Expose diagram data, such as shape text, shape data, and hyperlinks, to your
solution.
This article describes how to use the Visio JavaScript APIs with Visio on the web to build
your solutions for SharePoint Online. It introduces key concepts that are fundamental to
using the APIs, such as EmbeddedSession , RequestContext , and JavaScript proxy objects,
and the sync() , Visio.run() , and load() methods. The code examples show you how
to apply these concepts.

EmbeddedSession
The EmbeddedSession object initializes communication between the developer frame
and the Visio frame in the browser.

JavaScript

const session = new OfficeExtension.EmbeddedSession(url, { id: "embed-


iframe",container: document.getElementById("iframeHost") });
session.init().then(function () {
window.console.log("Session successfully initialized");
});

Visio.run(session, function(context) { batch })


Visio.run() executes a batch script that performs actions on the Visio object model.

The batch commands include definitions of local JavaScript proxy objects and sync()
methods that synchronize the state between local and Visio objects and promise
resolution. The advantage of batching requests in Visio.run() is that when the promise
is resolved, any tracked page objects that were allocated during the execution will be
automatically released.

The run function takes in session and RequestContext object and returns a promise
(typically, just the result of context.sync() ). It is possible to run the batch operation
outside of the Visio.run() . However, in such a scenario, any page object references
needs to be manually tracked and managed.

RequestContext
The RequestContext object facilitates requests to the Visio application. Because the
developer frame and the Visio web client run in two different iframes, the
RequestContext object (context in next example) is required to get access to Visio and
related objects such as pages and shapes, from the developer frame.

JavaScript
function hideToolbars() {
Visio.run(session, function(context){
const app = context.document.application;
app.showToolbars = false;
return context.sync().then(function () {
window.console.log("Toolbars Hidden");
});
}).catch(function(error)
{
window.console.log("Error: " + error);
});
};

Proxy objects
The Visio JavaScript objects declared and used in an embedded session are proxy
objects for the real objects in a Visio document. All actions taken on proxy objects are
not realized in Visio, and the state of the Visio document is not realized in the proxy
objects until the document state has been synchronized. The document state is
synchronized when context.sync() is run.

For example, the local JavaScript object getActivePage is declared to reference the
selected page. This can be used to queue the setting of its properties and invoking
methods. The actions on such objects are not realized until the sync() method is run.

JavaScript

const activePage = context.document.getActivePage();

sync()
The sync() method synchronizes the state between JavaScript proxy objects and real
objects in Visio by executing instructions queued on the context and retrieving
properties of loaded Office objects for use in your code. This method returns a promise,
which is resolved when synchronization is complete.

load()
The load() method is used to fill in the proxy objects created in the JavaScript layer.
When trying to retrieve an object such as a document, a local proxy object is created
first in the JavaScript layer. Such an object can be used to queue the setting of its
properties and invoking methods. However, for reading object properties or relations,
the load() and sync() methods need to be invoked first. The load() method takes in
the properties and relations that need to be loaded when the sync() method is called.

The following shows the syntax for the load() method.

JavaScript

object.load(string: properties); //or object.load(array: properties); //or


object.load({loadOption});

1. properties is the list of property names to be loaded, specified as comma-


delimited strings or array of names. See .load() methods under each object for
details.

2. loadOption specifies an object that describes the selection, expansion, top, and
skip options. See object load options for details.

Example: Printing all shapes text in active page


The following example shows you how to print shape text value from an array shapes
object. The Visio.run() function contains a batch of instructions. As part of this batch, a
proxy object is created that references shapes on the active document.

All these commands are queued and run when context.sync() is called. The sync()
method returns a promise that can be used to chain it with other operations.

JavaScript

Visio.run(session, function (context) {


const page = context.document.getActivePage();
const shapes = page.shapes;
shapes.load();
return context.sync().then(function () {
for(let i=0; i<shapes.items.length;i++) {
let shape = shapes.items[i];
window.console.log("Shape Text: " + shape.text );
}
});
}).catch(function(error) {
window.console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
window.console.log ("Debug info: " +
JSON.stringify(error.debugInfo));
}
});
Error messages
Errors are returned using an error object that consists of a code and a message. The
following table provides a list of possible error conditions that can occur.

error.code error.message

InvalidArgument The argument is invalid or missing or has an incorrect format.

GeneralException There was an internal error while processing the request.

NotImplemented The requested feature isn't implemented.

UnsupportedOperation The operation being attempted is not supported.

AccessDenied You cannot perform the requested operation.

ItemNotFound The requested resource doesn't exist.

Get started
You can use the example in this section to get started. This example shows you how to
programmatically display the shape text of the selected shape in a Visio diagram. To
begin, create a classic page in SharePoint Online or edit an existing page. Add a script
editor webpart on the page and copy-paste the following code.

HTML

<script src='https://appsforoffice.microsoft.com/embedded/1.0/visio-web-
embedded.js' type='text/javascript'></script>

Enter Visio File Url:<br/>


<script language="javascript">
document.write("<input type='text' id='fileUrl' size='120'/>");
document.write("<input type='button' value='InitEmbeddedFrame'
onclick='initEmbeddedFrame()' />");
document.write("<br />");
document.write("<input type='button' value='SelectedShapeText'
onclick='getSelectedShapeText()' />");
document.write("<textarea id='ResultOutput' style='width:350px;height:60px'>
</textarea>");
document.write("<div id='iframeHost' />");

let session; // Global variable to store the session and pass it afterwards
in Visio.run()
let textArea;
// Loads the Visio application and Initializes communication between
developer frame and Visio online frame
function initEmbeddedFrame() {
textArea = document.getElementById('ResultOutput');
let url = document.getElementById('fileUrl').value;
if (!url) {
window.alert("File URL should not be empty");
}
// APIs are enabled for EmbedView action only.
url = url.replace("action=view","action=embedview");
url = url.replace("action=interactivepreview","action=embedview");
url = url.replace("action=default","action=embedview");
url = url.replace("action=edit","action=embedview");

session = new OfficeExtension.EmbeddedSession(url, { id: "embed-


iframe",container: document.getElementById("iframeHost") });
return session.init().then(function () {
// Initialization is successful
textArea.value = "Initialization is successful";
});
}

// Code for getting selected Shape Text using the shapes collection object
function getSelectedShapeText() {
Visio.run(session, function (context) {
const page = context.document.getActivePage();
const shapes = page.shapes;
shapes.load();
return context.sync().then(function () {
textArea.value = "Please select a Shape in the Diagram";
for(let i=0; i<shapes.items.length;i++) {
let shape = shapes.items[i];
if ( shape.select == true) {
textArea.value = shape.text;
return;
}
}
});
}).catch(function(error) {
textArea.value = "Error: ";
if (error instanceof OfficeExtension.Error) {
textArea.value += "Debug info: " +
JSON.stringify(error.debugInfo);
}
});
}
</script>

After that, all you need is the URL of a Visio diagram that you want to work with. Just
upload the Visio diagram to SharePoint Online and open it in Visio on the web. From
there, open the Embed dialog and use the Embed URL in the above example.
If you are using Visio on the web in Edit mode, open the Embed dialog by choosing File
> Share > Embed. If you are using Visio on the web in View mode, open the Embed
dialog by choosing '...' and then Embed.

Visio JavaScript API reference


For detailed information about Visio JavaScript API, see the Visio JavaScript API
reference documentation.
visio package
Reference

Classes
Visio.Application Represents the Application.

[ API set: 1.1 ]

Visio.Comment Represents the Comment.

[ API set: 1.1 ]

Visio.CommentCollection Represents the CommentCollection for a given Shape.

[ API set: 1.1 ]

Visio.Document Represents the Document class.

[ API set: 1.1 ]

Visio.DocumentView Represents the DocumentView class.

[ API set: 1.1 ]

Visio.Hyperlink Represents the Hyperlink.

[ API set: 1.1 ]

Visio.HyperlinkCollection Represents the Hyperlink Collection.

[ API set: 1.1 ]

Visio.Page Represents the Page class.

[ API set: 1.1 ]

Visio.PageCollection Represents a collection of Page objects that are part of the


document.

[ API set: 1.1 ]

Visio.PageView Represents the PageView class.

[ API set: 1.1 ]

Visio.RequestContext The RequestContext object facilitates requests to the Visio


application. Since the Office add-in and the Visio application run
in two different processes, the request context is required to get
access to the Visio object model from the add-in.
Visio.Selection Represents the Selection in the page.

[ API set: 1.1 ]

Visio.Shape Represents the Shape class.

[ API set: 1.1 ]

Visio.ShapeCollection Represents the Shape Collection.

[ API set: 1.1 ]

Visio.ShapeDataItem Represents the ShapeDataItem.

[ API set: 1.1 ]

Visio.ShapeDataItemCollection Represents the ShapeDataItemCollection for a given Shape.

[ API set: 1.1 ]

Visio.ShapeView Represents the ShapeView class.

[ API set: 1.1 ]

Interfaces
Visio.BoundingBox Represents the BoundingBox of the shape.

[ API set: 1.1 ]

Visio.ConnectorBinding Connector bindings for data visualizer diagram.

[ API set: 1.1 ]

Visio.DataRefreshComplete Provides information about the document that raised the


EventArgs DataRefreshComplete event.

[ API set: 1.1 ]

Visio.DocumentErrorEventArgs Provides information about DocumentError event

[ API set: 1.1 ]

Visio.DocumentLoadComplete Provides information about the success or failure of the


EventArgs DocumentLoadComplete event.

[ API set: 1.1 ]

Visio.Highlight Represents the highlight data added to the shape.

[ API set: 1.1 ]


Visio.Interfaces.Application An interface describing the data returned by calling
Data application.toJSON() .

Visio.Interfaces.Application Represents the Application.


LoadOptions
[ API set: 1.1 ]

Visio.Interfaces.Application An interface for updating data on the Application object, for use
UpdateData in application.set({ ... }) .

Visio.Interfaces.CollectionLoad Provides ways to load properties of only a subset of members of


Options a collection.

Visio.Interfaces.Comment An interface describing the data returned by calling


CollectionData commentCollection.toJSON() .

Visio.Interfaces.Comment Represents the CommentCollection for a given Shape.


CollectionLoadOptions
[ API set: 1.1 ]

Visio.Interfaces.Comment An interface for updating data on the CommentCollection


CollectionUpdateData object, for use in commentCollection.set({ ... }) .

Visio.Interfaces.CommentData An interface describing the data returned by calling


comment.toJSON() .

Visio.Interfaces.CommentLoad Represents the Comment.


Options
[ API set: 1.1 ]

Visio.Interfaces.Comment An interface for updating data on the Comment object, for use
UpdateData in comment.set({ ... }) .

Visio.Interfaces.DocumentData An interface describing the data returned by calling


document.toJSON() .

Visio.Interfaces.Document Represents the Document class.


LoadOptions
[ API set: 1.1 ]

Visio.Interfaces.Document An interface for updating data on the Document object, for use
UpdateData in document.set({ ... }) .

Visio.Interfaces.Document An interface describing the data returned by calling


ViewData documentView.toJSON() .

Visio.Interfaces.Document Represents the DocumentView class.


ViewLoadOptions
[ API set: 1.1 ]

Visio.Interfaces.Document An interface for updating data on the DocumentView object, for


ViewUpdateData use in documentView.set({ ... }) .
Visio.Interfaces.Hyperlink An interface describing the data returned by calling
CollectionData hyperlinkCollection.toJSON() .

Visio.Interfaces.Hyperlink Represents the Hyperlink Collection.


CollectionLoadOptions
[ API set: 1.1 ]

Visio.Interfaces.Hyperlink An interface for updating data on the HyperlinkCollection object,


CollectionUpdateData for use in hyperlinkCollection.set({ ... }) .

Visio.Interfaces.HyperlinkData An interface describing the data returned by calling


hyperlink.toJSON() .

Visio.Interfaces.HyperlinkLoad Represents the Hyperlink.


Options
[ API set: 1.1 ]

Visio.Interfaces.PageCollection An interface describing the data returned by calling


Data pageCollection.toJSON() .

Visio.Interfaces.PageCollection Represents a collection of Page objects that are part of the


LoadOptions document.

[ API set: 1.1 ]

Visio.Interfaces.PageCollection An interface for updating data on the PageCollection object, for


UpdateData use in pageCollection.set({ ... }) .

Visio.Interfaces.PageData An interface describing the data returned by calling


page.toJSON() .

Visio.Interfaces.PageLoad Represents the Page class.


Options
[ API set: 1.1 ]

Visio.Interfaces.PageUpdate An interface for updating data on the Page object, for use in
Data page.set({ ... }) .

Visio.Interfaces.PageViewData An interface describing the data returned by calling


pageView.toJSON() .

Visio.Interfaces.PageViewLoad Represents the PageView class.


Options
[ API set: 1.1 ]

Visio.Interfaces.PageView An interface for updating data on the PageView object, for use in
UpdateData pageView.set({ ... }) .

Visio.Interfaces.SelectionData An interface describing the data returned by calling


selection.toJSON() .

Visio.Interfaces.Shape An interface describing the data returned by calling


CollectionData
shapeCollection.toJSON() .

Visio.Interfaces.Shape Represents the Shape Collection.


CollectionLoadOptions
[ API set: 1.1 ]

Visio.Interfaces.Shape An interface for updating data on the ShapeCollection object, for


CollectionUpdateData use in shapeCollection.set({ ... }) .

Visio.Interfaces.ShapeData An interface describing the data returned by calling


shape.toJSON() .

Visio.Interfaces.ShapeData An interface describing the data returned by calling


ItemCollectionData shapeDataItemCollection.toJSON() .

Visio.Interfaces.ShapeData Represents the ShapeDataItemCollection for a given Shape.


ItemCollectionLoadOptions
[ API set: 1.1 ]

Visio.Interfaces.ShapeData An interface for updating data on the ShapeDataItemCollection


ItemCollectionUpdateData object, for use in shapeDataItemCollection.set({ ... }) .

Visio.Interfaces.ShapeData An interface describing the data returned by calling


ItemData shapeDataItem.toJSON() .

Visio.Interfaces.ShapeData Represents the ShapeDataItem.


ItemLoadOptions
[ API set: 1.1 ]

Visio.Interfaces.ShapeLoad Represents the Shape class.


Options
[ API set: 1.1 ]

Visio.Interfaces.ShapeUpdate An interface for updating data on the Shape object, for use in
Data shape.set({ ... }) .

Visio.Interfaces.ShapeView An interface describing the data returned by calling


Data shapeView.toJSON() .

Visio.Interfaces.ShapeView Represents the ShapeView class.


LoadOptions
[ API set: 1.1 ]

Visio.Interfaces.ShapeView An interface for updating data on the ShapeView object, for use
UpdateData in shapeView.set({ ... }) .

Visio.PageLoadCompleteEvent Provides information about the page that raised the


Args PageLoadComplete event.

[ API set: 1.1 ]

Visio.PageRenderComplete Provides information about the page that raised the


EventArgs PageRenderComplete event.
[ API set: 1.1 ]

Visio.Position Represents the Position of the object in the view.

[ API set: 1.1 ]

Visio.SelectionChangedEvent Provides information about the shape collection that raised the
Args SelectionChanged event.

[ API set: 1.1 ]

Visio.ShapeBinding Shape binding informations required for data visualizer diagram

[ API set: 1.1 ]

Visio.ShapeMouseEnterEvent Provides information about the shape that raised the


Args ShapeMouseEnter event.

[ API set: 1.1 ]

Visio.ShapeMouseLeaveEvent Provides information about the shape that raised the


Args ShapeMouseLeave event.

[ API set: 1.1 ]

Visio.TaskPaneStateChanged Provides information about the TaskPaneStateChanged event.


EventArgs
[ API set: 1.1 ]

Enums
Visio.ColumnType Represents the type of column values.

[ API set: 1.1 ]

Visio.ConnectorDirection Direction of connector in DataVisualizer diagram.

[ API set: 1.1 ]

Visio.CrossFunctional Represents the orientation of the Cross Functional Flowchart


FlowchartOrientation diagram.

[ API set: 1.1 ]

Visio.DataSourceType Represents the type of source for the data connection.

[ API set: 1.1 ]

Visio.DataValidationErrorType Represents the types of data validation error.

[ API set: 1.1 ]


Visio.DataVisualizerDiagram Type of the Data Visualizer Diagram operation
OperationType
[ API set: 1.1 ]

Visio.DataVisualizerDiagram Result of Data Visualizer Diagram operations.


ResultType
[ API set: 1.1 ]

Visio.DataVisualizerDiagram DiagramType for Data Visualizer diagrams


Type
[ API set: 1.1 ]

Visio.ErrorCodes

Visio.EventType EventType represents the type of the events Host supports

[ API set: 1.1 ]

Visio.LayoutVariant Represents the type of layout.

[ API set: 1.1 ]

Visio.OverlayHorizontal Represents the Horizontal Alignment of the Overlay relative to


Alignment the shape.

[ API set: 1.1 ]

Visio.OverlayType Represents the type of the overlay.

[ API set: 1.1 ]

Visio.OverlayVerticalAlignment Represents the Vertical Alignment of the Overlay relative to the


shape.

[ API set: 1.1 ]

Visio.TaskPaneType TaskPaneType represents the types of the First Party TaskPanes


that are supported by Host through APIs. Used in case of Show
TaskPane API/ TaskPane State Changed Event etc

[ API set: 1.1 ]

Visio.ToolBarType Toolbar IDs of the app

[ API set: 1.1 ]

Functions
Visio.run(batch) Executes a batch script that performs actions on the Visio object
model, using a new request context. When the promise is
resolved, any tracked objects that were automatically allocated
during execution will be released.

Visio.run(object, batch) Executes a batch script that performs actions on the Visio object
model, using the request context of a previously-created API
object.

Visio.run(objects, batch) Executes a batch script that performs actions on the Visio object
model, using the request context of previously-created API
objects.

Visio.run(contextObject, Executes a batch script that performs actions on the Visio object
batch) model, using the RequestContext of a previously-created object.
When the promise is resolved, any tracked objects that were
automatically allocated during execution will be released.

Function Details

Visio.run(batch)
Executes a batch script that performs actions on the Visio object model, using a new
request context. When the promise is resolved, any tracked objects that were
automatically allocated during execution will be released.

TypeScript

export function run<T>(batch: (context: Visio.RequestContext) =>


Promise<T>): Promise<T>;

Parameters
batch (context: Visio.RequestContext) => Promise<T>
A function that takes in an Visio.RequestContext and returns a promise (typically, just
the result of "context.sync()"). The context parameter facilitates requests to the Visio
application. Since the Office add-in and the Visio application run in two different
processes, the request context is required to get access to the Visio object model
from the add-in.

Returns
Promise<T>

Visio.run(object, batch)
Executes a batch script that performs actions on the Visio object model, using the
request context of a previously-created API object.

TypeScript

export function run<T>(object: OfficeExtension.ClientObject |


OfficeExtension.EmbeddedSession, batch: (context: Visio.RequestContext)
=> Promise<T>): Promise<T>;

Parameters
object OfficeExtension.ClientObject | OfficeExtension.EmbeddedSession
A previously-created API object. The batch will use the same request context as the
passed-in object, which means that any changes applied to the object will be picked
up by "context.sync()".

batch (context: Visio.RequestContext) => Promise<T>


A function that takes in an Visio.RequestContext and returns a promise (typically, just
the result of "context.sync()"). When the promise is resolved, any tracked objects that
were automatically allocated during execution will be released.

Returns
Promise<T>

Visio.run(objects, batch)
Executes a batch script that performs actions on the Visio object model, using the
request context of previously-created API objects.

TypeScript

export function run<T>(objects: OfficeExtension.ClientObject[], batch:


(context: Visio.RequestContext) => Promise<T>): Promise<T>;

Parameters
objects OfficeExtension.ClientObject[]
An array of previously-created API objects. The array will be validated to make sure
that all of the objects share the same context. The batch will use this shared request
context, which means that any changes applied to these objects will be picked up by
"context.sync()".
batch (context: Visio.RequestContext) => Promise<T>
A function that takes in a Visio.RequestContext and returns a promise (typically, just
the result of "context.sync()"). When the promise is resolved, any tracked objects that
were automatically allocated during execution will be released.

Returns
Promise<T>

Visio.run(contextObject, batch)
Executes a batch script that performs actions on the Visio object model, using the
RequestContext of a previously-created object. When the promise is resolved, any
tracked objects that were automatically allocated during execution will be released.

TypeScript

export function run<T>(contextObject:


OfficeExtension.ClientRequestContext, batch: (context:
Visio.RequestContext) => Promise<T>): Promise<T>;

Parameters
contextObject OfficeExtension.ClientRequestContext
A previously-created Visio.RequestContext. This context will get re-used by the batch
function (instead of having a new context created). This means that the batch will be
able to pick up changes made to existing API objects, if those objects were derived
from this same context.

batch (context: Visio.RequestContext) => Promise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Visio
application. Since the Office add-in and the Visio application run in two different
processes, the RequestContext is required to get access to the Visio object model
from the add-in.

Returns
Promise<T>

Remarks
In addition to this signature, the method also has the following signatures:

run<T>(batch: (context: Visio.RequestContext) => Promise<T>): Promise<T>;

run<T>(object: OfficeExtension.ClientObject | OfficeExtension.EmbeddedSession,


batch: (context: Visio.RequestContext) => Promise<T>): Promise<T>;

run<T>(objects: OfficeExtension.ClientObject[], batch: (context:

Visio.RequestContext) => Promise<T>): Promise<T>;


Word add-ins documentation
With Word add-ins, you can use familiar web technologies such as HTML, CSS, and
JavaScript to build a solution that can run in Word across multiple platforms, including
on the web, Windows, Mac, and iPad. Learn how to build, test, debug, and publish Word
add-ins.

About Word add-ins

e OVERVIEW

What are Word add-ins?

f QUICKSTART

Build your first Word add-in

Explore APIs with Script Lab

c HOW-TO GUIDE

Use the Word JavaScript API to interact with document content and metadata

Test and debug a Word add-in

Deploy and publish a Word add-in

Key Office Add-ins concepts

e OVERVIEW

Office Add-ins platform overview

p CONCEPT

Core concepts for Office Add-ins

Design Office Add-ins

Develop Office Add-ins

Resources
i REFERENCE

Ask questions

Request features

Report issues

Office Add-ins additional resources


Word add-ins overview
Article • 04/27/2023

Do you want to create a solution that extends the functionality of Word? For example,
one that involves automated document assembly? Or a solution that binds to and
accesses data in a Word document from other data sources? You can use the Office
Add-ins platform, which includes the Word JavaScript API and the Office JavaScript API,
to extend Word clients running on a Windows desktop, on a Mac, or in the cloud.

Word add-ins are one of the many development options that you have on the Office
Add-ins platform. You can use add-in commands to extend the Word UI and launch task
panes that run JavaScript that interacts with the content in a Word document. Any code
that you can run in a browser can run in a Word add-in. Add-ins that interact with
content in a Word document create requests to act on Word objects and synchronize
object state.

7 Note

If you plan to publish your add-in to AppSource and make it available within the
Office experience, make sure that you conform to the Commercial marketplace
certification policies. For example, to pass validation, your add-in must work across
all platforms that support the methods that you define (for more information, see
section 1120.3 and the Office Add-in application and availability page).

The following figure shows an example of a Word add-in that runs in a task pane.

Figure 1. Add-in running in a task pane in Word


The Word add-in can do the following:

1. Send requests to the Word document.


2. Use JavaScript to access the paragraph object and update, delete, or move the
paragraph.

For example, the following code shows how to append a new sentence to that
paragraph.

JavaScript

await Word.run(async (context) => {


const paragraphs = context.document.getSelection().paragraphs;
paragraphs.load();
await context.sync();
paragraphs.items[0].insertText(' New sentence in the paragraph.',
Word.InsertLocation.end);
await context.sync();
});

You can use any web server technology to host your Word add-in, such as ASP.NET,
NodeJS, or Python. Use your favorite client-side framework -- Ember, Backbone,
Angular, React -- or stick with vanilla or plain JavaScript to develop your solution. You
can also use services like Azure to authenticate and host your application.
The Word JavaScript APIs give your application access to the objects and metadata
found in a Word document. You can use these APIs to create add-ins that target:

Word 2013 or later on Windows


Word on the web
Word 2016 or later on Mac
Word on iPad

Write your add-in once, and it will run in all versions of Word across multiple platforms.
For details, see Office client application and platform availability for Office Add-ins.

JavaScript APIs for Word


You can use two sets of JavaScript APIs to interact with the objects and metadata in a
Word document. The first is the Common API, which was introduced in Office 2013.
Many of the objects in the Common API can be used in add-ins hosted by two or more
Office clients. This API uses callbacks extensively.

The second is the Word JavaScript API. This is an application-specific API model that was
introduced with Word 2016. It's a strongly-typed object model that you can use to
create Word add-ins that target Word 2016 and later on Mac and on Windows. This
object model uses promises and provides access to Word-specific objects like body,
content controls, inline pictures, and paragraphs. The Word JavaScript API includes
TypeScript definitions and vsdoc files so that you can get code hints in your IDE.

Currently, all Word clients support the shared Office JavaScript API, and most clients
support the Word JavaScript API. For details about supported clients, see Office client
application and platform availability for Office Add-ins.

We recommend that you start with the Word JavaScript API because the object model is
easier to use. Use the Word JavaScript API if you need to do the following:

Access the objects in a Word document.

Use the shared Office JavaScript API when you need to do any of the following:

Target Word 2013.


Perform initial actions for the application.
Check the supported requirement set.
Access metadata, settings, and environmental information for the document.
Bind to sections in a document and capture events.
Open a dialog box.
Next steps
Ready to create your first Word add-in? See Build your first Word add-in. Use the add-in
manifest to describe where your add-in is hosted, how it's displayed, and define
permissions and other information.

To learn more about how to design a world-class Word add-in that creates a compelling
experience for your users, see Design guidelines and Best practices.

After you develop your add-in, you can publish it to a network share, an app catalog, or
AppSource.

See also
Developing Office Add-ins
Learn about the Microsoft 365 Developer Program
Office Add-ins platform overview
Word JavaScript API reference
Build your first Word task pane add-in
Article • 04/17/2023

In this article, you'll walk through the process of building a Word task pane add-in.

Create the add-in


You can create an Office Add-in by using the Yeoman generator for Office Add-ins or
Visual Studio. The Yeoman generator creates a Node.js project that can be managed
with Visual Studio Code or any other editor, whereas Visual Studio creates a Visual
Studio solution. Select the tab for the one you'd like to use and then follow the
instructions to create your add-in and test it locally.

Yeoman generator

Prerequisites

7 Note

If you aren't familiar with Node.js or npm, you should start by setting up your
development environment.

Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins.
To install these tools globally, run the following command via the command
prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend


you update your package to the latest version from npm.
Create the add-in project
Run the following command to create an add-in project using the Yeoman
generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the
data collection policies of Yeoman and the Office Add-in CLI tools. Use the
information that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: JavaScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Word

After you complete the wizard, the generator creates the project and installs
supporting Node components.

 Tip

You can ignore the next steps guidance that the Yeoman generator provides
after the add-in project's been created. The step-by-step instructions within
this article provide all of the guidance you'll need to complete this tutorial.

Explore the project


The add-in project that you've created with the Yeoman generator contains sample
code for a basic task pane add-in. If you'd like to explore the components of your
add-in project, open the project in your code editor and review the files listed
below. When you're ready to try out your add-in, proceed to the next section.

The ./manifest.xml file in the root directory of the project defines the settings
and capabilities of the add-in. To learn more about the manifest.xml file, see
Office Add-ins XML manifest.
The ./src/taskpane/taskpane.html file contains the HTML markup for the task
pane.
The ./src/taskpane/taskpane.css file contains the CSS that's applied to
content in the task pane.
The ./src/taskpane/taskpane.js file contains the Office JavaScript API code
that facilitates interaction between the task pane and the Office client
application.

Try it out
1. Navigate to the root folder of the project.

command line

cd "My Office Add-in"

2. Complete the following steps to start the local web server and sideload your
add-in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're
developing. If you're prompted to install a certificate after you run one of
the following commands, accept the prompt to install the certificate that
the Yeoman generator provides. You may also have to run your command
prompt or terminal as an administrator for the changes to be made.

 Tip
If you're testing your add-in on Mac, run the following command before
proceeding. When you run this command, the local web server starts.

command line

npm run dev-server

To test your add-in in Word, run the following command in the root
directory of your project. This starts the local web server (if it's not
already running) and opens Word with your add-in loaded.

command line

npm start

To test your add-in in Word on a browser, run the following command in


the root directory of your project. When you run this command, the local
web server starts. Replace "{url}" with the URL of a Word document on
your OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single


quotation marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798Bpuhwl

uxCMfF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP
If your add-in doesn't sideload in the document, manually sideload it by
following the instructions in Manually sideload add-ins to Office on the
web.

3. In Word, if the "My Office Add-in" task pane isn't already open, open a new
document, choose the Home tab, and then choose the Show Taskpane button
on the ribbon to open the add-in task pane.

4. At the bottom of the task pane, choose the Run link to add the text "Hello
World" to the document in blue font.

Next steps
Congratulations, you've successfully created a Word task pane add-in! Next, learn
more about the capabilities of a Word add-in and build a more complex add-in by
following along with the Word add-in tutorial.

Word add-in tutorial

Code samples
Word "Hello world" add-in : Learn how to build a simple Office Add-in with only
a manifest, HTML web page, and a logo.

See also
Office Add-ins platform overview
Develop Office Add-ins
Word add-ins overview
Word add-in code samples
Word JavaScript API reference
Using Visual Studio Code to publish
Tutorial: Create a Word task pane add-in
Article • 03/14/2023

In this tutorial, you'll create a Word task pane add-in that:

" Inserts a range of text


" Formats text
" Replaces text and inserts text in various locations
" Inserts images, HTML, and tables
" Creates and updates content controls

 Tip

If you've already completed the Build your first Word task pane add-in quick start,
and want to use that project as a starting point for this tutorial, go directly to the
Insert a range of text section to start this tutorial.

Prerequisites
Node.js (the latest LTS version).

The latest version of Yeoman and the Yeoman generator for Office Add-ins. To
install these tools globally, run the following command via the command prompt.

command line

npm install -g yo generator-office

7 Note

Even if you've previously installed the Yeoman generator, we recommend you


update your package to the latest version from npm.

Office connected to a Microsoft 365 subscription (including Office on the web).

7 Note

If you don't already have Office, you can join the Microsoft 365 developer
program to get a free, 90-day renewable Microsoft 365 subscription to use
during development.

Create your add-in project


Run the following command to create an add-in project using the Yeoman generator.

command line

yo office

7 Note

When you run the yo office command, you may receive prompts about the data
collection policies of Yeoman and the Office Add-in CLI tools. Use the information
that's provided to respond to the prompts as you see fit.

When prompted, provide the following information to create your add-in project.

Choose a project type: Office Add-in Task Pane project


Choose a script type: JavaScript
What do you want to name your add-in? My Office Add-in
Which Office client application would you like to support? Word

After you complete the wizard, the generator creates the project and installs supporting
Node components.

 Tip
You can ignore the next steps guidance that the Yeoman generator provides after
the add-in project's been created. The step-by-step instructions within this article
provide all of the guidance you'll need to complete this tutorial.

Insert a range of text


In this step of the tutorial, you'll programmatically test that your add-in supports the
user's current version of Word, and then insert a paragraph into the document.

Code the add-in


1. Open the project in your code editor.

2. Open the file ./src/taskpane/taskpane.html. This file contains the HTML markup
for the task pane.

3. Locate the <main> element and delete all lines that appear after the opening
<main> tag and before the closing </main> tag.

4. Add the following markup immediately after the opening <main> tag.

HTML

<button class="ms-Button" id="insert-paragraph">Insert


Paragraph</button><br/><br/>

5. Open the file ./src/taskpane/taskpane.js. This file contains the Office JavaScript API
code that facilitates interaction between the task pane and the Office client
application.

6. Remove all references to the run button and the run() function by doing the
following:

Locate and delete the line document.getElementById("run").onclick = run; .

Locate and delete the entire run() function.

7. Within the Office.onReady function call, locate the line if (info.host ===
Office.HostType.Word) { and add the following code immediately after that line.

Note:

This code adds an event handler for the insert-paragraph button.


The insertParagraph function is wrapped in a call to tryCatch (both
functions will be added in the next step). This allows any errors generated by
the Office JavaScript API layer to be handled separately from your service
code.

JavaScript

// Assign event handlers and other initialization logic.


document.getElementById("insert-paragraph").onclick = () =>
tryCatch(insertParagraph);

8. Add the following functions to the end of the file. Note:

Your Word.js business logic will be added to the function passed to Word.run .
This logic doesn't execute immediately. Instead, it's added to a queue of
pending commands.

The context.sync method sends all queued commands to Word for


execution.

The tryCatch function will be used by all the functions interacting with the
workbook from the task pane. Catching Office JavaScript errors in this fashion
is a convenient way to generically handle uncaught errors.

JavaScript

async function insertParagraph() {


await Word.run(async (context) => {

// TODO1: Queue commands to insert a paragraph into the


document.

await context.sync();
});
}

/** Default helper for invoking an action and handling errors. */


async function tryCatch(callback) {
try {
await callback();
} catch (error) {
// Note: In a production add-in, you'd want to notify the user
through your add-in's UI.
console.error(error);
}
}
9. Within the insertParagraph() function, replace TODO1 with the following code.
Note:

The first parameter to the insertParagraph method is the text for the new
paragraph.

The second parameter is the location within the body where the paragraph
will be inserted. Other options for insert paragraph, when the parent object is
the body, are "End" and "Replace".

JavaScript

const docBody = context.document.body;


docBody.insertParagraph("Office has several versions, including Office
2016, Microsoft 365 subscription, and Office on the web.",
Word.InsertLocation.start);

10. Save all your changes to the project.

Test the add-in


1. Complete the following steps to start the local web server and sideload your add-
in.

7 Note

Office Add-ins should use HTTPS, not HTTP, even while you're developing. If
you're prompted to install a certificate after you run one of the following
commands, accept the prompt to install the certificate that the Yeoman
generator provides. You may also have to run your command prompt or
terminal as an administrator for the changes to be made.

 Tip

If you're testing your add-in on Mac, run the following command in the root
directory of your project before proceeding. When you run this command, the
local web server starts.

command line

npm run dev-server


To test your add-in in Word, run the following command in the root directory
of your project. This starts the local web server (if it isn't already running) and
opens Word with your add-in loaded.

command line

npm start

To test your add-in in Word on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of a Word document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM
fF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. In Word, if the "My Office Add-in" task pane isn't already open, choose the Home
tab, and then choose the Show Taskpane button on the ribbon to open the add-in
task pane.
3. In the task pane, choose the Insert Paragraph button.

4. Make a change in the paragraph.

5. Choose the Insert Paragraph button again. Note that the new paragraph appears
above the previous one because the insertParagraph method is inserting at the
start of the document's body.

Format text
In this step of the tutorial, you'll apply a built-in style to text, apply a custom style to
text, and change the font of text.

Apply a built-in style to text


1. Open the file ./src/taskpane/taskpane.html.
2. Locate the <button> element for the insert-paragraph button, and add the
following markup after that line.

HTML

<button class="ms-Button" id="apply-style">Apply Style</button><br/>


<br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the insert-paragraph button, and add the following code after that line.

JavaScript

document.getElementById("apply-style").onclick = () =>
tryCatch(applyStyle);

5. Add the following function to the end of the file.

JavaScript

async function applyStyle() {


await Word.run(async (context) => {

// TODO1: Queue commands to style text.

await context.sync();
});
}

6. Within the applyStyle() function, replace TODO1 with the following code. Note
that the code applies a style to a paragraph, but styles can also be applied to
ranges of text.

JavaScript

const firstParagraph = context.document.body.paragraphs.getFirst();


firstParagraph.styleBuiltIn = Word.Style.intenseReference;

Apply a custom style to text


1. Open the file ./src/taskpane/taskpane.html.
2. Locate the <button> element for the apply-style button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="apply-custom-style">Apply Custom


Style</button><br/><br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the apply-style button, and add the following code after that line.

JavaScript

document.getElementById("apply-custom-style").onclick = () =>
tryCatch(applyCustomStyle);

5. Add the following function to the end of the file.

JavaScript

async function applyCustomStyle() {


await Word.run(async (context) => {

// TODO1: Queue commands to apply the custom style.

await context.sync();
});
}

6. Within the applyCustomStyle() function, replace TODO1 with the following code.
Note that the code applies a custom style that does not exist yet. You'll create a
style with the name MyCustomStyle in the Test the add-in step.

JavaScript

const lastParagraph = context.document.body.paragraphs.getLast();


lastParagraph.style = "MyCustomStyle";

7. Save all your changes to the project.

Change the font of text


1. Open the file ./src/taskpane/taskpane.html.
2. Locate the <button> element for the apply-custom-style button, and add the
following markup after that line.

HTML

<button class="ms-Button" id="change-font">Change Font</button><br/>


<br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the apply-custom-style button, and add the following code after that line.

JavaScript

document.getElementById("change-font").onclick = () =>
tryCatch(changeFont);

5. Add the following function to the end of the file.

JavaScript

async function changeFont() {


await Word.run(async (context) => {

// TODO1: Queue commands to apply a different font.

await context.sync();
});
}

6. Within the changeFont() function, replace TODO1 with the following code. Note
that the code gets a reference to the second paragraph by using the
ParagraphCollection.getFirst method chained to the Paragraph.getNext method.

JavaScript

const secondParagraph =
context.document.body.paragraphs.getFirst().getNext();
secondParagraph.font.set({
name: "Courier New",
bold: true,
size: 18
});

7. Save all your changes to the project.


Test the add-in
1. If the local web server is already running and your add-in is already loaded in
Word, proceed to step 2. Otherwise, start the local web server and sideload your
add-in.

To test your add-in in Word, run the following command in the root directory
of your project. This starts the local web server (if it isn't already running) and
opens Word with your add-in loaded.

command line

npm start

To test your add-in in Word on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of a Word document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document
https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document


https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp

npm run start:web -- --document https://contoso-my.sharepoint-


df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.
2. If the add-in task pane isn't already open in Word, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.

3. Be sure there are at least three paragraphs in the document. You can choose the
Insert Paragraph button three times. Check carefully that there's no blank
paragraph at the end of the document. If there is, delete it.

4. In Word, create a custom style named "MyCustomStyle". It can have any


formatting that you want.

5. Choose the Apply Style button. The first paragraph will be styled with the built-in
style Intense Reference.

6. Choose the Apply Custom Style button. The last paragraph will be styled with your
custom style. (If nothing seems to happen, the last paragraph might be blank. If so,
add some text to it.)

7. Choose the Change Font button. The font of the second paragraph changes to 18
pt., bold, Courier New.

Replace text and insert text


In this step of the tutorial, you'll add text inside and outside of selected ranges of text,
and replace the text of a selected range.

Add text inside a range


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the change-font button, and add the following
markup after that line.

HTML
<button class="ms-Button" id="insert-text-into-range">Insert
Abbreviation</button><br/><br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the change-font button, and add the following code after that line.

JavaScript

document.getElementById("insert-text-into-range").onclick = () =>
tryCatch(insertTextIntoRange);

5. Add the following function to the end of the file.

JavaScript

async function insertTextIntoRange() {


await Word.run(async (context) => {

// TODO1: Queue commands to insert text into a selected range.

// TODO2: Load the text of the range and sync so that the
// current range text can be read.

// TODO3: Queue commands to repeat the text of the original


// range at the end of the document.

await context.sync();
});
}

6. Within the insertTextIntoRange() function, replace TODO1 with the following code.
Note:

The function is intended to insert the abbreviation ["(M365)"] into the end of
the Range whose text is "Microsoft 365". It makes a simplifying assumption
that the string is present and the user has selected it.

The first parameter of the Range.insertText method is the string to insert


into the Range object.

The second parameter specifies where in the range the additional text should
be inserted. Besides "End", the other possible options are "Start", "Before",
"After", and "Replace".
The difference between "End" and "After" is that "End" inserts the new text
inside the end of the existing range, but "After" creates a new range with the
string and inserts the new range after the existing range. Similarly, "Start"
inserts text inside the beginning of the existing range and "Before" inserts a
new range. "Replace" replaces the text of the existing range with the string in
the first parameter.

You saw in an earlier stage of the tutorial that the insert* methods of the
body object don't have the "Before" and "After" options. This is because you
can't put content outside of the document's body.

JavaScript

const doc = context.document;


const originalRange = doc.getSelection();
originalRange.insertText(" (M365)", Word.InsertLocation.end);

7. We'll skip over TODO2 until the next section. Within the insertTextIntoRange()
function, replace TODO3 with the following code. This code is similar to the code
you created in the first stage of the tutorial, except that now you are inserting a
new paragraph at the end of the document instead of at the start. This new
paragraph will demonstrate that the new text is now part of the original range.

JavaScript

doc.body.insertParagraph("Original range: " + originalRange.text,


Word.InsertLocation.end);

Add code to fetch document properties into the task


pane's script objects
In all previous functions in this tutorial, you queued commands to write to the Office
document. Each function ended with a call to the context.sync() method which sends
the queued commands to the document to be executed. But the code you added in the
last step calls the originalRange.text property, and this is a significant difference from
the earlier functions you wrote, because the originalRange object is only a proxy object
that exists in your task pane's script. It doesn't know what the actual text of the range in
the document is, so its text property can't have a real value. It's necessary to first fetch
the text value of the range from the document and use it to set the value of
originalRange.text . Only then can originalRange.text be called without causing an

exception to be thrown. This fetching process has three steps.


1. Queue a command to load (that is, fetch) the properties that your code needs to
read.

2. Call the context object's sync method to send the queued command to the
document for execution and return the requested information.

3. Because the sync method is asynchronous, ensure that it has completed before
your code calls the properties that were fetched.

The following step must be completed whenever your code needs to read information
from the Office document.

1. Within the insertTextIntoRange() function, replace TODO2 with the following code.

JavaScript

originalRange.load("text");
await context.sync();

When you're done, the entire function should look like the following:

JavaScript

async function insertTextIntoRange() {


await Word.run(async (context) => {

const doc = context.document;


const originalRange = doc.getSelection();
originalRange.insertText(" (M365)", Word.InsertLocation.end);

originalRange.load("text");
await context.sync();

doc.body.insertParagraph("Original range: " + originalRange.text,


Word.InsertLocation.end);

await context.sync();
});
}

Add text between ranges


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the insert-text-into-range button, and add the
following markup after that line.
HTML

<button class="ms-Button" id="insert-text-outside-range">Add Version


Info</button><br/><br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the insert-text-into-range button, and add the following code after that line.

JavaScript

document.getElementById("insert-text-outside-range").onclick = () =>
tryCatch(insertTextBeforeRange);

5. Add the following function to the end of the file.

JavaScript

async function insertTextBeforeRange() {


await Word.run(async (context) => {

// TODO1: Queue commands to insert a new range before the


// selected range.

// TODO2: Load the text of the original range and sync so that
the
// range text can be read and inserted.

});
}

6. Within the insertTextBeforeRange() function, replace TODO1 with the following


code. Note:

The function is intended to add a range whose text is "Office 2019, " before
the range with text "Microsoft 365". It makes an assumption that the string is
present and the user has selected it.

The first parameter of the Range.insertText method is the string to add.

The second parameter specifies where in the range the additional text should
be inserted. For more details about the location options, see the previous
discussion of the insertTextIntoRange function.

JavaScript
const doc = context.document;
const originalRange = doc.getSelection();
originalRange.insertText("Office 2019, ", Word.InsertLocation.before);

7. Within the insertTextBeforeRange() function, replace TODO2 with the following


code.

JavaScript

originalRange.load("text");
await context.sync();

// TODO3: Queue commands to insert the original range as a


// paragraph at the end of the document.

// TODO4: Make a final call of context.sync here and ensure


// that it runs after the insertParagraph has been queued.

8. Replace TODO3 with the following code. This new paragraph will demonstrate the
fact that the new text is not part of the original selected range. The original range
still has only the text it had when it was selected.

JavaScript

doc.body.insertParagraph("Current text of original range: " +


originalRange.text, Word.InsertLocation.end);

9. Replace TODO4 with the following code.

JavaScript

await context.sync();

Replace the text of a range


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the insert-text-outside-range button, and add
the following markup after that line.

HTML

<button class="ms-Button" id="replace-text">Change Quantity


Term</button><br/><br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the insert-text-outside-range button, and add the following code after that
line.

JavaScript

document.getElementById("replace-text").onclick = () =>
tryCatch(replaceText);

5. Add the following function to the end of the file.

JavaScript

async function replaceText() {


await Word.run(async (context) => {

// TODO1: Queue commands to replace the text.

await context.sync();
});
}

6. Within the replaceText() function, replace TODO1 with the following code. Note
that the function is intended to replace the string "several" with the string "many".
It makes a simplifying assumption that the string is present and the user has
selected it.

JavaScript

const doc = context.document;


const originalRange = doc.getSelection();
originalRange.insertText("many", Word.InsertLocation.replace);

7. Save all your changes to the project.

Test the add-in


1. If the local web server is already running and your add-in is already loaded in
Word, proceed to step 2. Otherwise, start the local web server and sideload your
add-in.
To test your add-in in Word, run the following command in the root directory
of your project. This starts the local web server (if it isn't already running) and
opens Word with your add-in loaded.

command line

npm start

To test your add-in in Word on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of a Word document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM
fF1WZQj3VYhYQ?e=F4QM1R

npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. If the add-in task pane isn't already open in Word, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.

3. In the task pane, choose the Insert Paragraph button to ensure that there's a
paragraph at the start of the document.
4. Within the document, select the phrase "Microsoft 365 subscription". Be careful not
to include the preceding space or following comma in the selection.

5. Choose the Insert Abbreviation button. Note that " (M365)" is added. Note also
that at the bottom of the document a new paragraph is added with the entire
expanded text because the new string was added to the existing range.

6. Within the document, select the phrase "Microsoft 365". Be careful not to include
the preceding or following space in the selection.

7. Choose the Add Version Info button. Note that "Office 2019, " is inserted between
"Office 2016" and "Microsoft 365". Note also that at the bottom of the document a
new paragraph is added but it contains only the originally selected text because
the new string became a new range rather than being added to the original range.

8. Within the document, select the word "several". Be careful not to include the
preceding or following space in the selection.

9. Choose the Change Quantity Term button. Note that "many" replaces the selected
text.

Insert images, HTML, and tables


In this step of the tutorial, you'll learn how to insert images, HTML, and tables into the
document.

Define an image
Complete the following steps to define the image that you'll insert into the document in
the next part of this tutorial.

1. In the root of the project, create a new file named base64Image.js.


2. Open the file base64Image.js and add the following code to specify the Base64-
encoded string that represents an image.

JavaScript

export const base64Image =

"iVBORw0KGgoAAAANSUhEUgAAAZAAAAEFCAIAAABCdiZrAAAACXBIWXMAAAsSAAALEgHS3X
78AAAgAElEQVR42u2dzW9bV3rGn0w5wLBTRpSACAUDmDRowGoj1DdAtBA6suksZmtmV3Qj+
i8w3XUB00X3pv8CX68Gswq96aKLhI5bCKiM+gpVphIa1qQBcQbyQB/hTJlpOHUXlyEvD885
vLxfvCSfH7KIJVuUrnif+z7nPOd933v37h0IIWQe+BEvASGEgkUIIRQsQggFixBCKFiEEEL
BIoRQsAghhIJFCCEULEIIBYsQQihYhBBCwSKEULAIIYSCRQghFCxCCAWLEEIoWIQQQsEihC
wQCV4CEgDdJvYM9C77f9x8gkyJV4UEznvs6U780rvAfgGdg5EPbr9CyuC1IbSEJGa8KopqB
WC/gI7Fa0MoWCROHJZw/lxWdl3isITeBa8QoWCRyOk2JR9sVdF+qvwnnQPsF+SaRSEjFCwS
Cr0LNCo4rYkfb5s4vj/h33YOcFSWy59VlIsgIRQs4pHTGvYMdJvIjupOx5Ir0Tjtp5K/mTK
wXsSLq2hUWG0R93CXkKg9oL0+ldnFpil+yhlicIM06NA2cXgXySyuV7Fe5CUnFCziyQO2qm
g8BIDUDWzVkUiPfHY8xOCGT77EWkH84FEZbx4DwOotbJpI5nj5CQWLTOMBj8votuRqBWDP8
KJWABIr2KpLwlmHpeHKff4BsmXxFQmhYBGlBxzoy7YlljxOcfFAMottS6JH+4Xh69IhEgoW
cesBNdVQozLyd7whrdrGbSYdIqFgkQkecMD4epO9QB4I46v4tmbtGeK3QYdIKFhE7gEHjO/
odSzsfRzkS1+5h42q+MGOhf2CuPlIh0goWPSAogcccP2RJHI1riP+kQYdVK9Fh0goWPSAk8
2a5xCDG4zPJaWTxnvSIVKwKFj0gEq1go8QgxtUQQeNZtEhUrB4FZbaA9pIN+98hhhcatbNp
qRoGgRKpdAhUrDIMnpAjVrpJSNApK/uRi7pEClYZIk84KDGGQ+IBhhicMP6HRg1ycedgVI6
RELBWl4POFCr8VWkszpe3o76G1aFs9ws+dMhUrDIInvAAeMB0ZBCDG6QBh2kgVI6RAoWWRY
PqBEI9+oQEtKgg3sNpUOkYJGF8oADxgOioUauXKIKOkxV99EhUrDIgnhAG+mCUQQhBpeaNb
4JgOn3AegQKVhkvj2gjXRLLrIQgxtUQYdpNYsOkYJF5tUDarQg4hCDS1u3VZd83IOw0iFSs
MiceUCNWp3WYH0Wx59R6ls9W1c6RAoWmQ8PaCNdz55hiMEN4zsDNhMDpXSIFCwylx5Qo1a9
C3yVi69a2ajCWZ43NOkQKVgkph5wwHi+KQ4hBs9SC9+RMTpEChaJlwfUFylWEafP5uMKqII
OPv0sHSIFi8TFAzpLiXxF/KCbdetEGutFUSa6TXQsdKypv42UgZQhfrWOhbO6q8nPqqCD/z
U4OkQKFpm9B7SRbrTpQwzJHNaL/VHyiRVF0dfC2xpOzMnKlUgjW0amhGRW/ZM+w5sqzuqTN
Wtb9nKBZDLoEClYZGYe0EYaENWHGDaquHJv5CPnz/H9BToWkjmsFkTdOX0GS22p1ovYNEdU
r9vCeR3dJlIG1gojn2o8RKPiRX+D0iw6RAoWmYEH1HioiQZqq47VW32dalUlfi1fQf7ByEd
UQpMpYfOJ46UPcFweKaMSaWyaWL8z/Mibxzgqe3G4CC6pT4dIwSLReUCNWrkJMdjh8sMSuk
1d3bReRGb3hy97iS/SEl+5bQ0LqM4B9gvytaptC6kbwz++vD3ZG0r3EBDoWUg6RAoWCd0D9
isXReTKTYghZbhdUB/UYlKV2TSHitZtYc9QrqynDGy/GnGg+4XJr779ShJ0gNdAKR3i/PAj
XoIZe8BGBS+uhqtWAF4VXUWu3G//ORVqdVRiEumhWgFoVHT7gB1LnFAvVaJxYZJ+qx/XRuo
1X0+RFqzPsF/QFZuEgrVcHnDPCGbFylnajN/wAZZvqgpR8IzO275tTvjnwl/4sORC6C9xWJ
LoYCKNrbpuR3Jazp/jxdUJmksoWIvvAfcLsD4LuLfn5hOJhWlVQ+lyNZDFcUl636GY5/Wpy
zo3FRZ+WBeT1JhpGDVlIMMbjYfYM3Ba4zuXgkUPGBD5B5Kl6LaJ4/uh/CCDTvDjW4ROxZm4
gj7+dwZLY24067AkF9OtesCaRYdIwaIHDIzMrmSzv2NNTgl4fLlSXw6kjs8pWN+FfHu3n8p
/xpSBjWrwL0eHSMGiB/TL+h1JnNJ+xTA6MawXh1ogTWA5S5tvLS8vMVUM6s1j+TKZEASjQ6
RgkVl6wH4pcUM+zs8qBq9WyRyMGozP+5J0/nzygrrLSkS4ONPmNg/vyr1npiQG9+kQKVhkB
h5woFbSI8EuQwxTkS1j2xoG0zsHeBVcRsl/RNMqyoMOG9WRjAUd4pzD4GhoHjDsMIEqchX4
8JuUgU1zJN+kSa4D+LnjHfXiqqsa5Oejb8J/fs9TAZjFtiXXvgADpaqXZsqUFRY94NRq1ag
ErFbrRWzVR9Tq9JlOrWy75NncCf982n+o+sYCDJTSIVKw6AGnRhoQbZsBv3S+MlyxAtC7xP
F9WMUJDsi5M+gmVCWImpvolorOgXzTMPBAKR0iBWvuPWB4+4CiWj2Rz3MPcFSXHb90Nmawb
WDLRVZAc2pHZTkF2fWDKugQRqBUCvcQKVj0gI6qRxYQtfvGBIUdvHQ2fmk/VR7fk5Q5jr+2
fmfygrpTfM+fu8qa6lEFHcIIlGocolWkQwwcLrr79oBB9YRxg7SDXbDjJISue71LHJWnrno
+vRh+BX2Xq2QOO6+Hf3TTXsYl43M3BhVcZFNjEyvIluUNvAgrrIX1gINqRdpvM0C1EhatbB
vowaM5neOVe/L2VX176/jip88CUysAhyV5SRheoFRSfV+i8RAvckH+XKyweBW8qNWeEelEP
1XkKqgQw3j/T3sxyNv6cSKNm02xA3KrOvLV1gq4Xh1u3vUusWcE7KESK7jZlHvSoDqU+q/4
CAUrItomWtUoRvup1KpRCWxb0KiNqFXvcoreWCem/ETh+ILRYJnvJzlxz+7wrt/l9qkuHUI
IrMk9bxaZEjIltl2mYMWDjoVWFae1sAouVeQq2LUYZwfRaVG1dR9PnKp802EpxG016TCOgZ
sOb6tk9RayZVZVFKwZ8cff4b/+Htcq8sd17wInJt5UA17SUqnVWR0vbwf5Qn5KgPO6bo0mU
0K2LJetbgtvqjgxQw8uqcbthDH+OrHS/5FV19MuJDXreoSCFQC9C3yxisQK8hVk1dteZ3W8
qQY2VFm68OF/emj0JNJ430DKQCKN3gU6FrrNSHf9VaMrfI68F+ynXVKpkhxndRyX0TlQzv4
hFKyABWuwMPGROWxiJ6kdmmibaJu+7gTpPRbgDbZsqJa9/T8AMrvIlnWx/m4Tx+XhY4yC5R
XGGjzRbeHlbd3ZsWQO+Qp2mth84nFtSBoQtS0M1cobqqCD50BpMovrj/Dpufyk1OBXZueKg
yq6KVjEI/bZMf3ef6aErTp2XiOzO8UtIe0gCuCoHMWm5MLWyJfK09HTdihdvwPjc+w0J4wv
bJv4KhfF2VIKFnHLm8f4KjfhkF0yh00TN5vYfDJ510wVED0qR7ENv7Sa5SZQmlhB/gF2XsO
oTdj+O6tjz8Dh3Tlbaow9XMNy/153rGGpDIJ+Ycv5bm6bcvVR5YaiPFCy8Kze6s+4lj4VpI
HS1Vv4sORqa09YrlL5fa5hUbBmLFiDd/am6Soi0LtAqzqyMK9Sq8BDDEQVdMBooDSxgvXih
AV14RfqxgBSsChYcREsmyv3lImtcU5raJs4q8sjV/MYYpgLrj9SxlP2C/iuiXxFl1EYL4GP
ym5/TRQsCla8BKu/3qFNbLl80a9yVKuwUIWzpmKQrnIPBcsrXHQPT+AucXzf70l91lahclT
2FV7tNmEV8fI2t24jI8FLEC52Ysv9wpbAtsVLGNNy2+VyFWGFNX+4SWyReYHpKgrWUuAmsU
XiDNNVFKwlsxJBLGyRGVh7LlfFAq5hzeTd38LL27oo0ABpnykSIG766pzWYH3GS0XBWvJr7
yLg8/1F1J18l4pk1lXuhM1CaQkJPixN/jvXKlGMpVpa8u7CvSkj9CGshIIV92e7tOvxeBXG
hGFIrN6Sp0ZPa5Jw1gfsdEzBWmbGb4BuE4d3JbdKtszHe1jllZTjsqTBvJtymFCwFpbxpRM
77nAouzE+MnnBAiazK++rYZ9Flw4B4mODgrWkpG5I1nHf1gDFrPa1gveRNmQc+5jnOL2L/p
DqzoGkN2mArpChFgrWXD3eS5J38KDJjDTKsMG4aaDlrXTjr1UdJkJPTLpCChYBAEmzSqcHO
X8utySZXV65AFBFGezjgULBS1dIwaIflDzehVVeVZHFiIN/VFEGoZtVtyUxbtwrpGDNDb3f
heUH26Z4Nq3bkhw5TKT9dtciqihDtynpWN2mK6RgzS/vemH5QemU9kZF0tohX6Er8VteSTm
WPQlOZa5w4gwRQsFaZD/Yu5APLOhdyvs6XOfqu+faVhFlOKsrfwXjRRZHzFOwlumeKbkqr2
xaVUmOdL3IiEPA5ZXmhPn4b2edy1gUrOVh/O2uaY/Vu2TEITi1eiCPMrRNnD9XC9Yz0Zgnc
3SFFKxl9YPd5oT+Su2nkgQjIw7TklhR7ldMbOBzQldIwVpOxu+Z8SWScY7K8iKLEQf3bFTl
UYZWdZjXVT4zTLrCGD16eAlm6QfdCJZ9WEdYLbYjDmG3FU/mRqoJD90EV3+Ga//o5aUPS77
m2QiFrbQm6l24+ok6B+g2R0pj2xWy9SgFa6HV6o74kO9Ykx/vNsdlyficfGVkanRIgpV/4E
uw3v/E4xZBMheYYKn2VZ0HcfS0quK6YaaE4/t8U9MSLlN55X4aRedAXouxVZab54Q0ytBtT
nH933KvkIJFwdIEGsaRVjeZEiMOHsurRmWKyTfdlrj1wb1CCtZy+cHT2nSjorotuWbFvMj6
w6/xhxN81xL/G/zsvY7ks384wfdBDHBURRmkB3EmukIBHpOaBVzDmlF55Wa5ffyeyZZF4Vs
rILM79e0XGb/5JX7zS8nHt+r92rDz79gvhPPWVkcZpF0S9cgTpHf51maFtQSCpTqOo0d1WC
fPQRUyVFGGs7ouKaq5+IJmJdJYv8PLTMFaDj/ojcZDyd5ZMkd7IqKKMsDHqEcGsihYS+oHT
0zvX016v3FQhYBqrV1/EGeCKxw7pkPBomAtGokV8W3dbXq/Z6A4rMNpYE5Wb8mjDPA9SZuu
cOb3Ey9B6OVVUH5wwFEZW3Xxg5kSTkxfUmjj/MrCdz7+ovpvclxYo2HTVKqVz5xtqyo6zfW
il+VIQsGaGz/4xnevBelhHQD5Cl7eDqA88fCpcX6cns0Fv3JPHmUQWrZ7Y/yYDvcKaQkX2Q
+6P46j5+uS5IN2xCEO9C7xrTWbC36toiyOpgq+KS25SVfICmtpyqsTM5ivbA/7HN8Iy1emj
qQKOGu0lIHrj+SfEhD+5mFJ0t85AlQDJrrNwA6Kt01xuZCukIK1sILlIS+qolGRLJDZEQc/
N6dmxqfmU85dufbTANbpPKCa3wXfa+3Co6JjIWX4coWzWt2jJSRT+EGftc/4nSNdlMmWo86
R5ivDg3XdlryBVwR8ZCrVIdiTACdjrnBaJx7g24CCRcIqrwKvO1pVifNKpCPtoZwyRlrQfD
0jM6iJMgQuoEyQUrAWX7B6F8ELVu8S38jMTqYUXS8BZ4ag8VBnGyP7NgQb6z/qMX7ZhV/le
pGnoyhYMeP/vouRHxzw5rG80V0008CcZrBzEORS0VSoogxQDBz0D6fpULAWSrAi8IPDukYm
E2uF0LfbBTPooQVCIGiiDG0zrEbG7ac8pkPBWiCEwEG3GeLOd/up3IiFXWQ5Xdjx/ZntfKm
iDEC4FR9dIQVrQUhmxQXgsLf5pXem0JE9PDN4/jyAELnnS62JMoTa8P7EpCukYC0EH4QZv5
JiH9YZJ6SIg9MM9i5nZgY1VWQgB3EmXnNh9ZCCRcGaSz4cvYE7VhQjoaSHdUKKODjNYIDzu
KZl9ZZSI76pRJF1oiukYC2CH3TGoBHccRw99mGdcQKPODjN4Omz2YTabVRa3G3izeMovoHx
c+wssihYc+8H30Z1Szcq8tBmgKvv8TGDmV3xweC8DtEwPk2HgkXBmm8/eFoLd+lXuH+kCzc
BRhycZtAqzibUDiCxoiyvzuqRjuQQyuf1Ilu/UrDm2Q9G7Jikh3WCKrKcZvDN41BC7X/+Nz
Bq+Nk3yurJZnx6UPTllap8/oBFFgVrfv1gxILVu5QfnUvmcOWe3y8+CBB0DuRHgvyI1F//C
p9+i7/6Bdbv4E/zuv5/yayyH3QYB3EmVrXCr/jDEu8DCtZ8+sG2OYNz+e2n8m27a76ngQ3+
eYDtrlZv9UXqp3+BRMrVP9FUi1/PQiwEwUoZdIUULPrBaZAeoAtqUEXj4SzbOWmiDG0zuuV
C4bcsyDddIQVrDhCO43iblhrMLfRMmSP1+fCP4ITz//4WHUuZ7dpQJ0VndfR6vHkDXSEFa/
4E68Sc5Tejuns/Mn3dmVY4tUOvg9//J379C/zbTdQ/wN7HcsHSRBla1dmUV3SFFKy5JHVD7
HAS9nEcPefP5YZ0rTDd8BtBBIMKtf/oJwDwP/+N869w/Hf44n3861/iP/4WFy+U/0QTZfB/
EGe9qOyo5bKkFa4MXWE4sKd7OOVVtxnFcRw9x2X5cs+miRdXXX2Fb62RwRMB5hga/4Df/2o
6+dNEGfwfxLle7ddEnqOwp7WRY9gfliJK27PCIh4f0YJDmTmqwzruIw69C5zVh/8FyG//aT
q10nRl8H8QJ1/pq1VmVzKIyCXCpaYrpGDNkx98W4vFN3ZUlucPrlXm7JhueE2vEukRKfS8k
do5EDdPPWsfoWBF6gfP6gEvAKcM5Cv9/zIl5a0rKZEu5bVeUBGHaFi9pbz5/R/E2aiOaHcy
611oTkwKVti89+7dO14Fd49QC3sfyz+183qkwjosBXacba2AfEVcJrdlSHUKR9SmFdxsyjX
uRW6WO2vu+eRL5USc/YKvaHvKwPYriZV+kfPy1ZJZ7Iz63D1DuZT5c953rLBi4gcDyYsmc9
g08cmXkk29xAryD3CzqbyNBXVTzbnyE3GIrnrdVf6YpzW/B3Gc247dVl++PRdZ3Za40qf5O
rM6N07Boh8U7yKfO1a2VO28njCeM7GCT750dWupDuv4iThEQ2JFZ119TsRZL478+F+Xhsth
nv2ysPSu6TbzLYc/U7BmgvCm9Bm/ShnYtiRS1TlA4yEaD3H+fEQQN5+46imq2q3fqMb62mb
Lyvld/g/iOM8k2mcDBl/Tc5ElFNfJXHQDIilYxIVa3Rm5o3wex0kZ2KqL+3ftp3hxFXsGGh
U0Ktgv4Is0Xt4eytaVe5MrAlXT95Qx9Zj1yNBEGXoXk+c5pwydZR5EGWzXPCjWfBZZvUvxi
cWldwrWbHjXm1xe+Vy92jRH1KpzgL2P5U3Tz+ojp2TyD5SVyADV9r+wTRYfNFGGVnWC706k
YdTwyZfYqktkS4gytKrDKzxw9EEVWexBSsGaDb3fTRYsP3lRofl65wD7BV1fBGFH302RJbW
rwt0bEzRRBjcHca79UECt3pLIllOju60RKXd+cW9F1umzkQV1ukIKVoz8oLME8Hkcx6l9vU
vsFyZvJDnv29XC5JdQFVlOfxSf8krFUXlCeZXMiWLnlC3BBY+30BqUb56LrBO6QgpWHAUr0
OV2Z49NVUJdoGMNb103iqNq+o7wx0RPV2yqowzd5uSMW7eJPUOymDiQLWc1NL6057/Icr9X
SChY8ypYmnUQvWYNcBPLUk3WEfb4Z0ggUYZuE1YR1meSWmxgBp1r7SrF8VZkdQ5Glh2Tubj
HRyhYS+cHO5bfXXan9LhPFTrvBDfHiVWHdRCbiIMmynBWn24T9rSGr3LKo9HfXygX9Z11nL
ciS7jIbOlHwYpXeeW/PcP3DpHSz4xRlVQu+x84N8WcxCHikFjR7QB4OOdsByBe3pYsLyaz2
H6FTVOuj4PX8lZkveVeIQUrzoI10cQl0hNaxDkrLDfbdon0yMKT+0Mqvcv4Rhw2qsqqx89B
nLM69gx5CZzZxc5ryev6LLKEGauJdGCjISlYxK8fnHgcZ72Im01dh1+MtsfL7E7OVW1UR/b
LT8wpvn/VYZ3ZRhxSN3S1jM+DOGuF4b6EcFoAwJV7uNkUk1+DqtlbkSUU3SyyKFhzU14Zn/
crF826eO9iZP9r09S1kcmWR+zb6bOpl/xVh3VmGHHQ7FT6b9k+qJJ6l3hVxJ4h7jYOjpQPt
KljDWs6D0UWE6QUrFiQWBl53gpCI7d7Pyyg6B/UDUer39Vb2KpLNCuRxkYV1x+NfHEPjX1V
h3Uwo4jD+h2lmvufiOM85m235ek2cVjCy9uizUysYPMJdn6QLT8rWcI0HbpCCtZ8lFdOd5C
6oSuy7LvIaZGcD/y1AjIlbFsjDY57l97HmqpM1kwiDvryymcDDLuNcrclbpKe1bFfwOFd8e
sns9h80k9s+SmyGMgKGjbwc81ZvT+Rwfh85J3npodcIo2bzb4rPH+O/cIEQRQOFWqe4frjO
xPZfCIvHAY/bDTkHyjlwE6BBjVAO5nTLd7lH8i+gdbQIx/endp6f3o+LJN7F/hitf//mq6E
hBVWkH7QqVbdpqutK2d4WjO7eFCyfZVD4+GEgz7+1QrqoMBaIbqIw8QoQ1BqBXXyw3adL65
KfpvOFT2fK1l0hRSsOfCD475m05zwdLXvnz0DL66i8VByx3YOsGcEMDJeOPo7UvVENahCE2
VwcxAnQLpN7Bfw8rZygd/DShb3CilYMRKsN67Xp3sXw/Upu1mopn2KfXzXqGHnNfIPROGwT
WVQM01VveGTuSgiDvoog+cpgT69/4scju8HU9kJx3TWi3M2ryhmcA1rmvexVcSnjntbM5ZC
xaY5YrXsjaSOhY6FRBopA8kcUoauIUnjod8tM0kxpVhC6l0o85ZBoVnKiXgdTeJV09iojvy
+vM2nEC6vPaOEa1gUrNAFq22OpNWPyl5GeAqa5Z7z52hUAh5oOkAY/DOgbeLwbmjl6h0Yak
/tcyJOYDWggY1qf9vUw6I7xqbpnNZgfUbBoiWM3A96a89wWJrabpw+w8vb2C+EpVZQr75nS
iFGHDRRhrYZC7Wy6+j9AqzPvKRzB3WZc7WRrpAVVhRc/AvSPxOfk37sxnoRawUkc0ikJR6w
28J5HWd1nNYiGgm1/Up+cigka3blnq4/xLzMTPT2wx6WkCmxwqJghcnvj/DTDXElItgVk/c
NAPjWms3QOjtbr6oKA/5h1eNdAbSqOL6/UG+exMrI6udpDYk0BYuCFSZ//B3+5M/6/9+7wF
e5IPNBMUG1sBJsehPA9Ue6iTgLeW2FvHHHcttEiDjgGpZrBmqFIKalxhPVYZ1gIw6a+V0I4
iBOPBEie1QrCtbM3nwLQ+dAua6cLQfWxeEjU/mpbhONh4t5bdtPOZ6egjULuk1f01Jjjqrp
eyLtfYC7k9VburWbwCNmfM5RsFheLbQcqyfrCJMTvaFpu9qxIj2IEz0nJu8eClb0tf2iv+1
Uh3Xgu1XWlXu6TqpH5QW/sOfPAztQRcEiruhYvqalzgW9S3yjsGZrBe/9BhIruKZ2fGf1uC
RFWZ5TsFjVzxlvHitrAc9FluawN3y3bGd5TsEiEt4uzRNStf6dzMkb3enRRxna5uLXrf0K/
SCApkAULOK2nl+k8yITaoGnyqOL2fLUp+E+Mr2II4t0QsHyJVhLhUpH7L4r7pkYZViex8BS
FekULApWpGgm60wVcdCom7N59JLQbXHp3TMJXgK3vOvBqKF3gY6FbhPdJr5rLn5p8HVppJe
Tk+tVV10c9ONjF/UgzshNtoKUgR+nkTKGbRqJJ3j42f8Ds4luEx2rr2XfX6BjLdRNqJqsA8
AqTgj967sydJt4cXWh3gypG8M2DKsFAGzJQMGaE2wzdV7v/3/vYl43wpJZbFty0ZmoOJr5X
Qiha02U1+QnOSRz/ZbWdmsgTWiDULDmkt5Fv93VfPlKje40KsrjykJr4HFBn23Lds9ujoaO
gkVfGWtfqXF2mvZVQgcogZi0bKebo2CRBfSVmo7G0gahmv6lsy2v6OYoWMuL7ewiftPPyle
qJutA1oJd1SFe9fcXz83ZD5vvmlPPXiUUrBBpm8Pooz1gZmAr7LtlYXylZiqXUDFldnVtZA
IfHTZbN6e67IkVZMvIllm+UbDiR6uKRkWuDs5HfTI39CPz6Cs10/QGa1L6KIOf4ayzdXNTF
baZXWxUKVUUrBhjh7bdJyHt289pW+LvKzUrU4OIgz7KoNlVjJub8ybxmV3kK9xJpGDNj2wd
lX3Fi2LuKzV7f0dlvK3pogzjW4rxdHOef3H5CvcWKVhzSLeJ43KQrd/j4yuTOeUqsl21ae7
YjoXT2tyUk1N51Y9MShUFa845q6NRCTdtNFtfGc9rjgiDIMks8hXuA1KwFojTGo7LUcfZZ+
srI3Nz3/3g6aKP2nITkIK1yLRNHJVnHF6fua/06eZsVYrDYaYr93CtQqmiYC00024jRkZMf
KUtSQM3B8RxLAU3ASlYSydb31Tw5vEcfKsh+cqZuznPV2OjyhHzFKylpNtEozKXzVXc+8p4
ujkPpG7gepWbgBSspSeCbcRoGA+LzkX3GDdmmZuAsXpc8hLMkrUC1uo4q+Pr0nINYpiLQjJ
b1kX2ySzgEIp4yNZOE5tPkMzyYsSlYLzZpFpRsIiaTAnbFvIPph75R4L8Lexi5/WEIdWEgk
UAIJFGvoKbTS+jlYlPVm9h5zU2TUYWKFhketnaeY3MLi9GRFL1yZfYqlOqKFjEK8kcNk1sv
+qHoUgoFzmLzSfYqjOyQMEiQZAysFXHJ19OMWaZuCpjV3D9EXbYv5iCRQJnrYBti9uIgUmV
vYzBIcUAAAIqSURBVAmYLfNiULBIaGRK2GlyG9HfNdzFtsVNQAoWiYrBNiJlayq4CUjBIjM
yNWnkK9i2uI3oVqq4CUjBIjPG3kbcec1tRPUlysL4nJuAFCwSJ9mytxEpWyNF6Ao2n2CnqZ
yXQShYZGasFbBV5zZiX6rsTUDmFShYJNbY24jXHy3venxmt39omZuAFCwyH2TLy7iNuH6nv
wlIqaJgkXmzRcu0jWhvAho1bgJSsMg8M9hGXL+zoD9gtp9X4CYgBYssjmwZtUXbRrQPLe80
KVUULLKI2NuIxudzv41obwJuW9wEpGCRRWe92O/FPKfr8VfucROQgkWWjExp/rYR7c7FG1V
KFQWLLB+DXszx30a0NwF5aJlQsChb/W3EeMpW6gY3AQkFi4xipx9itY1obwJuW5QqIj5keQ
kIEJuRrhxfSlhhkSlka4YjXTm+lFCwyNREP9KV40sJBYv4sGY/bCNeuRfuC63ewvYrbgISC
hYJQrY2qmFtIw46F6cMXmlCwSIBEfhIV44vJRQsEi6BjHTl+FJCwSLR4XmkK8eXEgoWmQ3T
jnTl+FJCwSIzZjDSVQPHl5JAee/du3e8CsQX3Sa6Y730pB8khIJFCKElJIQQChYhhFCwCCE
ULEIIoWARQggFixBCwSKEEAoWIYRQsAghFCxCCKFgEUIIBYsQQsEihBAKFiGEULAIIRQsQg
ihYBFCCAWLEELBIoQQChYhhILFS0AIoWARQkjA/D87uqZQTj7xTgAAAABJRU5ErkJggg=="
;

Insert an image
1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the replace-text button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="insert-image">Insert Image</button><br/>


<br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Locate the Office.onReady function call near the top of the file and add the
following code immediately before that line. This code imports the variable that
you defined previously in the file ./base64Image.js.

JavaScript

import { base64Image } from "../../base64Image";

5. Within the Office.onReady function call, locate the line that assigns a click handler
to the replace-text button, and add the following code after that line.

JavaScript

document.getElementById("insert-image").onclick = () =>
tryCatch(insertImage);

6. Add the following function to the end of the file.

JavaScript
async function insertImage() {
await Word.run(async (context) => {

// TODO1: Queue commands to insert an image.

await context.sync();
});
}

7. Within the insertImage() function, replace TODO1 with the following code. Note
that this line inserts the Base64-encoded image at the end of the document. (The
Paragraph object also has an insertInlinePictureFromBase64 method and other
insert* methods. See the following "Insert HTML" section for an example.)

JavaScript

context.document.body.insertInlinePictureFromBase64(base64Image,
Word.InsertLocation.end);

Insert HTML
1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the insert-image button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="insert-html">Insert HTML</button><br/>


<br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the insert-image button, and add the following code after that line.

JavaScript

document.getElementById("insert-html").onclick = () =>
tryCatch(insertHTML);

5. Add the following function to the end of the file.

JavaScript
async function insertHTML() {
await Word.run(async (context) => {

// TODO1: Queue commands to insert a string of HTML.

await context.sync();
});
}

6. Within the insertHTML() function, replace TODO1 with the following code. Note:

The first line adds a blank paragraph to the end of the document.

The second line inserts a string of HTML at the end of the paragraph;
specifically two paragraphs, one formatted with the Verdana font, the other
with the default styling of the Word document. (As you saw in the
insertImage method earlier, the context.document.body object also has the

insert* methods.)

JavaScript

const blankParagraph =
context.document.body.paragraphs.getLast().insertParagraph("",
Word.InsertLocation.after);
blankParagraph.insertHtml('<p style="font-family: verdana;">Inserted
HTML.</p><p>Another paragraph</p>', Word.InsertLocation.end);

Insert a table
1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the insert-html button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="insert-table">Insert Table</button><br/>


<br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the insert-html button, and add the following code after that line.
JavaScript

document.getElementById("insert-table").onclick = () =>
tryCatch(insertTable);

5. Add the following function to the end of the file.

JavaScript

async function insertTable() {


await Word.run(async (context) => {

// TODO1: Queue commands to get a reference to the paragraph


// that will precede the table.

// TODO2: Queue commands to create a table and populate it with


data.

await context.sync();
});
}

6. Within the insertTable() function, replace TODO1 with the following code. Note
that this line uses the ParagraphCollection.getFirst method to get a reference to
the first paragraph and then uses the Paragraph.getNext method to get a
reference to the second paragraph.

JavaScript

const secondParagraph =
context.document.body.paragraphs.getFirst().getNext();

7. Within the insertTable() function, replace TODO2 with the following code. Note:

The first two parameters of the insertTable method specify the number of
rows and columns.

The third parameter specifies where to insert the table, in this case after the
paragraph.

The fourth parameter is a two-dimensional array that sets the values of the
table cells.

The table will have plain default styling, but the insertTable method returns
a Table object with many members, some of which are used to style the
table.
JavaScript

const tableData = [
["Name", "ID", "Birth City"],
["Bob", "434", "Chicago"],
["Sue", "719", "Havana"],
];
secondParagraph.insertTable(3, 3, Word.InsertLocation.after,
tableData);

8. Save all your changes to the project.

Test the add-in


1. If the local web server is already running and your add-in is already loaded in
Word, proceed to step 2. Otherwise, start the local web server and sideload your
add-in.

To test your add-in in Word, run the following command in the root directory
of your project. This starts the local web server (if it isn't already running) and
opens Word with your add-in loaded.

command line

npm start

To test your add-in in Word on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of a Word document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document
https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp

npm run start:web -- --document https://contoso-my.sharepoint-


df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?

e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. If the add-in task pane isn't already open in Word, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.

3. In the task pane, choose the Insert Paragraph button at least three times to ensure
that there are a few paragraphs in the document.

4. Choose the Insert Image button and note that an image is inserted at the end of
the document.

5. Choose the Insert HTML button and note that two paragraphs are inserted at the
end of the document, and that the first one has the Verdana font.

6. Choose the Insert Table button and note that a table is inserted after the second
paragraph.
Create and update content controls
In this step of the tutorial, you'll learn how to create Rich Text content controls in the
document, and then how to insert and replace content in the controls.

7 Note

There are several types of content controls that can be added to a Word document
through the UI, but currently only Rich Text content controls are supported by
Word.js.

Before you start this step of the tutorial, we recommend that you create and
manipulate Rich Text content controls through the Word UI, so you can be familiar
with the controls and their properties. For details, see Create forms that users
complete or print in Word .

Create a content control


1. Open the file ./src/taskpane/taskpane.html.

2. Locate the <button> element for the insert-table button, and add the following
markup after that line.

HTML

<button class="ms-Button" id="create-content-control">Create Content


Control</button><br/><br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the insert-table button, and add the following code after that line.

JavaScript

document.getElementById("create-content-control").onclick = () =>
tryCatch(createContentControl);

5. Add the following function to the end of the file.

JavaScript
async function createContentControl() {
await Word.run(async (context) => {

// TODO1: Queue commands to create a content control.

await context.sync();
});
}

6. Within the createContentControl() function, replace TODO1 with the following


code. Note:

This code is intended to wrap the phrase "Microsoft 365" in a content control.
It makes a simplifying assumption that the string is present and the user has
selected it.

The ContentControl.title property specifies the visible title of the content


control.

The ContentControl.tag property specifies an tag that can be used to get a


reference to a content control using the ContentControlCollection.getByTag
method, which you'll use in a later function.

The ContentControl.appearance property specifies the visual look of the


control. Using the value "Tags" means that the control will be wrapped in
opening and closing tags, and the opening tag will have the content control's
title. Other possible values are "BoundingBox" and "None".

The ContentControl.color property specifies the color of the tags or the


border of the bounding box.

JavaScript

const serviceNameRange = context.document.getSelection();


const serviceNameContentControl =
serviceNameRange.insertContentControl();
serviceNameContentControl.title = "Service Name";
serviceNameContentControl.tag = "serviceName";
serviceNameContentControl.appearance = "Tags";
serviceNameContentControl.color = "blue";

Replace the content of the content control


1. Open the file ./src/taskpane/taskpane.html.
2. Locate the <button> element for the create-content-control button, and add the
following markup after that line.

HTML

<button class="ms-Button" id="replace-content-in-control">Rename


Service</button><br/><br/>

3. Open the file ./src/taskpane/taskpane.js.

4. Within the Office.onReady function call, locate the line that assigns a click handler
to the create-content-control button, and add the following code after that line.

JavaScript

document.getElementById("replace-content-in-control").onclick = () =>
tryCatch(replaceContentInControl);

5. Add the following function to the end of the file.

JavaScript

async function replaceContentInControl() {


await Word.run(async (context) => {

// TODO1: Queue commands to replace the text in the Service


Name
// content control.

await context.sync();
});
}

6. Within the replaceContentInControl() function, replace TODO1 with the following


code. Note:

The ContentControlCollection.getByTag method returns a


ContentControlCollection of all content controls of the specified tag. We use
getFirst to get a reference to the desired control.

JavaScript

const serviceNameContentControl =
context.document.contentControls.getByTag("serviceName").getFirst();
serviceNameContentControl.insertText("Fabrikam Online Productivity
Suite", Word.InsertLocation.replace);

7. Save all your changes to the project.

Test the add-in


1. If the local web server is already running and your add-in is already loaded in
Word, proceed to step 2. Otherwise, start the local web server and sideload your
add-in.

To test your add-in in Word, run the following command in the root directory
of your project. This starts the local web server (if it isn't already running) and
opens Word with your add-in loaded.

command line

npm start

To test your add-in in Word on the web, run the following command in the
root directory of your project. When you run this command, the local web
server starts. Replace "{url}" with the URL of a Word document on your
OneDrive or a SharePoint library to which you have permissions.

7 Note

If you are developing on a Mac, enclose the {url} in single quotation


marks. Do not do this on Windows.

command line

npm run start:web -- --document {url}

The following are examples.


npm run start:web -- --document

https://contoso.sharepoint.com/:t:/g/EZGxP7ksiE5DuxvY638G798BpuhwluxCM

fF1WZQj3VYhYQ?e=F4QM1R
npm run start:web -- --document

https://1drv.ms/x/s!jkcH7spkM4EGgcZUgqthk4IK3NOypVw?e=Z6G1qp
npm run start:web -- --document https://contoso-my.sharepoint-

df.com/:t:/p/user/EQda453DNTpFnl1bFPhOVR0BwlrzetbXvnaRYii2lDr_oQ?
e=RSccmNP

If your add-in doesn't sideload in the document, manually sideload it by


following the instructions in Manually sideload add-ins to Office on the web.

2. If the add-in task pane isn't already open in Word, go to the Home tab and choose
the Show Taskpane button on the ribbon to open it.

3. In the task pane, choose the Insert Paragraph button to ensure that there's a
paragraph with "Microsoft 365" at the top of the document.

4. In the document, select the text "Microsoft 365" and then choose the Create
Content Control button. Note that the phrase is wrapped in tags labelled "Service
Name".

5. Choose the Rename Service button and note that the text of the content control
changes to "Fabrikam Online Productivity Suite".

Next steps
In this tutorial, you've created a Word task pane add-in that inserts and replaces text,
images, and other content in a Word document. To learn more about building Word
add-ins, continue to the following article.

Word add-ins overview


See also
Office Add-ins platform overview
Develop Office Add-ins
Word JavaScript API overview
Article • 03/09/2023

A Word add-in interacts with objects in Word by using the Office JavaScript API, which
includes two JavaScript object models:

Word JavaScript API: These are the application-specific APIs for Word. Introduced
with Office 2016, the Word JavaScript API provides strongly-typed objects that you
can use to access objects and metadata in a Word document.

Common APIs: Introduced with Office 2013, the Common API can be used to
access features such as UI, dialogs, and client settings that are common across
multiple types of Office applications.

This section of the documentation focuses on the Word JavaScript API, which you'll use
to develop the majority of functionality in add-ins that target Word on the web, or Word
2016 and later. For information about the Common API, see Common JavaScript API
object model.

Learn programming concepts


See Word JavaScript object model in Office Add-ins for information about important
programming concepts.

Learn about API capabilities


Use other articles in this section of the documentation to learn how to get the whole
document from an add-in, use search options in your Word add-in to find text, and
more. See the table of contents for the complete list of available articles.

For hands-on experience using the Word JavaScript API to access objects in Word,
complete the Word add-in tutorial.

For detailed information about the Word JavaScript API object model, see the Word
JavaScript API reference documentation.

Try out code samples in Script Lab


Use Script Lab to get started quickly with a collection of built-in samples that show how
to complete tasks with the API. You can run the samples in Script Lab to instantly see the
result in the task pane or document, examine the samples to learn how the API works,
and even use samples to prototype your own add-in.

See also
Word add-ins documentation
Word add-ins overview
Word JavaScript API reference
Office client application and platform availability for Office Add-ins
word package
Reference

Classes
Word.Application Represents the application object.

Word.Body Represents the body of a document or a section.

Word.Border Represents the Border object for text, a paragraph, or a table.

Word.Comment Represents a comment in the document.

Word.CommentCollection Contains a collection of Word.Comment objects.

Word.CommentContentRange

Word.CommentReply Represents a comment reply in the document.

Word.CommentReply Contains a collection of Word.CommentReply objects.


Collection Represents all comment replies in one comment thread.

Word.ContentControl Represents a content control. Content controls are bounded and


potentially labeled regions in a document that serve as
containers for specific types of content. Individual content
controls may contain contents such as images, tables, or
paragraphs of formatted text. Currently, only rich text and plain
text content controls are supported.

Word.ContentControl Contains a collection of Word.ContentControl objects. Content


Collection controls are bounded and potentially labeled regions in a
document that serve as containers for specific types of content.
Individual content controls may contain contents such as
images, tables, or paragraphs of formatted text. Currently, only
rich text and plain text content controls are supported.

Word.CustomProperty Represents a custom property.

Word.CustomProperty Contains the collection of Word.CustomProperty objects.


Collection

Word.CustomXmlPart Represents a custom XML part.

Word.CustomXmlPart Contains the collection of Word.CustomXmlPart objects.


Collection

Word.CustomXmlPartScoped Contains the collection of Word.CustomXmlPart objects with a


Collection specific namespace.
Word.Document The Document object is the top level object. A Document object
contains one or more sections, content controls, and the body
that contains the contents of the document.

Word.DocumentCreated The DocumentCreated object is the top level object created by


Application.CreateDocument. A DocumentCreated object is a
special Document object.

Word.DocumentProperties Represents document properties.

Word.Field Represents a field.

Word.FieldCollection Contains a collection of Word.Field objects.

Word.Font Represents a font.

Word.InlinePicture Represents an inline picture.

Word.InlinePictureCollection Contains a collection of Word.InlinePicture objects.

Word.List Contains a collection of Word.Paragraph objects.

Word.ListCollection Contains a collection of Word.List objects.

Word.ListItem Represents the paragraph list item format.

Word.ListLevel Represents a list level.

Word.ListLevelCollection Contains a collection of Word.ListLevel objects.

Word.ListTemplate Represents a ListTemplate.

Word.NoteItem Represents a footnote or endnote.

Word.NoteItemCollection Contains a collection of Word.NoteItem objects.

Word.Paragraph Represents a single paragraph in a selection, range, content


control, or document body.

Word.ParagraphCollection Contains a collection of Word.Paragraph objects.

Word.ParagraphFormat Represents a style of paragraph in a document.

Word.Range Represents a contiguous area in a document.

Word.RangeCollection Contains a collection of Word.Range objects.

Word.RequestContext The RequestContext object facilitates requests to the Word


application. Since the Office add-in and the Word application
run in two different processes, the request context is required to
get access to the Word object model from the add-in.
Word.SearchOptions Specifies the options to be included in a search operation. To
learn more about how to use search options in the Word
JavaScript APIs, read Use search options to find text in your
Word add-in.

Word.Section Represents a section in a Word document.

Word.SectionCollection Contains the collection of the document's Word.Section objects.

Word.Setting Represents a setting of the add-in.

Word.SettingCollection Contains the collection of Word.Setting objects.

Word.Shading Represents the shading object.

Word.Style Represents a style in a Word document.

Word.StyleCollection Contains a collection of Word.Style objects.

Word.Table Represents a table in a Word document.

Word.TableBorder Specifies the border style.

Word.TableCell Represents a table cell in a Word document.

Word.TableCellCollection Contains the collection of the document's TableCell objects.

Word.TableCollection Contains the collection of the document's Table objects.

Word.TableRow Represents a row in a Word document.

Word.TableRowCollection Contains the collection of the document's TableRow objects.

Word.TableStyle Represents the TableStyle object.

Interfaces
Word.CommentDetail A structure for the ID and reply IDs of this comment.

Word.CommentEventArgs Provides information about the comments that raised the


comment event.

Word.ContentControlAdded Provides information about the content control that raised


EventArgs contentControlAdded event.

Word.ContentControlData Provides information about the content control that raised


ChangedEventArgs contentControlDataChanged event.

Word.ContentControlDeleted Provides information about the content control that raised


EventArgs contentControlDeleted event.
Word.ContentControlEntered Provides information about the content control that raised
EventArgs contentControlEntered event.

Word.ContentControlEvent Provides information about the content control that raised an


Args event.

Word.ContentControlExited Provides information about the content control that raised


EventArgs contentControlExited event.

Word.ContentControlOptions Specifies the options that define which content controls are
returned.

Word.ContentControlSelection Provides information about the content control that raised


ChangedEventArgs contentControlSelectionChanged event.

Word.InsertFileOptions Specifies the options to determine what to copy when inserting


a file.

Word.Interfaces.BodyData An interface describing the data returned by calling


body.toJSON() .

Word.Interfaces.BodyLoad Represents the body of a document or a section.


Options

Word.Interfaces.BodyUpdate An interface for updating data on the Body object, for use in
Data body.set({ ... }) .

Word.Interfaces.BorderData An interface describing the data returned by calling


border.toJSON() .

Word.Interfaces.BorderLoad Represents the Border object for text, a paragraph, or a table.


Options

Word.Interfaces.BorderUpdate An interface for updating data on the Border object, for use in
Data border.set({ ... }) .

Word.Interfaces.Collection Provides ways to load properties of only a subset of members of


LoadOptions a collection.

Word.Interfaces.Comment An interface describing the data returned by calling


CollectionData commentCollection.toJSON() .

Word.Interfaces.Comment Contains a collection of Word.Comment objects.


CollectionLoadOptions

Word.Interfaces.Comment An interface for updating data on the CommentCollection


CollectionUpdateData object, for use in commentCollection.set({ ... }) .

Word.Interfaces.Comment An interface describing the data returned by calling


ContentRangeData commentContentRange.toJSON() .

Word.Interfaces.CommentContentRangeLoadOptions
Word.Interfaces.Comment An interface for updating data on the CommentContentRange
ContentRangeUpdateData object, for use in commentContentRange.set({ ... }) .

Word.Interfaces.Comment An interface describing the data returned by calling


Data comment.toJSON() .

Word.Interfaces.Comment Represents a comment in the document.


LoadOptions

Word.Interfaces.Comment An interface describing the data returned by calling


ReplyCollectionData commentReplyCollection.toJSON() .

Word.Interfaces.Comment Contains a collection of Word.CommentReply objects.


ReplyCollectionLoadOptions Represents all comment replies in one comment thread.

Word.Interfaces.Comment An interface for updating data on the CommentReplyCollection


ReplyCollectionUpdateData object, for use in commentReplyCollection.set({ ... }) .

Word.Interfaces.Comment An interface describing the data returned by calling


ReplyData commentReply.toJSON() .

Word.Interfaces.Comment Represents a comment reply in the document.


ReplyLoadOptions

Word.Interfaces.Comment An interface for updating data on the CommentReply object, for


ReplyUpdateData use in commentReply.set({ ... }) .

Word.Interfaces.Comment An interface for updating data on the Comment object, for use
UpdateData in comment.set({ ... }) .

Word.Interfaces.Content An interface describing the data returned by calling


ControlCollectionData contentControlCollection.toJSON() .

Word.Interfaces.Content Contains a collection of Word.ContentControl objects. Content


ControlCollectionLoadOptions controls are bounded and potentially labeled regions in a
document that serve as containers for specific types of content.
Individual content controls may contain contents such as
images, tables, or paragraphs of formatted text. Currently, only
rich text and plain text content controls are supported.

Word.Interfaces.Content An interface for updating data on the ContentControlCollection


ControlCollectionUpdateData object, for use in contentControlCollection.set({ ... }) .

Word.Interfaces.Content An interface describing the data returned by calling


ControlData contentControl.toJSON() .

Word.Interfaces.Content Represents a content control. Content controls are bounded and


ControlLoadOptions potentially labeled regions in a document that serve as
containers for specific types of content. Individual content
controls may contain contents such as images, tables, or
paragraphs of formatted text. Currently, only rich text and plain
text content controls are supported.

Word.Interfaces.Content An interface for updating data on the ContentControl object, for


ControlUpdateData use in contentControl.set({ ... }) .

Word.Interfaces.Custom An interface describing the data returned by calling


PropertyCollectionData customPropertyCollection.toJSON() .

Word.Interfaces.Custom Contains the collection of Word.CustomProperty objects.


PropertyCollectionLoad
Options

Word.Interfaces.Custom An interface for updating data on the CustomPropertyCollection


PropertyCollectionUpdateData object, for use in customPropertyCollection.set({ ... }) .

Word.Interfaces.Custom An interface describing the data returned by calling


PropertyData customProperty.toJSON() .

Word.Interfaces.Custom Represents a custom property.


PropertyLoadOptions

Word.Interfaces.Custom An interface for updating data on the CustomProperty object, for


PropertyUpdateData use in customProperty.set({ ... }) .

Word.Interfaces.CustomXml An interface describing the data returned by calling


PartCollectionData customXmlPartCollection.toJSON() .

Word.Interfaces.CustomXml Contains the collection of Word.CustomXmlPart objects.


PartCollectionLoadOptions

Word.Interfaces.CustomXml An interface for updating data on the CustomXmlPartCollection


PartCollectionUpdateData object, for use in customXmlPartCollection.set({ ... }) .

Word.Interfaces.CustomXml An interface describing the data returned by calling


PartData customXmlPart.toJSON() .

Word.Interfaces.CustomXml Represents a custom XML part.


PartLoadOptions

Word.Interfaces.CustomXml An interface describing the data returned by calling


PartScopedCollectionData customXmlPartScopedCollection.toJSON() .

Word.Interfaces.CustomXml Contains the collection of Word.CustomXmlPart objects with a


PartScopedCollectionLoad specific namespace.
Options

Word.Interfaces.CustomXml An interface for updating data on the


PartScopedCollectionUpdate CustomXmlPartScopedCollection object, for use in
Data customXmlPartScopedCollection.set({ ... }) .
Word.Interfaces.Document An interface describing the data returned by calling
CreatedData documentCreated.toJSON() .

Word.Interfaces.Document The DocumentCreated object is the top level object created by


CreatedLoadOptions Application.CreateDocument. A DocumentCreated object is a
special Document object.

Word.Interfaces.Document An interface for updating data on the DocumentCreated object,


CreatedUpdateData for use in documentCreated.set({ ... }) .

Word.Interfaces.Document An interface describing the data returned by calling


Data document.toJSON() .

Word.Interfaces.Document The Document object is the top level object. A Document object
LoadOptions contains one or more sections, content controls, and the body
that contains the contents of the document.

Word.Interfaces.Document An interface describing the data returned by calling


PropertiesData documentProperties.toJSON() .

Word.Interfaces.Document Represents document properties.


PropertiesLoadOptions

Word.Interfaces.Document An interface for updating data on the DocumentProperties


PropertiesUpdateData object, for use in documentProperties.set({ ... }) .

Word.Interfaces.Document An interface for updating data on the Document object, for use
UpdateData in document.set({ ... }) .

Word.Interfaces.Field An interface describing the data returned by calling


CollectionData fieldCollection.toJSON() .

Word.Interfaces.Field Contains a collection of Word.Field objects.


CollectionLoadOptions

Word.Interfaces.Field An interface for updating data on the FieldCollection object, for


CollectionUpdateData use in fieldCollection.set({ ... }) .

Word.Interfaces.FieldData An interface describing the data returned by calling


field.toJSON() .

Word.Interfaces.FieldLoad Represents a field.


Options

Word.Interfaces.FieldUpdate An interface for updating data on the Field object, for use in
Data field.set({ ... }) .

Word.Interfaces.FontData An interface describing the data returned by calling


font.toJSON() .

Word.Interfaces.FontLoad Represents a font.


Options

Word.Interfaces.FontUpdate An interface for updating data on the Font object, for use in
Data font.set({ ... }) .

Word.Interfaces.InlinePicture An interface describing the data returned by calling


CollectionData inlinePictureCollection.toJSON() .

Word.Interfaces.InlinePicture Contains a collection of Word.InlinePicture objects.


CollectionLoadOptions

Word.Interfaces.InlinePicture An interface for updating data on the InlinePictureCollection


CollectionUpdateData object, for use in inlinePictureCollection.set({ ... }) .

Word.Interfaces.InlinePicture An interface describing the data returned by calling


Data inlinePicture.toJSON() .

Word.Interfaces.InlinePicture Represents an inline picture.


LoadOptions

Word.Interfaces.InlinePicture An interface for updating data on the InlinePicture object, for


UpdateData use in inlinePicture.set({ ... }) .

Word.Interfaces.ListCollection An interface describing the data returned by calling


Data listCollection.toJSON() .

Word.Interfaces.ListCollection Contains a collection of Word.List objects.


LoadOptions

Word.Interfaces.ListCollection An interface for updating data on the ListCollection object, for


UpdateData use in listCollection.set({ ... }) .

Word.Interfaces.ListData An interface describing the data returned by calling


list.toJSON() .

Word.Interfaces.ListItemData An interface describing the data returned by calling


listItem.toJSON() .

Word.Interfaces.ListItemLoad Represents the paragraph list item format.


Options

Word.Interfaces.ListItem An interface for updating data on the ListItem object, for use in
UpdateData listItem.set({ ... }) .

Word.Interfaces.ListLevel An interface describing the data returned by calling


CollectionData listLevelCollection.toJSON() .

Word.Interfaces.ListLevel Contains a collection of Word.ListLevel objects.


CollectionLoadOptions

Word.Interfaces.ListLevel An interface for updating data on the ListLevelCollection object,


CollectionUpdateData for use in listLevelCollection.set({ ... }) .
Word.Interfaces.ListLevelData An interface describing the data returned by calling
listLevel.toJSON() .

Word.Interfaces.ListLevelLoad Represents a list level.


Options

Word.Interfaces.ListLevel An interface for updating data on the ListLevel object, for use in
UpdateData listLevel.set({ ... }) .

Word.Interfaces.ListLoad Contains a collection of Word.Paragraph objects.


Options

Word.Interfaces.ListTemplate An interface describing the data returned by calling


Data listTemplate.toJSON() .

Word.Interfaces.ListTemplate Represents a ListTemplate.


LoadOptions

Word.Interfaces.ListTemplate An interface for updating data on the ListTemplate object, for


UpdateData use in listTemplate.set({ ... }) .

Word.Interfaces.NoteItem An interface describing the data returned by calling


CollectionData noteItemCollection.toJSON() .

Word.Interfaces.NoteItem Contains a collection of Word.NoteItem objects.


CollectionLoadOptions

Word.Interfaces.NoteItem An interface for updating data on the NoteItemCollection object,


CollectionUpdateData for use in noteItemCollection.set({ ... }) .

Word.Interfaces.NoteItemData An interface describing the data returned by calling


noteItem.toJSON() .

Word.Interfaces.NoteItemLoad Represents a footnote or endnote.


Options

Word.Interfaces.NoteItem An interface for updating data on the NoteItem object, for use in
UpdateData noteItem.set({ ... }) .

Word.Interfaces.Paragraph An interface describing the data returned by calling


CollectionData paragraphCollection.toJSON() .

Word.Interfaces.Paragraph Contains a collection of Word.Paragraph objects.


CollectionLoadOptions

Word.Interfaces.Paragraph An interface for updating data on the ParagraphCollection


CollectionUpdateData object, for use in paragraphCollection.set({ ... }) .

Word.Interfaces.Paragraph An interface describing the data returned by calling


Data paragraph.toJSON() .
Word.Interfaces.Paragraph An interface describing the data returned by calling
FormatData paragraphFormat.toJSON() .

Word.Interfaces.Paragraph Represents a style of paragraph in a document.


FormatLoadOptions

Word.Interfaces.Paragraph An interface for updating data on the ParagraphFormat object,


FormatUpdateData for use in paragraphFormat.set({ ... }) .

Word.Interfaces.Paragraph Represents a single paragraph in a selection, range, content


LoadOptions control, or document body.

Word.Interfaces.Paragraph An interface for updating data on the Paragraph object, for use
UpdateData in paragraph.set({ ... }) .

Word.Interfaces.Range An interface describing the data returned by calling


CollectionData rangeCollection.toJSON() .

Word.Interfaces.Range Contains a collection of Word.Range objects.


CollectionLoadOptions

Word.Interfaces.Range An interface for updating data on the RangeCollection object,


CollectionUpdateData for use in rangeCollection.set({ ... }) .

Word.Interfaces.RangeData An interface describing the data returned by calling


range.toJSON() .

Word.Interfaces.RangeLoad Represents a contiguous area in a document.


Options

Word.Interfaces.RangeUpdate An interface for updating data on the Range object, for use in
Data range.set({ ... }) .

Word.Interfaces.Search An interface describing the data returned by calling


OptionsData searchOptions.toJSON() .

Word.Interfaces.Search Specifies the options to be included in a search operation. To


OptionsLoadOptions learn more about how to use search options in the Word
JavaScript APIs, read Use search options to find text in your
Word add-in.

Word.Interfaces.Search An interface for updating data on the SearchOptions object, for


OptionsUpdateData use in searchOptions.set({ ... }) .

Word.Interfaces.Section An interface describing the data returned by calling


CollectionData sectionCollection.toJSON() .

Word.Interfaces.Section Contains the collection of the document's Word.Section objects.


CollectionLoadOptions

Word.Interfaces.Section An interface for updating data on the SectionCollection object,


CollectionUpdateData
for use in sectionCollection.set({ ... }) .

Word.Interfaces.SectionData An interface describing the data returned by calling


section.toJSON() .

Word.Interfaces.SectionLoad Represents a section in a Word document.


Options

Word.Interfaces.Section An interface for updating data on the Section object, for use in
UpdateData section.set({ ... }) .

Word.Interfaces.Setting An interface describing the data returned by calling


CollectionData settingCollection.toJSON() .

Word.Interfaces.Setting Contains the collection of Word.Setting objects.


CollectionLoadOptions

Word.Interfaces.Setting An interface for updating data on the SettingCollection object,


CollectionUpdateData for use in settingCollection.set({ ... }) .

Word.Interfaces.SettingData An interface describing the data returned by calling


setting.toJSON() .

Word.Interfaces.SettingLoad Represents a setting of the add-in.


Options

Word.Interfaces.SettingUpdate An interface for updating data on the Setting object, for use in
Data setting.set({ ... }) .

Word.Interfaces.ShadingData An interface describing the data returned by calling


shading.toJSON() .

Word.Interfaces.ShadingLoad Represents the shading object.


Options

Word.Interfaces.Shading An interface for updating data on the Shading object, for use in
UpdateData shading.set({ ... }) .

Word.Interfaces.Style An interface describing the data returned by calling


CollectionData styleCollection.toJSON() .

Word.Interfaces.Style Contains a collection of Word.Style objects.


CollectionLoadOptions

Word.Interfaces.Style An interface for updating data on the StyleCollection object, for


CollectionUpdateData use in styleCollection.set({ ... }) .

Word.Interfaces.StyleData An interface describing the data returned by calling


style.toJSON() .

Word.Interfaces.StyleLoad Represents a style in a Word document.


Options
Word.Interfaces.StyleUpdate An interface for updating data on the Style object, for use in
Data style.set({ ... }) .

Word.Interfaces.TableBorder An interface describing the data returned by calling


Data tableBorder.toJSON() .

Word.Interfaces.TableBorder Specifies the border style.


LoadOptions

Word.Interfaces.TableBorder An interface for updating data on the TableBorder object, for use
UpdateData in tableBorder.set({ ... }) .

Word.Interfaces.TableCell An interface describing the data returned by calling


CollectionData tableCellCollection.toJSON() .

Word.Interfaces.TableCell Contains the collection of the document's TableCell objects.


CollectionLoadOptions

Word.Interfaces.TableCell An interface for updating data on the TableCellCollection object,


CollectionUpdateData for use in tableCellCollection.set({ ... }) .

Word.Interfaces.TableCellData An interface describing the data returned by calling


tableCell.toJSON() .

Word.Interfaces.TableCellLoad Represents a table cell in a Word document.


Options

Word.Interfaces.TableCell An interface for updating data on the TableCell object, for use in
UpdateData tableCell.set({ ... }) .

Word.Interfaces.Table An interface describing the data returned by calling


CollectionData tableCollection.toJSON() .

Word.Interfaces.Table Contains the collection of the document's Table objects.


CollectionLoadOptions

Word.Interfaces.Table An interface for updating data on the TableCollection object, for


CollectionUpdateData use in tableCollection.set({ ... }) .

Word.Interfaces.TableData An interface describing the data returned by calling


table.toJSON() .

Word.Interfaces.TableLoad Represents a table in a Word document.


Options

Word.Interfaces.TableRow An interface describing the data returned by calling


CollectionData tableRowCollection.toJSON() .

Word.Interfaces.TableRow Contains the collection of the document's TableRow objects.


CollectionLoadOptions
Word.Interfaces.TableRow An interface for updating data on the TableRowCollection object,
CollectionUpdateData for use in tableRowCollection.set({ ... }) .

Word.Interfaces.TableRowData An interface describing the data returned by calling


tableRow.toJSON() .

Word.Interfaces.TableRowLoad Represents a row in a Word document.


Options

Word.Interfaces.TableRow An interface for updating data on the TableRow object, for use in
UpdateData tableRow.set({ ... }) .

Word.Interfaces.TableStyle An interface describing the data returned by calling


Data tableStyle.toJSON() .

Word.Interfaces.TableStyle Represents the TableStyle object.


LoadOptions

Word.Interfaces.TableStyle An interface for updating data on the TableStyle object, for use
UpdateData in tableStyle.set({ ... }) .

Word.Interfaces.TableUpdate An interface for updating data on the Table object, for use in
Data table.set({ ... }) .

Word.ParagraphAddedEvent Provides information about the paragraphs that raised the


Args paragraphAdded event.

Word.ParagraphChangedEvent Provides information about the paragraphs that raised the


Args paragraphChanged event.

Word.ParagraphDeletedEvent Provides information about the paragraphs that raised the


Args paragraphDeleted event.

Enums
Word.Alignment

Word.BodyType

Word.BorderLocation

Word.BorderPositionType Represents the position of a style's border.

Word.BorderType

Word.BreakType Specifies the form of a break.

Word.BuiltInStyleName Represents the built-in style in a Word document.


Important: This enum was renamed from Style to
BuiltInStyleName in WordAPI 1.5.

Word.CellPaddingLocation

Word.ChangeTrackingMode ChangeTracking mode.

Word.ChangeTrackingState Specify the track state when ChangeTracking is on.

Word.ChangeTrackingVersion Specify the current version or the original version of the text.

Word.CloseBehavior Specifies the close behavior for Document.close .

Word.CommentChangeType Represents how the comments in the event were changed.

Word.ContentControl ContentControl appearance.


Appearance

Word.ContentControlType Specifies supported content control types and subtypes.

Word.DocumentPropertyType

Word.ErrorCodes

Word.EventSource An enum that specifies an event's source. It can be local or


remote (through coauthoring).

Word.EventType Provides information about the type of a raised event.

Word.FieldKind Represents the kind of field. Indicates how the field works in
relation to updating.

Word.FieldType Represents the type of Field.

Word.HeaderFooterType

Word.ImageFormat

Word.InsertLocation The insertion location types.

Word.LineStyle Represents the line style for the specified border.

Word.LineWidth Represents the width of a style's border.

Word.ListBuiltInNumberStyle

Word.ListBullet

Word.ListLevelType

Word.ListNumbering

Word.LocationRelation
Word.NoteItemType Note item type

Word.OutlineLevel Represents the outline levels.

Word.RangeLocation

Word.SaveBehavior Specifies the save behavior for Document.save .

Word.SelectionMode This enum sets where the cursor (insertion point) in the
document is after a selection.

Word.ShadingTextureType Represents the shading texture. To learn more about how to


apply backgrounds like textures, see Add, change, or delete the
background color in Word .

Word.StyleType Represents the type of style.

Word.TrailingCharacter Represents the character inserted after the list item mark.

Word.UnderlineType The supported styles for underline format.

Word.VerticalAlignment

Functions
Word.run(objects, batch) Executes a batch script that performs actions on the Word object
model, using the RequestContext of previously created API
objects.

Word.run(object, batch) Executes a batch script that performs actions on the Word object
model, using the RequestContext of a previously created API
object. When the promise is resolved, any tracked objects that
were automatically allocated during execution will be released.

Word.run(batch) Executes a batch script that performs actions on the Word object
model, using a new RequestContext. When the promise is
resolved, any tracked objects that were automatically allocated
during execution will be released.

Function Details

Word.run(objects, batch)
Executes a batch script that performs actions on the Word object model, using the
RequestContext of previously created API objects.
TypeScript

export function run<T>(objects: OfficeExtension.ClientObject[], batch:


(context: Word.RequestContext) => Promise<T>): Promise<T>;

Parameters
objects OfficeExtension.ClientObject[]
An array of previously created API objects. The array will be validated to make sure
that all of the objects share the same context. The batch will use this shared
RequestContext, which means that any changes applied to these objects will be
picked up by "context.sync()".

batch (context: Word.RequestContext) => Promise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Word
application. Since the Office add-in and the Word application run in two different
processes, the RequestContext is required to get access to the Word object model
from the add-in.

Returns
Promise<T>

Word.run(object, batch)
Executes a batch script that performs actions on the Word object model, using the
RequestContext of a previously created API object. When the promise is resolved,
any tracked objects that were automatically allocated during execution will be
released.

TypeScript

export function run<T>(object: OfficeExtension.ClientObject, batch:


(context: Word.RequestContext) => Promise<T>): Promise<T>;

Parameters
object OfficeExtension.ClientObject
A previously created API object. The batch will use the same RequestContext as the
passed-in object, which means that any changes applied to the object will be picked
up by "context.sync()".

batch (context: Word.RequestContext) => Promise<T>


A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Word
application. Since the Office add-in and the Word application run in two different
processes, the RequestContext is required to get access to the Word object model
from the add-in.

Returns
Promise<T>

Word.run(batch)
Executes a batch script that performs actions on the Word object model, using a new
RequestContext. When the promise is resolved, any tracked objects that were
automatically allocated during execution will be released.

TypeScript

export function run<T>(batch: (context: Word.RequestContext) =>


Promise<T>): Promise<T>;

Parameters
batch (context: Word.RequestContext) => Promise<T>
A function that takes in a RequestContext and returns a promise (typically, just the
result of "context.sync()"). The context parameter facilitates requests to the Word
application. Since the Office add-in and the Word application run in two different
processes, the RequestContext is required to get access to the Word object model
from the add-in.

Returns
Promise<T>
Word JavaScript object model in Office
Add-ins
Article • 03/09/2023

This article describes concepts that are fundamental to using the Word JavaScript API to
build add-ins.

) Important

See Using the application-specific API model to learn about the asynchronous
nature of the Word APIs and how they work with the document.

Office.js APIs for Word


A Word add-in interacts with objects in Word by using the Office JavaScript API. This
includes two JavaScript object models:

Word JavaScript API: The Word JavaScript API provides strongly-typed objects that
work with the document, ranges, tables, lists, formatting, and more.

Common APIs: The Common API give access to features such as UI, dialogs, and
client settings that are common across multiple Office applications.

While you'll likely use the Word JavaScript API to develop the majority of functionality in
add-ins that target Word, you'll also use objects in the Common API. For example:

Office.Context: The Context object represents the runtime environment of the


add-in and provides access to key objects of the API. It consists of document
configuration details such as contentLanguage and officeTheme and also provides
information about the add-in's runtime environment such as host and platform .
Additionally, it provides the requirements.isSetSupported() method, which you
can use to check whether a specified requirement set is supported by the Word
application where the add-in is running.
Office.Document: The Office.Document object provides the getFileAsync()
method, which you can use to download the Word file where the add-in is running.
This is separate from the Word.Document object.
Word-specific object model
To understand the Word APIs, you must understand how the components of a
document are related to one another.

The Document contains the Sections, and document-level entities such as settings
and custom XML parts.
A Section contains a Body.
A Body gives access to Paragraphs, ContentControls, and Range objects, among
others.
A Range represents a contiguous area of content, including text, white space,
Tables, and images. It also contains most of the text manipulation methods.
A List represents text in a numbered or bulleted list.

See also
Word JavaScript API overview
Build your first Word add-in
Word add-in tutorial
Word JavaScript API reference
Learn about the Microsoft 365 Developer Program
Create a dictionary task pane add-in
Article • 06/13/2023

This article shows you an example of a task pane add-in with an accompanying web
service that provides dictionary definitions or thesaurus synonyms for the user's current
selection in a Word document.

A dictionary Office Add-in is based on the standard task pane add-in with additional
features to support querying and displaying definitions from a dictionary XML web
service in additional places in the Office application's UI.

In a typical dictionary task pane add-in, a user selects a word or phrase in their
document, and the JavaScript logic behind the add-in passes this selection to the
dictionary provider's XML web service. The dictionary provider's webpage then updates
to show the definitions for the selection to the user.

The XML web service component returns up to three definitions in the format defined
by the example OfficeDefinitions XML schema, which are then displayed to the user in
other places in the hosting Office application's UI.

Figure 1 shows the selection and display experience for a Bing-branded dictionary add-
in that is running in Word.

Figure 1. Dictionary add-in displaying definitions for the selected word

It's up to you to determine if selecting the See More link in the dictionary add-in's
HTML UI displays more information within the task pane or opens a separate window to
the full webpage for the selected word or phrase.

Figure 2 shows the Define command in the context menu that enables users to quickly
launch installed dictionaries. Figures 3 through 5 show the places in the Office UI where
the dictionary XML services are used to provide definitions in Word.

Figure 2. Define command in the context menu

Figure 3. Definitions in the Spelling and Grammar panes


Figure 4. Definitions in the Thesaurus pane
Figure 5. Definitions in Reading Mode
To create a task pane add-in that provides a dictionary lookup, create two main
components.

An XML web service that looks up definitions from a dictionary service, and then
returns those values in an XML format that can be consumed and displayed by the
dictionary add-in.
A task pane add-in that submits the user's current selection to the dictionary web
service, displays definitions, and can optionally insert those values into the
document.

The following sections provide examples of how to create these components.

Prerequisites
Visual Studio 2019 or later with the Office/SharePoint development workload
installed.

7 Note

If you've previously installed Visual Studio, use the Visual Studio Installer to
ensure that the Office/SharePoint development workload is installed.

Office connected to a Microsoft 365 subscription (including Office on the web).

7 Note

If you don't already have Office, you can join the Microsoft 365 developer
program to get a free, 90-day renewable Microsoft 365 subscription to use
during development.

Next, create a Word add-in project in Visual Studio.

1. In Visual Studio, choose Create a new project.

2. Using the search box, enter add-in. Choose Word Web Add-in, then select Next.

3. Name your project and select Create.

4. Visual Studio creates a solution and its two projects appear in Solution Explorer.
The Home.html file opens in Visual Studio.

To learn more about the projects in a Word add-in solution, see the quick start.

Create a dictionary XML web service


The XML web service must return queries to the web service as XML that conforms to
the OfficeDefinitions XML schema. The following two sections describe the
OfficeDefinitions XML schema, and provide an example of how to code an XML web
service that returns queries in that XML format.

OfficeDefinitions XML schema


The following code shows sample XSD for the OfficeDefinitions XML schema example.

XML

<?xml version="1.0" encoding="utf-8"?>


<xs:schema
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="https://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.microsoft.com/contoso/OfficeDefinitions"
xmlns="http://schemas.microsoft.com/contoso/OfficeDefinitions">
<xs:element name="Result">
<xs:complexType>
<xs:sequence>
<xs:element name="SeeMoreURL" type="xs:anyURI"/>
<xs:element name="Definitions" type="DefinitionListType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="DefinitionListType">
<xs:sequence>
<xs:element name="Definition" maxOccurs="3">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:maxLength value="400"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>

Returned XML consists of a root <Result> element that contains a <Definitions>


element with zero to three <Definition> child elements. Each child element contains
definitions that are at most 400 characters in length. Additionally, the URL to the full
page on the dictionary site must be provided in the <SeeMoreURL> element. The
following example shows the structure of returned XML that conforms to the
OfficeDefinitions schema.

XML

<?xml version="1.0" encoding="utf-8"?>


<Result xmlns="http://schemas.microsoft.com/contoso/OfficeDefinitions">
<SeeMoreURL xmlns="">https://www.bing.com/search?q=example</SeeMoreURL>
<Definitions xmlns="">
<Definition>Definition1</Definition>
<Definition>Definition2</Definition>
<Definition>Definition3</Definition>
</Definitions>
</Result>

Sample dictionary XML web service


The following C# code provides an example of how to write code for an XML web
service that returns the result of a dictionary query in the OfficeDefinitions XML format.

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Xml;
using System.Text;
using System.IO;
using System.Net;
using System.Web.Script.Services;

/// <summary>
/// Summary description for _Default.
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this web service to be called from script, using ASP.NET AJAX,
include the following line.
[ScriptService]
public class WebService : System.Web.Services.WebService {

public WebService () {

// Uncomment the following line if using designed components.


// InitializeComponent();
}

// You can replace this method entirely with your own method that gets
definitions
// from your data source and then formats it into the example
OfficeDefinitions XML format.
// If you need a reference for constructing the returned XML, you can
use this example as a basis.
[WebMethod]
public XmlDocument Define(string word)
{

StringBuilder sb = new StringBuilder();


XmlWriter writer = XmlWriter.Create(sb);
{
writer.WriteStartDocument();

writer.WriteStartElement("Result",
"http://schemas.microsoft.com/contoso/OfficeDefinitions");

// See More URL should be changed to the dictionary


publisher's page for that word on
// their website.
writer.WriteElementString("SeeMoreURL",
"https://www.bing.com/search?q=" + word);

writer.WriteStartElement("Definitions");

writer.WriteElementString("Definition", "Definition
1 of " + word);
writer.WriteElementString("Definition", "Definition
2 of " + word);
writer.WriteElementString("Definition", "Definition
3 of " + word);

writer.WriteEndElement(); // End of Definitions element.

writer.WriteEndElement(); // End of Result element.

writer.WriteEndDocument();
}
writer.Close();
XmlDocument doc = new XmlDocument();
doc.LoadXml(sb.ToString());

return doc;
}
}

To get started with development, you can do the following.

Create the web service


1. Add a Web Service (ASMX) to the add-in's web application project in Visual Studio
and name it DictionaryWebService.
2. Replace the entire content of the associated .asmx.cs file with the preceding C#
code sample.

Update the web service markup

1. In the Solution Explorer, select the DictionaryWebService.asmx file then open its
context menu and choose View Markup.

2. Replace the contents of DictionaryWebService.asmx with the following code.

XML

<%@ WebService Language="C#" CodeBehind="DictionaryWebService.asmx.cs"


Class="WebService" %>

Update the web.config


1. In the Web.config of the add-in's web application project, add the following to the
<system.web> node.

XML

<webServices>
<protocols>
<add name="HttpGet" />
<add name="HttpPost" />
</protocols>
</webServices>

2. Save your changes.


Components of a dictionary add-in
A dictionary add-in consists of three main component files:

An XML manifest file that describes the add-in.


An HTML file that provides the add-in's UI.
A JavaScript file that provides logic to get the user's selection from the document,
sends the selection as a query to the web service, and then displays returned
results in the add-in's UI.

Example of a dictionary add-in's manifest file


The following is an example manifest file for a dictionary add-in.

XML

<?xml version="1.0" encoding="utf-8"?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<Id>7164e750-dc86-49c0-b548-1bac57abdc7c</Id>
<Version>15.0</Version>
<ProviderName>Microsoft Office Demo Dictionary</ProviderName>
<DefaultLocale>en-us</DefaultLocale>
<!--DisplayName is the name that will appear in the user's list of
applications.-->
<DisplayName DefaultValue="Microsoft Office Demo Dictionary" />
<!--Description is a 2-3 sentence description of this dictionary. -->
<Description DefaultValue="The Microsoft Office Demo Dictionary is an
example built to demonstrate how a
publisher can create a dictionary that integrates with Office. It
doesn't return real definitions." />
<!--IconUrl is the URI for the icon that will appear in the user's list of
applications.-->
<IconUrl
DefaultValue="http://contoso/_layouts/images/general/office_logo.jpg" />
<SupportUrl DefaultValue="[Insert the URL of a page that provides support
information for the app]" />
<!--Hosts specifies the kind of Office application your dictionary add-in
will support.
You shouldn't have to modify this area.-->
<Hosts>
<Host Name="Document"/>
</Hosts>
<DefaultSettings>
<!--SourceLocation is the URL for your dictionary.-->
<SourceLocation
DefaultValue="http://contoso/ExampleDictionary/DictionaryHome.html" />
</DefaultSettings>
<!--Permissions is the set of permissions a user will have to give your
dictionary.
If you need write access, such as to allow a user to replace the
highlighted word with a synonym,
use ReadWriteDocument. -->
<Permissions>ReadDocument</Permissions>
<Dictionary>
<!--TargetDialects is the set of regional languages your dictionary
contains. For example, if your
dictionary applies to Spanish (Mexico) and Spanish (Peru), but not
Spanish (Spain), you can specify
that here. Do not put more than one language (for example, Spanish
and English) here. Publish
separate languages as separate dictionaries. -->
<TargetDialects>
<TargetDialect>EN-AU</TargetDialect>
<TargetDialect>EN-BZ</TargetDialect>
<TargetDialect>EN-CA</TargetDialect>
<TargetDialect>EN-029</TargetDialect>
<TargetDialect>EN-HK</TargetDialect>
<TargetDialect>EN-IN</TargetDialect>
<TargetDialect>EN-ID</TargetDialect>
<TargetDialect>EN-IE</TargetDialect>
<TargetDialect>EN-JM</TargetDialect>
<TargetDialect>EN-MY</TargetDialect>
<TargetDialect>EN-NZ</TargetDialect>
<TargetDialect>EN-PH</TargetDialect>
<TargetDialect>EN-SG</TargetDialect>
<TargetDialect>EN-ZA</TargetDialect>
<TargetDialect>EN-TT</TargetDialect>
<TargetDialect>EN-GB</TargetDialect>
<TargetDialect>EN-US</TargetDialect>
<TargetDialect>EN-ZW</TargetDialect>
</TargetDialects>
<!--QueryUri is the address of this dictionary's XML web service (which
is used to put definitions in
additional contexts, such as the spelling checker.)-->
<QueryUri
DefaultValue="http://contoso/ExampleDictionary/WebService.asmx/Define?
word="/>
<!--Citation Text, Dictionary Name, and Dictionary Home Page will be
combined to form the citation line
(for example, this would produce "Examples by: Contoso",
where "Contoso" is a hyperlink to http://www.contoso.com).-->
<CitationText DefaultValue="Examples by: " />
<DictionaryName DefaultValue="Contoso" />
<DictionaryHomePage DefaultValue="http://www.contoso.com" />
</Dictionary>
</OfficeApp>

The <Dictionary> element and its child elements specific to creating a dictionary add-
in's manifest file are described in the following sections. For information about the other
elements in the manifest file, see Office Add-ins XML manifest.
Dictionary element
Specifies settings for dictionary add-ins.

Parent element

<OfficeApp>

Child elements

<TargetDialects>, <QueryUri>, <CitationText>, <Name>, <DictionaryHomePage>

Remarks

The <Dictionary> element and its child elements are added to the manifest of a task
pane add-in when you create a dictionary add-in.

TargetDialects element
Specifies the regional languages that this dictionary supports. Required for dictionary
add-ins.

Parent element

<Dictionary>

Child element

<TargetDialect>

Remarks

The <TargetDialects> element and its child elements specify the set of regional
languages your dictionary contains. For example, if your dictionary applies to both
Spanish (Mexico) and Spanish (Peru), but not Spanish (Spain), you can specify that in this
element. Do not specify more than one language (e.g., Spanish and English) in this
manifest. Publish separate languages as separate dictionaries.

Example

XML

<TargetDialects>
<TargetDialect>EN-AU</TargetDialect>
<TargetDialect>EN-BZ</TargetDialect>
<TargetDialect>EN-CA</TargetDialect>
<TargetDialect>EN-029</TargetDialect>
<TargetDialect>EN-HK</TargetDialect>
<TargetDialect>EN-IN</TargetDialect>
<TargetDialect>EN-ID</TargetDialect>
<TargetDialect>EN-IE</TargetDialect>
<TargetDialect>EN-JM</TargetDialect>
<TargetDialect>EN-MY</TargetDialect>
<TargetDialect>EN-NZ</TargetDialect>
<TargetDialect>EN-PH</TargetDialect>
<TargetDialect>EN-SG</TargetDialect>
<TargetDialect>EN-ZA</TargetDialect>
<TargetDialect>EN-TT</TargetDialect>
<TargetDialect>EN-GB</TargetDialect>
<TargetDialect>EN-US</TargetDialect>
<TargetDialect>EN-ZW</TargetDialect>
</TargetDialects>

TargetDialect element
Specifies a regional language that this dictionary supports. Required for dictionary add-
ins.

Parent element

<TargetDialects>

Remarks

Specify the value for a regional language in the RFC1766 language tag format, such as
EN-US.

Example

XML

<TargetDialect>EN-US</TargetDialect>

QueryUri element
Specifies the endpoint for the dictionary query service. Required for dictionary add-ins.

Parent element

<Dictionary>

Remarks
This is the URI of the XML web service for the dictionary provider. The properly escaped
query will be appended to this URI.

Example

XML

<QueryUri DefaultValue="http://msranlc-lingo1/proof.aspx?q="/>

CitationText element
Specifies the text to use in citations. Required for dictionary add-ins.

Parent element

<Dictionary>

Remarks

This element specifies the beginning of the citation text that will be displayed on a line
below the content that is returned from the web service (for example, "Results by: " or
"Powered by: ").

For this element, you can specify values for additional locales by using the <Override>
element. For example, if a user is running the Spanish SKU of Office, but using an
English dictionary, this allows the citation line to read "Resultados por: Bing" rather than
"Results by: Bing". For more information about how to specify values for additional
locales, see Localization.

Example

XML

<CitationText DefaultValue="Results by: " />

DictionaryName element
Specifies the name of this dictionary. Required for dictionary add-ins.

Parent element

<Dictionary>

Remarks
This element specifies the link text in the citation text. Citation text is displayed on a line
below the content that is returned from the web service.

For this element, you can specify values for additional locales.

Example

XML

<DictionaryName DefaultValue="Bing Dictionary" />

DictionaryHomePage element

Specifies the URL of the home page for the dictionary. Required for dictionary add-ins.

Parent element

<Dictionary>

Remarks

This element specifies the link URL in the citation text. Citation text is displayed on a line
below the content that is returned from the web service.

For this element, you can specify values for additional locales.

Example

XML

<DictionaryHomePage DefaultValue="https://www.bing.com" />

Update your dictionary add-in's manifest file


1. Open the XML manifest file in the add-in project.

2. Update the value of the <ProviderName> element with your name.

3. Replace the value of the <DisplayName> element's <DefaultValue> attribute with


an appropriate name, for example, "Microsoft Office Demo Dictionary".

4. Replace the value of the <Description> element's <DefaultValue> attribute with


an appropriate description, for example, "The Microsoft Office Demo Dictionary is
an example built to demonstrate how a publisher could create a dictionary that
integrates with Office. It doesn't return real definitions.".

5. Add the following code after the <Permissions> node, replacing "contoso"
references with your own company name, then save your changes.

XML

<Dictionary>
<!--TargetDialects is the set of regional languages your dictionary
contains. For example, if your
dictionary applies to Spanish (Mexico) and Spanish (Peru), but
not Spanish (Spain), you can
specify that here. Do not put more than one language (for
example, Spanish and English) here.
Publish separate languages as separate dictionaries. -->
<TargetDialects>
<TargetDialect>EN-AU</TargetDialect>
<TargetDialect>EN-BZ</TargetDialect>
<TargetDialect>EN-CA</TargetDialect>
<TargetDialect>EN-029</TargetDialect>
<TargetDialect>EN-HK</TargetDialect>
<TargetDialect>EN-IN</TargetDialect>
<TargetDialect>EN-ID</TargetDialect>
<TargetDialect>EN-IE</TargetDialect>
<TargetDialect>EN-JM</TargetDialect>
<TargetDialect>EN-MY</TargetDialect>
<TargetDialect>EN-NZ</TargetDialect>
<TargetDialect>EN-PH</TargetDialect>
<TargetDialect>EN-SG</TargetDialect>
<TargetDialect>EN-ZA</TargetDialect>
<TargetDialect>EN-TT</TargetDialect>
<TargetDialect>EN-GB</TargetDialect>
<TargetDialect>EN-US</TargetDialect>
<TargetDialect>EN-ZW</TargetDialect>
</TargetDialects>
<!--QueryUri is the address of this dictionary's XML web service
(which is used to put definitions in
additional contexts, such as the spelling checker.)-->
<QueryUri DefaultValue="~remoteAppUrl/DictionaryWebService.asmx"/>
<!--Citation Text, Dictionary Name, and Dictionary Home Page will be
combined to form the citation
line (for example, this would produce "Examples by: Contoso",
where "Contoso" is a hyperlink to
http://www.contoso.com).-->
<CitationText DefaultValue="Examples by: " />
<DictionaryName DefaultValue="Contoso" />
<DictionaryHomePage DefaultValue="http://www.contoso.com" />
</Dictionary>

Create a dictionary add-in's HTML user interface


The following two examples show the HTML and CSS files for the UI of the Demo
Dictionary add-in. To view how the UI is displayed in the add-in's task pane, see Figure 6
following the code. To see how the implementation of the JavaScript provides
programming logic for this HTML UI, see Write the JavaScript implementation
immediately following this section.

In the add-in's web application project in Visual Studio, you can replace the contents of
the ./Home.html file with the following sample HTML.

HTML

<!DOCTYPE html>
<html>

<head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

<!--The title will not be shown but is supplied to ensure valid HTML.-->
<title>Example Dictionary</title>

<!--Required library includes.-->


<script type="text/javascript"
src="https://ajax.microsoft.com/ajax/4.0/1/MicrosoftAjax.js"></script>
<script src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>

<!--Optional library includes.-->


<script type="text/javascript"
src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.js"></script>

<!--App-specific CSS and JS.-->


<link rel="Stylesheet" type="text/css" href="Home.css" />
<script type="text/javascript" src="Home.js"></script>
</head>

<body>
<div id="mainContainer">
<div>INSTRUCTIONS</div>
<ol>
<li>Ensure there's text in the document.</li>
<li>Select text.</li>
</ol>
<div id="header">
<span id="headword"></span>
</div>
<div>DEFINITIONS</div>
<ol id="definitions">
</ol>
<div id="SeeMore">
<a id="SeeMoreLink" target="_blank">See More...</a>
</div>
<div id="message"></div>
</div>
</body>

</html>

The following example shows the contents of the .css file.

In the add-in's web application project in Visual Studio, you can replace the contents of
the ./Home.css file with the following sample CSS.

CSS

#mainContainer
{
font-family: Segoe UI;
font-size: 11pt;
}

#headword
{
font-family: Segoe UI Semibold;
color: #262626;
}

#definitions
{
font-size: 8.5pt;
}
a
{
font-size: 8pt;
color: #336699;
text-decoration: none;
}
a:visited
{
color: #993366;
}
a:hover, a:active
{
text-decoration: underline;
}

Figure 6. Demo dictionary UI


Write the JavaScript implementation
The following example shows the JavaScript implementation in the .js file that's called
from the add-in's HTML page to provide the programming logic for the Demo
Dictionary add-in. This script uses the XML web service described previously. When
placed in the same directory as the example web service, the script will get definitions
from that service. It can be used with a public OfficeDefinitions-conforming XML web
service by modifying the xmlServiceURL variable at the top of the file.

The primary members of the Office JavaScript API (Office.js) that are called from this
implementation are shown in the following list.

The initialize event of the Office object, which is raised when the add-in context is
initialized, and provides access to a Document object instance that represents the
document the add-in is interacting with.
The addHandlerAsync method of the Document object, which is called in the
initialize function to add an event handler for the SelectionChanged event of
the document to listen for user selection changes.
The getSelectedDataAsync method of the Document object, which is called in the
tryUpdatingSelectedWord() function when the SelectionChanged event handler is

raised to get the word or phrase the user selected, coerce it to plain text, and then
execute the selectedTextCallback asynchronous callback function.
When the selectTextCallback asynchronous callback function that's passed as the
callback argument of the getSelectedDataAsync method executes, it gets the value
of the selected text when the callback returns. It gets that value from the callback's
selectedText argument (which is of type AsyncResult) by using the value property of
the returned AsyncResult object.
The rest of the code in the selectedTextCallback function queries the XML web
service for definitions.
The remaining code in the .js file displays the list of definitions in the add-in's
HTML UI.

In the add-in's web application project in Visual Studio, you can replace the contents of
the ./Home.js file with the following sample JavaScript.

JavaScript

// The document the dictionary add-in is interacting with.


let _doc;
// The last looked-up word, which is also the currently displayed word.
let lastLookup;

// The base URL for the OfficeDefinitions-conforming XML web service to


query for definitions.
const xmlServiceUrl = "DictionaryWebService.asmx/Define";

// Initialize the add-in.


// Office.initialize or Office.onReady is required for all add-ins.
Office.initialize = function (reason) {
// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {
// After the DOM is loaded, app-specific code can run.
// Store a reference to the current document.
_doc = Office.context.document;
// Check whether text is already selected.
tryUpdatingSelectedWord();
// Add a handler to refresh when the user changes selection.
_doc.addHandlerAsync("documentSelectionChanged",
tryUpdatingSelectedWord);
});
}

// Executes when event is raised on the user's selection changes, and at


initialization time.
// Gets the current selection and passes that to asynchronous callback
function.
function tryUpdatingSelectedWord() {
_doc.getSelectedDataAsync(Office.CoercionType.Text,
selectedTextCallback);
}

// Async callback that executes when the add-in gets the user's selection.
Determines whether anything should
// be done. If so, it makes requests that will be passed to various
functions.
function selectedTextCallback(selectedText) {
selectedText = $.trim(selectedText.value);
// Be sure user has selected text. The SelectionChanged event is raised
every time the user moves
// the cursor, even if no selection.
if (selectedText != "") {
// Check whether the user selected the same word the pane is
currently displaying to
// avoid unnecessary web calls.
if (selectedText != lastLookup) {
// Update the lastLookup variable.
lastLookup = selectedText;
// Set the "headword" span to the word you looked up.
$("#headword").text("Selected text: " + selectedText);
// AJAX request to get definitions for the selected word; pass
that to refreshDefinitions.
$.ajax(xmlServiceUrl,
{
data: { word: selectedText },
dataType: 'xml',
success: refreshDefinitions,
error: errorHandler
});
}
}

// This function is called when the add-in gets back the definitions target
word.
// It removes the old definitions and replaces them with the definitions for
the current word.
// It also sets the "See More" link.
function refreshDefinitions(data, textStatus, jqXHR) {
$(".definition").remove();

// Make a new list item for each returned definition that was returned,
set the CSS class,
// and append it to the definitions div.
$(data).find("Definition").each(function () {
$(document.createElement("li"))
.text($(this).text())
.addClass("definition")
.appendTo($("#definitions"));
});

// Change the "See More" link to direct to the correct URL.


$("#SeeMoreLink").attr("href", $(data).find("SeeMoreURL").text());
}

// Basic error handler that writes to a div with id='message'.


function errorHandler(jqXHR, textStatus, errorThrown) {
document.getElementById('message').innerText
+= ("textStatus:- " + textStatus
+ "\nerrorThrown:- " + errorThrown
+ "\njqXHR:- " + JSON.stringify(jqXHR));
}
Try it out
1. Using Visual Studio, test the newly created Word add-in by pressing F5 or
choosing Debug > Start Debugging to launch Word with the Show Taskpane add-
in button displayed on the ribbon. The add-in will be hosted locally on IIS.

2. In Word, if the add-in task pane isn't already open, choose the Home tab, and then
choose the Show Taskpane button to open the add-in task pane. (If you're using
the volume-licensed perpetual version of Office, instead of the Microsoft 365
version or a retail perpetual version, then custom buttons aren't supported.
Instead, the task pane will open immediately.)

3. In Word, add text to the document then select any or all of that text.
Get the whole document from an add-in
for Word or PowerPoint
Article • 07/14/2023

You can create an Office Add-in to send or publish a Word document or PowerPoint
presentation to a remote location. This article demonstrates how to build a simple task
pane add-in for PowerPoint or Word that gets all of the presentation or document as a
data object and sends that data to a web server via an HTTP request.

Prerequisites for creating an add-in for


PowerPoint or Word
This article assumes that you are using a text editor to create the task pane add-in for
PowerPoint or Word. To create the task pane add-in, you must create the following files.

On a shared network folder or on a web server, you need the following files.

An HTML file (GetDoc_App.html) that contains the user interface plus links to
the JavaScript files (including Office.js and application-specific .js files) and
Cascading Style Sheet (CSS) files.

A JavaScript file (GetDoc_App.js) to contain the programming logic of the add-


in.

A CSS file (Program.css) to contain the styles and formatting for the add-in.

An XML manifest file (GetDoc_App.xml) for the add-in, available on a shared


network folder or add-in catalog. The manifest file must point to the location of
the HTML file mentioned previously.

Alternatively, you can create an add-in for your Office application using one of the
following options. You won't have to create new files as the equivalent of each required
file will be available for you to update. For example, the Yeoman generator options
include ./src/taskpane/taskpane.html, ./src/taskpane/taskpane.js,
./src/taskpane/taskpane.css, and ./manifest.xml.

PowerPoint
Visual Studio
Yeoman generator for Office Add-ins
Word
Visual Studio
Yeoman generator for Office Add-ins

Core concepts to know for creating a task pane add-in


Before you begin creating this add-in for PowerPoint or Word, you should be familiar
with building Office Add-ins and working with HTTP requests. This article doesn't
discuss how to decode Base64-encoded text from an HTTP request on a web server.

Create the manifest for the add-in


The XML manifest file for an Office Add-in provides important information about the
add-in: what applications can host it, the location of the HTML file, the add-in title and
description, and many other characteristics.

1. In a text editor, add the following code to the manifest file.

XML

<?xml version="1.0" encoding="utf-8" ?>


<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="TaskPaneApp">
<Id>[Replace_With_Your_GUID]</Id>
<Version>1.0</Version>
<ProviderName>[Provider Name]</ProviderName>
<DefaultLocale>EN-US</DefaultLocale>
<DisplayName DefaultValue="Get Doc add-in" />
<Description DefaultValue="My get PowerPoint or Word document add-
in." />
<IconUrl
DefaultValue="http://officeimg.vo.msecnd.net/_layouts/images/general/of
fice_logo.jpg" />
<SupportUrl DefaultValue="[Insert the URL of a page that provides
support information for the app]" />
<Hosts>
<Host Name="Document" />
<Host Name="Presentation" />
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="[Network location of
app]/GetDoc_App.html" />
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
</OfficeApp>

2. Save the file as GetDoc_App.xml using UTF-8 encoding to a network location or to


an add-in catalog.
Create the user interface for the add-in
For the user interface of the add-in, you can use HTML written directly into the
GetDoc_App.html file. The programming logic and functionality of the add-in must be
contained in a JavaScript file (for example, GetDoc_App.js).

Use the following procedure to create a simple user interface for the add-in that
includes a heading and a single button.

1. In a new file in the text editor, add the HTML for your selected Office application.

PowerPoint

HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
<title>Publish presentation</title>
<link rel="stylesheet" type="text/css" href="Program.css"
/>
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-
1.9.0.min.js" type="text/javascript"></script>
<script
src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"
type="text/javascript"></script>
<script src="GetDoc_App.js"></script>
</head>
<body>
<form>
<h1>Publish presentation</h1>
<br />
<div><input id='submit' type="button" value="Submit" />
</div>
<br />
<div><h2>Status</h2>
<div id="status"></div>
</div>
</form>
</body>
</html>

2. Save the file as GetDoc_App.html using UTF-8 encoding to a network location or


to a web server.
7 Note

Be sure that the head tags of the add-in contains a script tag with a valid link
to the Office.js file.

3. We'll use some CSS to give the add-in a simple yet modern and professional
appearance. Use the following CSS to define the style of the add-in.

In a new file in the text editor, add the following CSS.

css

body
{
font-family: "Segoe UI Light","Segoe UI",Tahoma,sans-serif;
}
h1,h2
{
text-decoration-color:#4ec724;
}
input [type="submit"], input[type="button"]
{
height:24px;
padding-left:1em;
padding-right:1em;
background-color:white;
border:1px solid grey;
border-color: #dedfe0 #b9b9b9 #b9b9b9 #dedfe0;
cursor:pointer;
}

4. Save the file as Program.css using UTF-8 encoding to the network location or to
the web server where the GetDoc_App.html file is located.

Add the JavaScript to get the document


In the code for the add-in, a handler to the Office.initialize event adds a handler to the
click event of the Submit button on the form and informs the user that the add-in is
ready.

The following code example shows the event handler for the Office.initialize event
along with a helper function, updateStatus , for writing to the status div.

JavaScript
// The initialize or onReady function is required for all add-ins.
Office.initialize = function (reason) {

// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {

// Run sendFile when Submit is clicked.


$('#submit').click(function () {
sendFile();
});

// Update status.
updateStatus("Ready to send file.");
});
}

// Create a function for writing to the status div.


function updateStatus(message) {
var statusInfo = $('#status');
statusInfo[0].innerHTML += message + "<br/>";
}

When you choose the Submit button in the UI, the add-in calls the sendFile function,
which contains a call to the Document.getFileAsync method. The getFileAsync method
uses the asynchronous pattern, similar to other methods in the Office JavaScript API. It
has one required parameter, fileType, and two optional parameters, options and callback.

The fileType parameter expects one of three constants from the FileType enumeration:
Office.FileType.Compressed ("compressed"), Office.FileType.PDF ("pdf"), or

Office.FileType.Text ("text"). The current file type support for each platform is listed

under the Document.getFileType remarks. When you pass in Compressed for the
fileType parameter, the getFileAsync method returns the current document as a
PowerPoint presentation file (*.pptx) or Word document file (*.docx) by creating a
temporary copy of the file on the local computer.

The getFileAsync method returns a reference to the file as a File object. The File
object exposes the following four members.

size property
sliceCount property
getSliceAsync method
closeAsync method

The size property returns the number of bytes in the file. The sliceCount returns the
number of Slice objects (discussed later in this article) in the file.
Use the following code to get the current PowerPoint or Word document as a File
object using the Document.getFileAsync method and then make a call to the locally
defined getSlice function. Note that the File object, a counter variable, and the total
number of slices in the file are passed along in the call to getSlice in an anonymous
object.

JavaScript

// Get all of the content from a PowerPoint or Word document in 100-KB


chunks of text.
function sendFile() {
Office.context.document.getFileAsync("compressed",
{ sliceSize: 100000 },
function (result) {

if (result.status === Office.AsyncResultStatus.Succeeded) {

// Get the File object from the result.


var myFile = result.value;
var state = {
file: myFile,
counter: 0,
sliceCount: myFile.sliceCount
};

updateStatus("Getting file of " + myFile.size + " bytes");


getSlice(state);
} else {
updateStatus(result.status);
}
});
}

The local function getSlice makes a call to the File.getSliceAsync method to retrieve
a slice from the File object. The getSliceAsync method returns a Slice object from
the collection of slices. It has two required parameters, sliceIndex and callback. The
sliceIndex parameter takes an integer as an indexer into the collection of slices. Like
other methods in the Office JavaScript API, the getSliceAsync method also takes a
callback function as a parameter to handle the results from the method call.

The Slice object gives you access to the data contained in the file. Unless otherwise
specified in the options parameter of the getFileAsync method, the Slice object is 4
MB in size. The Slice object exposes three properties: size, data, and index. The size
property gets the size, in bytes, of the slice. The index property gets an integer that
represents the slice's position in the collection of slices.

JavaScript
// Get a slice from the file and then call sendSlice.
function getSlice(state) {
state.file.getSliceAsync(state.counter, function (result) {
if (result.status == Office.AsyncResultStatus.Succeeded) {
updateStatus("Sending piece " + (state.counter + 1) + " of " +
state.sliceCount);
sendSlice(result.value, state);
} else {
updateStatus(result.status);
}
});
}

The Slice.data property returns the raw data of the file as a byte array. If the data is in
text format (that is, XML or plain text), the slice contains the raw text. If you pass in
Office.FileType.Compressed for the fileType parameter of Document.getFileAsync , the
slice contains the binary data of the file as a byte array. In the case of a PowerPoint or
Word file, the slices contain byte arrays.

You must implement your own function (or use an available library) to convert byte array
data to a Base64-encoded string. For information about Base64 encoding with
JavaScript, see Base64 encoding and decoding .

Once you've converted the data to Base64, you can then transmit it to a web server in
several ways, including as the body of an HTTP POST request.

Add the following code to send a slice to a web service.

7 Note

This code sends a PowerPoint or Word file to the web server in multiple slices. The
web server or service must append each individual slice into a single file, and then
save it as a .pptx or .docx file before you can perform any manipulations on it.

JavaScript

function sendSlice(slice, state) {


var data = slice.data;

// If the slice contains data, create an HTTP request.


if (data) {

// Encode the slice data, a byte array, as a Base64 string.


// NOTE: The implementation of myEncodeBase64(input) function isn't
// included with this example. For information about Base64 encoding
with
// JavaScript, see
https://developer.mozilla.org/docs/Web/JavaScript/Base64_encoding_and_decodi
ng.
var fileData = myEncodeBase64(data);

// Create a new HTTP request. You need to send the request


// to a webpage that can receive a post.
var request = new XMLHttpRequest();

// Create a handler function to update the status


// when the request has been sent.
request.onreadystatechange = function () {
if (request.readyState == 4) {

updateStatus("Sent " + slice.size + " bytes.");


state.counter++;

if (state.counter < state.sliceCount) {


getSlice(state);
} else {
closeFile(state);
}
}
}

request.open("POST", "[Your receiving page or service]");


request.setRequestHeader("Slice-Number", slice.index);

// Send the file as the body of an HTTP POST


// request to the web server.
request.send(fileData);
}
}

As the name implies, the File.closeAsync method closes the connection to the
document and frees up resources. Although the Office Add-ins sandbox garbage
collects out-of-scope references to files, it's still a best practice to explicitly close files
once your code is done with them. The closeAsync method has a single parameter,
callback, that specifies the function to call on the completion of the call.

JavaScript

function closeFile(state) {
// Close the file when you're done with it.
state.file.closeAsync(function (result) {

// If the result returns as a success, the


// file has been successfully closed.
if (result.status === Office.AsyncResultStatus.Succeeded) {
updateStatus("File closed.");
} else {
updateStatus("File couldn't be closed.");
}
});
}

The final JavaScript file could look like the following:

JavaScript

/*
* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT license.
* See LICENSE in the project root for license information.
*/

// The initialize or onReady function is required for all add-ins.


Office.initialize = function (reason) {

// Checks for the DOM to load using the jQuery ready method.
$(document).ready(function () {

// Run sendFile when Submit is clicked.


$('#submit').click(function () {
sendFile();
});

// Update status.
updateStatus("Ready to send file.");
});
}

// Create a function for writing to the status div.


function updateStatus(message) {
var statusInfo = $('#status');
statusInfo[0].innerHTML += message + "<br/>";
}

// Get all of the content from a PowerPoint or Word document in 100-KB


chunks of text.
function sendFile() {
Office.context.document.getFileAsync("compressed",
{ sliceSize: 100000 },
function (result) {

if (result.status === Office.AsyncResultStatus.Succeeded) {

// Get the File object from the result.


var myFile = result.value;
var state = {
file: myFile,
counter: 0,
sliceCount: myFile.sliceCount
};
updateStatus("Getting file of " + myFile.size + " bytes");
getSlice(state);
} else {
updateStatus(result.status);
}
});
}

// Get a slice from the file and then call sendSlice.


function getSlice(state) {
state.file.getSliceAsync(state.counter, function (result) {
if (result.status == Office.AsyncResultStatus.Succeeded) {
updateStatus("Sending piece " + (state.counter + 1) + " of " +
state.sliceCount);
sendSlice(result.value, state);
} else {
updateStatus(result.status);
}
});
}

function sendSlice(slice, state) {


var data = slice.data;

// If the slice contains data, create an HTTP request.


if (data) {

// Encode the slice data, a byte array, as a Base64 string.


// NOTE: The implementation of myEncodeBase64(input) function isn't
// included with this example. For information about Base64 encoding
with
// JavaScript, see
https://developer.mozilla.org/docs/Web/JavaScript/Base64_encoding_and_decodi
ng.
var fileData = myEncodeBase64(data);

// Create a new HTTP request. You need to send the request


// to a webpage that can receive a post.
var request = new XMLHttpRequest();

// Create a handler function to update the status


// when the request has been sent.
request.onreadystatechange = function () {
if (request.readyState == 4) {

updateStatus("Sent " + slice.size + " bytes.");


state.counter++;

if (state.counter < state.sliceCount) {


getSlice(state);
} else {
closeFile(state);
}
}
}
request.open("POST", "[Your receiving page or service]");
request.setRequestHeader("Slice-Number", slice.index);

// Send the file as the body of an HTTP POST


// request to the web server.
request.send(fileData);
}
}

function closeFile(state) {
// Close the file when you're done with it.
state.file.closeAsync(function (result) {

// If the result returns as a success, the


// file has been successfully closed.
if (result.status === Office.AsyncResultStatus.Succeeded) {
updateStatus("File closed.");
} else {
updateStatus("File couldn't be closed.");
}
});
}
Read and write data to the active
selection in a document or spreadsheet
Article • 04/11/2023

The Document object exposes methods that let you read and write to the user's current
selection in a document or spreadsheet. To do that, the Document object provides the
getSelectedDataAsync and setSelectedDataAsync methods. This topic also describes

how to read, write, and create event handlers to detect changes to the user's selection.

The getSelectedDataAsync method only works against the user's current selection. If you
need to persist the selection in the document, so that the same selection is available to
read and write across sessions of running your add-in, you must add a binding using the
Bindings.addFromSelectionAsync method (or create a binding with one of the other
"addFrom" methods of the Bindings object). For information about creating a binding to
a region of a document, and then reading and writing to a binding, see Bind to regions
in a document or spreadsheet.

Read selected data


The following example shows how to get data from a selection in a document by using
the getSelectedDataAsync method.

JavaScript

Office.context.document.getSelectedDataAsync(Office.CoercionType.Text,
function (asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write('Action failed. Error: ' + asyncResult.error.message);
}
else {
write('Selected data: ' + asyncResult.value);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

In this example, the first parameter, coercionType, is specified as


Office.CoercionType.Text (you can also specify this parameter by using the literal string

"text" ). This means that the value property of the AsyncResult object that is available
from the asyncResult parameter in the callback function will return a string that contains
the selected text in the document. Specifying different coercion types will result in
different values. Office.CoercionType is an enumeration of available coercion type
values. Office.CoercionType.Text evaluates to the string "text".

 Tip

When should you use the matrix versus table coercionType for data access? If
you need your selected tabular data to grow dynamically when rows and columns
are added, and you must work with table headers, you should use the table data
type (by specifying the coercionType parameter of the getSelectedDataAsync
method as "table" or Office.CoercionType.Table ). Adding rows and columns
within the data structure is supported in both table and matrix data, but appending
rows and columns is supported only for table data. If you aren't planning on adding
rows and columns, and your data doesn't require header functionality, then you
should use the matrix data type (by specifying the coercionType parameter of
getSelectedDataAsync method as "matrix" or Office.CoercionType.Matrix ), which

provides a simpler model of interacting with the data.

The anonymous function that is passed into the method as the second parameter,
callback, is executed when the getSelectedDataAsync operation is completed. The
function is called with a single parameter, asyncResult, which contains the result and the
status of the call. If the call fails, the error property of the AsyncResult object provides
access to the Error object. You can check the value of the Error.name and Error.message
properties to determine why the set operation failed. Otherwise, the selected text in the
document is displayed.

The AsyncResult.status property is used in the if statement to test whether the call
succeeded. Office.AsyncResultStatus is an enumeration of available AsyncResult.status
property values. Office.AsyncResultStatus.Failed evaluates to the string "failed" (and,
again, can also be specified as that literal string).

Write data to the selection


The following example shows how to set the selection to show "Hello World!".

JavaScript

Office.context.document.setSelectedDataAsync("Hello World!", function


(asyncResult) {
if (asyncResult.status == Office.AsyncResultStatus.Failed) {
write(asyncResult.error.message);
}
});

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

Passing in different object types for the data parameter will have different results. The
result depends on what is currently selected in the document, which Office client
application is hosting your add-in, and whether the data passed in can be coerced to
the current selection.

The anonymous function passed into the setSelectedDataAsync method as the callback
parameter is executed when the asynchronous call is completed. When you write data to
the selection by using the setSelectedDataAsync method, the asyncResult parameter of
the callback provides access only to the status of the call, and to the Error object if the
call fails.

Detect changes in the selection


The following example shows how to detect changes in the selection by using the
Document.addHandlerAsync method to add an event handler for the SelectionChanged
event on the document.

JavaScript

Office.context.document.addHandlerAsync("documentSelectionChanged",
myHandler, function(result){}
);

// Event handler function.


function myHandler(eventArgs){
write('Document Selection Changed');
}

// Function that writes to a div with id='message' on the page.


function write(message){
document.getElementById('message').innerText += message;
}

The first parameter, eventType, specifies the name of the event to subscribe to. Passing
the string "documentSelectionChanged" for this parameter is equivalent to passing the
Office.EventType.DocumentSelectionChanged event type of the Office.EventType

enumeration.
The myHandler() function that is passed into the method as the second parameter,
handler, is an event handler that is executed when the selection is changed on the
document. The function is called with a single parameter, eventArgs, which will contain a
reference to a DocumentSelectionChangedEventArgs object when the asynchronous
operation completes. You can use the DocumentSelectionChangedEventArgs.document
property to access the document that raised the event.

7 Note

You can add multiple event handlers for a given event by calling the
addHandlerAsync method again and passing in an additional event handler function
for the handler parameter. This will work correctly as long as the name of each
event handler function is unique.

Stop detecting changes in the selection


The following example shows how to stop listening to the Document.SelectionChanged
event by calling the document.removeHandlerAsync method.

JavaScript

Office.context.document.removeHandlerAsync("documentSelectionChanged",
{handler:myHandler}, function(result){});

The myHandler function name that is passed as the second parameter, handler, specifies
the event handler that will be removed from the SelectionChanged event.

) Important

If the optional handler parameter is omitted when the removeHandlerAsync method


is called, all event handlers for the specified eventType will be removed.
Use search options in your Word add-in
to find text
Article • 03/16/2023

Add-ins frequently need to act based on the text of a document. A search method is
exposed by every content control (this includes Body, Paragraph, Range, Table,
TableRow, and the base ContentControl object). This method takes in a string (or
wildcard expression) representing the text you are searching for and a SearchOptions
object. It returns a collection of ranges which match the search text.

) Important

The Word client may limit the available search options. For more details about
current support, see Find and replace text .

Search options
The search options are a collection of boolean values defining how the search
parameter should be treated.

Property Description

ignorePunct Gets or sets a value indicating whether to ignore all punctuation characters
between words. Corresponds to the "Ignore punctuation characters"
checkbox in the Find and Replace dialog box.

ignoreSpace Gets or sets a value indicating whether to ignore all whitespace between
words. Corresponds to the "Ignore white-space characters" checkbox in the
Find and Replace dialog box.

matchCase Gets or sets a value indicating whether to perform a case-sensitive search.


Corresponds to the "Match case" checkbox in the Find and Replace dialog
box.

matchPrefix Gets or sets a value indicating whether to match words that begin with the
search string. Corresponds to the "Match prefix" checkbox in the Find and
Replace dialog box.

matchSuffix Gets or sets a value indicating whether to match words that end with the
search string. Corresponds to the "Match suffix" checkbox in the Find and
Replace dialog box.
Property Description

matchWholeWord Gets or sets a value indicating whether to find operation only entire words,
not text that is part of a larger word. Corresponds to the "Find whole words
only" checkbox in the Find and Replace dialog box.

matchWildcards Gets or sets a value indicating whether the search will be performed using
special search operators. Corresponds to the "Use wildcards" checkbox in the
Find and Replace dialog box.

Search for special characters


The following table lists the search notation for certain special characters.

To find Notation

Paragraph mark ^p

Tab mark ^t

Any character ^?

Any digit ^#

Any letter ^$

Caret character ^^

Section character ^%

Paragraph character ^v

Column break ^n

Em dash ^+

En dash ^=

Endnote mark ^e

Field ^d

Footnote mark ^f

Graphic ^g

Manual line break ^l

Manual page break ^m

Nonbreaking hyphen ^~
To find Notation

Nonbreaking space ^s

Optional hyphen ^-

Section break ^b

White Space ^w

Wildcard guidance
The following table provides guidance around the Word JavaScript API's search
wildcards.

To find Wildcard Sample

Any single character ? s?t finds sat and set.

Any string of characters * s*d finds sad and started.

The beginning of a word < <(inter) finds interesting and intercept,


but not splintered.

The end of a word > (in)> finds in and within, but not
interesting.

One of the specified characters [] w[io]n finds win and won.

Any single character in this range [-] [r-t]ight finds right, sight, and tight.
Ranges must be in ascending order.

Any single character except the characters in [!x-z] t[!a-m]ck finds tock and tuck, but not
the range inside the brackets tack or tick.

Exactly n occurrences of the previous {n} fe{2}d finds feed but not fed.
character or expression

At least n occurrences of the previous {n,} fe{1,}d finds fed and feed.
character or expression

From n to m occurrences of the previous {n,m} 10{1,3} finds 10, 100, and 1000.
character or expression

One or more occurrences of the previous @ lo@t finds lot and loot.
character or expression

Escape special characters


Wildcard search is essentially the same as searching on a regular expression. There are
special characters in regular expressions, including '[', ']', '(', ')', '{', '}', '*', '?', '<', '>', '!', and
'@'. If one of these characters is part of the literal string the code is searching for, then it
needs to be escaped, so that Word knows it should be treated literally and not as part of
the logic of the regular expression. To escape a character in the Word UI search, you
would precede it with a backslash character ('\'), but to escape it programmatically, put
it between '[]' characters. For example, '[*]*' searches for any string that begins with a '*'
followed by any number of other characters.

Examples
The following examples demonstrate common scenarios.

Ignore punctuation search


JavaScript

// Run a batch operation against the Word object model.


await Word.run(async (context) => {

// Queue a command to search the document and ignore punctuation.


const searchResults = context.document.body.search('video you',
{ignorePunct: true});

// Queue a command to load the font property values.


searchResults.load('font');

// Synchronize the document state.


await context.sync();
console.log('Found count: ' + searchResults.items.length);

// Queue a set of commands to change the font for each found item.
for (let i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.color = 'purple';
searchResults.items[i].font.highlightColor = '#FFFF00'; //Yellow
searchResults.items[i].font.bold = true;
}

// Synchronize the document state.


await context.sync();
});

Search based on a prefix


JavaScript
// Run a batch operation against the Word object model.
await Word.run(async (context) => {

// Queue a command to search the document based on a prefix.


const searchResults = context.document.body.search('vid', {matchPrefix:
true});

// Queue a command to load the font property values.


searchResults.load('font');

// Synchronize the document state.


await context.sync();
console.log('Found count: ' + searchResults.items.length);

// Queue a set of commands to change the font for each found item.
for (let i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.color = 'purple';
searchResults.items[i].font.highlightColor = '#FFFF00'; //Yellow
searchResults.items[i].font.bold = true;
}

// Synchronize the document state.


await context.sync();
});

Search based on a suffix


JavaScript

// Run a batch operation against the Word object model.


await Word.run(async (context) => {

// Queue a command to search the document for any string of characters


after 'ly'.
const searchResults = context.document.body.search('ly', {matchSuffix:
true});

// Queue a command to load the font property values.


searchResults.load('font');

// Synchronize the document state.


await context.sync();
console.log('Found count: ' + searchResults.items.length);

// Queue a set of commands to change the font for each found item.
for (let i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.color = 'orange';
searchResults.items[i].font.highlightColor = 'black';
searchResults.items[i].font.bold = true;
}
// Synchronize the document state.
await context.sync();
});

Search using a wildcard


JavaScript

// Run a batch operation against the Word object model.


await Word.run(async (context) => {

// Queue a command to search the document with a wildcard


// for any string of characters that starts with 'to' and ends with 'n'.
const searchResults = context.document.body.search('to*n',
{matchWildcards: true});

// Queue a command to load the font property values.


searchResults.load('font');

// Synchronize the document state.


await context.sync();
console.log('Found count: ' + searchResults.items.length);

// Queue a set of commands to change the font for each found item.
for (let i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.color = 'purple';
searchResults.items[i].font.highlightColor = 'pink';
searchResults.items[i].font.bold = true;
}

// Synchronize the document state.


await context.sync();
});

Search for a special character


JavaScript

// Run a batch operation against the Word object model.


await Word.run(async (context) => {

// Queue a command to search the document for tabs.


const searchResults = context.document.body.search('^t');

// Queue a command to load the font property values.


searchResults.load('font');

// Synchronize the document state.


await context.sync();
console.log('Found count: ' + searchResults.items.length);

// Queue a set of commands to change the font for each found item.
for (let i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.color = 'purple';
searchResults.items[i].font.highlightColor = 'pink';
searchResults.items[i].font.bold = true;
}

// Synchronize the document state.


await context.sync();
});

Try code examples in Script Lab


Get the Script Lab add-in and try out the code examples provided in this article. To
learn more about Script Lab, see Explore Office JavaScript API using Script Lab.

See also
More information can be found in the following:

Word JavaScript Reference API


Related Word code samples available in Script Lab:
Search
Get word count
Find and replace text in Word
Understand when and how to use Office
Open XML in your Word add-in
Article • 06/07/2023

Provided by: Stephanie Krieger, Microsoft Corporation | Juan Balmori Labra, Microsoft
Corporation

If you're building Office Add-ins to run in Word, you might already know that the Office
JavaScript API (Office.js) offers several formats for reading and writing document
content. These are called coercion types, and they include plain text, tables, HTML, and
Office Open XML.

Options for adding rich content


So what are your options when you need to add rich content to a document, such as
images, formatted tables, charts, or even just formatted text?

1. Word JavaScript APIs. Start with the APIs available through the WordApi
requirement sets to see if they provide what you need. For an example, see the
Insert formatted text code snippet. You can try this and other snippets in the
Script Lab add-in on Word! To learn more about Script Lab, see Explore Office
JavaScript API using Script Lab.

2. HTML coercion. If APIs aren't yet available, you can use HTML for inserting some
types of rich content, such as pictures. Depending on your scenario, there can be
drawbacks to HTML coercion, such as limitations in the formatting and positioning
options available to your content.

3. Office Open XML. Because Office Open XML is the language in which Word
documents (such as .docx and .dotx) are written, you can insert virtually any type of
content that a user can add to a Word document, with virtually any type of
formatting the user can apply. Determining the Office Open XML markup you need
to get it done is easier than you might think.

7 Note

Office Open XML is also the language behind PowerPoint and Excel (and, as of
Office 2013, Visio) documents. However, currently, you can coerce content as Office
Open XML only in Office Add-ins created for Word. For more information about
Office Open XML, including the complete language reference documentation, see
the See also section.

Download the companion code sample


Download the code sample Load and write Open XML in your Word add-in , which
contains the Office Open XML markup and Office.js code required for inserting any of
the following examples into Word.

Learn about content types


To begin, take a look at some of the content types you can insert using Office Open XML
coercion.

Throughout this article, the terms content types and rich content refer to the types of
rich content you can insert into a Word document.

Figure 1. Text with direct formatting

Use direct formatting to specify exactly what the text will look like regardless of existing
formatting in the user's document.

Figure 2. Text formatted using a style

Use a style to automatically coordinate the look of text you insert with the user's
document.

Figure 3. A simple image


Use the same method for inserting any Office-supported image format.

Figure 4. An image formatted using picture styles and effects

Adding high quality formatting and effects to your images requires much less markup
than you might expect.

Figure 5. A content control

Use content controls with your add-in to add content at a specified (bound) location
rather than at the selection.

Figure 6. A text box with WordArt formatting

Text effects are available in Word for text inside a text box (as shown here) or for regular
body text.

Figure 7. A shape
Insert built-in or custom drawing shapes, with or without text and formatting effects.

Figure 8. A table with direct formatting

Include text formatting, borders, shading, cell sizing, or any table formatting you need.

Figure 9. A table formatted using a table style

Use built-in or custom table styles just as easily as using a paragraph style for text.

Figure 10. A SmartArt diagram

Office offers a wide array of SmartArt diagram layouts (and you can use Office Open
XML to create your own).

Figure 11. A chart


You can insert Excel charts as live charts in Word documents, which also means you can
use them in your add-in for Word. As you can see by the preceding examples, you can
use Office Open XML coercion to insert essentially any type of content that a user can
insert into their own document. There are two simple ways to get the Office Open XML
markup you need. Either add your rich content to an otherwise blank Word document
and then save the file in Word XML Document format or use a test add-in with the
getSelectedDataAsync method to grab the markup. Both approaches provide essentially
the same result.

 Tip

An Office Open XML document is actually a compressed package of files that


represent the document contents. Saving the file in the Word XML Document
format gives you the entire Office Open XML package flattened into one XML file,
which is also what you get when using getSelectedDataAsync to retrieve the Office
Open XML markup.

If you save the file to an XML format from Word, note that there are two options under
the Save as Type list in the Save As dialog box for .xml format files. Be sure to choose
Word XML Document, not the Word 2003 option.

Download the code sample named Word-Add-in-Get-Set-EditOpen-XML , which you


can use as a tool to retrieve and test your markup.

So is that all there is to it? Well, not quite. Yes, for many scenarios, you could use the full,
flattened Office Open XML result you see with either of the preceding methods and it
would work. The good news is that you probably don't need most of that markup.

If you're one of the many add-in developers seeing Office Open XML markup for the
first time, trying to make sense of the massive amount of markup you get for the
simplest piece of content might seem overwhelming, but it doesn't have to be.
In this topic, you'll use some common scenarios we've been hearing from the Office
Add-ins developer community to show you techniques for simplifying Office Open XML
for use in your add-in. We'll explore the markup for some types of content shown earlier
along with the information you need for minimizing the Office Open XML payload. We'll
also look at the code you need for inserting rich content into a document at the active
selection and how to use Office Open XML with the bindings object to add or replace
content at specified locations.

Explore the Office Open XML document


package
When you use getSelectedDataAsync to retrieve the Office Open XML for a selection of
content (or when you save the document in Word XML Document format), what you're
getting isn't just the markup that describes your selected content; it's an entire
document with many options and settings that you almost certainly don't need. In fact,
if you use that method from a document that contains a task pane add-in, the markup
you get even includes your task pane.

Even a simple Word document package includes parts for document properties, styles,
theme (formatting settings), web settings, fonts, and then some, in addition to parts for
the actual content.

For example, say that you want to insert just a paragraph of text with direct formatting,
as shown earlier in Figure 1. When you grab the Office Open XML for the formatted text
using getSelectedDataAsync , you see a large amount of markup. That markup includes a
package element that represents an entire document, which contains several parts
(commonly referred to as document parts or, in the Office Open XML, as package parts),
as you see listed in Figure 13. Each part represents a separate file within the package.

You can edit Office Open XML markup in a text editor like Notepad. If you open it in
Visual Studio, use Edit > Advanced > Format Document (Ctrl+K, Ctrl+D) to format the
package for easier editing. Then you can collapse or expand document parts or sections
of them, as shown in Figure 12, to more easily review and edit the content of the Office
Open XML package. Each document part begins with a pkg:part tag.

Figure 12. Collapse and expand package parts for easier editing in Visual Studio
Figure 13. The parts included in a basic Word Office Open XML document package

With all that markup, you might be surprised to discover that the only elements you
actually need to insert the formatted text example are pieces of the .rels part and the
document.xml part.

 Tip

The two lines of markup above the package tag (the XML declarations for version
and Office program ID) are assumed when you use the Office Open XML coercion
type, so you don't need to include them. Keep them if you want to open your
edited markup as a Word document to test it.

Several of the other types of content shown at the start of this topic require additional
parts as well (beyond those shown in Figure 13), and you'll address those later in this
topic. Meanwhile, since you'll see most of the parts shown in Figure 13 in the markup for
any Word document package, here's a quick summary of what each of these parts is for
and when you need it:

Inside the package tag, the first part is the .rels file, which defines relationships
between the top-level parts of the package (these are typically the document
properties, thumbnail (if any), and main document body). Some of the content in
this part is always required in your markup because you need to define the
relationship of the main document part (where your content resides) to the
document package.
The document.xml.rels part defines relationships for additional parts required by
the document.xml (main body) part, if any.

) Important

The .rels files in your package (such as the top-level .rels, document.xml.rels,
and others you may see for specific types of content) are an extremely
important tool that you can use as a guide for helping you quickly edit down
your Office Open XML package. To learn more about how to do this, see
Create your own markup: best practices later in this topic.

The document.xml part is the content in the main body of the document. You need
elements of this part, of course, since that's where your content appears. But, you
don't need everything you see in this part. We'll look at that in more detail later.

Many parts are automatically ignored by the Set methods when inserting content
into a document using Office Open XML coercion, so you might as well remove
them. These include the theme1.xml file (the document's formatting theme), the
document properties parts (core, add-in, and thumbnail), and setting files
(including settings, webSettings, and fontTable).

In the Figure 1 example, text formatting is directly applied (that is, each font and
paragraph formatting setting applied individually). But, if you use a style (such as if
you want your text to automatically take on the formatting of the Heading 1 style
in the destination document) as shown earlier in Figure 2, then you would need
part of the styles.xml part as well as a relationship definition for it. For more
information, see the topic section Add objects that use additional Office Open XML
parts.

Insert document content at the selection


Let's take a look at the minimal Office Open XML markup required for the formatted
text example shown in Figure 1 and the JavaScript required for inserting it at the active
selection in the document.

Simplified Office Open XML markup


The Office Open XML example shown here was edited as described in the preceding
section to leave just required document parts and only required elements within each of
those parts. You'll walk through how to edit the markup yourself (and we'll explain a bit
more about the pieces that remain here) in the next section of the topic.
XML

<pkg:package
xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage">
<pkg:part pkg:name="/_rels/.rels"
pkg:contentType="application/vnd.openxmlformats-package.relationships+xml"
pkg:padding="512">
<pkg:xmlData>
<Relationships
xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/of
ficeDocument" Target="word/document.xml"/>
</Relationships>
</pkg:xmlData>
</pkg:part>
<pkg:part pkg:name="/word/document.xml"
pkg:contentType="application/vnd.openxmlformats-
officedocument.wordprocessingml.document.main+xml">
<pkg:xmlData>
<w:document
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" >
<w:body>
<w:p>
<w:pPr>
<w:spacing w:before="360" w:after="0" w:line="480"
w:lineRule="auto"/>
<w:rPr>
<w:color w:val="70AD47" w:themeColor="accent6"/>
<w:sz w:val="28"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:color w:val="70AD47" w:themeColor="accent6"/>
<w:sz w:val="28"/>
</w:rPr>
<w:t>This text has formatting directly applied to achieve its
font size, color, line spacing, and paragraph spacing.</w:t>
</w:r>
</w:p>
</w:body>
</w:document>
</pkg:xmlData>
</pkg:part>
</pkg:package>

7 Note

If you add the markup shown here to an XML file along with the XML declaration
tags for version and mso-application at the top of the file (shown in Figure 13), you
can open it in Word as a Word document. Or, without those tags, you can still open
it using File > Open in Word. You'll see Compatibility Mode on the title bar in
Word, because you removed the settings that tell Word this is a Word document.
Since you're adding this markup to an existing Word document, that won't affect
your content at all.

JavaScript for using setSelectedDataAsync


Once you save the preceding Office Open XML as an XML file that's accessible from
your solution, use the following function to set the formatted text content in the
document using Office Open XML coercion.

In the following function, notice that all but the last line are used to get your saved
markup for use in the setSelectedDataAsync method call at the end of the function.
setSelectedDataASync requires only that you specify the content to be inserted and the
coercion type.

Replace yourXMLfilename with the name and path of the XML file as you've saved it in
your solution. If you aren't sure where to include XML files in your solution or how to
reference them in your code, see the Load and write Open XML in your Word add-in
code sample for examples of that and a working example of the markup and JavaScript
shown here.

JavaScript

function writeContent() {
const myOOXMLRequest = new XMLHttpRequest();
let myXML;
myOOXMLRequest.open('GET', 'yourXMLfilename', false);
myOOXMLRequest.send();
if (myOOXMLRequest.status === 200) {
myXML = myOOXMLRequest.responseText;
}
Office.context.document.setSelectedDataAsync(myXML, { coercionType:
'ooxml' });
}

Create your own markup: best practices


Let's take a closer look at the markup you need to insert the preceding formatted text
example.
For this example, start by simply deleting all document parts from the package other
than .rels and document.xml. Then, you'll edit those two required parts to simplify things
further.

) Important

Use the .rels parts as a map to quickly gauge what's included in the package and
determine what parts you can delete completely (that is, any parts not related to or
referenced by your content). Remember that every document part must have a
relationship defined in the package and those relationships appear in the .rels files.
So you should see all of them listed in either .rels, document.xml.rels, or a content-
specific .rels file.

The following markup shows the required .rels part before editing. Since we're deleting
the add-in and core document property parts, and the thumbnail part, you need to
delete those relationships from .rels as well. Notice that this will leave only the
relationship (with the relationship ID "rID1" in the following example) for document.xml.

XML

<pkg:part pkg:name="/_rels/.rels"
pkg:contentType="application/vnd.openxmlformats-package.relationships+xml"
pkg:padding="512">
<pkg:xmlData>
<Relationships
xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId3"
Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/
core-properties" Target="docProps/core.xml"/>
<Relationship Id="rId2"
Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/
thumbnail" Target="docProps/thumbnail.emf"/>
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/of
ficeDocument" Target="word/document.xml"/>
<Relationship Id="rId4"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/ex
tended-properties" Target="docProps/app.xml"/>
</Relationships>
</pkg:xmlData>
</pkg:part>

Remove the relationships (that is, the Relationship tag) for any parts that you
completely remove from the package. Including a part without a corresponding
relationship, or excluding a part and leaving its relationship in the package, will result in
an error.
The following markup shows the document.xml part, which includes our sample
formatted text content before editing.

XML

<pkg:part pkg:name="/word/document.xml"
pkg:contentType="application/vnd.openxmlformats-
officedocument.wordprocessingml.document.main+xml">
<pkg:xmlData>
<w:document mc:Ignorable="w14 w15 wp14"
xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanva
s" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships
" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDraw
ing"
xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDra
wing" xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml"
xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup
"
xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk"
xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml"
xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape
">
<w:body>
<w:p>
<w:pPr>
<w:spacing w:before="360" w:after="0" w:line="480"
w:lineRule="auto"/>
<w:rPr>
<w:color w:val="70AD47" w:themeColor="accent6"/>
<w:sz w:val="28"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:color w:val="70AD47" w:themeColor="accent6"/>
<w:sz w:val="28"/>
</w:rPr>
<w:t>This text has formatting directly applied to achieve its
font size, color, line spacing, and paragraph spacing.</w:t>
</w:r>
<w:bookmarkStart w:id="0" w:name="_GoBack"/>
<w:bookmarkEnd w:id="0"/>
</w:p>
<w:p/>
<w:sectPr>
<w:pgSz w:w="12240" w:h="15840"/>
<w:pgMar w:top="1440" w:right="1440" w:bottom="1440"
w:left="1440" w:header="720" w:footer="720" w:gutter="0"/>
<w:cols w:space="720"/>
</w:sectPr>
</w:body>
</w:document>
</pkg:xmlData>
</pkg:part>

Since document.xml is the primary document part where you place your content, take a
quick walk through that part. (Figure 14, which follows this list, provides a visual
reference to show how some of the core content and formatting tags explained here
relate to what you see in a Word document.)

The opening w:document tag includes several namespace ( xmlns ) listings. Many
of those namespaces refer to specific types of content and you only need them if
they're relevant to your content.

Notice that the prefix for the tags throughout a document part refers back to the
namespaces. In this example, the only prefix used in the tags throughout the
document.xml part is w:, so the only namespace that you need to leave in the
opening w:document tag is xmlns:w.

 Tip

If you're editing your markup in Visual Studio, after you delete namespaces in
any part, look through all tags of that part. If you've removed a namespace
that's required for your markup, you'll see a red squiggly underline on the
relevant prefix for affected tags. If you remove the xmlns:mc namespace, you
must also remove the mc:Ignorable attribute that precedes the namespace
listings.

Inside the opening body tag, you see a paragraph tag ( w:p ), which includes our
sample content for this example.

The w:pPr tag includes properties for directly-applied paragraph formatting, such
as space before or after the paragraph, paragraph alignment, or indents. (Direct
formatting refers to attributes that you apply individually to content rather than as
part of a style.) This tag also includes direct font formatting that's applied to the
entire paragraph, in a nested w:rPr (run properties) tag, which contains the font
color and size set in our sample.

You might notice that font sizes and some other formatting settings in Word Office
Open XML markup look like they're double the actual size. That's because
paragraph and line spacing, as well some section formatting properties shown in
the preceding markup, are specified in twips (one-twentieth of a point). Depending
on the types of content you work with in Office Open XML, you may see several
additional units of measure, including English Metric Units (914,400 EMUs to an
inch), which are used for some Office Art (drawingML) values and 100,000 times
actual value, which is used in both drawingML and PowerPoint markup. PowerPoint
also expresses some values as 100 times actual and Excel commonly uses actual
values.

Within a paragraph, any content with like properties is included in a run ( w:r ),
such as is the case with the sample text. Each time there's a change in formatting
or content type, a new run starts. (That is, if just one word in the sample text was
bold, it would be separated into its own run.) In this example, the content includes
just the one text run.

Notice that, because the formatting included in this sample is font formatting (that
is, formatting that can be applied to as little as one character), it also appears in
the properties for the individual run.

Also notice the tags for the hidden "_GoBack" bookmark (w:bookmarkStart and
w:bookmarkEnd ), which appear in Word documents by default. You can always
delete the start and end tags for the GoBack bookmark from your markup.

The last piece of the document body is the w:sectPr tag, or section properties. This
tag includes settings such as margins and page orientation. The content you insert
using setSelectedDataAsync will take on the active section properties in the
destination document by default. So, unless your content includes a section break
(in which case you'll see more than one w:sectPr tag), you can delete this tag.

Figure 14. How common tags in document.xml relate to the content and layout of a Word
document
 Tip

In markup you create, you might see another attribute in several tags that includes
the characters w:rsid, which you don't see in the examples used in this topic. These
are revision identifiers. They're used in Word for the Combine Documents feature
and they're on by default. You'll never need them in markup you're inserting with
your add-in and turning them off makes for much cleaner markup. You can easily
remove existing RSID tags or disable the feature (as described in the following
procedure) so that they aren't added to your markup for new content.

Be aware that if you use the co-authoring capabilities in Word (such as the ability to
simultaneously edit documents with others), you should enable the feature again when
finished generating the markup for your add-in.

To turn off RSID attributes in Word for documents you create going forward, do the
following:

1. In Word, choose File and then choose Options.


2. In the Word Options dialog box, choose Trust Center and then choose Trust
Center Settings.
3. In the Trust Center dialog box, choose Privacy Options and then disable the
setting Store random numbers to improve Combine accuracy. Note that this
setting may not be available in newer versions of Word.

To remove RSID tags from an existing document, try the following shortcut with the
document open in Office Open XML.

1. With your insertion point in the main body of the document, press Ctrl+Home to
go to the top of the document.
2. On the keyboard, press Spacebar, Delete, Spacebar. Then, save the document.

After removing the majority of the markup from this package, you're left with the
minimal markup that needs to be inserted for the sample, as shown in the preceding
section.

Use the same Office Open XML structure for


different content types
Several types of rich content require only the .rels and document.xml components
shown in the preceding example, including content controls, Office drawing shapes and
text boxes, and tables (unless a style is applied to the table). In fact, you can reuse the
same edited package parts and swap out just the body content in document.xml for the
markup of your content.

To check out the Office Open XML markup for the examples of each of these content
types shown earlier in Figures 5 through 8, explore the Load and write Open XML in
your Word add-in code sample referenced in the overview section.

Before you move on, take a look at differences to note for a couple of these content
types and how to swap out the pieces you need.

Understand drawingML markup (Office graphics) in Word


If the markup for your shape or text box looks far more complex than you would expect,
there's a reason for it. With the release of Office 2007, we saw the introduction of the
Office Open XML Formats as well as the introduction of a new Office graphics engine
that PowerPoint and Excel fully adopted. In the 2007 release, Word only incorporated
part of that graphics engine, adopting the updated Excel charting engine, SmartArt
graphics, and advanced picture tools. For shapes and text boxes, Word 2007 continued
to use legacy drawing objects (VML). It was in the 2010 release that Word took the
additional steps with the graphics engine to incorporate updated shapes and drawing
tools.

Typically, as you see for the shape and text box examples included in the Load and write
Open XML in your Word add-in code sample, the fallback markup can be removed.
Word automatically adds missing fallback markup to shapes when a document is saved.
However, if you prefer to keep the fallback markup to ensure that you're supporting all
user scenarios, there's no harm in retaining it.

If you have grouped drawing objects included in your content, you'll see additional (and
apparently repetitive) markup, but this must be retained. Portions of the markup for
drawing shapes are duplicated when the object is included in a group.

) Important

When working with text boxes and drawing shapes, be sure to check namespaces
carefully before removing them from document.xml. (Or, if you're reusing markup
from another object type, be sure to add back any required namespaces you might
have previously removed from document.xml.) A substantial portion of the
namespaces included by default in document.xml are there for drawing object
requirements.
About graphic positioning
In the code samples Load and write Open XML in your Word add-in and Word-Add-
in-Get-Set-EditOpen-XML , the text box and shape are set up using different types of
text wrapping and positioning settings. (Also be aware that the image examples in those
code samples are set up using in line with text formatting, which positions a graphic
object on the text baseline.)

The shape in those code samples is positioned relative to the right and bottom page
margins. Relative positioning lets you more easily coordinate with a user's unknown
document setup because it will adjust to the user's margins and run less risk of looking
awkward because of paper size, orientation, or margin settings. To retain relative
positioning settings when you insert a graphic object, you must retain the paragraph
mark (w:p) in which the positioning (known in Word as an anchor) is stored. If you insert
the content into an existing paragraph mark rather than including your own, you may be
able to retain the same initial visual, but many types of relative references that enable
the positioning to automatically adjust to the user's layout may be lost.

Work with content controls


Content controls are an important feature in Word that can greatly enhance the power
of your add-in for Word in multiple ways, including giving you the ability to insert
content at designated places in the document rather than only at the selection.

In Word, find content controls on the Developer tab of the ribbon, as shown here in
Figure 15.

Figure 15. The Controls group on the Developer tab in Word

Types of content controls in Word include rich text, plain text, picture, building block
gallery, check box, dropdown list, combo box, date picker, and repeating section.

Use the Properties command, shown in Figure 15, to edit the title of the control
and to set preferences such as hiding the control container.

Enable Design Mode to edit placeholder content in the control.

If your add-in works with a Word template, you can include controls in that template to
enhance the behavior of the content. You can also use XML data binding in a Word
document to bind content controls to data, such as document properties, for easy form
completion or similar tasks. (Find controls that are already bound to built-in document
properties in Word on the Insert tab, under Quick Parts.)

When you use content controls with your add-in, you can also greatly expand the
options for what your add-in can do using a different type of binding. You can bind to a
content control from within the add-in and then write content to the binding rather
than to the active selection.

Don't confuse XML data binding in Word with the ability to bind to a control via your
add-in. These are completely separate features. However, you can include named
content controls in the content you insert via your add-in using OOXML coercion and
then use code in the add-in to bind to those controls.

Also be aware that both XML data binding and Office.js can interact with custom XML
parts in your app, so it's possible to integrate these powerful tools. To learn about
working with custom XML parts in the Office JavaScript API, see the See also section of
this topic.

Working with bindings in your Word add-in is covered in the next section of this topic.
First, take a look at an example of the Office Open XML required for inserting a rich text
content control that you can bind to using your add-in.

) Important

Rich text controls are the only type of content control you can use to bind to a
content control from within your add-in.

XML

<pkg:package
xmlns:pkg="http://schemas.microsoft.com/office/2006/xmlPackage">
<pkg:part pkg:name="/_rels/.rels"
pkg:contentType="application/vnd.openxmlformats-package.relationships+xml"
pkg:padding="512">
<pkg:xmlData>
<Relationships
xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1"
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/of
ficeDocument" Target="word/document.xml"/>
</Relationships>
</pkg:xmlData>
</pkg:part>
<pkg:part pkg:name="/word/document.xml"
pkg:contentType="application/vnd.openxmlformats-
officedocument.wordprocessingml.document.main+xml">
<pkg:xmlData>
<w:document
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" >
<w:body>
<w:p/>
<w:sdt>
<w:sdtPr>
<w:alias w:val="MyContentControlTitle"/>
<w:id w:val="1382295294"/>
<w15:appearance w15:val="hidden"/>
<w:showingPlcHdr/>
</w:sdtPr>
<w:sdtContent>
<w:p>
<w:r>
<w:t>[This text is inside a content control that has its
container hidden. You can bind to a content control to add or interact with
content at a specified location in the document.]</w:t>
</w:r>
</w:p>
</w:sdtContent>
</w:sdt>
</w:body>
</w:document>
</pkg:xmlData>
</pkg:part>
</pkg:package>

As already mentioned, content controls, like formatted text, don't require additional
document parts, so only edited versions of the .rels and document.xml parts are
included here.

The w:sdt tag that you see within the document.xml body represents the content
control. If you generate the Office Open XML markup for a content control, you'll see
that several attributes have been removed from this example, including the tag and
document part properties. Only essential (and a couple of best practice) elements have
been retained, including the following:

The alias is the title property from the Content Control Properties dialog box in
Word. This is a required property (representing the name of the item) if you plan to
bind to the control from within your add-in.

The unique id is a required property. If you bind to the control from within your
add-in, the ID is the property the binding uses in the document to identify the
applicable named content control.
The appearance attribute is used to hide the control container, for a cleaner look.
This feature was introduced in Word 2013, as you see by the use of the w15
namespace. Because this property is used, the w15 namespace is retained at the
start of the document.xml part.

The showingPlcHdr attribute is an optional setting that sets the default content
you include inside the control (text in this example) as placeholder content. So, if
the user clicks or taps in the control area, the entire content is selected rather than
behaving like editable content in which the user can make changes.

Although the empty paragraph mark ( w:p/ ) that precedes the sdt tag isn't
required for adding a content control (and will add vertical space above the
control in the Word document), it ensures that the control is placed in its own
paragraph. This may be important, depending upon the type and formatting of
content that will be added in the control.

If you intend to bind to the control, the default content for the control (what's
inside the sdtContent tag) must include at least one complete paragraph (as in this
example), in order for your binding to accept multi-paragraph rich content.

The document part attribute that was removed from this sample w:sdt tag may appear
in a content control to reference a separate part in the package where placeholder
content information can be stored (parts located in a glossary directory in the Office
Open XML package). Although document part is the term used for XML parts (that is,
files) within an Office Open XML package, the term document parts as used in the sdt
property refers to the same term in Word that's used to describe some content types
including building blocks and document property quick parts (for example, built-in XML
data-bound controls). If you see parts under a glossary directory in your Office Open
XML package, you may need to retain them if the content you're inserting includes
these features. For a typical content control that you intend to use to bind to from your
add-in, they aren't required. Just remember that, if you do delete the glossary parts from
the package, you must also remove the document part attribute from the w:sdt tag.

The next section will discuss how to create and use bindings in your Word add-in.

Insert content at a designated location


You've already looked at how to insert content at the active selection in a Word
document. If you bind to a named content control that's in the document, you can insert
any of the same content types into that control.

So when might you want to use this approach?


When you need to add or replace content at specified locations in a template, such
as to populate portions of the document from a database.

When you want the option to replace content that you're inserting at the active
selection, such as to provide design element options to the user.

When you want the user to add data in the document that you can access for use
with your add-in, such as to populate fields in the task pane based upon
information the user adds in the document.

Download the code sample Word-Add-in-JavaScript-AddPopulateBindings , which


provides a working example of how to insert and bind to a content control, and how to
populate the binding.

Add and bind to a named content control


As you examine the JavaScript that follows, consider these requirements:

As previously mentioned, you must use a rich text content control in order to bind
to the control from your Word add-in.

The content control must have a name (this is the Title field in the Content Control
Properties dialog box, which corresponds to the Alias tag in the Office Open XML
markup). This is how the code identifies where to place the binding.

You can have several named controls and bind to them as needed. Use a unique
content control name, unique content control ID, and a unique binding ID.

JavaScript

function addAndBindControl() {

Office.context.document.bindings.addFromNamedItemAsync("MyContentControlTitl
e", "text", { id: 'myBinding' }, function (result) {
if (result.status == "failed") {
if (result.error.message == "The named item does not exist.")
const myOOXMLRequest = new XMLHttpRequest();
let myXML;
myOOXMLRequest.open('GET',
'../../Snippets_BindAndPopulate/ContentControl.xml', false);
myOOXMLRequest.send();
if (myOOXMLRequest.status === 200) {
myXML = myOOXMLRequest.responseText;
}
Office.context.document.setSelectedDataAsync(myXML, {
coercionType: 'ooxml' }, function (result) {

Office.context.document.bindings.addFromNamedItemAsync("MyContentControlTitl
e", "text", { id: 'myBinding' });
});
}
});
}

The code shown here takes the following steps.

Attempts to bind to the named content control, using addFromNamedItemAsync.

Take this step first if there's a possible scenario for your add-in where the named
control could already exist in the document when the code runs. For example,
you'll want to do this if the add-in was inserted into and saved with a template
that's been designed to work with the add-in, where the control was placed in
advance. You also need to do this if you need to bind to a control that was placed
earlier by the add-in.

The callback in the first call to the addFromNamedItemAsync method checks the
status of the result to see if the binding failed because the named item doesn't
exist in the document (that is, the content control named MyContentControlTitle in
this example). If so, the code adds the control at the active selection point (using
setSelectedDataAsync ) and then binds to it.

As mentioned earlier and shown in the preceding code, the name of the content control
is used to determine where to create the binding. However, in the Office Open XML
markup, the code adds the binding to the document using both the name and the ID
attribute of the content control.

After running code, if you examine the markup of the document in which your add-in
created bindings, you'll see two parts to each binding. In the markup for the content
control where a binding was added (in document.xml), you'll see the attribute
w15:webExtensionLinked/.

In the document part named webExtensions1.xml, you'll see a list of the bindings you've
created. Each is identified using the binding ID and the ID attribute of the applicable
control, such as the following, where the appref attribute is the content control ID:
we:binding id="myBinding" type="text" appref="1382295294"/.

) Important

You must add the binding at the time you intend to act upon it. Don't include the
markup for the binding in the Office Open XML for inserting the content control
because the process of inserting that markup will strip the binding.
Populate a binding
The code for writing content to a binding is similar to that for writing content to a
selection.

JavaScript

function populateBinding(filename) {
const myOOXMLRequest = new XMLHttpRequest();
let myXML;
myOOXMLRequest.open('GET', filename, false);
myOOXMLRequest.send();
if (myOOXMLRequest.status === 200) {
myXML = myOOXMLRequest.responseText;
}
Office.select("bindings#myBinding").setDataAsync(myXML, { coercionType:
'ooxml' });
}

As with setSelectedDataAsync , you specify the content to be inserted and the coercion
type. The only additional requirement for writing to a binding is to identify the binding
by ID. Notice how the binding ID used in this code (bindings#myBinding) corresponds
to the binding ID established (myBinding) when the binding was created in the previous
function.

 Tip

The preceding code is all you need whether you are initially populating or replacing
the content in a binding. When you insert a new piece of content at a bound
location, the existing content in that binding is automatically replaced. Check out
an example of this in the previously-referenced code sample Word-Add-in-
JavaScript-AddPopulateBindings , which provides two separate content samples
that you can use interchangeably to populate the same binding.

Add objects that use additional Office Open


XML parts
Many types of content require additional document parts in the Office Open XML
package, meaning that they either reference information in another part or the content
itself is stored in one or more additional parts and referenced in document.xml.

For example, consider the following:


Content that uses styles for formatting (such as the styled text shown earlier in
Figure 2 or the styled table shown in Figure 9) requires the styles.xml part.

Images (such as those shown in Figures 3 and 4) include the binary image data in
one (and sometimes two) additional parts.

SmartArt diagrams (such as the one shown in Figure 10) require multiple additional
parts to describe the layout and content.

Charts (such as the one shown in Figure 11) require multiple additional parts,
including their own relationship (.rels) part.

You can see edited examples of the markup for all of these content types in the
previously-referenced code sample Load and write Open XML in your Word add-in .
You can insert all of these content types using the same JavaScript code shown earlier
(and provided in the referenced code samples) for inserting content at the active
selection and writing content to a specified location using bindings.

Remember, if you're retaining any additional parts referenced in document.xml, you will
need to retain document.xml.rels and the relationship definitions for the applicable parts
you're keeping, such as styles.xml or an image file.

Before you explore the samples, take a look at a few tips for working with each of these
content types.

Working with styles


The same approach to editing the markup that you looked at for the preceding example
with directly-formatted text applies when using paragraph styles or table styles to
format your content. However, the markup for working with paragraph styles is
considerably simpler, so that's the example described here.

Editing the markup for content using paragraph styles


The following markup represents the body content for the styled text example shown in
Figure 2.

XML

<w:body>
<w:p>
<w:pPr>
<w:pStyle w:val="Heading1"/>
</w:pPr>
<w:r>
<w:t>This text is formatted using the Heading 1 paragraph style.</w:t>
</w:r>
</w:p>
</w:body>

As you see, the markup for formatted text in document.xml is considerably simpler when
you use a style, because the style contains all of the paragraph and font formatting that
you otherwise need to reference individually. However, as explained earlier, you might
want to use styles or direct formatting for different purposes: use direct formatting to
specify the appearance of your text regardless of the formatting in the user's document;
use a paragraph style (particularly a built-in paragraph style name, such as Heading 1
shown here) to have the text formatting automatically coordinate with the user's
document.

Use of a style is a good example of how important it is to read and understand the
markup for the content you're inserting, because it isn't explicit that another document
part is referenced here. If you include the style definition in this markup and don't
include the styles.xml part, the style information in document.xml will be ignored
regardless of whether or not that style is in use in the user's document.

However, if you take a look at the styles.xml part, you'll see that only a small portion of
this long piece of markup is required when editing markup for use in your add-in:

The styles.xml part includes several namespaces by default. If you are only
retaining the required style information for your content, in most cases you only
need to keep the xmlns:w namespace.

The w:docDefaults tag content that falls at the top of the styles part will be
ignored when your markup is inserted via the add-in and can be removed.

The largest piece of markup in a styles.xml part is for the w:latentStyles tag that
appears after docDefaults, which provides information (such as appearance
attributes for the Styles pane and Styles gallery) for every available style. This
information is also ignored when inserting content via your add-in and so it can be
removed.

Following the latent styles information, you see a definition for each style in use in
the document from which you're markup was generated. This includes some
default styles that are in use when you create a new document and may not be
relevant to your content. You can delete the definitions for any styles that aren't
used by your content.

Each built-in heading style has an associated Char style that's a character style
version of the same heading format. Unless you've applied the heading style as a
character style, you can remove it. If the style is used as a character style, it appears
in document.xml in a run properties tag ( w:rPr ) rather than a paragraph
properties ( w:pPr ) tag. This should only be the case if you've applied the style to
just part of a paragraph, but it can occur inadvertently if the style was incorrectly
applied.

If you're using a built-in style for your content, you don't have to include a full
definition. You only must include the style name, style ID, and at least one
formatting attribute in order for the coerced Office Open XML to apply the style to
your content upon insertion.

However, it's a best practice to include a complete style definition (even if it's the
default for built-in styles). If a style is already in use in the destination document,
your content will take on the resident definition for the style, regardless of what
you include in styles.xml. If the style isn't yet in use in the destination document,
your content will use the style definition you provide in the markup.

So, for example, the only content you needed to retain from the styles.xml part for the
sample text shown in Figure 2, which is formatted using Heading 1 style, is the
following:

7 Note

A complete Word definition for the Heading 1 style has been retained in this
example.

XML

<pkg:part pkg:name="/word/styles.xml"
pkg:contentType="application/vnd.openxmlformats-
officedocument.wordprocessingml.styles+xml">
<pkg:xmlData>
<w:styles
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" >
<w:style w:type="paragraph" w:styleId="Heading1">
<w:name w:val="heading 1"/>
<w:basedOn w:val="Normal"/>
<w:next w:val="Normal"/>
<w:link w:val="Heading1Char"/>
<w:uiPriority w:val="9"/>
<w:qFormat/>
<w:pPr>
<w:keepNext/>
<w:keepLines/>
<w:spacing w:before="240" w:after="0" w:line="259"
w:lineRule="auto"/>
<w:outlineLvl w:val="0"/>
</w:pPr>
<w:rPr>
<w:rFonts w:asciiTheme="majorHAnsi"
w:eastAsiaTheme="majorEastAsia" w:hAnsiTheme="majorHAnsi"
w:cstheme="majorBidi"/>
<w:color w:val="2E74B5" w:themeColor="accent1" w:themeShade="BF"/>
<w:sz w:val="32"/>
<w:szCs w:val="32"/>
</w:rPr>
</w:style>
</w:styles>
</pkg:xmlData>
</pkg:part>

Edit the markup for content using table styles


When your content uses a table style, you need the same relative part of styles.xml as
described for working with paragraph styles. That is, you only need to retain the
information for the style you're using in your content, and you must include the name,
ID, and at least one formatting attribute, but are better off including a complete style
definition to address all potential user scenarios.

However, when you look at the markup both for your table in document.xml and for
your table style definition in styles.xml, you see enormously more markup than when
working with paragraph styles.

In document.xml, formatting is applied by cell even if it's included in a style. Using


a table style won't reduce the volume of markup. The benefit of using table styles
for the content is for easy updating and easily coordinating the look of multiple
tables.

In styles.xml, you'll see a substantial amount of markup for a single table style as
well, because table styles include several types of possible formatting attributes for
each of several table areas, such as the entire table, heading rows, odd and even
banded rows and columns (separately), the first column, etc.

Work with images


The markup for an image includes a reference to at least one part that includes the
binary data to describe your image. For a complex image, this can be hundreds of pages
of markup and you can't edit it. Since you don't ever have to touch the binary parts, you
can simply collapse it if you're using a structured editor such as Visual Studio, so that
you can still easily review and edit the rest of the package.
If you check out the example markup for the simple image shown earlier in Figure 3,
available in the previously-referenced code sample Load and write Open XML in your
Word add-in , you'll see that the markup for the image in document.xml includes size
and position information as well as a relationship reference to the part that contains the
binary image data. That reference is included in the a:blip tag, as follows:

XML

<a:blip r:embed="rId4" cstate="print">

Be aware that, because a relationship reference is explicitly used ( r:embed="rID4" ) and


that related part is required in order to render the image, if you don't include the binary
data in your Office Open XML package, you'll get an error. This is different from
styles.xml, explained previously, which won't throw an error if omitted since the
relationship isn't explicitly referenced and the relationship is to a part that provides
attributes to the content (formatting) rather than being part of the content itself.

When you review the markup, notice the additional namespaces used in the a:blip tag.
You'll see in document.xml that the xlmns:a namespace (the main drawingML
namespace) is dynamically placed at the beginning of the use of drawingML references
rather than at the top of the document.xml part. However, the relationships namespace
(r) must be retained where it appears at the start of document.xml. Check your picture
markup for additional namespace requirements. Remember that you don't have to
memorize which types of content require what namespaces, you can easily tell by
reviewing the prefixes of the tags throughout document.xml.

Understanding additional image parts and formatting


When you use some Office picture formatting effects on your image, such as for the
image shown in Figure 4, which uses adjusted brightness and contrast settings (in
addition to picture styling), a second binary data part for an HD format copy of the
image data may be required. This additional HD format is required for formatting
considered a layering effect, and the reference to it appears in document.xml similar to
the following:

XML

<a14:imgLayer r:embed="rId5">

See the required markup for the formatted image shown in Figure 4 (which uses
layering effects among others) in the Load and write Open XML in your Word add-in
code sample.
Work with SmartArt diagrams
A SmartArt diagram has four associated parts, but only two are always required. You can
examine an example of SmartArt markup in the Load and write Open XML in your Word
add-in code sample. First, take a look at a brief description of each of the parts and
why they are or aren't required:

 Tip

If your content includes more than one diagram, they will be numbered
consecutively, replacing the '1' in the file names listed here.

layout1.xml: This part is required. It includes the markup definition for the layout
appearance and functionality.

data1.xml: This part is required. It includes the data in use in your instance of the
diagram.

drawing1.xml: This part isn't always required but if you apply custom formatting to
elements in your instance of a diagram, such as directly formatting individual
shapes, you might need to retain it.

colors1.xml: This part isn't required. It includes color style information, but the
colors of your diagram will coordinate by default with the colors of the active
formatting theme in the destination document, based on the SmartArt color style
you apply from the SmartArt Tools design tab in Word before saving out your
Office Open XML markup.

quickStyles1.xml: This part isn't required. Similar to the colors part, you can remove
this as your diagram will take on the definition of the applied SmartArt style that's
available in the destination document (that is, it will automatically coordinate with
the formatting theme in the destination document).

The SmartArt layout1.xml file is a good example of places you may be able to further
trim your markup but mightn't be worth the extra time to do so (because it removes
such a small amount of markup relative to the entire package). If you would like to get
rid of every last line you can of markup, you can delete the dgm:sampData tag and its
contents. This sample data defines how the thumbnail preview for the diagram will
appear in the SmartArt styles galleries. However, if it's omitted, default sample data is
used.

Be aware that the markup for a SmartArt diagram in document.xml contains relationship
ID references to the layout, data, colors, and quick styles parts. You can delete the
references in document.xml to the colors and styles parts when you delete those parts
and their relationship definitions (and it's certainly a best practice to do so, since you're
deleting those relationships), but you won't get an error if you leave them, since they
aren't required for your diagram to be inserted into a document. Find these references
in document.xml in the dgm:relIds tag. Regardless of whether or not you take this step,
retain the relationship ID references for the required layout and data parts.

Work with charts


Similar to SmartArt diagrams, charts contain several additional parts. However, the setup
for charts is a bit different from SmartArt, in that a chart has its own relationship file.
Following is a description of required and removable document parts for a chart.

 Tip

As with SmartArt diagrams, if your content includes more than one chart, they will
be numbered consecutively, replacing the '1' in the file names listed here.

In document.xml.rels, you'll see a reference to the required part that contains the
data that describes the chart (chart1.xml).

You also see a separate relationship file for each chart in your Office Open XML
package, such as chart1.xml.rels.

There are three files referenced in chart1.xml.rels, but only one is required. These
include the binary Excel workbook data (required) and the color and style parts
(colors1.xml and styles1.xml) that you can remove.

Charts that you can create and edit natively in Word are Excel charts, and their data is
maintained on an Excel worksheet that's embedded as binary data in your Office Open
XML package. Like the binary data parts for images, this Excel binary data is required,
but there's nothing to edit in this part. So you can just collapse the part in the editor to
avoid having to manually scroll through it all to examine the rest of your Office Open
XML package.

However, similar to SmartArt, you can delete the colors and styles parts. If you've used
the chart styles and color styles available in to format your chart, the chart will take on
the applicable formatting automatically when it's inserted into the destination
document.

See the edited markup for the example chart shown in Figure 11 in the Load and write
Open XML in your Word add-in code sample.
Edit the Office Open XML for use in your task
pane add-in
You've already seen how to identify and edit the content in your markup. If the task still
seems difficult when you take a look at the massive Office Open XML package
generated for your document, following is a quick summary of recommended steps to
help you edit that package down quickly.

Remember that you can use all .rels parts in the package as a map to quickly check for
document parts that you can remove.

1. Open the flattened XML file in Visual Studio and press Ctrl+K, Ctrl+D to format the
file. Then use the collapse/expand buttons on the left to collapse the parts you
know you need to remove. You might also want to collapse long parts you need,
but know you won't need to edit (such as the base64 binary data for an image file),
making the markup faster and easier to visually scan.

2. There are several parts of the document package that you can almost always
remove when you are preparing Office Open XML markup for use in your add-in.
You might want to start by removing these (and their associated relationship
definitions), which will greatly reduce the package right away. These include the
theme1, fontTable, settings, webSettings, thumbnail, both the core and add-in
properties files, and any taskpane or webExtension parts.

3. Remove any parts that don't relate to your content, such as footnotes, headers, or
footers that you don't require. Again, remember to also delete their associated
relationships.

4. Review the document.xml.rels part to see if any files referenced in that part are
required for your content, such as an image file, the styles part, or SmartArt
diagram parts. Delete the relationships for any parts your content doesn't require
and confirm that you have also deleted the associated part. If your content doesn't
require any of the document parts referenced in document.xml.rels, you can delete
that file also.

5. If your content has an additional .rels part (such as chart#.xml.rels), review it to see
if there are other parts referenced there that you can remove (such as quick styles
for charts) and delete both the relationship from that file as well as the associated
part.

6. Edit document.xml to remove namespaces not referenced in the part, section


properties if your content doesn't include a section break, and any markup that
isn't related to the content that you want to insert. If inserting shapes or text
boxes, you might also want to remove extensive fallback markup.

7. Edit any additional required parts where you know that you can remove substantial
markup without affecting your content, such as the styles part.

After you've taken the preceding seven steps, you've likely cut between about 90 and
100 percent of the markup you can remove, depending on your content. In most cases,
this is likely to be as far as you want to trim.

Regardless of whether you leave it here or choose to delve further into your content to
find every last line of markup you can cut, remember that you can use the previously-
referenced code sample Word-Add-in-Get-Set-EditOpen-XML as a scratch pad to
quickly and easily test your edited markup.

 Tip

If you update an Office Open XML snippet in an existing solution while developing,
clear temporary Internet files before you run the solution again to update the
Office Open XML used by your code. Markup that's included in your solution in
XML files is cached on your computer. You can, of course, clear temporary Internet
files from your default web browser. To access Internet options and delete these
settings from inside Visual Studio 2019, on the Debug menu, choose Options.
Then, under Environment, choose Web Browser and then choose Internet Explorer
Options.

Create an add-in for both template and


standalone use
In this topic, you've seen several examples of what you can do with Office Open XML in
your add-ins. You've looked at a wide range of rich content type examples that you can
insert into documents by using the Office Open XML coercion type, together with the
JavaScript methods for inserting that content at the selection or to a specified (bound)
location.

So, what else do you need to know if you're creating your add-in both for standalone
use (that is, inserted from the Store or a proprietary server location) and for use in a pre-
created template that's designed to work with your add-in? The answer might be that
you already know all you need.
The markup for a given content type and methods for inserting it are the same whether
your add-in is designed to standalone or work with a template. If you are using
templates designed to work with your add-in, just be sure that your JavaScript includes
callbacks that account for scenarios where referenced content might already exist in the
document (such as demonstrated in the binding example shown in the section Add and
bind to a named content control).

When using templates with your app, whether the add-in will be resident in the
template at the time that the user created the document or the add-in will be inserting a
template, you might also want to incorporate other elements of the API to help you
create a more robust, interactive experience. For example, you may want to include
identifying data in a customXML part that you can use to determine the template type
in order to provide template-specific options to the user. To learn more about how to
work with custom XML in your add-ins, see the additional resources that follow.

See also
Office JavaScript API
The complete language reference and related documentation on Open XML:
Standard ECMA-376: Office Open XML File Formats
Explore Office JavaScript API using Script Lab
Exploring the Office JavaScript API: Data Binding and Custom XML Parts
Companion code sample: Load and write Open XML in your Word add-in
Other code samples referenced in this article:
Word-Add-in-Get-Set-EditOpen-XML
Word-Add-in-JavaScript-AddPopulateBindings
Troubleshoot Word add-ins
Article • 06/27/2023

This article discusses troubleshooting issues that are unique to Word. Use the feedback
tool at the end of the page to suggest other issues that can be added to the article.

Body.insertFileFromBase64 doesn't insert


header or footer
It's by design that the Body.insertFileFromBase64 method excludes any header or footer
that was in the source file.

To include any headers or footers from the source file, use


Document.insertFileFromBase64 instead.

Layout breaks when using insertHtml while


cursor is in content control in header
This issue may occur when the following three conditions are met.

1. Have at least one content control in the header and at least one in the footer of
the Word document.
2. Ensure the cursor is inside a content control in the header.
3. Call insertHtml to set a content control in the footer.

The footer is then unexpectedly mixed with the header. To avoid this, clear the content
control in the footer before setting it, as shown in the following code sample.

TypeScript

await Word.run(async (context) => {


// Credit to https://github.com/barisbikmaz for this version of the
workaround.
// For more information, see https://github.com/OfficeDev/office-
js/issues/129.

// Let's say there are 2 content controls in the header and 1 in the
footer.
const contentControls = context.document.contentControls;
contentControls.load();

await context.sync().then(function () {
// Clear the 2 content controls in the header.
contentControls.items[0].clear();
contentControls.items[1].clear();

// Clear the control control in the footer then update it.


contentControls.items[2].clear();
contentControls.items[2].insertHtml('<p>New Footer</p>', 'Replace');
});
});

Meaning of null property values in the


response
null has special implications in the Word JavaScript APIs. It's used to represent default
values or no formatting.

Formatting properties such as color will contain null values in the response when
different values exist in the specified range. For example, if you retrieve a range and load
its range.font.color property:

If all text in the range has the same font color, range.font.color specifies that
color.
If multiple font colors are present within the range, range.font.color is null .

See also
Troubleshoot development errors with Office Add-ins
Troubleshoot user errors with Office Add-ins
Office Add-in code samples
Article • 07/18/2023

These code samples are written to help you learn how to use various features when
developing Office Add-ins.

Getting started
The following samples show how to build the simplest Office Add-in with only a
manifest, HTML web page, and a logo. These components are the fundamental parts of
an Office Add-in. For additional getting started information, see our quick starts and
tutorials.

Excel "Hello world" add-in


Outlook "Hello world" add-in
PowerPoint "Hello world" add-in
Word "Hello world" add-in

Blazor WebAssembly
If your development background is in building VSTO Add-ins, the following samples
show how to build Office Web Add-ins using .NET Blazor WebAssembly. You can keep
much of your code in C# and Visual Studio.

Create a Blazor WebAssembly Excel add-in


Create a Blazor WebAssembly Outlook add-in
Create a Blazor WebAssembly Word add-in

Excel
Name Description

Data types explorer Builds an Excel add-in that allows you to create and explore data
(preview) types in your workbooks. Data types enable add-in developers to
organize complex data structures as objects, such as formatted
number values, web images, and entity values.

Open in Teams Create a new Excel spreadsheet in Microsoft Teams containing data
you define.

Insert an external Excel file Insert an existing template from an external Excel file into the
and populate it with JSON currently open Excel workbook. Then, populate the template with
Name Description

data data from a JSON web service.

Create custom contextual Create a custom contextual tab on the ribbon in the Office UI. The
tabs on the ribbon sample creates a table, and when the user moves the focus inside
the table, the custom tab is displayed. When the user moves outside
the table, the custom tab is hidden.

Use keyboard shortcuts Set up a basic Excel add-in project that utilizes keyboard shortcuts.
for Office Add-in actions

Custom function sample Use web workers in custom functions to prevent blocking the UI of
using web worker your Office Add-in.

Use storage techniques to Implement localStorage to enable limited functionality for your
access data from an Office Office Add-in when a user experiences lost connection.
Add-in when offline

Custom function batching Batch multiple calls into a single call to reduce the number of
pattern network calls to a remote service.

Outlook
Name Description

Encrypt attachments, process Use event-based activation to encrypt attachments when


meeting request attendees, and added by the user. Also use event handling for recipients
react to appointment date/time changed in a meeting request, and changes to the start or end
changes date or time in a meeting request.

Use Outlook event-based Use event-based activation to run an Outlook add-in when the
activation to tag external user changes recipients while composing a message. The add-
recipients in also uses the appendOnSendAsync API to add a disclaimer.

Use Outlook event-based Use event-based activation to run an Outlook add-in when the
activation to set the signature user creates a new message or appointment. The add-in can
respond to events, even when the task pane is not open. It
also uses the setSignatureAsync API.

Use Outlook Smart Alerts Use Outlook Smart Alerts to verify that required color
categories are applied to a new message or appointment
before it's sent.

Verify the sensitivity label of a Use the sensitivity label API in an event-based add-in to verify
message and apply the Highly Confidential sensitivity label to
applicable outgoing messages.
Word
Name Description

Get, edit, and set This sample shows how to get, edit, and set OOXML content in a Word
OOXML content in a document. The sample add-in provides a scratch pad to get Office Open
Word document with a XML for your own content and test your own edited Office Open XML
Word add-in snippets.

Load and write Open This sample add-in shows you how to add a variety of rich content types
XML in your Word to a Word document using the setSelectedDataAsync method with
add-in ooxml coercion type. The add-in also gives you the ability to show the
Office Open XML markup for each sample content type right on the
page.

Authentication, authorization, and single sign-


on (SSO)
Name Description

Use SSO with event-based Shows how to use SSO to access a user's Microsoft Graph data
activation in an Outlook add- from an event fired in an Outlook add-in.
in

Single Sign-on (SSO) Sample Use Office's SSO feature to give the add-in access to Microsoft
Outlook Add-in Graph data.

Get OneDrive data using Build an Office Add-in, as a single-page application (SPA) with no
Microsoft Graph and msal.js backend, that connects to Microsoft Graph, and access
in an Office Add-in workbooks stored in OneDrive for Business to update a
spreadsheet.

Office Add-in auth to Learn how to build a Microsoft Office Add-in that connects to
Microsoft Graph Microsoft Graph, and access workbooks stored in OneDrive for
Business to update a spreadsheet.

Outlook Add-in auth to Build an Outlook add-in that connects to Microsoft Graph, and
Microsoft Graph . access workbooks stored in OneDrive for Business to compose a
new email message.

Single Sign-on (SSO) Office Use the getAccessToken API in Office.js to give the add-in access
Add-in with ASP.NET to Microsoft Graph data. This sample is built on ASP.NET.

Single Sign-on (SSO) Office Use the getAccessToken API in Office.js to give the add-in access
Add-in with Node.js to Microsoft Graph data. This sample is built on Node.js.
Office
Name Description

Save custom Save custom settings inside an Office Add-in. The add-in stores data as key-
settings in your value pairs, using the JavaScript API for Office property bag, browser cookies,
Office Add-in web storage (localStorage and sessionStorage), or by storing the data in a
hidden div in the document.

Shared runtime
Name Description

Share global data with a Set up a basic project that uses the shared runtime to run code
shared runtime for ribbon buttons, task pane, and custom functions in a single
browser runtime.

Manage ribbon and task pane Create contextual ribbon buttons that are enabled based on the
UI, and run code on doc open state of your add-in.

Additional samples
Name Description

Use a shared library to migrate your Visual Provides a strategy for code reuse when
Studio Tools for Office add-in to an Office web migrating from VSTO Add-ins to Office Add-ins.
add-in

Integrate an Azure function with your Excel Integrate Azure functions with custom functions
custom function to move to the cloud or integrate additional
services.

Dynamic DPI code samples A collection of samples for handling DPI


changes in COM, VSTO, and Office Add-ins.

Next steps
Join the Microsoft 365 Developer Program. Get a free sandbox, tools, and other
resources you need to build solutions for the Microsoft 365 platform.

Free developer sandbox Get a free, renewable 90-day Microsoft 365 E5 developer
subscription.
Sample data packs Automatically configure your sandbox by installing user data
and content to help you build your solutions.
Access to experts Access community events to learn from Microsoft 365 experts.
Personalized recommendations Find developer resources quickly from your
personalized dashboard.
Create an Excel spreadsheet from your
web page, populate it with data, and
embed your Office Add-in
Article • 03/09/2023

Microsoft partners with SaaS web applications know that their customers often want to
open their data from a web page in an Excel spreadsheet. They use Excel to do analysis
on the data, or other types of number crunching. Then they upload the data back to the
web site.

Instead of multiple steps to export the data from the web site to a .csv file, import the
.csv file into Excel, work with the data, then export it from Excel, and upload it back to
the web site, we can simplify this process to one button click.

This article shows how to add an Excel button to your web site. When a customer
chooses the button, it automatically creates a new spreadsheet with the requested data,
uploads it to the customer's OneDrive, and opens it in Excel on a new browser tab. With
one click the requested data is opened in Excel and formatted correctly. Additionally the
pattern embeds your own Office Add-in inside the spreadsheet so that customers can
still access your services from the context of Excel.

Microsoft partners who implemented this pattern have seen increased customer
satisfaction. They've also seen a significant increase in engagement with their add-ins by
embedding them in the Excel spreadsheet. We recommend that if you have a web site
for customers to work with data, that you consider implementing this pattern in your
own solution.

Prerequisites
Visual Studio 2022 or later . Add the Office/SharePoint development workload
when configuring Visual Studio.
Visual Studio Code .
Microsoft 365. You can get a free developer sandbox that provides a renewable 90-
day Microsoft 365 E5 developer subscription. The developer sandbox includes a
Microsoft Azure subscription that you can use for app registrations in later steps in
this article. If you prefer, you can use a separate Microsoft Azure subscription for
app registrations. Get a trial subscription at Microsoft Azure.
One or more files and folders on OneDrive in the Microsoft 365 account.

Run the sample code


The sample code for this article is named Create a spreadsheet from your web site,
populate it with data, and embed your Excel add-in To run the sample, follow the
instructions in the readme .

Solution architecture

Web page
Azure Func ons
App
Pass data to Azure Func ons app

Return new spreadsheet Use Open XML SDK


to create
spreadsheet

Upload to OneDrive
Microso Graph

Web URL of spreadsheet


API

OneDrive

Open new tab


with web URL

Your Office Add-in


The solution described in this article adds an Open in Microsoft Excel button to the web
site and interacts with Azure Functions, and the Microsoft Graph API. The following
sequence of events occurs when the user wants to open their data in a new Excel
spreadsheet.

1. The user chooses the Open in Microsoft Excel button. The web page passes the
data to a function in an Azure Functions app.
2. The function uses the Open XML SDK to create a new Excel spreadsheet in
memory. It populates the spreadsheet with the data, and embeds your Office Add-
in.
3. The function returns the spreadsheet as a Base64 encoded string to the web page.
4. The web page calls the Microsoft Graph API to upload the spreadsheet to the
user's OneDrive.
5. Microsoft Graph returns the web url location of the new spreadsheet file.
6. The web page opens a new browser tab to open the spreadsheet at the web url.
The spreadsheet contains the data, and your add-in.

Key parts of the solution


The solution has two projects that you build:

An Azure Functions app containing a FunctionCreateSpreadsheet function.


A Node.js web application project.

The following sections describe important concepts and implementation details for
constructing the solution. A full reference implementation can be found in the sample
code for additional implementation details.

Excel button and Fluent UI

You need a button on the web site that creates the Excel spreadsheet. A best practice is
to use the Fluent UI to help your users transition between Microsoft products. You
should always use an Office icon to indicate which Office application will be launched
from your web page. For more information, see Office Brand Icons on the Fluent UI
developer portal.
Sign in the user
The sample code is built from the Microsoft identity sample named Vanilla JavaScript
single-page application using MSAL.js to authenticate users to call Microsoft Graph .
All authentication code and UI is from this sample. Please refer to this sample for more
information about writing code for authentication and authorization. For a full list of
identity samples for a wide range of platforms, see Microsoft identity platform code
samples.

Create the spreadsheet with the Open XML SDK


The sample code uses the Open XML SDK to create the spreadsheet. Because the Open
XML SDK uses .NET it is encapsulated in an Azure Functions app named
FunctionCreateSpreadsheet . You can call this function from your Node.js web
application. FunctionCreateSpreadsheet uses the SpreadsheetBuilder helper class to
create a new spreadsheet in memory. The code is based on Create a spreadsheet
document by providing a file name (Open XML SDK).

Populate the spreadsheet with data


The FunctionCreateSpreadsheet function accepts a JSON body containing the row and
column data. This is passed to the SpreadsheetBuilder.InsertData method which
iterates through all rows and columns and adds them to the worksheet.

Much of the SpreadsheetBuilder class contains code that was generated by the Open
XML 2.5 SDK Productivity Tools. These are available at the link for the Open XML 2.5
SDK.

Embed your Office Add-in inside the spreadsheet


The SpreadsheetBuilder class also embeds the Script Lab add-in inside the spreadsheet
and configures to display when the document is opened.

The SpreadsheetBuilder.GenerateWebExtensionPart1Content method in the


SpreadsheetBuilder.cs file sets the reference to the ID of Script Lab in Microsoft
AppSource:

C#

We.WebExtensionStoreReference webExtensionStoreReference1 = new


We.WebExtensionStoreReference() { Id = "wa104380862", Version = "1.1.0.0",
Store = "en-US", StoreType = "OMEX" };
The StoreType value is "OMEX", an alias for Microsoft AppSource.
The Store value is "en-US" found in the Microsoft AppSource culture section for
Script Lab.
The Id value is the Microsoft AppSource asset ID for Script Lab.

You can change these values to embed your own Office Add-in. This makes it
discoverable to the user and increases engagement with your add-in and web services.
If your add-in is deployed through central deployment, use the following values instead.

C#

We.WebExtensionStoreReference webExtensionStoreReference1 = new


We.WebExtensionStoreReference() { Id = "<Your add-in GUID>", Version = "
<Your version>", Store = "excatalog", StoreType = "excatalog" };
We.WebExtensionStoreReference webExtensionStoreReference2 = new
We.WebExtensionStoreReference() { Id = "<Your add-in GUID>", Version = "
<Your version>", Store = "omex", StoreType = "omex" };
webExtensionReferenceList1.Append(webExtensionStoreReference2);

For more information about alternative values for these attributes, see Automatically
open a task pane with a document and [MS-OWEXML]: CT_OsfWebExtensionReference

Upload the spreadsheet to OneDrive


When the spreadsheet is fully constructed the FunctionCreateSpreadsheet function
returns a Base64 encoded string version of the spreadsheet to the web application. Then
the web application uses the Microsoft Graph API to upload the spreadsheet to the
user's OneDrive. The web application creates the file at \openinexcel\spreadsheet.xlsx ,
but you can modify the code to use any folder and filename you prefer.

Additional considerations for your solution


Everyone’s solution is different in terms of technologies and approaches. The following
considerations will help you plan how to modify your solution to open documents and
embed your Office Add-in.

Read custom properties when your add-in starts


When you embed your add-in inside the spreadsheet, you can include custom
properties. The SpreadsheetBuilder.cs file includes commented code that shows how to
insert a user name if you have a userName variable.
C#

// CUSTOM MODIFICATION BEGIN


// Uncomment the following code to add your own custom name/value pair
properties for the add-in.
// We.WebExtensionProperty webExtensionProperty2 = new
We.WebExtensionProperty() { Name = "userName", Value = userName };
// webExtensionPropertyBag1.Append(webExtensionProperty2);
// CUSTOM MODIFICATION END

Uncomment the code and change it to add any customer properties you need. In your
add-in, use the Office Settings get method to retrieve a custom property. The following
sample shows how to get the user name property from the spreadsheet.

JavaScript

let userName = Office.context.document.settings.get('userName'));

U Caution

Don't store sensitive information in custom properties such as auth tokens or


connection strings. Properties in the spreadsheet are not encrypted or protected.

See Persist add-in state and settings for complete details on how to read custom
properties when your add-in starts.

Use single sign-on


To simplify authentication, we recommend your add-in implements single sign-on. This
ensure the user does not need to sign in a second time to access your add-in. For more
information, see Enable single sign-on for Office Add-ins

See also
Welcome to the Open XML SDK 2.5 for Office
Automatically open a task pane with a document
Persisting add-in state and settings
Create a spreadsheet document by providing a file name
Create a standalone Office Add-in from
your Script Lab code
Article • 06/23/2023

If you created a snippet in Script Lab, you may want to turn it into a standalone add-in.
You can copy the code from Script Lab into a project generated by the Yeoman
Generator for Office Add-ins (also called "Yo Office"). Then you can continue developing
the code as an add-in that you can eventually deploy to others.

The steps in this article refer to Visual Studio Code , but you can use any code editor
that you prefer.

Create a new Yo Office project


You need to create the standalone add-in project which will be the new development
location for your snippet code.

Run the command yo office --projectType taskpane --ts true --host <host> --name
"basic-sample" , where <host> is one of the following values.

excel
outlook
powerpoint
word

) Important

The --name argument value must be in double quotation marks, even if it has no
spaces.

The previous command creates a new project folder named basic-sample. It's
configured to run in the host you specified, and uses TypeScript. Script Lab uses
TypeScript by default, but most of the snippets are JavaScript. You can build a Yo Office
JavaScript project if you prefer, but just be sure any code you copy over is JavaScript.

Open the snippet in Script Lab


Use an existing snippet in Script Lab to learn how to copy a snippet to a Yo Office
generated project.
1. Open Office (Word, Excel, PowerPoint, or Outlook) and then open Script Lab.
2. Select Script Lab > Code. If you're working in Outlook, open an email message to
see Script Lab on the ribbon.
3. In the Script Lab task pane, choose Samples. Then select a basic sample based on
which Office host you are working in.

For Excel, PowerPoint, or Word, choose the Basic API Call (TypeScript)
sample.
For Outlook, choose the Use add-in settings sample.

Copy snippet code to Visual Studio code


Now you can copy the code from the snippet to the Yo Office project in VS Code.

In VS Code, open the basic-sample project.

In the next steps, you'll copy code from several tabs in Script Lab.

Copy task pane code


1. In VS Code, open the /src/taskpane/taskpane.ts file. If you're using a JavaScript
project, the filename is taskpane.js.
2. In Script Lab, select the Script tab.
3. Copy all of the code in the Script tab to the clipboard. Replace the entire contents
of taskpane.ts (or taskpane.js for JavaScript) with the code you copied.

Copy task pane HTML


1. In VS Code, open the /src/taskpane/taskpane.html file.
2. In Script Lab, select the HTML tab.
3. Copy all of the HTML in the HTML tab to the clipboard. Replace all of the HTML
inside the <body> tag with the HTML you copied.

Copy task pane CSS


1. In VS Code, open the /src/taskpane/taskpane.css file.
2. In Script Lab, select the CSS tab.
3. Copy all of the CSS in the CSS tab to the clipboard. Replace the entire contents of
taskpane.css with the CSS you copied.
4. Save all changes to the files you updated in previous steps.

Add jQuery support


Script Lab uses jQuery in the snippets. You need to add this dependency to the Yo Office
project to run the code successfully.

1. Open the taskpane.html file, and add the following script tag to the <head>
section.

HTML

<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.3.1.js">
</script>

7 Note

The specific version of jQuery may vary. You can determine which version
Script Lab is using by choosing the Libraries tab.

2. Open a terminal in VS Code and enter the following commands.

command line

npm install --save-dev jquery@3.1.1


npm install --save-dev @types/jquery@3.3.1

If you created a snippet that has additional library dependencies, be sure to add them to
the Yo Office project. Find a list of all library dependencies on the Libraries tab in Script
Lab.

Handle initialization
Script Lab handles the Office.onReady initialization automatically. You'll need to modify
the code to provide your own Office.onReady handler.

1. Open the taskpane.ts (or taskpane.js for JavaScript) file.

2. For Excel, PowerPoint, or Word, replace:


TypeScript

$("#run").click(() => tryCatch(run));

with:

TypeScript

Office.onReady(function () {
// Office is ready.
$(document).ready(function () {
// The document is ready
$("#run").click(() => tryCatch(run));
});
});

3. For Outlook, replace:

TypeScript

$("#get").click(get);
$("#set").click(set);
$("#save").click(save);

with:

TypeScript

Office.onReady(function () {
// Office is ready
$(document).ready(function () {
// The document is ready
$("#get").click(get);
$("#set").click(set);
$("#save").click(save);
});
});

4. Save the file.

Custom functions
If your snippet uses custom functions, you need to use the Yo Office custom functions
template. To turn custom functions into a standalone add-in, follow these steps.
1. Run the command yo office --projectType excel-functions --ts true --name
"functions-sample" .

) Important

The --name argument value must be in double quotation marks, even if it has
no spaces.

2. Open Excel, and then open Script Lab.

3. Select Script Lab > Code.

4. In the Script Lab task pane, choose Samples, and then choose the Basic custom
function sample.

5. Open the /src/functions/functions.ts file. If you're using a JavaScript project, the


filename is functions.js.

6. In Script Lab, select the Script tab.

7. Copy all of the code in the Script tab to the clipboard. Paste the code at the top of
the functions.ts (or functions.js for JavaScript) with the code you copied.

8. Save the file.

Test the standalone add-in


Once all the steps are complete, run and test out your standalone add-in. Run the
following command to get started.

command line

npm start

Office will start and you can open the task pane for your add-in from the ribbon.
Congratulations! Now you can continue building your add-in as a standalone project.

Console logging
Many snippets in Script Lab write output to a console section at the bottom of the task
pane. The Yo Office project doesn't have a console section. All console.log* statements
will write to the default debug console (such as your browser developer tools). If you
want the output to go to your task pane, you'll need to update the code.
Batch custom function calls for a remote
service
Article • 03/09/2023

If your custom functions call a remote service you can use a batching pattern to reduce
the number of network calls to the remote service. To reduce network round trips you
batch all the calls into a single call to the web service. This is ideal when the spreadsheet
is recalculated.

For example, if someone used your custom function in 100 cells in a spreadsheet, and
then recalculated the spreadsheet, your custom function would run 100 times and make
100 network calls. By using a batching pattern, the calls can be combined to make all
100 calculations in a single network call.

) Important

Note that Excel custom functions are available on the following platforms.

Office on Windows
Microsoft 365 subscription
retail perpetual Office 2016 and later
Office on Mac
Office on the web

Excel custom functions are currently not supported in the following:

Office on iPad
volume-licensed perpetual versions of Office 2019 or earlier

View the completed sample


To view the completed sample, follow this article and paste the code examples into your
own project. For example, to create a new custom function project for TypeScript use
the Yeoman generator for Office Add-ins, then add all the code from this article to the
project. Run the code and try it out.

Alternatively, download or view the complete sample project at Custom function


batching pattern . If you want to view the code in whole before reading any further,
take a look at the script file .
Create the batching pattern in this article
To set up batching for your custom functions you'll need to write three main sections of
code.

1. A push operation to add a new operation to the batch of calls each time Excel calls
your custom function.
2. A function to make the remote request when the batch is ready.
3. Server code to respond to the batch request, calculate all of the operation results,
and return the values.

In the following sections, you'll learn how to construct the code one example at a time.
It's recommended you create a brand-new custom functions project using the Yeoman
generator for Office Add-ins generator. To create a new project, see Get started
developing Excel custom functions. You can use TypeScript or JavaScript.

Batch each call to your custom function


Your custom functions work by calling a remote service to perform the operation and
calculate the result they need. This provides a way for them to store each requested
operation into a batch. Later you'll see how to create a _pushOperation function to batch
the operations. First, take a look at the following code example to see how to call
_pushOperation from your custom function.

In the following code, the custom function performs division but relies on a remote
service to do the actual calculation. It calls _pushOperation to batch the operation along
with other operations to the remote service. It names the operation div2. You can use
any naming scheme you want for operations as long as the remote service is also using
the same scheme (more on the remote service later). Also, the arguments the remote
service will need to run the operation are passed.

Add the div2 custom function


Add the following code to your functions.js or functions.ts file (depending on if you
used JavaScript or TypeScript).

JavaScript

/**
* Divides two numbers using batching
* @CustomFunction
* @param dividend The number being divided
* @param divisor The number the dividend is divided by
* @returns The result of dividing the two numbers
*/
function div2(dividend, divisor) {
return _pushOperation("div2", [dividend, divisor]);
}

Add global variables for tracking batch requests


Next, add two global variables to your functions.js or functions.ts file.
_isBatchedRequestScheduled is important later for timing batch calls to the remote

service.

JavaScript

let _batch = [];


let _isBatchedRequestScheduled = false;

Add the _pushOperation function


When Excel calls your custom function, you need to push the operation into the batch
array. The following _pushOperation function code shows how to add a new operation
from a custom function. It creates a new batch entry, creates a new promise to resolve
or reject the operation, and pushes the entry into the batch array.

This code also checks to see if a batch is scheduled. In this example, each batch is
scheduled to run every 100ms. You can adjust this value as needed. Higher values result
in bigger batches being sent to the remote service, and a longer wait time for the user
to see results. Lower values tend to send more batches to the remote service, but with a
quick response time for users.

The function creates an invocationEntry object that contains the string name of which
operation to run. For example, if you had two custom functions named multiply and
divide , you could reuse those as the operation names in your batch entries. args holds

the arguments that were passed to your custom function from Excel. And finally,
resolve or reject methods store a promise holding the information the remote service
returns.

Add the following code to your functions.js or functions.ts file.

JavaScript

// This function encloses your custom functions as individual entries,


// which have some additional properties so you can keep track of whether or
not
// a request has been resolved or rejected.
function _pushOperation(op, args) {
// Create an entry for your custom function.
console.log("pushOperation");
const invocationEntry = {
operation: op, // e.g., sum
args: args,
resolve: undefined,
reject: undefined,
};

// Create a unique promise for this invocation,


// and save its resolve and reject functions into the invocation entry.
const promise = new Promise((resolve, reject) => {
invocationEntry.resolve = resolve;
invocationEntry.reject = reject;
});

// Push the invocation entry into the next batch.


_batch.push(invocationEntry);

// If a remote request hasn't been scheduled yet,


// schedule it after a certain timeout, e.g., 100 ms.
if (!_isBatchedRequestScheduled) {
console.log("schedule remote request");
_isBatchedRequestScheduled = true;
setTimeout(_makeRemoteRequest, 100);
}

// Return the promise for this invocation.


return promise;
}

Make the remote request


The purpose of the _makeRemoteRequest function is to pass the batch of operations to
the remote service, and then return the results to each custom function. It first creates a
copy of the batch array. This allows concurrent custom function calls from Excel to
immediately begin batching in a new array. The copy is then turned into a simpler array
that does not contain the promise information. It wouldn't make sense to pass the
promises to a remote service since they would not work. The _makeRemoteRequest will
either reject or resolve each promise based on what the remote service returns.

Add the following code to your functions.js or functions.ts file.

JavaScript
// This is a private helper function, used only within your custom function
add-in.
// You wouldn't call _makeRemoteRequest in Excel, for example.
// This function makes a request for remote processing of the whole batch,
// and matches the response batch to the request batch.
function _makeRemoteRequest() {
// Copy the shared batch and allow the building of a new batch while you
are waiting for a response.
// Note the use of "splice" rather than "slice", which will modify the
original _batch array
// to empty it out.
try{
console.log("makeRemoteRequest");
const batchCopy = _batch.splice(0, _batch.length);
_isBatchedRequestScheduled = false;

// Build a simpler request batch that only contains the arguments for each
invocation.
const requestBatch = batchCopy.map((item) => {
return { operation: item.operation, args: item.args };
});
console.log("makeRemoteRequest2");
// Make the remote request.
_fetchFromRemoteService(requestBatch)
.then((responseBatch) => {
console.log("responseBatch in fetchFromRemoteService");
// Match each value from the response batch to its corresponding
invocation entry from the request batch,
// and resolve the invocation promise with its corresponding response
value.
responseBatch.forEach((response, index) => {
if (response.error) {
batchCopy[index].reject(new Error(response.error));
console.log("rejecting promise");
} else {
console.log("fulfilling promise");
console.log(response);

batchCopy[index].resolve(response.result);
}
});
});
console.log("makeRemoteRequest3");
} catch (error) {
console.log("error name:" + error.name);
console.log("error message:" + error.message);
console.log(error);
}
}

Modify _makeRemoteRequest for your own solution


The _makeRemoteRequest function calls _fetchFromRemoteService which, as you'll see
later, is just a mock representing the remote service. This makes it easier to study and
run the code in this article. But when you want to use this code for an actual remote
service you should make the following changes.

Decide how to serialize the batch operations over the network. For example, you
may want to put the array into a JSON body.
Instead of calling _fetchFromRemoteService you need to make the actual network
call to the remote service passing the batch of operations.

Process the batch call on the remote service


The last step is to handle the batch call in the remote service. The following code sample
shows the _fetchFromRemoteService function. This function unpacks each operation,
performs the specified operation, and returns the results. For learning purposes in this
article, the _fetchFromRemoteService function is designed to run in your web add-in and
mock a remote service. You can add this code to your functions.js or functions.ts file so
that you can study and run all the code in this article without having to set up an actual
remote service.

Add the following code to your functions.js or functions.ts file.

JavaScript

// This function simulates the work of a remote service. Because each


service
// differs, you will need to modify this function appropriately to work with
the service you are using.
// This function takes a batch of argument sets and returns a promise that
may contain a batch of values.
// NOTE: When implementing this function on a server, also apply an
appropriate authentication mechanism
// to ensure only the correct callers can access it.
async function _fetchFromRemoteService(requestBatch) {
// Simulate a slow network request to the server.
console.log("_fetchFromRemoteService");
await pause(1000);
console.log("postpause");
return requestBatch.map((request) => {
console.log("requestBatch server side");
const { operation, args } = request;

try {
if (operation === "div2") {
// Divide the first argument by the second argument.
return {
result: args[0] / args[1]
};
} else if (operation === "mul2") {
// Multiply the arguments for the given entry.
const myResult = args[0] * args[1];
console.log(myResult);
return {
result: myResult
};
} else {
return {
error: `Operation not supported: ${operation}`
};
}
} catch (error) {
return {
error: `Operation failed: ${operation}`
};
}
});
}

function pause(ms) {
console.log("pause");
return new Promise((resolve) => setTimeout(resolve, ms));
}

Modify _fetchFromRemoteService for your live remote


service
To modify the _fetchFromRemoteService function to run in your live remote service, make
the following changes.

Depending on your server platform (Node.js or others) map the client network call
to this function.
Remove the pause function which simulates network latency as part of the mock.
Modify the function declaration to work with the parameter passed if the
parameter is changed for network purposes. For example, instead of an array, it
may be a JSON body of batched operations to process.
Modify the function to perform the operations (or call functions that do the
operations).
Apply an appropriate authentication mechanism. Ensure that only the correct
callers can access the function.
Place the code in the remote service.

Next steps
Learn about the various parameters you can use in your custom functions. Or review the
basics behind making a web call through a custom function.

See also
Volatile values in functions
Create custom functions in Excel
Excel custom functions tutorial
Office Add-ins glossary
Article • 03/21/2023

This is a glossary of terms commonly used throughout the Office Add-ins


documentation.

add-in
Office Add-ins are web applications that extend Office applications. These web
applications add new functionality to the Office application, such as bring in external
data, automate processes, or embed interactive objects in Office documents.

Office Add-ins differ from VBA, COM, and VSTO add-ins because they offer cross-
platform support (usually web, Windows, Mac, and iPad) and are based on standard web
technologies (HTML, CSS, and JavaScript). The primary programming language of an
Office Add-in is JavaScript or TypeScript.

add-in commands
Add-in commands are UI elements, such as buttons and menus, that extend the Office
UI for your add-in. When users select an add-in command element, they initiate actions
such as running JavaScript code or displaying the add-in in a task pane. Add-in
commands let your add-in look and feel like a part of Office, which gives users more
confidence in your add-in. See Add-in commands to learn more.

See also: ribbon, ribbon button.

application
Application refers to an Office application. The Office applications that support Office
Add-ins are Excel, OneNote, Outlook, PowerPoint, Project, and Word.

See also: client, host, Office application, Office client.

application-specific API
Application-specific APIs provide strongly-typed objects that interact with objects that
are native to a specific Office application. For example, you call the Excel JavaScript APIs
for access to worksheets, ranges, tables, charts, and more. Application-specific APIs are
currently available for Excel, OneNote, PowerPoint, Visio, and Word. See Application-
specific API model to learn more.

See also: Common API.

client
Client typically refers to an Office application. The Office applications, or clients, that
support Office Add-ins are Excel, OneNote, Outlook, PowerPoint, Project, and Word.

See also: application, host, Office application, Office client.

Common API
Common APIs are used to access features such as UI, dialogs, and client settings that are
common across multiple Office applications. This API model uses callbacks , which
allow you to specify only one operation in each request sent to the Office application.

Common APIs were introduced with Office 2013. Some Common APIs are legacy APIs
from the early 2010s. Excel, PowerPoint, and Word all have Common API functionality,
but most of this functionality has been replaced or superseded by the application-
specific API model. The application-specific APIs are preferred when possible.

Other Common APIs, such as the Common APIs related to Outlook, UI, and
authentication, are the modern and preferred APIs for these purposes. For details about
the Common API object model, see Common JavaScript API object model.

See also: application-specific API.

content add-in
Content add-ins are webviews, or web browser views, that are embedded directly into
Excel, OneNote, or PowerPoint documents. Content add-ins give users access to
interface controls that run code to modify documents or display data from a data
source. Use content add-ins when you want to embed functionality directly into the
document. See Content Office Add-ins to learn more.

See also: webview.

content delivery network (CDN)


A content delivery network or CDN is a distributed network of servers and data centers.
It typically provides higher resource availability and performance when compared to a
single server or data center.

Contoso
Contoso Ltd. (also known as Contoso and Contoso University) is a fictional company
used by Microsoft as an example company and domain.

custom function
A custom function is a user-defined function that is packaged with an Excel add-in.
Custom functions enable developers to add new functions, beyond the typical Excel
features, by defining those functions in JavaScript as part of an add-in. Users within
Excel can access custom functions just as they would any native function in Excel. See
Create custom functions in Excel to learn more.

7 Note

Custom function is a general term that is interchangeable with user-defined


function. Both terms apply to VBA, COM, and Office.js add-ins. The Office Add-ins
documentation uses the term custom function when referring to custom functions
that use Office JavaScript APIs.

custom functions runtime


A custom functions runtime is a JavaScript-only runtime that runs custom functions on
some combinations of Office host and platform. It has no UI and cannot interact with
Office.js APIs. If your add-in only has custom functions, this is a good lightweight
runtime to use. If your custom functions need to interact with the task pane or Office.js
APIs, configure a shared runtime. See Configure your Office Add-in to use a shared
runtime to learn more.

See also: runtime, shared runtime.

custom functions-only add-in


An add-in that contains a custom function, but no UI such as a task pane. The custom
functions in this kind of add-in run in a JavaScript-only runtime. A custom function that
does include a UI can use either a shared runtime or a combination of a JavaScript-only
runtime and an HTML-supporting runtime. We recommend that if you have a UI, you
use a shared runtime.

See also: custom function, custom functions runtime.

host
<Host> typically refers to an Office application. The Office applications, or hosts, that
support Office Add-ins are Excel, OneNote, Outlook, PowerPoint, Project, and Word.

See also: application, client, Office application, Office client.

Office application, Office client


Office client refers to an Office application. The Office applications, or clients, that
support Office Add-ins are Excel, OneNote, Outlook, PowerPoint, Project, and Word.

See also: application, client, host.

perpetual
Perpetual refers to versions of Office available through a volume-licensing agreement or
retail channels.

Other Microsoft content may use the term non-subscription to represent this concept.

See also: retail, retail perpetual, volume-licensed, volume-licensed perpetual, volume


licensing

platform
A platform usually refers to the operating system running the Office application.
Platforms that support Office Add-ins include Windows, Mac, iPad, and web browsers.

quick start
A quick start is a high-level description of key skills and knowledge required for the
basic operation of a particular program. In the Office Add-ins documentation, a quick
start is an introduction to developing an add-in for a particular application, such as
Outlook. A quick start contains a series of steps that an add-in developer can complete
in approximately 5 minutes, resulting in a functioning add-in and functional
development environment.

See also: tutorial.

requirement set
Requirement sets are named groups of API members. Requirement sets can be specific
to Office applications, such as the ExcelApi 1.7 requirement set (a set of APIs that can
only be used in Excel), or common to multiple applications, such as the DialogApi 1.1
requirement set (a set of APIs that can be used in any Office application that supports
the Dialog API).

Your add-in can use requirement sets to determine whether the Office application
supports the API members that it needs to use. For more information about this, see
Specify Office applications and API requirements.

Requirement set support varies by Office application, version, and platform. For detailed
information about the platforms, requirement sets, and Common APIs that each Office
application supports, see Office client application and platform availability for Office
Add-ins.

retail, retail perpetual


Retail refers to perpetual versions of Office available through retail channels. These do
not include versions provided by a Microsoft 365 subscription nor volume-licensing
agreement.

Other Microsoft content may use the term one-time purchase or consumer to represent
this concept.

See also: perpetual

ribbon, ribbon button


A ribbon is a command bar that organizes an application's features into a series of tabs
or buttons at the top of a window. A ribbon button is one of the buttons within this
series. See Show or hide the ribbon in Office for more information.

runtime
A runtime is the host environment (including a JavaScript engine and usually also an
HTML rendering engine) that the add-in runs in. In Office on Windows and Office on
Mac, the runtime is an embedded browser control (or webview) such as Internet
Explorer, Edge Legacy, Edge WebView2, or Safari. Different parts of an add-in run in
separate runtimes. For example, add-in commands, custom functions, and task pane
code typically use separate runtimes unless you configure a shared runtime. See
Runtimes in Office Add-ins and Browsers and webview controls used by Office Add-ins
for more information.

See also: custom functions runtime, shared runtime, webview.

shared runtime
A shared runtime, enables all code in your add-in, including task pane, add-in
commands, and custom functions, to run in the same runtime and continue running
even when the task pane is closed. See shared runtime and Tips for using the shared
runtime in your Office Add-in to learn more.

See also: custom functions runtime, runtime.

subscription
Subscription refers to versions of Office available with a Microsoft 365 subscription.

task pane
Task panes are interface surfaces, or webviews, that typically appear on the right side of
the window within Excel, Outlook, PowerPoint, and Word. Task panes give users access
to interface controls that run code to modify documents or emails, or display data from
a data source. Use task panes when you don't need to or can't embed functionality
directly into the document. See Task panes in Office Add-ins to learn more.

See also: webview.

tutorial
A tutorial is a teaching aid designed to help people learn to use a product or procedure.
In the Office Add-ins context, a tutorial guides an add-in developer through the
complete add-in development process for a particular application, such as Excel. This
involves following 20 or more steps and is a greater time investment than a quick start.
See also: quick start.

volume-licensed, volume-licensed perpetual,


volume licensing
Volume-licensed refers to a perpetual version of Office available through a volume-
licensing agreement between Microsoft and your company.

Other Microsoft content may use the term commercial to represent this concept.

See also: perpetual

web add-in
Web add-in is a legacy term for an Office Add-in. This term may be used when the
Microsoft 365 documentation needs to distinguish modern Office Add-ins from other
types of add-ins like VBA, COM, or VSTO.

See also: add-in.

webview
A webview is an element or view that displays web content inside an application.
Content add-ins and task panes both contain embedded web browsers and are
examples of webviews in Office Add-ins.

See also: content add-in, task pane.

XLL
An XLL add-in is an Excel add-in file that provides user-defined functions and has the
file extension .xll. An XLL file is a type of dynamic link library (DLL) file that can only be
opened by Excel. XLL add-in files must be written in C or C++. Custom functions are the
modern equivalent of XLL user-defined functions. Custom functions offer support across
platforms and are backwards compatible with XLL files. See Extend custom functions
with XLL user-defined functions for more information.

See also: custom function.

Yeoman generator, yo office


The Yeoman generator for Office Add-ins uses the open source Yeoman tool to
generate an Office Add-in via the command line. yo office is the command that runs
the Yeoman generator for Office Add-ins. The Office Add-ins quick starts and tutorials
use the Yeoman generator.

See also
Office Add-ins additional resources
Office Add-ins additional resources
Article • 03/08/2023

These resources provide additional information and support for developing Office Add-
ins.

Product support and service issues


Use Microsoft 365 Admin Center or Azure Admin Center for any business-critical
issues that need SLA-based support.

Product support Contact

Microsoft 365 If you have a Premier support contract for Microsoft 365, visit the Microsoft
product issues and 365 Admin Center and use the Support menu to open a service request .
failures

Azure help and If you have a paid Azure subscription, visit the Azure Admin Center to open
support a ticket .

General questions If you have general questions about Microsoft 365 or Office, submit your
about Microsoft questions on Microsoft 365 and Office Community .
365

Developer community help


Our community of developers use Stack Overflow, GitHub, and Microsoft Q&A to
connect with other developers to ideate, get clarifications, and submit queries. You can
also get latest updates in our Microsoft Office Add-ins community call.

Developer community forums


Post your questions and help other community members by sharing and responding to
Office Add-in Development questions.

Developer Contact
need
Developer Contact
need

Office Add-in / Post API questions to Stack Overflow using the office-js tag, and also
Office include the outlook-web-addins tag if your question relates to Outlook
JavaScript API add-ins. Please note that Stack Overflow has guidelines such as requiring
questions a descriptive title, a complete and concise problem statement, and
sufficient details to reproduce your issue. Feature requests or overly
broad questions are off-topic; new users should visit the Stack Overflow
Help Center for more details.
You can also post questions using the Office Development tag on
Microsoft Q&A .

Documentation If the documentation is missing something that would help you understand
gaps how to create add-ins, please submit an issue to the Office Add-ins
documentation GitHub repository

Report issues
Submit issues and ask general questions related to add-ins, documentation, and
samples using the community help channels.

Issue type Contact

Office Add-ins or If you encounter an issue (bug) with the Office JavaScript API, please
Office JavaScript API submit an issue to the Office JavaScript APIs GitHub repository .
issues

Documentation issues If you encounter an issue (bug) with the documentation, please submit
an issue to the Office Add-ins documentation GitHub repository .

Issues with code Submit issues with samples to the Office Add-ins code samples
samples repository.

Documentation feedback
Submit documentation feedback or updates using the community help channels.

Feedback type Contact

Documentation If you encounter an issue (bug) with the documentation, please submit an issue
issues to the Office Add-ins documentation GitHub repository .

Documentation To make changes to the documentation yourself, choose the Edit link on an
updates article and submit a pull request to the Office Add-ins documentation GitHub
repository .
Feature request and general help
Suggest a feature or vote up existing feature requests.

Support need Contact

Office Add-ins or To submit a feature request for the Office JavaScript API, please post your
Office JavaScript idea to the Microsoft 365 Developer Platform Tech Community .
API feature
requests

General Send general questions about the Office Add-ins platform to Office Add-ins
questions Community Help. We encourage posting questions on the channels
mentioned here and using email only if no other mode of communication is
applicable.

External learning resources


The following books and classes provide add-ins information from a different
perspective.

Resource More information

Pluralsight John Brown's course "Fundamentals of Building Office Add-ins with Office JavaScript
course APIs" teaches you how to develop a PowerPoint add-in from scratch, including
about information about how to communicate with Trello and publish the add-in to the
Office Office Store.
Add-ins

LinkedIn Bill Ayer's course "Microsoft Office Add-Ins for Developers" gives an overview of
course the platform and explains many of the major concepts of Office Add-ins.
about
Office
Add-ins

Building Michael Zlatkovsky's book Building Office Add-ins using Office.js describes the
Office principles and design patterns shared by the 2016 APIs for Excel, Word, and
Add-ins OneNote. At the time of writing, Michael was a member of the Office Extensibility
using team at Microsoft, but this book is independently authored without input from
Office.js Microsoft. Neither Microsoft nor Michael collect any profit on this book, as proceeds
book are donated to disaster-relief and humanitarian charitable causes.

See also
Learn about the Microsoft 365 Developer Program
Explore Office JavaScript API using Script Lab
Microsoft Office Add-ins community call
Article • 08/10/2023

The Microsoft Office Add-ins community call is a monthly call where you can learn more
about new features, development practices, and additional information about creating
Office Add-ins. The community call occurs the second Wednesday of each month at 8:00
AM Pacific Time. You can download the calendar invite at
https://aka.ms/officeaddinscommunitycall .

Agenda for September 13, 2023 call


The agenda for the September call will be posted the week of the call.

Previous calls
Missed a previous community call? Check out the following blog resources to catch up
on the discussion!

June 14, 2023


May 10, 2023
April 12, 2023
March 8, 2023
February 8, 2023
January 11, 2023
December 14, 2022
November 9, 2022
October 12, 2022
September 14, 2022
August 10, 2022
July 13, 2022
June 8, 2022
May 11, 2022
April 13, 2022
March 9, 2022
February 9, 2022
January 12, 2022
December 8, 2021
November 10, 2021
October 13, 2021
September 8, 2021
August 11, 2021
July 14, 2021
June 9, 2021
May 12, 2021
April 14, 2021
March 10, 2021
Archived Office Add-ins community calls – June 10, 2020 - February 10, 2021

See also
Recurring, monthly community call calendar invite
Community call topic requests and questions
Microsoft 365 developer YouTube channel
Microsoft 365 community YouTube channel
Microsoft 365 community site
Microsoft 365 community blog
VSTO add-in developer's guide to Office
Web Add-ins
Article • 05/20/2023

So, you've made some VSTO add-ins for Office applications that run on Windows and
now you're exploring the new way of extending Office that will run on Windows, Mac,
and the web browser version of the Office suite: Office Web Add-ins.

) Important

COM and VSTO add-ins aren't supported in the new Outlook on Windows that's
currently in preview. These add-ins are still supported in the classic Outlook on
Windows desktop client. To learn more, see Develop Outlook add-ins for new
Outlook on Windows (preview).

Your understanding of the object models for the Excel, Word, and the other Office
applications will be a huge help because the object models in Office Web Add-ins follow
similar patterns. But there are going to be some challenges:

You'll be working with a different language (either JavaScript or TypeScript) instead


of C# or Visual Basic .NET. (There is also a way, described later, to reuse some of
your existing code in a web add-in.)
Office Web Add-ins are deployed differently from VSTO add-ins.
Office Web Add-ins are web applications that run in a simplified webview control
that is embedded in the Office application, so you need to gain a basic
understanding of web applications and how they're hosted on web servers or
cloud accounts.

For these reasons, much of this article duplicates our Beginner's guide to Office
extensions. What we've added are some learning resources to help VSTO add-in
developers leverage their experience, and also help them reuse their existing code.

Step 0: Prerequisites
Office Web Add-ins (also referred to as Office Add-ins) are essentially web
applications embedded in Office. So, you should first have a basic understanding
of web applications and how they're hosted on the web. There's an enormous
amount of information about this on the Internet, in books, and in online courses.
A good way to start if you have no prior knowledge of web applications at all is to
search for "What is a web app?" in your search engine.
The primary programming language you'll use to create Office Add-ins is
JavaScript or TypeScript. If you're not familiar with either of these languages, but
you have experience with VBA, VB.NET, C#, you'll probably find TypeScript easier to
learn. Again, there's a wealth of information about these languages on the Internet,
in books, and in online courses.

Step 1: Begin with fundamentals


We know you're eager to start coding, but there are some things about Office Add-ins
that you should read before you open your IDE or code editor.

Office Add-ins Platform Overview: Find out what Office Web Add-ins are and how
they differ from older ways of extending Office, such as VSTO add-ins.
Develop Office Add-ins: Get an overview of Office Add-in development and
lifecycle including tooling, creating an add-in UI, and using the JavaScript APIs to
interact with the Office document.

There are a lot of links in those articles, but if you're transitioning to Office Web Add-ins,
we recommend that you come back here when you've read them and continue with the
next section.

Step 2: Install tools and create your first add-in


You've got the big picture now, so dive in with one of our quick starts. For purposes of
learning the platform, we recommend the Excel quick start. There's a version based on
Visual Studio and another based on Node.js and Visual Studio Code. If you're
transitioning from VSTO add-ins, you'll probably find the Visual Studio version easier to
work with.

Visual Studio
Node.js and Visual Studio Code

Step 3: Code
You can't learn to drive by reading the owner's manual, so start coding with this Excel
tutorial. You'll be using the Office JavaScript library and some XML in the add-in's
manifest. There's no need to memorize anything, because you'll be getting more
background about both in a later step.
Step 4: Understand the JavaScript library
Get the big picture of the Office JavaScript library with the Understand the Office
JavaScript APIs tutorial from Microsoft Learn training.

Then, explore the Office JavaScript APIs with the Script Lab tool -- a sandbox for running
and exploring the APIs.

Special resource for VSTO add-in developers


This would be a good place to take a look at the sample add-in, Excel Add-in JavaScript
SalesTracker . It was created to highlight the similarities and differences between VSTO
add-ins and Office Web Add-ins, and the readme of the sample calls out the important
points of comparison.

Step 5: Understand the manifest


Get an understanding of the purposes of the web add-in manifest and an introduction
to its XML markup or JSON in Office Add-ins manifest.

Step 6 (for VSTO developers only): Reuse your


VSTO code
You can reuse some of your VSTO add-in code in an Office web add-in by moving it to
your web application's back end on the server and making it available to your JavaScript
or TypeScript as a web API. For guidance, see Tutorial: Share code between both a VSTO
Add-in and an Office Add-in by using a shared code library.

Next Steps
Congratulations on finishing the VSTO add-in developer's learning path for Office Web
Add-ins! Here are some suggestions for further exploration of our documentation:

Tutorials or quick starts for other Office applications:


OneNote quick start
Outlook tutorial
PowerPoint tutorial
Project quick start
Word tutorial
Other important subjects:
Develop Office Add-ins
Best practices for developing Office Add-ins
Design Office Add-ins
Test and debug Office Add-ins
Deploy and publish Office Add-ins
Resources
Learn about the Microsoft 365 Developer Program
Tutorial: Share code between both a
VSTO Add-in and an Office Add-in with
a shared code library
Article • 05/02/2023

Visual Studio Tools for Office (VSTO) Add-ins are great for extending Office to provide
solutions for your business or others. They've been around for a long time and there are
thousands of solutions built with VSTO. However, they only run on Office on Windows.
You can't run VSTO Add-ins on Mac, on the web, or on mobile platforms.

) Important

COM and VSTO add-ins aren't supported in the new Outlook on Windows that's
currently in preview. These add-ins are still supported in the classic Outlook on
Windows desktop client. To learn more, see Develop Outlook add-ins for new
Outlook on Windows (preview).

Office Add-ins use HTML, JavaScript, and additional web technologies to build Office
solutions on all platforms. Migrating your existing VSTO Add-in to an Office Add-in is a
great way to make your solution available across all platforms.

You may want to maintain both your VSTO Add-in and a new Office Add-in that both
have the same functionality. This enables you to continue servicing your customers that
use the VSTO Add-in on Office on Windows. This also enables you to provide the same
functionality in an Office Add-in for customers across all platforms. You can also Make
your Office Add-in compatible with the existing VSTO Add-in.

However, it's best to avoid rewriting all the code from your VSTO Add-in for the Office
Add-in. This tutorial shows how to avoid rewriting code by using a shared code library
for both add-ins.

Shared code library


This tutorial walks you through the steps of identifying and sharing common code
between your VSTO Add-in and a modern Office Add-in. It uses a very simple VSTO
Add-in example for the steps so that you can focus on the skills and techniques you'll
need to work with your own VSTO Add-ins.
The following diagram shows how the shared code library works for migration. Common
code is refactored into a new shared code library. The code can remain written in its
original language, such as C# or VB. This means you can continue using the code in the
existing VSTO Add-in by creating a project reference. When you create the Office Add-
in, it will also use the shared code library by calling into it through REST APIs.

Skills and techniques in this tutorial:

Create a shared class library by refactoring code into a .NET class library.
Create a REST API wrapper using ASP.NET Core for the shared class library.
Call the REST API from the Office Add-in to access shared code.

Prerequisites
To set up your development environment:

1. Install Visual Studio 2019 .


2. Install the following workloads.

ASP.NET and web development


.NET Core cross-platform development
Office/SharePoint development
The following Individual components.
Visual Studio Tools for Office (VSTO)
.NET Core 3.0 Runtime
You also need the following:

A Microsoft 365 account. You can join the Microsoft 365 developer program that
provides a renewable 90-day Microsoft 365 subscription that includes Office apps.
A Microsoft Azure Tenant. A trial subscription can be acquired here: Microsoft
Azure .

The Cell analyzer VSTO Add-in


This tutorial uses the VSTO Add-in shared library for Office Add-in PnP solution. The
/start folder contains the VSTO Add-in solution that you'll migrate. Your goal is to
migrate the VSTO Add-in to a modern Office Add-in by sharing code when possible.

7 Note

The sample uses C#, but you can apply the techniques in this tutorial to a VSTO
Add-in written in any .NET language.

1. Download the VSTO Add-in shared library for Office Add-in PnP solution to a
working folder on your computer.
2. Start Visual Studio 2019 and open the /start/Cell-Analyzer.sln solution.
3. On the Debug menu, choose Start Debugging.

The add-in is a custom task pane for Excel. You can select any cell with text, and then
choose the Show unicode button. In the Result section, the add-in will display a list of
each character in the text along with its corresponding Unicode number.

Analyze types of code in the VSTO Add-in


The first technique to apply is to analyze the add-in for which parts of code can be
shared. In general, project will break down into three types of code.
UI code
UI code interacts with the user. In VSTO UI code works through Windows Forms. Office
Add-ins use HTML, CSS, and JavaScript for UI. Because of these differences, you can't
share UI code with the Office Add-in. UI will need to be recreated in JavaScript.

Document code
In VSTO, code interacts with the document through .NET objects, such as
Microsoft.Office.Interop.Excel.Range . However, Office Add-ins use the Office.js library.

Although these are similar, they aren't exactly the same. So again, you can't share
document interaction code with the Office Add-in.

Logic code
Business logic, algorithms, helper functions, and similar code often make up the heart of
a VSTO Add-in. This code works independently of the UI and document code to perform
analysis, connect to backend services, run calculations, and more. This is the code that
can be shared so that you don't have to rewrite it in JavaScript.

Let's examine the VSTO Add-in. In the following code, each section is identified as
DOCUMENT, UI, or ALGORITHM code.

C#

// *** UI CODE ***


private void btnUnicode_Click(object sender, EventArgs e)
{
// *** DOCUMENT CODE ***
Microsoft.Office.Interop.Excel.Range rangeCell;
rangeCell = Globals.ThisAddIn.Application.ActiveCell;

string cellValue = "";

if (null != rangeCell.Value)
{
cellValue = rangeCell.Value.ToString();
}

// *** ALGORITHM CODE ***


//convert string to Unicode listing
string result = "";
foreach (char c in cellValue)
{
int unicode = c;

result += $"{c}: {unicode}\r\n";


}

// *** UI CODE ***


//Output the result
txtResult.Text = result;
}

Using this approach, you can see that one section of code can be shared with the Office
Add-in. The following code needs to be refactored into a separate class library.

C#

// *** ALGORITHM CODE ***


//convert string to Unicode listing
string result = "";
foreach (char c in cellValue)
{
int unicode = c;

result += $"{c}: {unicode}\r\n";


}

Create a shared class library


VSTO Add-ins are created in Visual Studio as .NET projects, so we'll reuse .NET as much
as possible to keep things simple. Our next technique is to create a class library and
refactor shared code into that class library.

1. If you haven't already, start Visual Studio 2019 and open the \start\Cell-
Analyzer.sln solution.

2. Right-click the solution in Solution Explorer and choose Add > New Project.

3. In the Add a new project dialog, choose Class Library (.NET Framework), and
choose Next.

7 Note

Don't use the .NET Core class library because it won't work with your VSTO
project.

4. In the Configure your new project dialog, set the following fields.

Set the Project name to CellAnalyzerSharedLibrary.


Leave the Location at its default value.
Set the Framework to 4.7.2.

5. Choose Create.

6. After the project is created, rename the Class1.cs file to CellOperations.cs. A


prompt to rename the class appears. Rename the class name so that it matches the
file name.

7. Add the following code to the CellOperations class to create a method named
GetUnicodeFromText .

C#

public class CellOperations


{
static public string GetUnicodeFromText(string value)
{
string result = "";
foreach (char c in value)
{
int unicode = c;

result += $"{c}: {unicode}\r\n";


}
return result;
}
}

Use the shared class library in the VSTO Add-in


Now you need to update the VSTO Add-in to use the class library. This is important that
both the VSTO Add-in and Office Add-in use the same shared class library so that future
bug fixes or features are made in one location.

1. In Solution Explorer, right-click the Cell-Analyzer project, and choose Add


Reference.

2. Select CellAnalyzerSharedLibrary, and choose OK.

3. In Solution Explorer, expand the Cell-Analyzer project, right-click the


CellAnalyzerPane.cs file, and choose View Code.

4. In the btnUnicode_Click method, delete the following lines of code.

C#
//Convert to Unicode listing
string result = "";
foreach (char c in cellValue)
{
int unicode = c;
result += $"{c}: {unicode}\r\n";
}

5. Update the line of code under the //Output the result comment to read as
follows:

C#

//Output the result


txtResult.Text =
CellAnalyzerSharedLibrary.CellOperations.GetUnicodeFromText(cellValue);

6. On the Debug menu, choose Start Debugging. The custom task pane should work
as expected. Enter some text in a cell, and then test that you can convert it to a
Unicode list with the add-in.

Create a REST API wrapper


The VSTO Add-in can use the shared class library directly since they are both .NET
projects. However the Office Add-in won't be able to use .NET since it uses JavaScript.
Next, you'll create a REST API wrapper. This enables the Office Add-in to call a REST API,
which then passes the call along to the shared class library.

1. In Solution Explorer, right-click the Cell-Analyzer project, and choose Add > New
Project.

2. In the Add a new project dialog, choose ASP.NET Core Web Application, and
choose Next.

3. In the Configure your new project dialog, set the following fields.

Set the Project name to CellAnalyzerRESTAPI.


In the Location field, leave the default value.

4. Choose Create.

5. In the Create a new ASP.NET Core web application dialog, select ASP.NET Core 3.1
for the version, and select API in the list of projects.
6. Leave all other fields at default values and choose the Create button.

7. After the project is created, expand the CellAnalyzerRESTAPI project in Solution


Explorer.

8. Right-click Dependencies, and choose Add Reference.

9. Select CellAnalyzerSharedLibrary, and choose OK.

10. Right-click the Controllers folder, and choose Add > Controller.

11. In the Add New Scaffolded Item dialog, choose API Controller - Empty, then
choose Add.

12. In the Add Empty API Controller dialog, name the controller
AnalyzeUnicodeController, then choose Add.

13. Open the AnalyzeUnicodeController.cs file and add the following code as a
method to the AnalyzeUnicodeController class.

C#

[HttpGet]
public ActionResult<string> AnalyzeUnicode(string value)
{
if (value == null)
{
return BadRequest();
}
return
CellAnalyzerSharedLibrary.CellOperations.GetUnicodeFromText(value);
}

14. Right-click the CellAnalyzerRESTAPI project, and choose Set as Startup Project.

15. On the Debug menu, choose Start Debugging.

16. A browser will launch. Enter the following URL to test that the REST API is working:
https://localhost:<ssl port number>/api/analyzeunicode?value=test . You can
reuse the port number from the URL in the browser that Visual Studio launched.
You should see a string returned with Unicode values for each character.

Create the Office Add-in


When you create the Office Add-in, it will make a call to the REST API. But first, you need
to get the port number of the REST API server and save it for later.
Save the SSL port number
1. If you haven't already, start Visual Studio 2019, and open the \start\Cell-
Analyzer.sln solution.
2. In the CellAnalyzerRESTAPI project, expand Properties, and open the
launchSettings.json file.
3. Find the line of code with the sslPort value, copy the port number, and save it
somewhere.

Add the Office Add-in project


To keep things simple, keep all the code in one solution. Add the Office Add-in project
to the existing Visual Studio solution. However, if you're familiar with the Yeoman
generator for Office Add-ins and Visual Studio Code, you can also run yo office to
build the project. The steps are very similar.

1. In Solution Explorer, right-click the Cell-Analyzer solution, and choose Add > New
Project.
2. In the Add a new project dialog, choose Excel Web Add-in, and choose Next.
3. In the Configure your new project dialog, set the following fields.

Set the Project name to CellAnalyzerOfficeAddin.


Leave the Location at its default value.
Set the Framework to 4.7.2 or later.

4. Choose Create.
5. In the Choose the add-in type dialog, select Add new functionalities to Excel, and
choose Finish.

Two projects will be created:

CellAnalyzerOfficeAddin - This project configures the manifest XML files that


describes the add-in so Office can load it correctly. It contains the ID, name,
description, and other information about the add-in.
CellAnalyzerOfficeAddinWeb - This project contains web resources for your add-
in, such as HTML, CSS, and scripts. It also configures an IIS Express instance to host
your add-in as a web application.

Add UI and functionality to the Office Add-in


1. In Solution Explorer, expand the CellAnalyzerOfficeAddinWeb project.
2. Open the Home.html file, and replace the <body> contents with the following
HTML.

HTML

<button id="btnShowUnicode" onclick="showUnicode()">Show


Unicode</button>
<p>Result:</p>
<div id="txtResult"></div>

3. Open the Home.js file, and replace the entire contents with the following code.

JavaScript

(function () {
"use strict";
// The initialize function must be run each time a new page is
loaded.
Office.initialize = function (reason) {
$(document).ready(function () {
});
};
})();

function showUnicode() {
Excel.run(function (context) {
const range = context.workbook.getSelectedRange();
range.load("values");
return context.sync(range).then(function (range) {
const url = "https://localhost:<ssl port
number>/api/analyzeunicode?value=" + range.values[0][0];
$.ajax({
type: "GET",
url: url,
success: function (data) {
let htmlData = data.replace(/\r\n/g, '<br>');
$("#txtResult").html(htmlData);
},
error: function (data) {
$("#txtResult").html("error occurred in ajax call.");
}
});
});
});
}

4. In the previous code, enter the sslPort number you saved previously from the
launchSettings.json file.
In the previous code, the returned string will be processed to replace carriage return line
feeds with <br> HTML tags. You may occasionally run into situations where a return
value that works perfectly fine for .NET in the VSTO Add-in will need to be adjusted on
the Office Add-in side to work as expected. In this case, the REST API and shared class
library are only concerned with returning the string. The showUnicode() function is
responsible for formatting return values correctly for presentation.

Allow CORS from the Office Add-in


The Office.js library requires CORS on outgoing calls, such as the one made from the
ajax call to the REST API server. Use the following steps to allow calls from the Office

Add-in to the REST API.

1. In Solution Explorer, select the CellAnalyzerOfficeAddinWeb project.

2. From the View menu, choose Properties Window, if the window isn't already
displayed.

3. In the properties window, copy the value of the SSL URL, and save it somewhere.
This is the URL that you need to allow through CORS.

4. In the CellAnalyzerRESTAPI project, open the Startup.cs file.

5. Add the following code to the top of the ConfigureServices method. Be sure to
substitute the URL SSL you copied previously for the builder.WithOrigins call.

C#

services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("<your URL SSL>")
.AllowAnyMethod()
.AllowAnyHeader();
});
});

7 Note

Leave the trailing / from the end of the URL when you use it in the
builder.WithOrigins method. For example, it should appear similar to

https://localhost:44000 . Otherwise, you'll get a CORS error at runtime.


6. Add the following field to the Startup class.

C#

readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

7. Add the following code to the configure method just before the line of code for
app.UseEndpoints .

C#

app.UseCors(MyAllowSpecificOrigins);

When done, your Startup class should look similar to the following code (your localhost
URL may be different).

C#

public class Startup


{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

public IConfiguration Configuration { get; }

// NOTE: The following code configures CORS for the localhost:44397


port.
// This is for development purposes. In production code, you should
update this to
// use the appropriate allowed domains.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("https://localhost:44397")
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure
the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

app.UseHttpsRedirection();

app.UseRouting();

app.UseAuthorization();

app.UseCors(MyAllowSpecificOrigins);

app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}

Run the add-in


1. In Solution Explorer, right-click the top node Solution 'Cell-Analyzer', and choose
Set Startup Projects.

2. In the Solution 'Cell-Analyzer' Property Pages dialog, select Multiple startup


projects.

3. Set the Action property to Start for each of the following projects.

CellAnalyzerRESTAPI
CellAnalyzerOfficeAddin
CellAnalyzerOfficeAddinWeb

4. Choose OK.

5. From the Debug menu, choose Start Debugging.

Excel will run and sideload the Office Add-in. You can test that the localhost REST API
service is working correctly by entering a text value into a cell, and choosing the Show
Unicode button in the Office Add-in. It should call the REST API and display the unicode
values for the text characters.
Publish to an Azure App Service
You eventually want to publish the REST API project to the cloud. In the following steps
you'll see how to publish the CellAnalyzerRESTAPI project to a Microsoft Azure App
Service. See Prerequisites for information on how to get an Azure account.

1. In Solution Explorer, right-click the CellAnalyzerRESTAPI project, and choose


Publish.
2. In the Pick a publish target dialog, select Create New, and choose Create Profile.
3. In the App Service dialog, select the correct account, if it isn't already selected.
4. The fields for the App Service dialog will be set to defaults for your account.
Generally, the defaults work fine, but you can change them if you prefer different
settings.
5. In the App Service dialog, choose Create.
6. The new profile will be displayed in a Publish page. Choose Publish to build and
deploy the code to the App Service.

You can now test the service. Open a browser and enter a URL that goes directly to the
new service. For example, use
https://<myappservice>.azurewebsites.net/api/analyzeunicode?value=test , where

myappservice is the unique name you created for the new App Service.

Use the Azure App Service from the Office Add-in


The final step is to update the code in the Office Add-in to use the Azure App Service
instead of localhost.

1. In Solution Explorer, expand the CellAnalyzerOfficeAddinWeb project, and open


the Home.js file.

2. Change the url constant to use the URL for your Azure App Service as shown in
the following line of code. Replace <myappservice> with the unique name you
created for the new App Service.

JavaScript

const url =
"https://<myappservice>.azurewebsites.net/api/analyzeunicode?value=" +
range.values[0][0];

3. In Solution Explorer, right-click the top node Solution 'Cell-Analyzer', and choose
Set Startup Projects.
4. In the Solution 'Cell-Analyzer' Property Pages dialog, select Multiple startup
projects.

5. Enable the Start action for each of the following projects.

CellAnalyzerOfficeAddinWeb
CellAnalyzerOfficeAddin

6. Choose OK.

7. From the Debug menu, choose Start Debugging.

Excel will run and sideload the Office Add-in. To test that the App Service is working
correctly, enter a text value into a cell, and choose Show Unicode in the Office Add-in. It
should call the service and display the unicode values for the text characters.

Conclusion
In this tutorial, you learned how to create an Office Add-in that uses shared code with
the original VSTO add-in. You learned how to maintain both VSTO code for Office on
Windows, and an Office Add-in for Office on other platforms. You refactored VSTO C#
code into a shared library and deployed it to an Azure App Service. You created an
Office Add-in that uses the shared library, so that you don't have to rewrite the code in
JavaScript.
Make your Office Add-in compatible
with an existing COM add-in
Article • 02/07/2023

If you have an existing COM add-in, you can build equivalent functionality in your Office
Add-in, thereby enabling your solution to run on other platforms such as Office on the
web or Mac. In some cases, your Office Add-in may not be able to provide all of the
functionality that's available in the corresponding COM add-in. In these situations, your
COM add-in may provide a better user experience on Windows than the corresponding
Office Add-in can provide.

) Important

COM and VSTO add-ins aren't supported in the new Outlook on Windows that's
currently in preview. These add-ins are still supported in the classic Outlook on
Windows desktop client. To learn more, see Develop Outlook add-ins for new
Outlook on Windows (preview).

You can configure your Office Add-in so that when the equivalent COM add-in is already
installed on a user's computer, Office on Windows runs the COM add-in instead of the
Office Add-in. The COM add-in is called "equivalent" because Office will seamlessly
transition between the COM add-in and the Office Add-in according to which one is
installed on a user's computer.

) Important

The equivalent add-in feature is supported by the following platform and


applications. COM add-ins cannot be installed on any other platform, so on those
platforms the manifest element that is discussed later in this article,
EquivalentAddins , is ignored.

Excel, Word, and PowerPoint on Windows (Version 1904 or later)


Outlook on Windows (Version 2102 or later) against a supported Exchange
server version
Exchange Online
Exchange 2019 Cumulative Update 10 or later (KB5003612 )
Exchange 2016 Cumulative Update 21 or later (KB5003611 )
Specify an equivalent COM add-in

Manifest

) Important

Applies to Excel, Outlook, PowerPoint, and Word.

To enable compatibility between your Office Add-in and COM add-in, identify the
equivalent COM add-in in the manifest of your Office Add-in. Then, Office on Windows
will use the COM add-in instead of the Office Add-in, if they're both installed.

The following example shows the portion of the manifest that specifies a COM add-in as
an equivalent add-in. The value of the ProgId element identifies the COM add-in and
the EquivalentAddins element must be positioned immediately before the closing
VersionOverrides tag.

XML

<VersionOverrides>
...
<EquivalentAddins>
<EquivalentAddin>
<ProgId>ContosoCOMAddin</ProgId>
<Type>COM</Type>
</EquivalentAddin>
</EquivalentAddins>
</VersionOverrides>

 Tip

For information about COM add-in and XLL UDF compatibility, see Make your
custom functions compatible with XLL user-defined functions. Not applicable for
Outlook.

Group policy

) Important

Applies to Outlook only.


To declare compatibility between your Outlook web add-in and COM/VSTO add-in,
identify the equivalent COM add-in in the group policy Deactivate Outlook web add-ins
whose equivalent COM or VSTO add-in is installed by configuring it on the user's
machine. Then, Outlook on Windows will use the COM add-in instead of the web add-in,
if they're both installed.

1. Download the latest Administrative Templates tool , paying attention to the tool's
Install Instructions.

2. Open the Local Group Policy Editor (gpedit.msc).

3. Navigate to User Configuration > Administrative Templates > Microsoft Outlook


2016 > Miscellaneous.

4. Select the setting Deactivate Outlook web add-ins whose equivalent COM or
VSTO add-in is installed.

5. Open the link to edit the policy setting.

6. In the dialog Outlook web add-ins to deactivate:


a. Set Value name to the Id found in the web add-in's manifest. Important: Do
not add curly braces {} around the entry.
b. Set Value to the ProgId of the equivalent COM/VSTO add-in.
c. Select OK to put the update into effect.

Equivalent behavior for users


When an equivalent COM add-in is specified, Office on Windows will not display your
Office Add-in's user interface (UI) if the equivalent COM add-in is installed. Office only
hides the ribbon buttons of the Office Add-in and doesn't prevent installation.
Therefore, your Office Add-in will still appear in the following locations within the UI.
Under My add-ins.
As an entry on the ribbon manager (Excel, Word, and PowerPoint only).

7 Note

Specifying an equivalent COM add-in in the manifest has no effect on other


platforms, like Office on the web or on Mac.

The following scenarios describe what happens depending on how the user acquires the
Office Add-in.

AppSource acquisition of an Office Add-in


If a user acquires the Office Add-in from AppSource and the equivalent COM add-in is
already installed, then Office will:

1. Install the Office Add-in.


2. Hide the Office Add-in UI on the ribbon.
3. Display a call-out for the user that points out the COM add-in ribbon button.

Centralized deployment of Office Add-in


If an admin deploys the Office Add-in to their tenant using centralized deployment, and
the equivalent COM add-in is already installed, the user must restart Office before they'll
see any changes. After Office restarts, it will:

1. Install the Office Add-in.


2. Hide the Office Add-in UI on the ribbon.
3. Display a call-out for the user that points out the COM add-in ribbon button.

Document shared with embedded Office Add-in


If a user has the COM add-in installed, and then gets a shared document with the
embedded Office Add-in, then when they open the document, Office will:

1. Prompt the user to trust the Office Add-in.


2. If trusted, the Office Add-in will install.
3. Hide the Office Add-in UI on the ribbon.

Other COM add-in behavior


Excel, PowerPoint, Word
If a user uninstalls the equivalent COM add-in, then Office on Windows restores the
Office Add-in UI.

After you specify an equivalent COM add-in for your Office Add-in, Office stops
processing updates for your Office Add-in. To acquire the latest updates for the Office
Add-in, the user must first uninstall the COM add-in.

Outlook
The COM/VSTO add-in must be connected when Outlook is started in order for the
corresponding web add-in to be disabled.

If the COM/VSTO add-in is then disconnected during a subsequent Outlook session, the
web add-in will likely remain disabled until Outlook is restarted.

See also
Make your Custom Functions compatible with XLL User Defined Functions

You might also like