Building a MS Teams app using React & FluentUI

using_namespace_matt
The Startup
Published in
9 min readMay 9, 2020

Email is finally dying — hooray! In most organisations today, instant messaging apps such as MS Teams are rapidly replacing traditional emails, bringing with them faster and easier communications. One of the biggest benefits these apps provide though, is the space for integration of other applications within our communications tool.

A popup screen in Microsoft Teams showing the ‘marketplace’ where people can choose to download and install apps from.
So just how do we get our own app added to this list?

It really makes sense to take advantage of these opportunities — communications is at the heart of everything we do at work. So when I was inspired to bring one of our internal apps into our Teams channel, I found it hard to believe just how difficult it was to find a comprehensive example of how to actually do it. After following multiple half-baked examples from Microsoft and reading through API documents, I’m excited to have created a full working Teams application, using React, Fluent UI (for a consistent Teams UI) and Azure AD authentication.

Note: This tutorial explains how to implement a custom Tab in a Teams channel, if you are looking for bot integrations, I am currently writing a separate article on implementing this which I will provide a link to when complete.

How does it work?

Firstly, we need to understand (as I originally didn’t) that MS Teams doesn’t ‘host’ an app as such, you simply give it the information it needs to go find your app somewhere else and display it within an iframe.

This information is held in a file called manifest.json and includes things like your application’s name, icon, description and most importantly, the URL to configure your app from when it is being added to a channel.

That’s right — you don’t actually give Teams the URL to the site you want to display, but a configuration page which will then give Teams the real URL for your content. This gives us a bit more flexibility, so that depending on any number of factors (e.g. the tenant, the channel the app is being added to, settings in the configuration page, etc.), you can change where the actual application is pulled from.

A diagram linking the Microsoft Teams application and the external website it’s pulling the custom app from.
How MS Teams tabs work — they simply host an iframe which (with some configuration in the manifest.json) uses the magic of the internet to put your website inside Teams!

Let’s begin — set up the React app:

For this example, we’ll be using the out-of-the-box React/Typescript configuration, so run this command to quickly build our app (I’m using yarn here, but of course this works with npm too):

yarn create react-app <your app name> --template typescript

Deploy the app to a static website:

For the purposes of this tutorial, I’ll be assuming that you don’t have a spare web server with a certified https domain ready to go (a requirement for Teams to display your app. Also heads up — Teams doesn’t allow ngrok/other tunnelling service URLs), so we’ll be deploying our website to an Azure static web server as a super easy and cost effective option. Although if you do have a spare web server with https, feel free to use that and skip to the next section.

There’s plenty of tutorials on how to create a free/very cheap static website on Azure (e.g. here and here) so I won’t be showing the steps on how to do this.

However, there is one important difference: Because we’ll be using client-side routing, we need to modify our static hosting settings to redirect 404 errors back to the main app, which will then go and render the requested page all within the browser.

The Azure page where we configure the static web server.
By setting the ‘Error document path’ to index.html, any files/pages which aren’t found on the server will be given to the app again for the client-side router to deal with.

From here, we can easily build the project using

yarn build

If you’re using VS Code, adding the Azure Storage plugin lets you deploy to your website by simply right clicking the build directory and selecting Deploy to Static Website. If you’re not using VS Code/using your own web server, I’ll leave it to you to find out how to deploy the site.

Once you’ve deployed the website, visit the URL to make sure you can see the default React website before proceeding.

Coding time! Create our first two pages:

Before we continue, let’s install all the dependencies we need for the remainder of the project:

  • React router: Handles routing between pages
  • Teams-js: Gives us access to inbuilt Teams functions/APIs
  • FluentUI: Creates MS Teams-themed UI elements
  • ADAL Angular: Azure AD authentication
yarn add @types/react-router-dom @microsoft/teams-js @fluentui/react-northstar adal-angular

We all have our preferred project layouts, so take a moment here to implement yours. The way I like to lay out my React projects is shown below, but feel free to go with however you normally do things:

A comparison of the default and my own directory structures for React applications
Left: The default React project structure, Right: My preferred structure.

We’ll now make two pages: One for configuring our app before being added to a channel and the other with some actual content to be shown in the tab.

The tab configuration page — this example has no configuration options, it simply enables the save button when loaded and when saved, sets the tab to display the page at this domain’s root (our home page).
The initial MS Teams tab homepage — it’s not much, but it’s enough to let us know if the tab is working.

You can check the full repo if you need to see how these pages are tied to different routes (the code is at /src/config/routes.tsx).

There’s one more important modification to make in our top-level component, which is to import the teams-js library and initialise it. Once we’ve done this, we can access the teams-js library functions anywhere in our app!

Run the app locally and test that you can access the routes ‘/’ (displays the heading ‘Our new Teams Tab’) and ‘/configure’ (displays the heading ‘Configure’) before continuing.

The first real test, add the app to a Teams channel:

Hey — we now have a working Teams app! Sure it’s primitive, but it’s a real working app and we should test that we can now add it to a Teams channel.

There are two ways to do this, either build the manifest.json file ourselves, or use the GUI in the MS Teams App Studio plugin to do it for us, I’ll be going with the latter option.

Once you have the App Studio installed, select it from the left pane, then go to the Manifest editor tab. When you click Create a new app, you will be shown a screen like the one below, where you will be asked to edit some basic details about your application.

A page in Microsoft Teams where the user is prompted to fill in basic details about their application such as it’s name.
Filling in some basic values for the ‘App details’ section

Once you’ve finished this section, go to the Tabs section on the left hand side of the screen and click Add under the Team Tab section. If you wish for this app to show up in the left pane (e.g. where App Studio is shown), you can also create a Personal Tab.

Choosing to create a team tab will bring up a screen like the one below, which prompts you for a few options, the main one being the URL where we can configure our app from. If you’re following the example code I’ve written, this is hosted at the configure page.

A popup in Teams, prompting for a URL to where the custom application is configured from.
In the Capabilities > Tabs menu, you will be asked to provide a URL where your app will be configured from.

Now that our app has been configured, go to the Test and distribute section of the form. This give us three options:

  1. Install the app directly to a team.
  2. Download a copy of our app’s manifest.
  3. Submit this app to the Teams app store for approval, so that it can be added to the public marketplace.

In this case, I’m going for option 1 and installing it directly to a Team— but if you plan on this app being publicly available, go for option 3!

Congratulations!

You now have a working React app running in your MS Teams channel!

If this is all you needed, you’re done. Though feel free to keep reading for instructions on how to add Teams-themed UI components and authentication…

Add MS-themed UI elements and automatic theming:

Now we actually have a working MS Teams app, let’s make it look like one with the help of FluentUI.

As you can see in the gist below, the main change in our App component is that everything is now wrapped in a Provider component, which implements the current theme (stored in the app’s state) across all its children. The ThemeHelper class is simply there to help with converting a theme string into a ThemePrepared object.

Also notice the following line:

msTeams.registerOnThemeChangeHandler(this.updateTheme.bind(this));

This binds the App’s updateTheme() method to an inbuilt Teams hook which is called whenever the theme is changed. This means that we don’t need to reload the page after a user changes their theme for the changes to take effect.

From here, I implemented a set of vertical tabs on our home page to simply illustrate the Teams themed-components, you can see the results below in both the light and dark themes.

An example of the Teams tab, shown in both light and dark themes.
A simple example of some FluentUI components when the user changes between light and dark themes.

Nice! We now have real dynamically-themed MS components in our app!

PS. There is a minor flaw in this implementation. You’ll note that we initially set the theme to ‘default’, because unfortunately the getContext() function which gives us the current theme is asynchronous. There’s plenty of ways around this e.g. you can make a blocking render method, show a loading page, etc. but for my use case, a half-second delay of getting the theme isn’t a big deal so I won’t be implementing it.

Add Azure AD Authentication:

Firstly, we need to understand how authentication is handled in MS Teams. As I’ve mentioned, your app is displayed within an iframe. Unfortunately, passing credentials through an iframe is a security risk, so most authentication providers won’t allow it. To get around this, Teams follows the authentication flow shown below:

  1. The user begins the login flow (e.g. by clicking a login button).
  2. Teams opens a popup window hosted on your website, whose sole purpose is to redirect the user to the MS login page for your organisation.
  3. The user logs in to their MS account within the popup window.
  4. AAD processes the login request, then sends the generated auth token to a designated ‘redirect page’. This page will run a script to take the token from the request and save it somewhere the application can access it (e.g. in localStorage).
  5. Finally, the popup closes and the token is available to the application.
A diagram describing the process MS Teams uses for authentication within it’s custom tabs.
The MS Teams with AAD authentication flow.

To facilitate this flow, I’ve put together the 3 pages we need (login, the popup start page and the popup end page) along with the login helper class — together, these allow us to implement the 5 steps above.

Note: There are two extra changes you can make to the login process which may offer different useful features, depending on your application:

  • Silent authentication: Runs the above flow in a hidden popup first in case no user input is required (e.g. because you have logged in recently somewhere else). If this fails, we then simply run the usual process instead. (MS Documentation)
  • SSO (currently in developer preview mode only): This will theoretically implement the dream for Teams-app authentication — that is, using the user’s Teams authentication as the authentication for your app, so you don’t need to explicitly log in. (MS Documentation — note this is still not production ready as of time of writing).

Now the code is doing its part, let’s add an app registration in our AAD tenant so this actually works! This isn’t hard, so I’ll assume you can work this part out for yourself (or follow this guide).

Once we have the registration, simply go to the authentication blade and click Add a platform, then select Web. In the redirect URI field, add your app’s domain with ‘/auth/signinend’ added to the end of it. Finally, ensure the Access tokens and ID tokens checkboxes are selected. Your configuration should look something like below:

The AAD configuration page.
The Azure AD configuration registered to redirect auth requests back to our application.

Now that we can login and authenticate within our Teams app, we simply need to implement our logic for how we exclude users from viewing particular pages if they are not logged in.

There are plenty of ways to accomplish this such as layouts, middleware, checks on every page etc. But given we’re using client-side routing, we can follow a really simple, elegant approach: Don’t render the routes for pages which need to be authenticated.

In React, all this really comes down to is one line of code:

render() {
return (
<Provider theme={this.state.theme}>
{ this.state.loggedIn ? <RouterHelper.AuthenticatedRoutes />
: <RouterHelper.UnauthenticatedRoutes /> }

</Provider>
);
}

Check here for the implementation of the RouterHelper class.

Conclusion

That’s it! In this article, we have developed website (built using React) which can be added to MS Teams in a custom tab. Our app also allows us to take advantage of two important built in functions: Theming and Authentication.

Thank you for reading and I hope this can help with your next project!

--

--