Myntra Frontend Interview Questions
Fashion e-commerce. Image-heavy pages and performance.
Myntra is a fashion e-commerce company in the Flipkart group. Its listing and product pages are image-heavy, reached largely on phones, and under heavy load during sale events. Page speed, image loading and layout stability are everyday concerns.
Candidates commonly describe a loop close to Flipkart's: a machine coding round, a JavaScript and UI round and a problem-solving round.
- Questions
- 15, 5 with free answers
- Interview rounds
- 5
- Last reviewed
- October 2026
Myntra frontend interview process
The usual shape of the loop. A guide to what to prepare, not a promise of what you will get.
- 1
Machine coding
Build a working feature such as a product listing, a carousel or a cart in 60 to 90 minutes.
- 2
UI technology
JavaScript internals, polyfills, React rendering and browser performance.
- 3
Problem solving
Data structure and algorithm questions at a moderate level.
- 4
Frontend design
For senior roles: design a listing or product page for speed and scale.
- 5
Hiring manager
Past projects and ownership.
What interviewers tend to look for
- Core Web Vitals, and LCP and CLS in particular on image-heavy pages.
- Image optimisation and lazy loading.
- Long product lists: pagination, infinite scroll and virtualisation.
- Server rendering, streaming and hydration cost.
Your Myntra mock interview
15 questions. Type an answer under each one and an AI reviewer scores it and tells you what you missed. Stuck on one? Ask for a hint. A free account gets 3 AI replies a day.
Your mock interview
0 of 15 answered
Answer a question and your score shows up here.
JavaScript
4 questions- Question 1 of 15JavaScript
Myntra interviewer
Write a polyfill for Function.prototype.bind.
Model answer
Function.prototype.myBind = function (context, ...preset) { const fn = this; return function bound(...args) { // Called with `new`: the bound `this` is ignored. if (new.target) return new fn(...preset, ...args); return fn.apply(context, [...preset, ...args]); }; }; function greet(greeting, name) { return greeting + ', ' + name + ' from ' + this.city; } const hello = greet.myBind({ city: 'Pune' }, 'Hello'); hello('Asha'); // "Hello, Asha from Pune"Three things to cover: it returns a new function, it supports partial application (the preset arguments come first), and a bound function used with
newignores the boundthis. Most candidates miss the third. - Question 2 of 15JavaScript
Myntra interviewer
Implement curry, so that add(1)(2)(3) and add(1, 2)(3) both work.
Model answer
function curry(fn) { return function curried(...args) { if (args.length >= fn.length) { return fn.apply(this, args); } return (...more) => curried.apply(this, [...args, ...more]); }; } const add = curry((a, b, c) => a + b + c); add(1)(2)(3); // 6 add(1, 2)(3); // 6It collects arguments until there are as many as the original function declares, then calls it.
The limitation to name:
fn.lengthdoes not count rest parameters or parameters with defaults, so this cannot curry a variadic function. The follow-up for that case is an infinitesum(1)(2)(3)()that stops on an empty call. - Question 3 of 15JavaScript
Myntra interviewer
What is the difference between a shallow and a deep copy? How would you deep clone an object?
Model answer
A shallow copy (
{ ...obj },Object.assign,arr.slice()) copies one level. Nested objects are still shared with the original.structuredClone(value)is the built-in deep clone. It handles dates, maps, sets, typed arrays and circular references. It throws on functions and DOM nodes and does not keep the prototype chain.JSON.parse(JSON.stringify(value))is weaker: it dropsundefinedand functions, turns dates into strings and fails on cycles.Interviewers usually want it written by hand:
function deepClone(value, seen = new WeakMap()) { if (value === null || typeof value !== 'object') return value; if (seen.has(value)) return seen.get(value); // circular reference const copy = Array.isArray(value) ? [] : {}; seen.set(value, copy); for (const key of Reflect.ownKeys(value)) { copy[key] = deepClone(value[key], seen); } return copy; }Say what it does not cover (Date, Map, Set, class instances) and how you would add each one.
- Question 4 of 15JavaScript
Myntra interviewer
What is event delegation? Explain bubbling and capturing.
Model answer
A DOM event travels in three phases: down from
windowto the target (capturing), at the target, then back up towindow(bubbling). Listeners run in the bubbling phase unless you pass{ capture: true }.Delegation uses bubbling: you put one listener on a common ancestor and work out which child was clicked.
list.addEventListener('click', (event) => { const item = event.target.closest('li[data-id]'); if (!item || !list.contains(item)) return; select(item.dataset.id); });Benefits: one listener instead of hundreds, and it keeps working for items added later.
Details that come up:
stopPropagationstops the event travelling,preventDefaultcancels the browser's default action, and a few events such asfocusandblurdo not bubble (usefocusinandfocusout).
TypeScript
1 question- Question 5 of 15TypeScript
Myntra interviewer
Which utility types do you use, and can you implement Pick and Omit yourself?
Model answer
The everyday ones:
Partial,Required,Readonly,Pick,Omit,Record,ReturnType,Parameters,AwaitedandNonNullable.They are built from mapped types, which loop over keys with
in:type MyPartial<T> = { [K in keyof T]?: T[K] }; type MyPick<T, K extends keyof T> = { [P in K]: T[P] }; type MyOmit<T, K extends PropertyKey> = MyPick<T, Exclude<keyof T, K>>; type MyRecord<K extends PropertyKey, V> = { [P in K]: V };A typical use is deriving one type from another so they cannot drift apart:
interface User { id: string; name: string; email: string; } type NewUser = Omit<User, 'id'>; type UserPatch = Partial<NewUser>;A common harder follow-up is
DeepPartialorDeepReadonly, which apply the same mapping recursively.
React
3 questions- Question 6 of 15React
Myntra interviewer
How would you render a list of 10,000 rows without the page slowing down?
Model answer
Render only the rows that are on screen. This is called windowing or virtualisation.
The container gets the full scroll height (row count times row height). From the scroll position you work out which rows are visible, render those plus a few extra above and below, and position them with a transform. Scrolling changes which twenty or so rows exist.
In practice use a library such as TanStack Virtual or react-window. Writing a fixed-height version by hand is a common interview task.
Things to raise:
- Rows of unknown height have to be measured after they render.
- Fetch the data in pages too. Virtualising 10,000 rows you downloaded in one request only solves half the problem.
- Use stable keys and memoised rows.
- The browser's find-in-page cannot see rows that are not rendered, and screen readers need
aria-rowcountandaria-rowindex. content-visibility: autoin CSS is a cheaper option for moderately long pages.
- Question 7 of 15React
Myntra interviewer
What is hydration, and what causes a hydration mismatch?
Model answer
With server rendering, the browser receives finished HTML. Hydration is React running your components in the browser and attaching event handlers and state to that existing HTML instead of creating it again. Until it finishes, the page is visible but not interactive.
A mismatch happens when the first render in the browser produces something different from the server's HTML. Causes:
Date.now(),Math.random()or a locale-dependent format used during render.- Reading
window,localStorageor the screen size during render. - Invalid nesting, such as a
divinside ap, which the browser repairs before React sees it. - Browser extensions that edit the page.
Fixes: render the same output in both places, move browser-only values into
useEffectso that they appear after hydration, or render that part on the client only.suppressHydrationWarningis for a single value that cannot match, such as a timestamp.Hydration cost grows with the amount of JavaScript. Streaming with Suspense lets React hydrate parts of the page independently, and Server Components reduce how much there is to hydrate.
- Question 8 of 15React
Myntra interviewer
Is setState asynchronous? What is batching?
Model answer
Calling a state setter does not change the variable in the code that is running. It asks React to render again with the new value. Each render sees its own fixed snapshot of state.
const [count, setCount] = useState(0); function addThreeWrong() { setCount(count + 1); setCount(count + 1); setCount(count + 1); // count is 0 in all three: result is 1 } function addThree() { setCount((c) => c + 1); setCount((c) => c + 1); setCount((c) => c + 1); // each gets the pending value: result is 3 }Batching means React collects all the updates made in one tick and renders once. Since React 18 this happens everywhere, including inside timeouts, promises and native event handlers. Before 18 it only happened in React event handlers.
flushSyncforces an immediate render when you need the DOM updated straight away, for example to measure it. It is rarely needed.
Next.js
3 questions- Question 9 of 15Next.js
Myntra interviewer
Compare SSG, ISR, SSR and CSR. When would you choose each?
Model answer
- SSG (static generation): HTML is built once at build time and served from a CDN. Fastest option. Use it for content that is the same for everyone and changes rarely: marketing pages, docs, blog posts.
- ISR (incremental static regeneration): a static page that is rebuilt in the background after a set time or on demand. Use it for content that changes but can be a few minutes old: product pages, listings.
- SSR (server rendering per request): HTML is built for each request. Use it when the page depends on the request, such as cookies or a search query, or must always be fresh. The cost is a slower first byte and server load.
- CSR (client rendering): the browser builds the page after JavaScript loads. Fine for screens behind a login where search engines do not matter.
The questions that decide it: does the content differ per user, how stale can it be, and does it need to rank in search.
In the App Router you rarely pick a mode by name. A route is static by default and becomes dynamic when it reads something request-specific, such as cookies, headers or search params. One page can mix both, with a static shell and dynamic parts streamed in.
- Question 10 of 15Next.js
Myntra interviewer
What do next/image and next/font do for performance?
Model answer
next/imagewraps theimgelement and handles the things people forget:- It serves a correctly sized file for each screen through
srcset, in a modern format such as WebP or AVIF. - It lazy-loads images that are off screen.
- It requires
widthandheight(orfillwith a sized parent), so the browser reserves the space and the layout does not jump. - The image that is the largest element above the fold should be marked for eager, high-priority loading so that it does not hurt LCP.
Remote image hosts have to be allowed in
next.config, andsizesshould describe how wide the image is at each breakpoint, otherwise the browser may download a file larger than it needs.next/fontdownloads font files at build time and serves them from your own domain, so there is no extra connection to a font host. It also generates a fallback font with matching metrics, which reduces the shift when the real font loads. - It serves a correctly sized file for each screen through
- Question 11 of 15Next.js
Myntra interviewer
What is streaming in Next.js, and how does Suspense fit in?
Model answer
Without streaming, the server has to finish all data fetching before it sends any HTML, so one slow query delays the whole page.
With streaming, the server sends the page shell immediately, with a fallback wherever something is still loading. Each of those parts is sent down the same response when its data arrives, and React swaps it in.
export default function ProductPage() { return ( <> <ProductDetails /> {/* fast */} <Suspense fallback={<ReviewsSkeleton />}> <Reviews /> {/* slow: streams in later */} </Suspense> </> ); }Each
Suspenseboundary is one streamed unit. Aloading.tsxfile wraps the whole route segment in a boundary for you.Design points: give skeletons the same size as the real content so that nothing shifts, start independent fetches in parallel instead of awaiting them one by one, and remember that the status code is sent with the first chunk, so a later failure cannot turn the response into a 404.
CSS
1 question- Question 12 of 15CSS
Myntra interviewer
How do you build a responsive layout?
Model answer
Start mobile-first: the base styles are for the smallest screen and
min-widthmedia queries add to them as space allows..title { font-size: clamp(1.5rem, 1rem + 2vw, 2.5rem); /* fluid, with limits */ } .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); } .card { container-type: inline-size; } @container (min-width: 28rem) { .card-body { display: flex; /* reacts to the card's width, not the screen's */ } }- Prefer layouts that adapt by themselves, such as wrapping flex rows and
auto-fitgrids, over a breakpoint for every device. - Container queries style a component by the width of its container, so the same card works in a sidebar and in a wide page.
- Serve images with
srcsetandsizes, and give them an aspect ratio. - Include the viewport meta tag. Use
dvhinstead ofvhfor full-height sections on phones. - Keep touch targets large, and respect
prefers-reduced-motionandprefers-color-scheme.
- Prefer layouts that adapt by themselves, such as wrapping flex rows and
Browser, performance and system design
1 question- Question 13 of 15Browser, performance and system design
Myntra interviewer
What are the Core Web Vitals, and how do you improve each one?
Model answer
- LCP (Largest Contentful Paint): when the largest image or text block in the first screen is drawn. Good is 2.5 seconds or less. Improve it with a faster server response, a correctly sized and prioritised hero image, fewer render-blocking files, and server-rendered content.
- INP (Interaction to Next Paint): how long the page takes to visibly respond to a click, tap or key press. It replaced FID in 2024. Good is 200 ms or less. Improve it by breaking up long tasks, shipping less JavaScript, avoiding large re-renders on input, and moving heavy work off the main thread.
- CLS (Cumulative Layout Shift): how much content moves unexpectedly. Good is 0.1 or less. Improve it by giving images, ads and embeds a reserved size, by not inserting content above what the user is reading, and by loading fonts with matching fallbacks.
Each is judged at the 75th percentile of real visits, not the average.
Know the two kinds of data. Lab data from Lighthouse is one run on one simulated device, useful for debugging. Field data from real users, collected with the
web-vitalslibrary or from the Chrome UX Report, is what counts. INP in particular can only be measured properly in the field.
Machine coding
2 questions- Question 14 of 15Machine coding
Myntra interviewer
Build an image carousel.
Model answer
State is one number, the current index. Next and previous wrap around with modulo arithmetic.
const next = () => setIndex((i) => (i + 1) % slides.length); const prev = () => setIndex((i) => (i - 1 + slides.length) % slides.length); <div className="viewport"> <div className="track" style={{ transform: 'translateX(-' + index * 100 + '%)' }} > {slides.map((slide) => ( <img key={slide.src} src={slide.src} alt={slide.alt} /> ))} </div> </div>The track is a flex row inside a viewport with
overflow: hidden, moved with atransformso the slide animates on the compositor.- Previous and next buttons with accessible labels, and dot indicators that jump to a slide.
- Autoplay with an interval that is cleared on unmount and paused on hover and on focus.
- Left and right arrow keys.
- Lazy-load images other than the current one and its neighbours.
- Handle zero and one slide without errors.
Follow-ups: swipe support with pointer events, an endless loop without a visible jump from last to first, and
aria-roledescription="carousel"with each slide labelled "2 of 5". - Question 15 of 15Machine coding
Myntra interviewer
Build a product list with a shopping cart.
Model answer
Store the cart as a map from product id to quantity. Totals, item counts and line prices are derived from that and the product list; they are not stored.
function cartReducer(cart, action) { switch (action.type) { case 'add': return { ...cart, [action.id]: (cart[action.id] ?? 0) + 1 }; case 'setQuantity': { if (action.quantity <= 0) { const { [action.id]: removed, ...rest } = cart; return rest; } return { ...cart, [action.id]: action.quantity }; } case 'clear': return {}; default: return cart; } } const totalPaise = Object.entries(cart).reduce( (sum, [id, quantity]) => sum + productsById[id].pricePaise * quantity, 0 );- Adding a product that is already in the cart raises its quantity. It does not add a second line.
- A quantity stepper with a minimum of 1, or removal at 0, and a maximum from stock.
- Keep money as whole paise (or cents) and format only for display, so that floating point errors never reach a price.
- An empty cart state.
- Save the cart to
localStorage.
Follow-ups: coupon codes and tax, sharing the cart across pages with context or a store, and syncing with a server optimistically.
Practise for Myntra
Reading answers is the easy half. These problems run in the browser against real tests.
