PhonePe Frontend Development Interview Questions & Answers
Curated interview practice questions with answers. These are practice scenarios, not a verified list of past interview questions.
About PhonePe hiring for Frontend Development roles
PhonePe is an Indian digital payments and financial services company, majority-owned by Walmart, known for UPI payments, insurance and investment distribution, and the Pincode commerce app. Its engineering teams build high-scale payments systems. It hires for software engineers, SDETs, data analysts, data engineers and data scientists.
For Frontend Development roles, this PhonePe practice set focuses most on Build Tools and Tooling (7), Frontend Architecture (5), CSS Architecture and Accessibility (4), Web Performance (4). It has 2 easy, 25 medium, 3 hard questions, so you can start easy and work up to the harder ones.
Company type
Fintech
Headquarters
Bengaluru, India
Main India offices
Bengaluru, Pune
PhonePe Frontend Development interview process
Online coding assessment
Machine coding round
DSA or problem solving round
Design or hiring manager round
HR round
Typical pattern based on public candidate experiences. Rounds vary by role, team and year.
Solved for PhonePeStart 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
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.
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
What is transpilation and how does browserslist control it?
Transpilation converts modern JavaScript or TypeScript into syntax that target browsers understand. Tools include Babel, SWC and esbuild. A browserslist setting, such as '> 0.5%, last 2 versions, not dead', or a build target like es2020, decides which features need conversion and which polyfills to include. Targeting only modern browsers produces smaller, faster code, so the support list should be based on real analytics.
04
Run linting and formatting automatically before each commit.
Use Husky with lint-staged:
npm i -D husky lint-staged
npx husky init
echo 'npx lint-staged' > .husky/pre-commit
// package.json
"lint-staged": {
"*.{ts,tsx,js}": ["eslint --fix", "prettier --write"],
"*.{css,md,json}": "prettier --write"
}
Only staged files are checked, so commits stay fast. CI should still run the full lint because hooks can be skipped locally.
05
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.
06
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.
07
What is the same-origin policy and how does postMessage work around it?
An origin is the combination of scheme, host and port. The same-origin policy stops scripts from reading data or DOM of another origin, such as an embedded iframe from a payment provider. window.postMessage lets two windows or iframes exchange messages safely across origins. The receiver must check event.origin against an allow-list, and the sender should specify the exact target origin instead of '*'.
08
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.
Component-driven development builds UIs from the bottom up as small, reusable, isolated components, such as buttons and inputs, composed into larger patterns like forms and pages. Each component has a clear API through props or attributes and is developed and documented in isolation, often in Storybook. Composition over configuration keeps components flexible. This approach enables design systems, parallel work and easier testing.
A tagged template is a function placed before a template literal, like html`<p>${name}</p>`. The function receives the static string parts as an array and the interpolated values separately, so it can escape, transform or validate values before joining them. Libraries use this pattern for safe HTML templating, CSS-in-JS and GraphQL queries. Normal template literals simply interpolate values and support multi-line strings.
11
Write a GitHub Actions workflow that lints, tests and builds a frontend app.
A typical CI pipeline:
name: CI
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test -- --run
- run: npm run build
npm ci installs exactly from the lockfile, and caching speeds up repeat runs. Branch protection can require this check before merging.
12
Your app must now support an Arabic right-to-left version. What changes are needed in CSS?
I would set dir='rtl' on the html element for that locale and replace physical properties like margin-left, padding-right and left with logical ones such as margin-inline-start and inset-inline-start. Flexbox and Grid follow direction automatically. Directional icons like arrows need flipping with a transform in RTL, while logos and media do not. I would test layouts with real Arabic text, which has different line heights.
13
Write a minimal observable store in plain JavaScript.
Micro-frontends split a large web app into independently developed and deployed pieces, each owned by a team, composed at runtime with Module Federation, web components or server-side composition. They help large organisations where many teams block each other on one release. The costs are duplicated dependencies, inconsistent UX, complex shared state and harder performance tuning. For most teams, a well-structured monorepo is simpler.
15
Tell me about a time you received critical feedback in a code review.
Situation: a senior engineer rejected my pull request for a new dashboard because the CSS was hard to maintain. Task: I had to rework it without missing the sprint. Action: I asked for a short call to understand the concerns, refactored styles into reusable components with tokens, and added notes explaining my choices. Result: the PR was approved, and the components were later reused in two other screens.
16
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.
A union type A | B means the value can be either type, such as string | number or 'success' | 'error'. You must narrow it before using type-specific members. An intersection type A & B combines all properties of both, for example User & { permissions: string[] } adds a field to an existing type. Unions model alternatives, while intersections model composition of features.
18
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.
preconnect opens an early connection, including DNS, TCP and TLS, to an important third-party origin like a font or API server. preload fetches a resource needed for the current page at high priority, such as the hero image or main font. prefetch downloads resources likely needed for the next navigation at low priority during idle time. Overusing preload wastes bandwidth and can delay more critical resources.
20
Test a DOM form with Testing Library and user-event.
Simulate real typing and clicks:
import { screen } from '@testing-library/dom';
import userEvent from '@testing-library/user-event';
import { mountSignupForm } from './signup';
it('shows an error for an invalid mobile number', async () => {
document.body.innerHTML = '<div id=app></div>';
mountSignupForm(document.getElementById('app'));
const user = userEvent.setup();
await user.type(screen.getByLabelText('Mobile'), '12345');
await user.click(screen.getByRole('button', { name: 'Sign up' }));
expect(screen.getByRole('alert')).toHaveTextContent('Enter a valid mobile number');
});
toHaveTextContent comes from @testing-library/jest-dom.
Destructuring pulls values out of arrays or objects into variables. Defaults apply only when the value is undefined, not when it is null. For example, const { name, city = 'Delhi' } = user sets city to Delhi if the property is missing. Function parameters can be destructured too, like function render({ title, size = 'md' } = {}), which avoids errors when no argument is passed and documents expected options clearly.
22
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.
23
Create a type where all fields are optional except id.
A monorepo holds multiple apps and shared packages, such as a design system and utilities, in one repository using package manager workspaces. Turborepo and Nx add a task graph that understands dependencies between packages, run builds and tests only for affected packages, and cache outputs locally and remotely. This keeps CI fast as the repo grows and makes cross-package changes atomic in one pull request.
26
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.
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.
28
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.
29
Collect Core Web Vitals from real users with the web-vitals library.
Report metrics to your analytics endpoint:
import { onLCP, onINP, onCLS } from 'web-vitals';
function send(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
page: location.pathname
});
navigator.sendBeacon('/analytics/vitals', body);
}
onLCP(send);
onINP(send);
onCLS(send);
sendBeacon delivers data reliably even when the page is closing. Aggregate at the 75th percentile per page.
:has() is often called the parent selector because it styles an element based on what it contains or what follows it. For example, .card:has(img) can change padding for cards with images, and form:has(input:invalid) can highlight a form with errors. It removes many cases where JavaScript was used only to toggle classes. It is supported in all modern browsers.
Tips to crack the PhonePe Frontend Development interview
Practise machine coding problems and focus on clean, extensible object-oriented design within the time limit.
Understand how UPI transactions work at scale, including idempotency, retries and reconciliation.
For analyst roles, prepare cases on transaction success rates, merchant adoption and fraud detection metrics.
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; PhonePe interviewers usually ask follow-ups on your first answer.
Frequently asked questions
How many rounds are there in the PhonePe Frontend Development interview?
Candidates usually go through 5 stages: Online coding assessment; Machine coding round; DSA or problem solving round; Design or hiring manager round; HR 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 PhonePe Frontend Development interview?
This practice set leans on Build Tools and Tooling (7), Frontend Architecture (5), CSS Architecture and Accessibility (4), Web Performance (4). Revise those first, then practise explaining one or two of your own projects end to end, because most PhonePe interviewers ask you to walk through real work.
Are these the exact questions asked at PhonePe?
No. These are curated practice questions that match the skills and difficulty usually tested in fintech interviews like PhonePe's. They are not a leaked or verified list of past questions.
Where does PhonePe hire Frontend Development professionals in India?
PhonePe has large teams in Bengaluru, Pune. Openings and locations change often, so check the PhonePe careers page and filter by city and experience.
Frontend Development questions for similar companies