Microsoft Teams App Development: The Developer's Complete Guide

20 min read

A comprehensive guide to building apps for Microsoft Teams covering app types, development approaches, SDKs, authentication, deployment, and how to choose the right architecture for your scenario.


Microsoft Teams is installed on virtually every knowledge worker’s desktop across millions of organizations worldwide. Yet most of those organizations aren’t building custom apps for it.

That’s a massive missed opportunity.

I’ve been teaching Microsoft 365 development for over two decades, and I can tell you that Microsoft Teams is one of the most underrated platforms for enterprise applications. You get desktop, mobile, and web clients for free. Built-in authentication. Deployment to every employee in your organization. All without building separate applications for each platform.

The catch? Microsoft’s documentation is scattered across multiple sites, the SDK landscape has been a moving target with confusing renames and overlapping tools, and the development guidance has been… let’s just say inconsistent.

That’s why I wrote this guide. I want to give you a single, trusted resource that cuts through the confusion and helps you understand everything you need to know about building Microsoft Teams apps, from understanding the different app types to choosing your development approach, handling authentication, and deploying to production.

Let’s dig in.

Diagram of the Microsoft Teams app platform: a central Teams client defined by a manifest.json, connected to the five app types (tabs, bots, meeting extensions, messaging extensions, and modal dialogs), running on the desktop, mobile, and web clients

The Microsoft Teams app platform: tabs, bots, messaging extensions, meeting extensions, and modal dialogs, all running in the Teams desktop, mobile, and web clients.

What are Microsoft Teams apps?

Here’s the thing that trips up a lot of developers when they first start looking at Microsoft Teams development: a Teams app is fundamentally just a web app.

Your code, whether it’s a React app, an Angular app, vanilla JavaScript, or even a .NET Blazor app, runs inside the Teams client. For tabs, Teams loads your web app in an iframe. For bots, Teams routes messages through the Bot Framework channel to your server. Your app lives on your infrastructure, wherever you want to host it: Microsoft Azure, AWS, Google Cloud, your own servers, it doesn’t matter.

Every Microsoft Teams app is defined by a JSON manifest file. This manifest tells Teams what capabilities your app has (tabs, bots, messaging extensions, etc.) and where to find your code. That’s it. It’s essentially a configuration file that connects your web app to the Teams platform.

So why is this such a big deal?

Think about the traditional approach to building enterprise apps. You’d build a web app, then maybe a separate desktop client, then a mobile app for iOS and Android. That’s four separate projects, four separate deployments, and the headache of keeping them all in sync.

With Microsoft Teams, you build one web app and Teams gives you all those surfaces automatically. Your app runs in the Teams desktop client (Windows, Mac, Linux), the Teams mobile client (iOS, Android), and Teams in the browser. One deployment target, one codebase, one authentication flow.

And because Teams handles identity through Microsoft Entra ID (formerly Azure AD), you get Single Sign-On (SSO) essentially for free. Your users are already signed into Teams, and your app can leverage that identity without making them sign in again.

--- title: How a Teams app connects to your web app --- flowchart LR subgraph client["Teams client (desktop / mobile / web)"] iframe["Your tab<br/>(rendered in an iframe)"] end subgraph infra["Your infrastructure (Azure, AWS, anywhere)"] webapp["Your web app"] end sdk["@microsoft/teams-js<br/>bridge"] iframe <-->|"context, SSO, theming"| sdk sdk <--> webapp

Types of Microsoft Teams apps

Microsoft Teams supports several distinct app types, and understanding the differences is critical for making the right architectural decisions. Let me walk through each one.

Personal apps and static tabs

A personal app is your app pinned in the user’s left rail in the Teams client. It’s a full-page web experience, almost like a mini app running inside Teams. These are ideal for dashboards, personal productivity tools, and single-user workflows.

Microsoft made some undocumented changes to personal app scopes in recent manifest versions that caught a lot of developers off guard. If you’re building personal apps, make sure you’re working with the latest manifest schema.

Configurable tabs (channel and group chat)

