Personal Tabs vs. Channel Tabs
A tab in Teams is fundamentally an iframe pointing at a web page you host, surfaced inside the Teams shell. Personal tabs live in the user's personal app bar on the left side and show the same content to that user regardless of which team or chat they're in; they're declared in the manifest's staticTabs array with a fixed contentUrl. Channel (or group) tabs, declared as configurableTabs, are added inside a specific channel or group chat and can hold different configuration per instance, for example a Planner tab pinned to one channel showing that channel's specific plan.
Cricket analogy: A personal tab is like a player's individual kit bag that travels with them to every match regardless of venue, while a channel tab is like a scoreboard fixed to one specific stadium showing only that ground's live match.
Configuring a Channel Tab
When a user adds a configurableTab to a channel, Teams opens the configurationUrl in an iframe and calls the Teams JS SDK's pages.config.registerOnSaveHandler, letting the tab set entityId, contentUrl, and a display name for this specific instance before the user clicks Save. Those values are persisted per channel, so the same tab app can show a different Trello board, Jira project, or SharePoint document library depending on which channel it was configured in. The SDK also exposes pages.config.setValidityState(true) so the Save button only becomes clickable once the user has picked valid configuration options.
Cricket analogy: The configurationUrl step is like a groundskeeper setting up a pitch's specific dimensions and boundary rope before a match, a one-time setup that determines everything that happens on that ground afterward.
Using the Teams JavaScript SDK Inside a Tab
Every tab must call app.initialize() from the @microsoft/teams-js package before doing anything else; this handshake tells the Teams client the iframe is ready and returns context via app.getContext(), including the current user's UPN, tenant id, theme (default, dark, or high-contrast), and the entityId if the tab is channel-scoped. Tabs should also register app.registerOnThemeChangeHandler to restyle instantly when a user toggles Teams' dark mode, since the SDK does not do this automatically. For pages needing an identity beyond the basic context, authentication.getAuthToken() (or the newer app.getContext combined with an Azure AD SSO flow) retrieves an access token scoped to the app's Azure AD registration without a separate login popup, provided webApplicationInfo is correctly set in the manifest.
Cricket analogy: app.initialize() is like a batter tapping their bat on the crease to signal readiness to the umpire before the bowler runs in, a required handshake before any play can proceed.
import { app, pages, authentication } from "@microsoft/teams-js";
async function initTab() {
await app.initialize();
const context = await app.getContext();
console.log(`User: ${context.user?.userPrincipalName}, Theme: ${context.app.theme}`);
app.registerOnThemeChangeHandler((theme) => {
document.body.className = theme; // "default" | "dark" | "contrast"
});
// Configurable tab: enable Save once a project is chosen
pages.config.registerOnSaveHandler(async (saveEvent) => {
await pages.config.setConfig({
entityId: "project-123",
contentUrl: "https://tabs.contoso.com/project/123",
suggestedDisplayName: "Project 123"
});
saveEvent.notifySuccess();
});
pages.config.setValidityState(true);
// Silent SSO
const token = await authentication.getAuthToken();
}
initTab();app.getContext() is also how a tab detects it's rendering inside a meeting stage versus a normal channel tab, via context.page.frameContext. Meeting-aware tabs use this to switch between a full collaborative view (sidePanel or meetingStage) and a compact configuration view.
Deep Links and Tab Navigation
Tabs can navigate the Teams client itself, not just their own iframe, using pages.navigateToApp to jump to another tab within the same app, or pages.currentApp.navigateTo to move between subpages of a single-page app while keeping Teams' back-button and browser history in sync. Deep links generated with pages.shareDeepLink or the standard https://teams.microsoft.com/l/entity/{appId}/{entityId} format let a tab construct a URL that opens Teams directly to a specific entity, which is heavily used for notification emails or bot cards that need to route a click back into a precise tab state.
Cricket analogy: A deep link to a specific entityId is like a TV broadcast cutting directly to over 42.3 of a match rather than making viewers scrub through the whole day's play from ball one.
Forgetting to call app.initialize() (or calling Teams JS SDK APIs before it resolves) is the most common tab bug — the iframe will appear to hang or context calls will silently return undefined. Always await it as the very first line of your tab's bootstrap code.
- Personal tabs (staticTabs) show identical content to a user everywhere; channel tabs (configurableTabs) are configured per channel or group chat instance.
- Channel tabs require a one-time configurationUrl setup that calls pages.config.registerOnSaveHandler and setValidityState.
- Every tab must call app.initialize() before any other Teams JS SDK call, or context calls will fail silently.
- app.getContext() exposes user, tenant, theme, and frame context (e.g., meeting stage) for adaptive rendering.
- registerOnThemeChangeHandler must be wired manually to support Teams' dark and high-contrast themes.
- authentication.getAuthToken() enables silent SSO when webApplicationInfo is correctly configured in the manifest.
- Deep links via entityId let notifications and bot cards route users directly into a specific tab state.
Practice what you learned
1. What is the key structural difference between a personal tab and a channel tab?
2. What must every Teams tab call before making any other Teams JS SDK call?
3. Which SDK call enables the Save button on a channel tab's configuration screen?
4. Why must a tab explicitly register a theme change handler?
5. What does context.page.frameContext (from app.getContext()) help a tab determine?
Was this page helpful?
You May Also Like
The Teams App Manifest
The manifest.json file is the declarative blueprint that tells Microsoft Teams who an app is, what it can do, and where it is allowed to run.
Adaptive Cards
Adaptive Cards are a JSON-based UI format for rendering rich, interactive content consistently across bots, tabs, and message extensions in Teams.
Bots with the Bot Framework SDK
Teams bots are conversational agents built on the Bot Framework SDK, registered with Azure Bot Service, and driven by activity handlers that react to messages and events.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesMFC (Microsoft Foundation Classes) Study Notes
C++ · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics