How We Brought Grammarly to Microsoft Word on Mac

How We Brought Grammarly to Microsoft Word on Mac

For more than six years, we’ve supported Grammarly’s add-in for Word and Outlook on Windows. Mac users have been feeling left out: an add-in for Word on Mac has long been a top requested feature. There were many roadblocks to making this happen at the quality we expect for all of our offerings—but we were finally able to release Grammarly for Microsoft Word on Mac in beta at the end of 2019, followed by a full release in March 2020. 

The path to get there was not always straightforward or direct. Here we’ll tell the story of the sometimes rocky road to our much-anticipated add-in for Word on Mac. 

Building a proof of concept 

Year after year, we’ve been evaluating this project. We didn’t want to start a new project on old tech, but for a while the only option was to build the add-in with Visual Basic Script. That changed when Microsoft introduced the Office add-in API, which is based on JavaScript and works across all devices as well as online in the browser. Last year, this new API looked stable and mature enough for us to set aside the necessary resources to tackle the project.

We needed a proof of concept to plan the UX, and we started by building out a few options. We had to choose between two different directions. The first was to build a pop-up editor, similar to what we have for the Grammarly browser extension. The pop-up editor works wonderfully in that circumstance. But when we tested the same UI in Word, we discovered that it didn’t serve the application as well. Using the pop-up editor means being taken momentarily away from the screen where you’re writing. People who write in Word are often doing so because they’re very familiar and comfortable with that writing tool, so taking them outside of the application to review writing suggestions led to a subpar user experience. We could do better.

The second option we considered was putting Grammarly’s suggestions directly in the Word sidebar while underlining the corresponding text in the document. This would essentially mimic the design of our add-in for Word for Windows:

When we started working with the JavaScript API, however, we quickly ran into restrictions that meant we wouldn’t be able to achieve the exact same functionality as our Word for Windows add-in, which is built on COM. Even so, putting the suggestions into the sidebar would make for the best option from a UX perspective—so this was the path we chose to follow.

Shape the way millions of people communicate!
Open Roles

Productionizing (and hitting snags) 

Last year, we set to work productionizing the proof of concept while doing our best to get around the limitations of the Office add-in API. We wanted to achieve parity with our add-in for Word on Windows. 

Unfortunately, we were often hampered by limited functionality. We’ll step through what we set out to achieve and give an indication of how well we fared in each case. 

Render Grammarly suggestions in the sidebar 😌

This was possible to do using the Office add-in API, which gives applications full control to render content in the sidebar. We were able to make Grammarly’s writing suggestions appear in the sidebar cleanly and considered this a success.

Render Grammarly icon in the navigation bar 🙁

The top bar in Word is called a “ribbon” and is the standard element for navigation across Microsoft products. We wanted to be able to add badging to the Grammarly icon in the ribbon and update the user with the number of alerts in their document. Grammarly’s add-in for Word on Windows supports this with the COM API, but when using the new JavaScript API for Mac, we unfortunately could only render a static icon.

Here is the ribbon in Word on Windows, where the Grammarly icon is dynamic. While Grammarly is running, we can show badges in the navigation bar that update with real-time stats about the document:

Here is the ribbon in Word on Mac, where the Grammarly icon is static. Clicking on it lets the user open or close the application in the sidebar:

Access text and get textChanged events 😕

Grammarly’s writing assistant reviews text as it’s being written to offer suggestions in real-time. This means that our add-in for Word on Mac needed not only to be able to detect when the text was updated but also to get access to the text itself so that we could deliver our suggestions. 

The first roadblock was the lack of a textChanged event in the JavaScript API. This doesn’t exist in the COM API on Windows, either, so it was a problem we already had some experience solving by polling the text. 

The bigger issue we had to work around involved accessing ranges. A range is a section of text delineated by start and end points measured in characters. For example, in the text "This is a test", delineating range(start: 5, end: 7) will give you the word "is".

Grammarly needs the range function to find the position in the text for a suggestion and to update the text when the user accepts the suggestion. While the JavaScript API provides some limited ways to access the text, it does not expose a way to get a range with specific start and end points. Our solution was to retrieve paragraphs from the document and then use the search API function with wildcards, a form of simplified regular expressions supported by Word. For example, search(`?{40}`) will return the first 40 characters in the text.

To better illustrate, below is some sample code for how we find ranges.

Here’s the helper function to find a range from the beginning of the text to a certain position:

Here’s the function to find the range given a starting and ending position:

Here’s an example of how to use the findRange function, which returns the text of the range:

This workaround gave us the results we needed, but the API design still led us to experience some performance issues (we will discuss these in more detail later on). 

Move cursor to range 😌

When a user selects a Grammarly suggestion in the sidebar, they should be taken to the corresponding text. This means that we need to move the user’s cursor to that range of text when they click. Thankfully, the API supports a range.select function and we were able to make this possible—another success.

Get range’s vertical coordinate 🙁

From a UX perspective, it would be nice to be able to align our suggestions with the corresponding piece of text in the document—that way a user would see them directly side by side. To do this, we’d need to have the vertical coordinate for a range of text. Unfortunately, while the COM API for Word on Windows supports this, the JavaScript add-in API for Mac does not—and there is no workaround.

Underline range 🙁

Probably the most disappointing limitation we encountered was regarding underlining text. We typically indicate where Grammarly’s suggestions are in the text by underlining the relevant parts of the text. On Windows, we are able to add custom underlines that are not applied to the actual document. However, with the JavaScript API, there is no way to achieve this in the way we need. We can change the formatting of a range of text, but that formatting change will be saved in the document itself, which is not what we want to do for our writing assistant. 

For a brief moment, we considered adding a toggle in the sidebar for a user to click Remove Underlines or Add Underlines. But we quickly realized that this would be much too dangerous for users. Microsoft Word is evolving toward no longer relying on save functionality—instead the document is being constantly updated in the cloud. So the underlines we might apply to a text to alert users to Grammarly suggestions could inadvertently get saved with the document—and nobody wants to have a bunch of saved underlines in what they’re writing!

With no other option, we decided to avoid underlining and instead try highlighting the corresponding text when the user clicks on a Grammarly suggestion in the sidebar. This is done by just selecting the text (which the API enables), without making any underlying changes. 

Change the text for a range 😐

Finally, for cases in which a suggestion is accepted, there needs to be a way for Grammarly to access the text and update it. Thankfully, the API does allow for this for Word using the range.insertText function.

Performance issues and other caveats

You may recall from our discussion of the textChangedissue that we are using search several times to retrieve a range of text. Each search is an API call, and each API call is a heavy operation. If we call search, Word doesn’t return the result immediately because it doesn’t want to lock the UI. It returns a Promise.

Accessing a range of text takes multiple API calls, and we are often retrieving the range multiple times to perform what the user would think of as a single operation. We’ve found that when we are hitting Word with multiple API calls at once, we run into lags of up to 500 milliseconds (and sometimes more). To handle this we have an internal queue so we can wait until our first call finishes before making another. 

We have another workaround for performance issues encountered due to polling the text. As the API doesn’t expose a way to know when the document has changed, we need to continually poll the text and check for updates. But when we’re polling the text and making a call to the API at the same time, we end up with poor performance. To handle this, we have implemented cancellable text polling. Once a user clicks on a card or accepts a suggestion, we stop text polling (which may be in progress or might not have started yet), perform the necessary API calls to complete the user’s operation, and then resume text polling.

Since we’re using the search API so often, we found some nuanced issues. For example, we might receive an incorrect range if the paragraph we’re searching through contains hyperlinks or inline images. Thankfully, there is an API function to detect whether a paragraph contains hyperlinks or images. So we wrote some code to find these artifacts, remember whether they are, and split the paragraphs around them to avoid running into errors with our ranges.

Releasing our add-in for Word for Mac

The version of the add-in we released in March 2020 has a UI with slight changes from the beta. We did some more work to support Word Online, and in that application, the sidebar is a bit more narrow, about 200–300 pixels, so we updated our UI in the desktop version to match. Here’s a peek at the March release:

As we were building the add-in, we were able to take advantage of some work other teams at Grammarly were doing on the Grammarly Editor, our central web-based writing application. There’s an ongoing effort—which deserves its own blog post!—to split the editor into logical parts: the sidebar suggestions, the logic that handles the suggestions, libraries for handling user authorization and settings, libraries for real-time communication with back-end text-checking services, and so on. Based on work that had been done in this effort, we were able to reuse a lot of existing components and only had to adjust the Office add-in API to the required interface and implement the workarounds discussed. We’re pretty excited about how these new building blocks will help us rapidly prototype and launch new feature sets for Grammarly across applications and platforms. 

If you’re interested in joining our team to build a product that helps more than 20 million people around the world write every day, check out our jobs page—Grammarly is hiring! 

Your writing, at its best.
Get Grammarly for free
Works on all your favorite websites
Related Articles
Shape the way millions of people communicate every day!
Explore open roles