Configurable tabs are embedded in a Teams channel or group chat. Every member of that channel or group chat sees the same tab, which makes them collaborative by nature. When a user adds a configurable tab, they go through a configuration dialog where they can set options for what the tab displays.

These are great for project dashboards shared across a team, collaborative tools, or anything where everyone in a channel needs access to the same view.

Custom bots

Bots are conversational interfaces powered by the Bot Framework. They range from simple command bots that respond to specific keywords to AI-powered conversational agents that can handle complex, natural language interactions.

You can build personal chat bots (one-on-one conversations), channel bots (that participate in channel discussions), and notification bots (proactive messaging to users or channels). With Microsoft’s retirement of Office 365 Connectors, migrating to bots has become the recommended path for notification scenarios.

Messaging extensions

Messaging extensions let users interact with your app from the compose box in Teams. There are two types: search-based extensions (users search your app’s data and insert results into a message) and action-based extensions (users trigger an action from a message). Link unfurling is another powerful capability where Teams automatically generates rich previews when users paste specific URLs.

Meeting extensions and meeting apps

This is where Teams app development gets really interesting. You can build experiences that span the entire meeting lifecycle: pre-meeting apps for agenda setup, in-meeting side panels and stages for interactive content during meetings, and post-meeting experiences for follow-up actions and summaries.

Meeting apps open up scenarios like live Q&A, collaborative whiteboards, voting, and real-time data visualization during meetings.

Modal dialogs (historically called “task modules”) are popup experiences for collecting input or displaying information. They can render either Adaptive Cards (for lightweight, structured forms) or full web views (for more complex UIs). Dialogs are used across tabs, bots, and messaging extensions for focused interactions.

Copilot agents (declarative agents)

The newest extension point for Microsoft Teams is building custom agents for Microsoft 365 Copilot. Declarative agents extend Copilot with your organization’s data and APIs, enabling AI-powered interactions grounded in your business context. I’m keeping this brief here since I’ve written a separate comprehensive guide on how Microsoft 365 Copilot works and its extensibility options.

App typeWhere it appears in TeamsKey use casesComplexity
Personal app / static tabPinned in the user’s left railDashboards, personal productivity, single-user workflowsLow
Configurable tabInside a channel or group chatShared team dashboards, collaborative toolsLow–Medium
Bot1:1 chat, channels, proactive messagesCommand bots, notifications, conversational agentsMedium–High
Messaging extensionThe compose boxSearch-and-insert, action commands, link unfurlingMedium
Meeting extensionPre-, in-, and post-meeting surfacesLive Q&A, polling, agendas, follow-upsMedium–High
Modal dialogPopover over tabs, bots, extensionsFocused input forms, Adaptive Cards, web viewsLow–Medium
Copilot (declarative) agentMicrosoft 365 CopilotGrounded AI over your org’s data and APIsMedium
 1{
 2  "$schema": "https://developer.microsoft.com/json-schemas/teams/v1.19/MicrosoftTeams.schema.json",
 3  "manifestVersion": "1.19",
 4  "id": "00000000-0000-0000-0000-000000000000",
 5  "name": { "short": "Contoso Hub", "full": "Contoso Employee Hub" },
 6  "developer": {
 7    "name": "Contoso",
 8    "websiteUrl": "https://contoso.com",
 9    "privacyUrl": "https://contoso.com/privacy",
10    "termsOfUseUrl": "https://contoso.com/terms"
11  },
12  "icons": { "color": "color.png", "outline": "outline.png" },
13  // A personal tab: a full-page web experience pinned in the left rail.
14  "staticTabs": [
15    {
16      "entityId": "home",
17      "name": "Home",
18      "contentUrl": "https://contoso.com/tab",
19      "scopes": ["personal"]
20    }
21  ],
22  // A bot: Teams routes messages to your server via this Entra app id.
23  "bots": [
24    { "botId": "00000000-0000-0000-0000-000000000000", "scopes": ["personal", "team"] }
25  ],
26  // Domains Teams is allowed to load your app from.
27  "validDomains": ["contoso.com"]
28}

Choosing your development approach

When it comes to building Microsoft Teams apps, you’ve essentially got three approaches. Each comes with real tradeoffs, and I have opinions about all of them.

