Razorpay Frontend Development Interview Questions & Answers
Curated interview practice questions with answers. These are practice scenarios, not a verified list of past interview questions.
About Razorpay hiring for Frontend Development roles
Razorpay is an Indian fintech company providing payment gateway, payment links, payroll, business banking through RazorpayX and other tools for businesses. Its engineering and analytics teams work on payment success rates, risk, reconciliation and merchant products. It hires for software engineers, SDETs, data analysts and data scientists.
For Frontend Development roles, this Razorpay practice set focuses most on TypeScript (7), Frontend Architecture (6), Web Performance (5), CSS Architecture and Accessibility (4). It has 2 easy, 24 medium, 4 hard questions, so you can start easy and work up to the harder ones.
Company type
Fintech
Headquarters
Bengaluru, India
Main India offices
Bengaluru, Mumbai
Razorpay Frontend Development interview process
Online assessment or assignment
Machine coding or technical round
Design or problem solving round
Hiring manager round
Culture fit round
Typical pattern based on public candidate experiences. Rounds vary by role, team and year.
Solved for RazorpayStart 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
Animate a modal smoothly when it opens without causing layout work.
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.
02
State is passed through many component layers and global state has become chaotic. How do you restructure it?
I would first classify state: server data moves to a data-fetching cache like TanStack Query, URL-worthy state such as filters moves to query parameters, and truly local state moves back into components. What remains global, like user session and theme, goes into a small, well-defined store or context split by concern. Composition patterns, passing components as children, also remove much prop drilling.
03
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.
04
Write a minimal observable store in plain JavaScript.
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.
06
What are WeakMap and WeakRef, and when are they useful?
A WeakMap holds object keys weakly, so when no other reference to the key exists, the entry can be garbage collected. This makes it ideal for attaching private metadata or caching computed data for DOM nodes without causing memory leaks. WeakMap keys are not enumerable. WeakRef holds a weak reference to a single object and is rarely needed, mostly for caches, because garbage collection timing is unpredictable.
07
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.
Load flags once and check them where needed:
let flags = {};
export async function loadFlags(userId) {
const res = await fetch('/api/flags?user=' + encodeURIComponent(userId));
flags = res.ok ? await res.json() : {};
}
export const isEnabled = (name) => flags[name] === true;
// usage
await loadFlags(currentUser.id);
if (isEnabled('new-checkout')) renderNewCheckout();
else renderOldCheckout();
Defaulting to off when the request fails keeps the stable path. Flags enable gradual rollouts, but old flags must be removed to avoid clutter.
09
What are pure functions and why does immutability matter in frontend code?
A pure function returns the same output for the same input and has no side effects such as changing external variables. Immutability means creating new objects instead of modifying existing ones, for example using spread syntax to update state. Together they make code predictable and easy to test, and they let frameworks detect changes with a simple reference comparison, which enables efficient re-rendering.
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.
11
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.
12
Type checking takes several minutes in a large monorepo. How do you speed it up?
I would enable project references with composite builds so only changed packages are rechecked, and turn on incremental with build info files cached in CI. skipLibCheck avoids rechecking node_modules declarations. Very complex conditional types and huge unions slow the checker, so I would profile with --generateTrace and simplify hotspots. The bundler can use fast transpilation while type checking runs separately in parallel.
13
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.
14
Search results sometimes show data for an older query. What is happening?
This is a race condition: requests for 'ja' and 'java' are in flight together, and if the older one responds last, it overwrites the newer results. I would cancel the previous request with AbortController, or tag each request with an id and ignore responses that are not the latest. Debouncing the input reduces the number of requests, and caching repeated queries makes responses faster and more consistent.
15
Write a React custom hook that fetches data and cancels on unmount.
Listen for online and offline events and keep a small queue:
const queue = [];
async function send(req) {
if (!navigator.onLine) { queue.push(req); showBanner('You are offline'); return; }
await fetch(req.url, req.options);
}
window.addEventListener('online', async () => {
hideBanner();
while (queue.length) await send(queue.shift());
});
window.addEventListener('offline', () => showBanner('You are offline'));
navigator.onLine can report true on captive networks, so treat failed fetches as offline too. Persist the queue in IndexedDB for reliability.
any turns off type checking completely, so errors slip through silently. unknown also accepts any value, but you must narrow it with typeof, instanceof or a type guard before using it, which makes it the safe choice for API responses and catch errors. never represents values that never occur, such as a function that always throws, or the leftover case in an exhaustive switch over a union.
18
Make an embedded video keep a 16:9 ratio at any width.
Cache-Control controls caching. Files with content hashes in their names, like app.3f9a2c.js, can use max-age=31536000, immutable because a new build produces a new filename. HTML should use no-cache, which forces revalidation so users get new asset references after a deploy. ETag and Last-Modified allow cheap revalidation with 304 Not Modified responses. Wrong headers cause either stale apps or needless re-downloads.
20
Create a type where all fields are optional except id.
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.
22
Marketing has added many third-party scripts and the site has become slow. What do you do?
I would measure each script's cost using the Performance panel and request blocking in DevTools. Then I would remove unused tags, load the rest with async or defer, delay non-essential ones like chat widgets until user interaction or idle time, and use facades, such as a static image for embedded videos. Moving tag management to a server-side setup and agreeing on a script budget with marketing prevents repeat problems.
23
Write a fetch wrapper that retries with exponential backoff.
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.
25
Several teams contribute CSS to one large app and styles keep breaking each other. What do you propose?
I would introduce scoping so styles cannot leak, using CSS Modules or a consistent naming convention per team, and organise global styles with cascade layers for reset, tokens, components and utilities. A shared design token set and component library reduces one-off styles. Linting with Stylelint, visual regression tests in CI and clear ownership of global files prevent new conflicts.
26
Your team uses 'as any' everywhere to silence errors. How do you improve this?
I would first understand why: often the real types are unclear or the API response is untyped. I would add shared API types, introduce unknown with type guards for external data, and enable the ESLint rule no-explicit-any as a warning. Then I would fix the most risky areas first and block new any in CI. Showing a bug that types would have caught helps get team buy-in.
27
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.
28
What is the difference between lab data and field data in performance?
Lab data comes from controlled tests like Lighthouse or WebPageTest on a fixed device and network, which is great for debugging and comparing changes. Field data, or real user monitoring, comes from actual visitors through the Chrome UX Report or your own analytics, capturing real devices, networks and behaviour. Google ranks on field data. A page can score well in the lab but fail in the field on low-end phones.
29
Tell me about a time you convinced your team to adopt a new tool like TypeScript.
Situation: our JavaScript app kept breaking after API changes. Task: I believed TypeScript would help but the team worried about slowing down. Action: I converted one small module as a pilot, showed three bugs it caught, and proposed gradual adoption with no deadline pressure. Result: the team agreed, we migrated over two quarters, and production errors related to wrong data shapes dropped significantly.
30
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.
Tips to crack the Razorpay Frontend Development interview
Learn how a payment gateway works end to end, including authorisation, settlement, refunds and chargebacks.
Practise API testing with tools like Postman and understand webhooks if you are applying for QA roles.
Read Razorpay's engineering blog to understand its scale challenges and use them in design discussions.
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; Razorpay interviewers usually ask follow-ups on your first answer.
Frequently asked questions
How many rounds are there in the Razorpay Frontend Development interview?
Candidates usually go through 5 stages: Online assessment or assignment; Machine coding or technical round; Design or problem solving round; Hiring manager round; Culture fit 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 Razorpay Frontend Development interview?
This practice set leans on TypeScript (7), Frontend Architecture (6), Web Performance (5), CSS Architecture and Accessibility (4). Revise those first, then practise explaining one or two of your own projects end to end, because most Razorpay interviewers ask you to walk through real work.
Are these the exact questions asked at Razorpay?
No. These are curated practice questions that match the skills and difficulty usually tested in fintech interviews like Razorpay's. They are not a leaked or verified list of past questions.
Where does Razorpay hire Frontend Development professionals in India?
Razorpay has large teams in Bengaluru, Mumbai. Openings and locations change often, so check the Razorpay careers page and filter by city and experience.
Frontend Development questions for similar companies