Microsoft Frontend Development Interview Questions & Answers
Curated interview practice questions with answers. These are practice scenarios, not a verified list of past interview questions.
About Microsoft hiring for Frontend Development roles
Microsoft builds software, cloud and hardware products including Windows, Microsoft 365, Azure, Dynamics, GitHub, LinkedIn and Xbox. The India Development Center in Hyderabad and Bengaluru is one of its largest engineering hubs outside the US. It hires for software engineers, data scientists, data and applied scientists, and engineers working on Azure data and quality.
For Frontend Development roles, this Microsoft practice set focuses most on Build Tools and Tooling (6), Browser APIs (5), Frontend Architecture (5), TypeScript (4). It has 1 easy, 24 medium, 5 hard questions, so you can start easy and work up to the harder ones.
Company type
Product
Headquarters
Redmond, Washington, USA
Main India offices
Hyderabad, Bengaluru, Noida, Gurugram, Pune
Microsoft Frontend Development interview process
Online coding assessment
Technical round 1 (DSA)
Technical round 2 (DSA or design)
Hiring manager round
AA (As Appropriate) round
Typical pattern based on public candidate experiences. Rounds vary by role, team and year.
Solved for MicrosoftStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
✓ 3 questions free • Unlock the rest in seconds
01
Write a custom type guard to check if a value is a User.
A function returning value is User narrows the type:
interface User { id: number; email: string; }
function isUser(value: unknown): value is User {
return typeof value === 'object' && value !== null &&
'id' in value && typeof (value as User).id === 'number' &&
'email' in value && typeof (value as User).email === 'string';
}
const data: unknown = JSON.parse(raw);
if (isUser(data)) console.log(data.email);
Inside the if block, data is treated as User.
02
When a user logs out in one tab, other open tabs stay logged in. How do you fix it?
I would broadcast a logout message using BroadcastChannel, or the storage event as a fallback, so every tab clears its in-memory state and redirects to the login page. The server must also invalidate the session or refresh token, so stale tabs fail their next API call with a 401, which the app handles by logging out. Both client sync and server invalidation are needed.
03
npm audit reports a critical vulnerability in a dependency. What do you do?
I would check whether the vulnerable package is actually used in the production bundle or only in build tooling, and whether the vulnerable code path is reachable. Then I would update to a patched version, using npm update or overrides for transitive dependencies. If no fix exists, I would look for an alternative package or mitigate. Automated tools like Dependabot or Renovate keep dependencies current.
04
In a single-page app, the back button takes users out of the site instead of to the previous filter. Why?
The app changes the view without adding history entries, so the browser has nothing to go back to within the site. Each meaningful navigation, like changing a category or opening a product, should call pushState or the router's navigate function, while minor changes such as typing in search can use replaceState. The app must also listen to popstate and restore the view and scroll position from the URL.
05
Mock fetch in a Vitest test to check error handling.
Stub the global fetch and assert behaviour:
import { it, expect, vi, afterEach } from 'vitest';
import { loadCourses } from './api';
afterEach(() => vi.unstubAllGlobals());
it('returns an empty list when the server fails', async () => {
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(
new Response('Server error', { status: 500 })
));
const result = await loadCourses();
expect(result).toEqual([]);
expect(fetch).toHaveBeenCalledWith('/api/courses');
});
For larger suites, MSW handlers are cleaner than stubbing fetch in every test.
06
The build works on your machine but fails in CI. How do you troubleshoot?
I would compare environments first: Node version, package manager version, and whether CI uses npm ci with the committed lockfile while I used npm install. Other common causes are case-sensitive file paths on Linux, missing environment variables, and files ignored by Git. Pinning Node with an .nvmrc or engines field and reproducing the CI command locally in a clean clone or container usually reveals the difference.
07
How would you choose a frontend framework for a new project?
I would base it on project needs and team context rather than trends: team experience, hiring market in India, SEO and rendering requirements, ecosystem maturity, long-term support and performance on target devices. React has the largest ecosystem and talent pool, Angular suits large enterprise teams wanting an opinionated structure, Vue is approachable, and Svelte or Astro produce less JavaScript. A small proof of concept helps confirm the choice.
08
Write a minimal observable store in plain JavaScript.
Animate only transform and opacity, which run on the compositor:
.modal {
opacity: 0;
transform: translateY(16px) scale(0.98);
transition: opacity 200ms ease, transform 200ms ease;
}
.modal.is-open {
opacity: 1;
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.modal { transition: opacity 100ms linear; transform: none; }
}
Animating top, height or margin instead would trigger layout on every frame and cause jank on low-end phones.
11
Keep product filters in sync with the URL query string.
Read params on load and update them without reload:
const params = new URLSearchParams(location.search);
const category = params.get('category') ?? 'all';
applyFilters({ category });
function updateFilter(key, value) {
const p = new URLSearchParams(location.search);
value ? p.set(key, value) : p.delete(key);
history.pushState(null, '', '?' + p.toString());
applyFilters(Object.fromEntries(p));
}
window.addEventListener('popstate', () =>
applyFilters(Object.fromEntries(new URLSearchParams(location.search))));
Users can now share and bookmark filtered views.
12
TypeScript types say a field exists, but the app crashes because the API did not send it. How do you prevent this?
TypeScript only checks at compile time; it cannot guarantee what a server returns at runtime. I would validate responses at the boundary with a schema library like Zod or Valibot, deriving TypeScript types from the schema so they never drift. Invalid data then fails clearly with a logged error and a fallback UI. Generating types from OpenAPI specs also keeps frontend and backend contracts aligned.
13
Field data shows poor INP on the product page's Add to Cart button. How do you investigate?
I would reproduce with CPU throttling in the Performance panel and record the click to see what runs. Typical findings are a heavy handler, analytics calls running synchronously, or a large re-render of the whole page. Fixes include updating the button state immediately, deferring non-urgent work like tracking with scheduler.yield or requestIdleCallback, and narrowing which components re-render. Attribution data from web-vitals helps find the exact element and script.
14
Tailwind classes built dynamically like 'bg-' + colour work locally but are missing in production. Why?
Tailwind finds classes by scanning source files for complete strings, so a class assembled at runtime never appears in the code and its CSS is not generated. The fix is to map values to full class names, such as const colours = { success: 'bg-green-600', error: 'bg-red-600' }. If truly dynamic values are needed, use inline styles with CSS variables or explicitly safelist those classes.
15
Set up ESLint with the flat config for a TypeScript project.
Install packages and create eslint.config.js:
// npm i -D eslint @eslint/js typescript-eslint
import js from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
js.configs.recommended,
...tseslint.configs.recommended,
{
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'no-console': ['warn', { allow: ['error'] }]
}
},
{ ignores: ['dist/'] }
);
Run npx eslint . locally and in CI. Leave formatting to Prettier so the two tools do not conflict.
16
How do you estimate the time needed for a frontend feature?
I break the feature into small parts: components, states such as loading, empty and error, API integration, responsive behaviour, accessibility and tests. I estimate each part, add time for reviews and unknowns, and flag dependencies like pending designs or APIs. For example, a filter panel I estimated at five days was delivered in five because I had listed edge cases upfront instead of discovering them late.
Design tokens are named values for design decisions, such as color-primary, space-4 or radius-md, stored in one place and shared by designers and developers. They are often kept in JSON and converted into CSS custom properties, Tailwind theme values or mobile platform formats. Changing a token updates every component consistently, which makes rebranding or dark mode much easier than hunting hard-coded values.
18
How do HTTP/2 and HTTP/3 change frontend bundling strategy?
HTTP/1.1 allowed few parallel requests per domain, so developers concatenated everything into one big bundle and used domain sharding. HTTP/2 multiplexes many requests over one connection, and HTTP/3 runs over QUIC to avoid head-of-line blocking on lossy mobile networks. This makes moderate chunk splitting efficient and improves caching, because changing one module invalidates only its chunk. Still, too many tiny files add overhead.
19
Most of your users are on low-end Android phones with slow networks. How does that change your approach?
I would test with real budget devices and network throttling rather than a fast laptop. The priorities become sending less JavaScript, rendering important content on the server, compressing images aggressively, and keeping the initial page under a strict size budget. Caching with a service worker helps repeat visits. Features like large animations and heavy carousels should be reduced or loaded only when the device can handle them.
20
Model API request state with a discriminated union.
A shared status field lets TypeScript narrow each case:
type ApiState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; message: string };
function render(state: ApiState<string[]>) {
switch (state.status) {
case 'success': return state.data.join(', ');
case 'error': return 'Error: ' + state.message;
default: return 'Loading...';
}
}
Impossible states, like having both data and an error, cannot be represented.
21
Errors from failed promises are silently disappearing in production. How do you catch them?
Promises without a catch produce unhandled rejections that do not stop the app but hide failures. I would add a global window.addEventListener('unhandledrejection') handler and window.onerror that report errors to a monitoring tool like Sentry with source maps. In code, every async call should have try and catch or a .catch, and ESLint rules such as no-floating-promises in TypeScript projects catch missing handlers.
22
How do environment variables work in frontend builds, and what is the risk?
Build tools replace variables at build time, for example Vite exposes only variables prefixed with VITE_ through import.meta.env. The values become plain text inside the JavaScript bundle that anyone can read. So frontend environment variables must never hold secrets like private API keys or database passwords; those belong on a server or backend proxy. Use them only for public configuration such as API base URLs.
23
A dashboard must show live order updates. Would you use polling, Server-Sent Events or WebSockets?
Polling is simplest and fine if updates every 30 seconds are acceptable. Server-Sent Events push one-way updates from server to browser over HTTP with automatic reconnection, ideal for live orders, notifications and stock tickers. WebSockets provide full two-way communication, needed for chat or collaborative editing, but require more infrastructure. For a one-way dashboard, I would choose SSE and reconnect gracefully on network changes.
24
Configure cache headers for hashed assets and HTML in Nginx.
Long cache for fingerprinted files, revalidation for HTML:
location ~* \.(js|css|woff2|avif|webp|svg)$ {
add_header Cache-Control 'public, max-age=31536000, immutable';
}
location / {
try_files $uri /index.html;
add_header Cache-Control 'no-cache';
}
This works only when build tools add content hashes to filenames. Returning users then load assets from cache instantly, while every deploy still reaches them through fresh HTML.
25
Which rendering strategy would you choose for a large e-commerce site?
I would mix strategies by page type. Category and product pages need SEO and fast LCP, so I would use static generation with incremental revalidation or server rendering with CDN caching. Cart, checkout and account pages are personalised, so server rendering without shared caching or client rendering works. Prices and stock can be fetched fresh on the client. Frameworks like Next.js, Nuxt and Astro support this per-route choice.
26
Tell me about a time you pushed back on releasing code without tests.
Situation: near a release deadline, a teammate suggested skipping tests for a new checkout coupon feature. Task: I felt the risk to payments was too high. Action: I proposed covering only the critical paths, valid, expired and invalid coupons, and wrote those tests myself in two hours. Result: one test caught a discount rounding bug before release, and the feature shipped on time.
27
Register a service worker that caches static assets with a cache-first strategy.
Register from the page, then handle install and fetch in sw.js:
// main.js
if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js');
// sw.js
const CACHE = 'static-v3';
self.addEventListener('install', e => {
e.waitUntil(caches.open(CACHE).then(c => c.addAll(['/', '/app.css', '/app.js'])));
});
self.addEventListener('fetch', e => {
e.respondWith(caches.match(e.request).then(hit => hit || fetch(e.request)));
});
Changing the cache name on each release lets the activate step delete old caches.
The worker runs on its own thread and talks through messages:
// worker.js
self.onmessage = (e) => {
const total = e.data.reduce((sum, row) => sum + row.amount, 0);
self.postMessage(total);
};
// main.js
const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
worker.postMessage(bigArrayOfRows);
worker.onmessage = (e) => showTotal(e.data);
The UI stays responsive. Data is copied between threads, so for huge binary data use transferable objects like ArrayBuffer.
29
After a release, the app shows a blank page in some older browsers. What could be wrong?
The build probably targets newer syntax or uses an API that older browsers lack, causing a parse error that stops the whole bundle. I would check the console in that browser, compare the build target and browserslist with our support policy, and add missing polyfills for used APIs. Error monitoring with browser breakdowns catches this quickly, and testing on the oldest supported browser should be part of release checks.
Let callers specify the expected response type:
async function getJson<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error(`Request failed: ${res.status}`);
return res.json() as Promise<T>;
}
interface Course { id: number; title: string; }
const courses = await getJson<Course[]>('/api/courses');
courses[0].title.toUpperCase();
The cast trusts the server, so for untrusted data validate with a schema library like Zod before returning.
Tips to crack the Microsoft Frontend Development interview
Practise DSA on linked lists, trees and graphs, and be ready to write clean, compilable code with edge cases handled.
Learn basic Azure data services such as Azure SQL, Synapse and Data Factory if you are applying for data roles.
Prepare examples showing a growth mindset, which Microsoft highlights as a core part of its culture.
Keep a one-minute story ready for every project on your CV: problem, your part, tools, result.
Practise the hard questions on this page aloud; Microsoft interviewers usually ask follow-ups on your first answer.
Frequently asked questions
How many rounds are there in the Microsoft Frontend Development interview?
Candidates usually go through 5 stages: Online coding assessment; Technical round 1 (DSA); Technical round 2 (DSA or design); Hiring manager round; AA (As Appropriate) round. The exact rounds change with the role, team, location and your experience, so confirm the process with the recruiter.
Which topics should I prepare for the Microsoft Frontend Development interview?
This practice set leans on Build Tools and Tooling (6), Browser APIs (5), Frontend Architecture (5), TypeScript (4). Revise those first, then practise explaining one or two of your own projects end to end, because most Microsoft interviewers ask you to walk through real work.
Are these the exact questions asked at Microsoft?
No. These are curated practice questions that match the skills and difficulty usually tested in product interviews like Microsoft's. They are not a leaked or verified list of past questions.
Where does Microsoft hire Frontend Development professionals in India?
Microsoft has large teams in Hyderabad, Bengaluru, Noida, Gurugram, Pune. Openings and locations change often, so check the Microsoft careers page and filter by city and experience.
Frontend Development questions for similar companies