Microsoft 365 Agents Toolkit (ATK)

The Microsoft 365 Agents Toolkit (ATK), formerly known as Teams Toolkit, is Microsoft’s official scaffolding and development tool for Teams apps. It’s a VS Code extension that handles project creation, local debugging, and Azure deployment.

Here’s the thing: ATK is genuinely good technology that suffers from a lack of developer empathy. The underlying architecture with Bicep deployments and flexible build toolchains is solid. But the naming confusion (it’s been renamed multiple times), the v6.2 release that dropped all project templates without migration guidance, and the documentation gaps have frustrated a lot of developers.

I still recommend ATK as your starting point for most Teams app scenarios. Just go in with your eyes open about the rough edges.

SharePoint Framework (SPFx)

The SharePoint Framework (SPFx) can surface web parts as Microsoft Teams tabs. If your primary target is SharePoint and you want Teams as a secondary surface, this can work. But I’ve written extensively about why SPFx isn’t ideal for Teams tabs and how to decide between SPFx and Teams apps.

The limitations are significant: SPFx is client-side only (no bots, no messaging extensions, no meeting apps), it uses a shared permissions model that grants overly broad permissions to all client-side apps in your tenant, and it locks you into older versions of React and ES5 JavaScript output.

If you’re building a small widget that needs to appear in both SharePoint and Teams, SPFx might be the right call. For anything else, I’d look at the other two approaches.

Custom web apps (bring your own stack)

This is the approach I advocate most often: build a web app, not a Microsoft 365 app. Build a standard web app with whatever stack your team knows best, host it wherever you want, and register it as a Teams app through the manifest.

You get maximum flexibility, full control over your tech stack, and an app that’s portable. If you ever need to run it outside of Teams, you already can because it’s just a web app.

The tradeoff is that you’re responsible for more of the infrastructure and configuration yourself. There’s no scaffolding tool generating Bicep files for you. But for experienced developers, that control is a feature, not a bug.

--- title: Choosing your Teams development approach --- flowchart TD start{"Is your app primarily<br/>a SharePoint solution?"} start -->|Yes| spfx["SharePoint Framework (SPFx)"] start -->|No| bots{"Need bots, messaging,<br/>or meeting extensions?"} bots -->|No, simple widget| spfx bots -->|Yes| stack{"Want full control<br/>of your tech stack?"} stack -->|No, want scaffolding| atk["Microsoft 365 Agents Toolkit (ATK)"] stack -->|Yes| custom["Custom web app<br/>(bring your own stack)"]

Microsoft Teams SDKs

If there’s one area where Microsoft’s naming confusion really shines, it’s the SDK landscape. Let me cut through the noise.

There are three SDKs you need to understand for Microsoft Teams development, plus one essential supporting SDK.

Teams JavaScript client SDK

The @microsoft/teams-js package (currently at v2.x) is the bridge between your web app and the Teams client. If you’re building tabs or modal dialogs, you’ll use this SDK. It handles initialization, SSO authentication, deep linking, theme detection, and all the Teams-specific APIs your client-side code needs.

This is the one SDK that hasn’t been caught up in the naming chaos, and it’s rock solid.

Teams SDK (server-side)

Formerly known as “Teams AI v2,” the Teams SDK is for building bots and server-side Teams app logic. If you’re building a bot that only needs to work in Microsoft Teams, this is your SDK.

Microsoft 365 Agents SDK

The M365 Agents SDK is the broader, multi-channel successor. If you’re building agents that need to work across Teams, Microsoft 365 Copilot, and other surfaces, this is where Microsoft is heading. It’s newer and less battle-tested, but it’s the forward-looking choice for agent-based scenarios.

Microsoft Graph SDK

While not Teams-specific, the Microsoft Graph SDK is essential for any Teams app that needs to access Microsoft 365 data like users, calendars, files, mail, and more from your server-side code.

I’ve written a detailed breakdown of all three Teams SDKs and covered the historical evolution of the Teams SDK landscape if you want the full picture and context behind Microsoft’s decisions.

SDKPackage / libraryUse forClient or serverStatus
Teams JavaScript client SDK@microsoft/teams-js (v2.x)Tabs, dialogs, SSO, deep links, themingClientStable
Teams SDK (server-side)Teams SDK (formerly “Teams AI v2”)Bots and server logic in Teams-only scenariosServerStable
Microsoft 365 Agents SDKM365 Agents SDKMulti-channel agents across Teams + CopilotServerNewer / forward-looking
Microsoft Graph SDK@microsoft/microsoft-graph-clientAccessing Microsoft 365 data (not Teams-specific)ServerStable
 1import { app } from '@microsoft/teams-js';
 2
 3// Every tab must initialize the SDK before calling any other Teams API.
 4await app.initialize();
 5
 6// getContext() tells you who and where you are: the user, the team,
 7// the channel, the current theme, and which client (web/desktop/mobile).
 8const context = await app.getContext();
 9console.log(context.user?.userPrincipalName);
10console.log(context.app.theme); // 'default' | 'dark' | 'contrast'

Authentication in Microsoft Teams apps

Authentication is both Microsoft Teams’ biggest superpower and the topic that confuses the most new Teams developers. Let me break it down.

The SSO flow

Here’s the “magic” of Teams apps: Teams already knows who the user is. When your tab app calls getAuthToken() via the Teams JavaScript SDK, Teams returns an SSO token for the signed-in user without any login prompt. No popup, no redirect, no friction. The user is already authenticated in Teams, and your app piggybacks on that identity.

This is what makes Teams apps feel native. Users don’t have to sign in again, and the experience is seamless.

Microsoft Entra ID

All Teams authentication flows through Microsoft Entra ID (formerly Azure AD). To use SSO, you’ll register your app in the Entra ID portal and configure the permissions your app needs. There are two permission types to understand: delegated permissions (actions on behalf of the signed-in user) and application permissions (actions the app performs on its own, without a user context).

The token exchange

The SSO token you get from getAuthToken() is a limited-scope token. It’s enough to identify the user, but not enough to call Microsoft Graph or your own APIs. Your server-side code takes that token and exchanges it for a more capable token using the On-Behalf-Of (OBO) flow. That new token can then access Microsoft Graph data, call your custom APIs, or whatever your app needs.

Bot authentication

Bots work differently. The Bot Framework handles identity through the bot’s own Entra ID registration. If your bot needs to access user-specific data from Microsoft Graph, it initiates an OAuth card flow where the user authenticates through a card in the chat.

Common pitfalls

A few things that trip developers up: admin consent is required for some permission scopes and your app won’t work until a tenant admin approves it. The difference between “what the user can access” and “what the app can access” through permissions catches people off guard. And caching issues with tokens and consent states can make debugging authentication problems frustrating.

--- title: Teams SSO and on-behalf-of token exchange --- sequenceDiagram participant U as User participant T as Teams client participant Tab as Your tab participant S as Your server participant G as Microsoft Graph U->>T: Opens the tab Tab->>T: getAuthToken() T-->>Tab: SSO token (identifies user) Tab->>S: Call API with SSO token S->>S: Exchange via On-Behalf-Of (OBO) S->>G: Call Graph with OBO token G-->>S: User data S-->>Tab: Response
 1// CLIENT (tab): ask Teams for an SSO token for the signed-in user.
 2import { authentication, app } from '@microsoft/teams-js';
 3
 4await app.initialize();
 5const ssoToken = await authentication.getAuthToken();
 6
 7// Send it to your API; never call Microsoft Graph with this token directly.
 8const res = await fetch('/api/me', {
 9  headers: { Authorization: `Bearer ${ssoToken}` }
10});
 1// SERVER (Node/TypeScript): exchange the SSO token for a Graph token
 2// using the On-Behalf-Of (OBO) flow.
 3import { ConfidentialClientApplication } from '@azure/msal-node';
 4
 5const cca = new ConfidentialClientApplication({
 6  auth: {
 7    clientId: process.env.AAD_CLIENT_ID!,
 8    clientSecret: process.env.AAD_CLIENT_SECRET!,
 9    authority: `https://login.microsoftonline.com/${process.env.AAD_TENANT_ID}`
10  }
11});
12
13const result = await cca.acquireTokenOnBehalfOf({
14  oboAssertion: ssoTokenFromAuthorizationHeader,
15  scopes: ['https://graph.microsoft.com/User.Read']
16});
17// result.accessToken now calls Microsoft Graph on behalf of the user.

Development environment setup

Let me be opinionated here because I think that’s more helpful than giving you a generic list of options.

Node.js with a version manager

You’ll need Node.js, but don’t just install it directly. Use a version manager so you can switch between Node versions as different projects require different versions. I recommend fast node manager (fnm) as it’s significantly faster than nvm. If you’re already using nvm, that works too, just be aware of the differences when managing global packages.

Visual Studio Code with the Agents Toolkit

Visual Studio Code (VS Code) is the IDE for Teams development. Install the Microsoft 365 Agents Toolkit (ATK) extension, which provides project scaffolding, local debugging, and deployment tools. It’s not perfect, but it’s the best starting point and the tooling Microsoft actively maintains.

Dev tunnels

If you’re building bots or any server-side component that needs to receive webhooks from Teams, you’ll need a way for Teams to reach your locally running code. Microsoft’s dev tunnels (integrated into ATK) or ngrok are your options. Watch out for some known issues with Azure Functions on macOS if you go that route.

Microsoft 365 developer tenant

You need a Microsoft 365 tenant to test Teams apps. The Microsoft 365 Developer Program provides free developer tenants, though the program’s availability has been inconsistent. If you’re at an enterprise organization, ask your IT team about a dedicated development tenant.

Deploying Microsoft Teams apps

Deployment is where a lot of new Teams developers get confused, because there are actually two separate things you’re deploying.

Your web app

First, you deploy your web app code to wherever you’re hosting it: Azure App Service, AWS, a container, your own servers. This is standard web deployment and nothing Teams-specific about it.

Your Teams app package

Second, you deploy your Teams app package. This is just a .zip file containing your manifest.json and two icon files (color and outline). The package tells Teams about your app, its capabilities, and where to find your hosted code. The app package doesn’t contain your actual application code.

Deployment targets

There are several ways to make your Teams app available:

Sideloading is for development and testing. You upload the app package directly to Teams. This requires sideloading to be enabled in your tenant, which is an admin-level setting.

Organization app catalog is how you deploy to your company. Upload to the Teams Admin Center and IT admins control who can access the app through app policies.

Microsoft Teams Store is for ISVs building apps for the broader market. Microsoft has an app validation process with specific requirements.

To be clear: your web app and your Teams app package are deployed separately. The package just points to your hosted web app. This distinction confuses a lot of people who expect to deploy their code “to Teams.” You deploy your code to your hosting provider and your manifest to the Teams app catalog.

--- title: Two deployment tracks — web app and Teams package --- flowchart TD subgraph track1["Track 1 — your web app"] code["App code"] --> host["Hosting (Azure App Service, container, ...)"] end subgraph track2["Track 2 — your Teams app package"] manifest["manifest.json + 2 icons"] --> zip[".zip package"] zip --> catalog["App catalog (sideload / org / Store)"] end host --> install["User installs the app"] catalog --> install

Testing and debugging Microsoft Teams apps

Let me be honest: debugging Teams apps can be frustrating. Between iframes, tunnels, caching, and manifest updates, there are a lot of moving pieces. But it’s manageable once you understand the landscape.

Local debugging with ATK

The Agents Toolkit’s F5 debugging experience is the fastest way to iterate. It handles sideloading your app, setting up dev tunnels, and launching the Teams web client with your app loaded. Under the hood, it’s doing a lot of work that you’d otherwise handle manually.

Browser DevTools

Since tabs are web apps running in iframes, your browser’s developer tools work normally. The trick is finding the right iframe in the DOM. In the Teams desktop client, you can use the built-in DevTools (Ctrl+Shift+I on Windows, Cmd+Option+I on Mac) to inspect your app.

Testing strategies

Here’s something I think the Teams development community doesn’t talk about enough: standard web testing tools apply to Teams apps because they’re web apps. Jest, Vitest, Playwright, whatever your team uses for testing web applications will work for your Teams app code. The testing challenge for Microsoft 365 projects isn’t that the tools don’t exist. It’s that the community hasn’t built a strong culture around using them.

Don’t forget mobile testing. Your app will run in the Teams mobile clients and the experience can be very different from desktop. Test early and test often on actual devices.

Microsoft Teams apps vs. Power Apps

This comparison matters a lot to my audience. I know many of you are enterprise developers who’ve been asked “why not just use Power Apps?” Let me give you the honest answer.

The cost reality

Power Apps charges per-user licensing. That adds up fast. For a 500-user app over three years, you could be looking at significant ongoing costs. Custom Teams apps have higher upfront development costs, but no per-user licensing. The long-term math often favors Teams apps for widely deployed applications.

Capability differences

Power Apps excels at simple data-entry forms and workflows built by citizen developers. But when you need complex UIs, real-time collaboration, deep Teams integration (bots, messaging extensions, meeting apps), or custom business logic, you’ll quickly hit Power Apps’ ceiling.

Teams apps can do anything a web app can do, because they are web apps. There’s no UI complexity limit, no expression language to learn, and no platform constraints on your architecture.

When Power Apps is the right choice

To be fair, Power Apps has its place. For simple internal forms, data capture tools, and scenarios where citizen developers need to build solutions without involving a development team, Power Apps makes sense. It’s fast to build, requires no hosting infrastructure, and integrates well with Dataverse and SharePoint lists.

But for enterprise-grade, developer-built applications that need to scale, perform, and integrate deeply with Microsoft Teams, custom Teams apps are the better platform. If you’re a developer reading this guide, you’re probably already sensing that Power Apps isn’t the right tool for your scenario.

CapabilityTeams appsPower Apps
Cost modelHigher upfront build, no per-user licensingLow build, per-user licensing that scales with users
UI complexityAnything a web app can doBest for simple forms; hits a ceiling on complex UIs
Mobile supportFree across Teams desktop, mobile, and webSupported, within Power Apps’ constraints
Teams integration depthNative: bots, messaging/meeting extensionsLimited to embedding
Developer skill requiredWeb developer (bring your own stack)Citizen developer / low-code
GovernanceStandard app-lifecycle and hosting controlsManaged through the Power Platform
ScalabilityScales like any web app you ownConstrained by platform limits and licensing

Getting started with Microsoft Teams app development

If you’ve read this far, you’re probably ready to start building. Here’s the learning path I recommend.

Step-by-step progression

Start by understanding the platform (you’ve just done that by reading this guide). Then set up your development environment with VS Code, Node.js, and the Agents Toolkit. Build your first personal tab app to get comfortable with the fundamentals. Add SSO authentication so your app can access user data. From there, explore bots, meeting extensions, or messaging extensions based on what your project needs. Finally, deploy to your organization’s app catalog.

Free resources to start

I’ve put together several free resources to help you get started. My Microsoft Teams App Development OnRamp is a free email course that introduces you to Teams app development concepts over several days. It’s a great way to get oriented without committing to a paid course.

I’ve also written a developer’s survival guide for navigating the Microsoft Teams developer docs, because let’s be honest, the official documentation can be challenging to navigate.

For a broader view of the Microsoft 365 development landscape, check out my free guides for getting started with Microsoft 365 app development.

Go deeper with structured training

When you’re ready for comprehensive, hands-on training, I run live workshops and a cohort-based Microsoft Teams developer accelerator over on Voitanos.

What’s next for Teams development

Microsoft Teams app development continues to evolve. The convergence of Teams apps with Microsoft 365 Copilot through declarative agents is opening up entirely new scenarios. The SDK landscape is stabilizing (finally), and the tooling is getting better with each release of the Agents Toolkit.

If you’re a web developer looking to become your organization’s Microsoft 365 expert, Teams app development is one of the highest-value skills you can build right now.

What are you building in Microsoft Teams? What’s tripping you up? I’d love to hear from you.

Share this article

Feedback & comments