Skip to main content

Frontend engineer frontend interview questions

30 questions on frontend for frontend engineer candidates. Expect prompts such as “What happens when you type a URL into the browser?” and “Implement a debounce utility from scratch”, each with a worked answer outline and the follow-ups interviewers push on.

Questions

Showing 1 to 25 of 30 frontend questions (page 1 of 2).

As asked

Walk me through every step that happens between you typing a URL and pixels appearing on screen. Go as deep as you can.

Sample answer outline

Cover the layers: input handling and autocomplete in the address bar; DNS resolution (local cache, OS resolver, recursive resolvers); TCP + TLS handshake; HTTP request; server-side processing (which the candidate can stay shallow on if frontend-only); response received and parsed; HTML parsing builds the DOM; CSS parsing builds the CSSOM; JS execution may block parsing; render tree built; layout (reflow) computes geometry; paint produces pixels; composite combines layers on the GPU. Modern additions: HTTP/3, Service Worker interception, Early Hints, streaming SSR, hydration.

Expect these follow-ups

  • Where in this flow does a Service Worker fit in?
  • Which step is usually the biggest LCP contributor and how do you measure it?
  • What changes if the page is served by a Next.js App Router with PPR?
browser-internalsperformancenetworking

As asked

Implement a debounce function in TypeScript. It should take a function and a delay in ms and return a debounced version. Bonus: support leading edge and a cancel method.

Sample answer outline

Maintain a timer ID in closure. On each call, clear the existing timer and schedule a new one. Pay attention to: preserving `this` correctly, typing the generic so the returned function has the same signature as the input, exposing `cancel` and (optionally) `flush`. Discuss difference vs throttle. Edge case: what if the callback throws.

Reference implementation (typescript)

TypeScript
export function debounce<TArgs extends unknown[]>(
  fn: (...args: TArgs) => void,
  delayMs: number,
): ((...args: TArgs) => void) & { cancel: () => void } {
  let timer: ReturnType<typeof setTimeout> | null = null;
  const debounced = (...args: TArgs) => {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delayMs);
  };
  debounced.cancel = () => {
    if (timer) clearTimeout(timer);
    timer = null;
  };
  return debounced;
}

Expect these follow-ups

  • How would you write a throttle that fires both leading and trailing?
  • How do you cancel a pending debounce on component unmount in React?
  • What is the difference between requestAnimationFrame-based debounce and setTimeout-based?
closuresjavascript-fundamentals

As asked

Explain when React Server Components are the right tool and when they are not. What changes in your mental model versus traditional React?

Sample answer outline

RSCs run on the server, never ship to the client, and can directly read from the database or filesystem. Good fit: pages that compose lots of data and want zero client JS for that composition (dashboards, content sites, lists). Not a fit: highly interactive UI (charts you can drag, editors, live forms) which still needs client components. The mental model shift: split your tree into server and client boundaries, pass data down as props (serialisable only), avoid mixing concerns. Pitfalls: passing event handlers across the boundary, accidental waterfalls, and the suspense story for streaming.

Expect these follow-ups

  • What is the hydration story for a page that mixes RSC and client components?
  • When does RSC make a page slower, not faster?
  • How does this change with Partial Prerendering?
reactrscnext-js

As asked

At a high level, what are the main steps that happen between typing a URL into the browser and the page appearing? Hit the big stages; you do not need to go deep into each one.

Sample answer outline

Give the clear high-level chain: the browser resolves the domain name to an IP via DNS, opens a connection to the server, sends an HTTP request, and receives the response. The browser then parses the HTML into the DOM, applies CSS, runs any JavaScript, and finally lays out and paints the page to the screen. At this level the goal is a confident, correct overview without rabbit-holing into TLS internals or the render pipeline. Showing you know the ordered stages and roughly what each does is enough for an early-career answer.

Expect these follow-ups

  • What does DNS do in one sentence?
  • Why does CSS or JavaScript sometimes delay the page appearing?
  • What is the difference between the DOM and the HTML you wrote?
browser-internalsfundamentalsnetworking

As asked

Walk me through what happens when a user loads a page in an app you own end to end. I want you to cover both the server side, where your backend generates the response, and the client side, where the browser turns it into pixels, and the choices that span both.

Sample answer outline

A full-stack engineer should narrate both halves and the seam between them. Server side: the request hits your application, which may run middleware, query the database, render markup or serialise data, and set caching and security headers. The response then travels back, and the browser parses HTML into the DOM, builds the CSSOM, runs JavaScript, and lays out and paints. The interesting decisions span both: what you render on the server versus hydrate on the client, how cache headers and a CDN change the second visit, how the payload shape avoids client over-fetching, and where authentication is checked. The strong answer shows you reason about the whole round trip rather than treating frontend and backend as separate worlds.

Expect these follow-ups

  • Which work do you keep on the server and which do you defer to the client?
  • How do cache headers and a CDN change the second page load?
  • Where in this flow do you enforce authentication?
browser-internalsfull-stackssrcaching

As asked

Explain in plain terms what a debounce function is for, give an example of where you would use it, and write a basic version in JavaScript or TypeScript.

Sample answer outline

Debounce delays running a function until a pause in activity, so it fires once after the user stops rather than on every event. The classic example is a search-as-you-type box: you wait until the user stops typing before calling the API. The basic implementation keeps a timer in a closure, clears it on each call, and schedules a fresh one. Keep the explanation concrete and the code small; an early-career answer does not need leading edge or a cancel method, just a correct timer that resets on every call and the contrast with calling on every keystroke.

Reference implementation (typescript)

TypeScript
export function debounce(fn, delayMs) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delayMs);
  };
}

Expect these follow-ups

  • What is the difference between debounce and throttle in one line?
  • What happens if you forget to clear the previous timer?
  • Why is search-as-you-type a good fit for debounce?
closuresjavascript-fundamentalsearly-career

As asked

As a full-stack engineer, how do React Server Components change how you connect the UI to your backend and database? Walk me through the data access pattern, what stays on the server, and the mistakes you would guard against.

Sample answer outline

From a full-stack perspective, server components collapse part of the API layer: a server component can query the database or call an internal service directly, so for read paths you often do not need a separate client-side fetch and an endpoint to back it. Keep secrets, database clients, and heavy data access strictly on the server side of the boundary, and pass only the plain data the client needs down as props. For writes, use server actions or API routes, and keep the client component thin. The mistakes to guard against are leaking server-only modules or credentials into client code, creating accidental waterfalls by chaining dependent server queries, and over-fetching by sending whole records to the client when a slice would do. The full-stack signal is owning the data path from database to rendered UI and deciding deliberately what crosses the network.

Expect these follow-ups

  • When do you still need a separate API endpoint with server components?
  • How do you keep database credentials from leaking into the client bundle?
  • Where do server actions fit for write operations?
reactrscfull-stackdata-fetching

As asked

You are adding a Modal component to a shared React design system. How do you design the API so product teams can use it safely without losing flexibility?

Sample answer outline

Start from required behaviour, not props: focus trap, escape handling, backdrop behaviour, labelling, scroll locking, and return focus on close. The API should make the accessible path the default, such as requiring a title or aria label and exposing structured subcomponents for header, body, and footer. Allow controlled and uncontrolled usage where the design system already supports both, but avoid prop combinations that create impossible states. Document examples for destructive confirmation, forms, and simple messages. Candidates often over-focus on visual variants and under-specify lifecycle, accessibility, and composition.

Expect these follow-ups

  • How would you prevent nested modals or competing focus traps?
  • What props would you refuse to add?
  • How do you handle teams that need a custom animation?
component-apireactaccessibility

As asked

Airbnb's listing page shows host and guest reviews. Build a React component that fetches reviews from a paginated API, displays them, handles loading and error states, and lets the user load more. Describe your state shape first.

Sample answer outline

Strong answers define a clear state shape (items array, page cursor, loading flag, error state), use useEffect with cleanup for in-flight request cancellation, render a skeleton or spinner during load, show an inline error with a retry button, and append rather than replace items on 'load more'. They should mention debouncing rapid clicks and accessibility (aria-busy on the list).

Expect these follow-ups

  • How would you virtualize this list if there could be 2,000 reviews?
  • How do you handle the case where a new review is posted by the current user and you want it to appear at the top immediately?
company:airbnbreactfrontendpaginationstate-managementaccessibility

As asked

Implement an optimistic update for a like button in React. The button shows the liked state immediately, but reverts if the server call fails. Handle the case where the user clicks multiple times in quick succession.

Sample answer outline

Local state mirrors the server state. On click, flip the local state immediately and fire the mutation. On success, no-op. On failure, revert and show an error. The interesting case is rapid clicks: a single in-flight mutation with the last-clicked state, or a queue that collapses. With useTransition and useOptimistic in React 19, the framework handles the revert path for you. Discuss the alternative: pessimistic with a spinner, which avoids confusion but feels slower.

Reference implementation (typescript)

TypeScript
import { useOptimistic, useTransition } from "react";

export function LikeButton({ postId, initialLiked }: { postId: string; initialLiked: boolean }) {
  const [optimisticLiked, setOptimisticLiked] = useOptimistic(initialLiked);
  const [isPending, startTransition] = useTransition();

  const toggle = () => {
    startTransition(async () => {
      setOptimisticLiked(!optimisticLiked);
      const res = await fetch(`/api/posts/${postId}/like`, { method: "POST" });
      if (!res.ok) throw new Error("like-failed");
    });
  };

  return (
    <button onClick={toggle} disabled={isPending} aria-pressed={optimisticLiked}>
      {optimisticLiked ? "Liked" : "Like"}
    </button>
  );
}

Expect these follow-ups

  • What if the server is the source of truth for the count, not just the boolean?
  • How do you handle a network so slow the user navigates away first?
  • When does optimistic update mislead users?
reactoptimistic-uiux

As asked

Walk through the full URL-to-pixels flow, but at each stage tell me precisely how it contributes to Largest Contentful Paint, Interaction to Next Paint, and Cumulative Layout Shift, and what a senior engineer would do to optimise each. Assume an App Router app with streaming SSR.

Sample answer outline

Tie every stage to a metric. DNS, connection setup, and TTFB gate when anything can render, so reducing server response time and using early hints or preconnect helps LCP directly. The HTML response and its render-blocking CSS determine first paint; inlining critical CSS and deferring the rest moves LCP earlier. The LCP element itself, usually a hero image or heading, should be prioritised with fetchpriority and not lazy-loaded. CLS comes from layout instability: reserve space for images, fonts, and late-injected content, and prefer font-display strategies that avoid reflow. INP is about main-thread availability after hydration: minimise hydration cost, break up long tasks, and stream server components so interactivity is not blocked on the whole tree. The senior signal is naming the measurement tool for each, from field data to a trace, rather than guessing.

Expect these follow-ups

  • How does streaming SSR change which stage dominates LCP?
  • Why can aggressive hydration hurt INP, and how do you bound it?
  • What specifically causes CLS from web fonts and how do you remove it?
browser-internalsperformancecore-web-vitalsssr

As asked

Implement a production-grade debounce in TypeScript that supports leading and trailing invocation, a maxWait so the call cannot be starved indefinitely, a cancel method, a flush method, and correct return-value propagation. Type it so the wrapped function keeps its original signature.

Sample answer outline

This is the lodash-grade version and the difficulty is in the edge interactions, not the timer. Track the last call time, the last invoke time, and the pending arguments. Leading edge fires immediately if no timer is active; trailing edge fires after the quiet period; with both, fire on lead and again on trail only if more than one call happened. maxWait requires a second deadline so a continuous stream of calls still invokes at a bounded interval. flush invokes any pending trailing call now; cancel clears state without invoking. Return-value handling means storing the last result and returning it from subsequent debounced calls. The generic signature should preserve parameter and return types. Call out the subtle correctness traps: preserving this, and not double-firing when leading and trailing both apply to a single call.

Reference implementation (typescript)

TypeScript
type Debounced<TArgs extends unknown[], TR> = ((...args: TArgs) => TR | undefined) & {
  cancel: () => void;
  flush: () => TR | undefined;
};

export function debounce<TArgs extends unknown[], TR>(
  fn: (...args: TArgs) => TR,
  delayMs: number,
  opts: { leading?: boolean; trailing?: boolean; maxWait?: number } = {},
): Debounced<TArgs, TR> {
  const { leading = false, trailing = true, maxWait } = opts;
  let timer: ReturnType<typeof setTimeout> | null = null;
  let lastCall = 0;
  let lastInvoke = 0;
  let lastArgs: TArgs | null = null;
  let result: TR | undefined;

  const invoke = (time: number) => {
    lastInvoke = time;
    if (lastArgs) result = fn(...lastArgs);
    lastArgs = null;
    return result;
  };

  const debounced = (...args: TArgs): TR | undefined => {
    const now = Date.now();
    lastArgs = args;
    lastCall = now;
    const callLeading = leading && !timer;
    if (callLeading) invoke(now);
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      timer = null;
      if (trailing && lastArgs) invoke(Date.now());
    }, delayMs);
    if (maxWait && now - lastInvoke >= maxWait) invoke(now);
    return result;
  };

  debounced.cancel = () => {
    if (timer) clearTimeout(timer);
    timer = null;
    lastArgs = null;
  };
  debounced.flush = () => (lastArgs ? invoke(Date.now()) : result);
  return debounced;
}

Expect these follow-ups

  • How does maxWait interact with the trailing edge when calls never stop?
  • Why is the leading-plus-trailing case the easiest place to introduce a double fire?
  • How would you make this safe to use as a React event handler across renders?
closuresjavascript-fundamentalsedge-cases

As asked

A search box calls a backend endpoint on every keystroke and the API is getting hammered. As the full-stack engineer who owns both sides, how do you fix it? Debounce on the client, but tell me what you also do on the server.

Sample answer outline

Treat the client debounce as necessary but not sufficient. On the client, debounce the input so the request fires after the user pauses, and cancel any in-flight request when a newer query supersedes it so results do not arrive out of order. On the server, never trust the client to be the only line of defence: add rate limiting per user or IP, cache popular query results, and make the endpoint cheap enough to absorb bursts. The full-stack signal is owning the whole path: debounce reduces request volume, request cancellation fixes the race, rate limiting protects the service from a misbehaving or malicious client, and caching cuts the cost of the requests that do get through. Mention that out-of-order responses are the bug most people forget after adding debounce.

Reference implementation (typescript)

TypeScript
// Client: debounce + cancel stale requests so results stay in order
let controller: AbortController | null = null;
const search = debounce(async (q: string) => {
  controller?.abort();
  controller = new AbortController();
  const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, {
    signal: controller.signal,
  });
  if (res.ok) render(await res.json());
}, 300);

Expect these follow-ups

  • How do you cancel a superseded in-flight request on the client?
  • Why is server-side rate limiting still needed if the client debounces?
  • Where would you cache search results and for how long?
closuresrate-limitingfull-stackux

As asked

In a React app that uses server components, what is the difference between a server component and a client component, and how do you decide which one to use for a given piece of UI?

Sample answer outline

Keep the mental model simple. A server component runs on the server, never ships its code to the browser, and can fetch data directly, but it cannot use state, effects, or event handlers. A client component is the React you already know: it runs in the browser and can be interactive. The rule of thumb for an earlier-career engineer: default to server components for displaying data, and reach for a client component, marked with the use client directive, when a piece of UI needs interactivity like clicks, inputs, or local state. A clear example, such as a static product list as a server component with an interactive add-to-cart button as a client component, shows the boundary well.

Expect these follow-ups

  • What does the use client directive do?
  • Why can't a server component use useState?
  • Give an example of UI that must be a client component.
reactrscfundamentals

As asked

In a large App Router application, server and client components are deeply interleaved and the team is seeing request waterfalls, oversized client bundles, and serialisation errors at the boundary. Diagnose the likely causes and tell me how you would restructure the tree and data fetching.

Sample answer outline

This is the senior, failure-mode version. Waterfalls usually come from sequential awaits down a server tree where independent data fetches are not parallelised or hoisted; fix by initiating fetches concurrently and lifting them so they start as early as possible, using Suspense boundaries to stream rather than block. Oversized client bundles come from marking too much as use client high in the tree, which drags children client-side; push the client boundary down to the leaves that truly need interactivity and pass server-rendered children through as props rather than importing them into client components. Serialisation errors come from passing non-serialisable values such as functions or class instances across the server-to-client boundary; the fix is to keep the boundary props plain data and move behaviour into the client component. The strong answer also covers measuring the bundle and the request timeline rather than guessing.

Expect these follow-ups

  • How do you pass a server-rendered subtree into a client component without making it client-side?
  • Which values are not serialisable across the boundary, and how do you work around it?
  • How do you turn a request waterfall into parallel streamed fetches?
reactrscperformancenext-js

As asked

A React page feels sluggish when typing in a form because half the screen re-renders on every keystroke. Walk me through how you would find what is re-rendering, why, and how you would fix it without reaching for memo on everything.

Sample answer outline

First measure rather than assume: use the React Profiler to see which components render on each keystroke and why it says they rendered. Usually the cause is state lifted too high, so a single input's value sits in a parent and every child re-renders with it. The cleanest fix is often structural: colocate the fast-changing state in the smallest component that needs it, so a keystroke only re-renders the input, not the whole tree. Where state genuinely must live high, split context so unrelated consumers do not wake up, and pass stable callbacks. Reach for memo, useMemo, and useCallback only after you have identified a specific expensive child that re-renders with unchanged props, because applying them blindly adds comparison cost and complexity for no gain. The signal is that you treat it as a state-architecture question first and a memoisation question second.

Expect these follow-ups

  • Why is lifting state too high a common cause of this?
  • When does wrapping a component in memo actually help versus hurt?
  • How does splitting a context reduce re-renders?
reactperformancerenderingstate

As asked

Implement a modal dialog that is genuinely accessible. Tell me what it takes beyond rendering a centered box: focus, keyboard, screen readers, and the things people routinely get wrong.

Sample answer outline

A correct modal is mostly about focus and semantics, not styling. On open, move focus into the dialog and remember what was focused before so you can restore it on close. Trap focus so Tab cycles within the dialog and cannot wander to the page behind it, and close on Escape. Mark the element with role dialog and aria-modal, label it with aria-labelledby pointing at its title, and hide the rest of the page from assistive tech so a screen reader does not read content underneath. The details people miss: returning focus to the trigger on close, preventing background scroll, and making sure the close button is reachable and labelled. Note that the native dialog element now handles much of this, including the focus trap and a backdrop, so the modern answer is to lean on it and only hand-roll when you must support older targets.

Expect these follow-ups

  • What should happen to focus when the dialog opens and when it closes?
  • How do you hide the rest of the page from a screen reader?
  • What does the native dialog element give you for free?
accessibilityreactfocus-managementaria

As asked

A junior teammate says their CSS rule just is not taking effect even though it looks right. Explain how the cascade and specificity decide the winner, and how you would debug a style that refuses to apply.

Sample answer outline

The browser picks a value through the cascade: it weighs origin and importance, then specificity, then source order as the final tiebreak. Specificity is roughly a count of id selectors, then class, attribute, and pseudo-class selectors, then element selectors, compared left heaviest first, so a single id beats any number of classes. A more specific rule elsewhere, an inline style, or an important declaration is the usual reason a correct-looking rule loses. To debug, open the element in dev tools and look at the computed styles: the browser shows which rule won and strikes through the ones that were overridden, which points straight at the competing selector. The good habit to mention is keeping specificity low and flat so styles stay predictable, rather than escalating to important and starting a specificity war.

Expect these follow-ups

  • How do id, class, and element selectors compare in specificity?
  • Where does source order come into the decision?
  • Why is reaching for important usually the wrong fix?
cssspecificityfundamentalsdebugging

As asked

A React app started with a bit of local state and now has a tangle of prop drilling, duplicated server data, and one giant global store. How do you reason about what state goes where, and when an external state library is warranted?

Sample answer outline

The first move is to categorise state by kind rather than reaching for one tool for everything. Server state, data fetched from an API, has its own concerns like caching, revalidation, and staleness, and is best handled by a data-fetching library rather than hand-managed in a global store, which removes a huge amount of the duplication. URL state, like the current filter or page, belongs in the URL so it is shareable and survives refresh. Genuinely local UI state stays in the component. What is left, truly shared client state, is usually small, and only that needs a client store. With this lens most tangles shrink: the giant store was holding server data that a query cache should own. An external library is warranted when shared client state is non-trivial and crosses many components, not by default. The signal is matching the tool to the kind of state instead of globalising everything.

Expect these follow-ups

  • Why is server data a poor fit for a hand-rolled global store?
  • What belongs in the URL rather than in state?
  • How do you decide a piece of state should be local versus shared?
reactstate-managementarchitecturedata-fetching

As asked

You are building a LiveView dashboard with filters, charts, and realtime updates. What state belongs in the socket assigns, what belongs in the URL, and what belongs in the database?

Sample answer outline

Durable business state belongs in the database, shareable navigation state such as filters belongs in URL params, and ephemeral view state belongs in assigns. The answer should cover mount versus handle_params, reconnect behaviour, and how PubSub updates are merged without overwriting a user's current filters. Expensive chart data should be paged or summarised rather than stored wholesale in the socket. The interviewer wants to hear that LiveView is server stateful and therefore memory per connection matters. Weak answers treat LiveView like React with a socket bolted on and forget reconnects, browser history, and process memory.

Expect these follow-ups

  • How do you make the dashboard deep-linkable?
  • What happens to assigns during a reconnect?
  • How do you prevent one user connection from consuming too much memory?
liveviewstatephoenix

As asked

You are building a component in Adobe Express that renders a list of potentially thousands of user-uploaded assets. React's default reconciliation works fine for small lists, but you are seeing jank and slow re-renders. Walk me through why React struggles with large lists, what the reconciliation algorithm does, and what techniques you would apply to fix the performance.

Sample answer outline

React's fiber reconciler diffs the virtual DOM tree; for lists without keys it must do O(n^2) comparisons. With keys, it drops to O(n) but still mounts and unmounts DOM nodes for off-screen items. The fix for large lists is windowing (react-window or react-virtual): only render the visible viewport plus a small overscan buffer. Additional wins come from memoizing list-item components with React.memo to avoid re-renders on parent state changes, and using useCallback for event handlers passed as props. The candidate should know the trade-offs of absolute positioning vs transform in the windowing library.

Expect these follow-ups

  • Your windowed list has items of variable and unknown height. How do you handle that?
  • A user types into a search box that filters the list. How do you keep the filtering responsive without blocking the main thread?
company:adobereactperformancevirtualizationfrontendreconciliation

As asked

On Airbnb's search results page, the map and the listing cards must stay in sync: hovering a card highlights a pin, and panning the map filters the cards. How would you architect the state and event flow for this in a React application?

Sample answer outline

Strong answers avoid prop-drilling by lifting state to a shared context or lightweight store (Zustand, Jotai, or React context). They describe the hover highlight as ephemeral UI state (not global state), the map bounds as the authoritative filter, and use a ref-based approach or a geospatial filter function to derive visible listings without re-fetching. They should discuss the performance risk of re-rendering the full card list on every map pan and how to debounce or use useMemo.

Expect these follow-ups

  • What happens when the user pans to an area with thousands of pins? How do you cluster them?
  • Would you use a URL-based state for the map bounds, and what are the tradeoffs?
company:airbnbreactstate-managementfrontend-architecturemapsperformance

As asked

You have a custom UIView that draws a star rating control (1 to 5 stars) using Core Graphics. VoiceOver currently reads nothing. What do you do to make it fully accessible?

Sample answer outline

The candidate should cover: setting isAccessibilityElement = true, providing a meaningful accessibilityLabel (e.g., 'Rating'), accessibilityValue (e.g., '3 out of 5 stars'), and accessibilityTraits (.adjustable for a control that can be modified). For the adjustable trait, implement accessibilityIncrement() and accessibilityDecrement() to change the rating and call UIAccessibility.post(notification: .announcement, argument:) to speak the new value. A strong answer also mentions making the individual star sub-elements inaccessible so VoiceOver treats the control as a single element, and testing with VoiceOver actually turned on rather than the Accessibility Inspector alone.

Expect these follow-ups

  • How do you handle dynamic type so the control's touch target scales with accessibility font sizes?
  • What is the difference between accessibilityLabel and accessibilityHint?
company:appleaccessibilityvoiceoveruikitios

As asked

A UIView has cornerRadius set and clipsToBounds set to true. It also has a shadow. Performance on older devices is bad during animation. Explain what is happening and how you fix it.

Sample answer outline

The combination of clipsToBounds and layer.shadowPath requires two off-screen render passes: one to apply the clip mask and one to composite the shadow. Off-screen rendering is expensive because it requires context switching on the GPU and allocating a temporary buffer. The fix is to separate the two effects onto different layers: put the corner radius and clip on the content layer, and apply the shadow to a background layer behind it without clipsToBounds. Alternatively, use a pre-baked image with the corner already cut and use shadowPath with a pre-computed CGPath to avoid masksToBounds-based clipping entirely.

Expect these follow-ups

  • How do you detect off-screen rendering in the Simulator?
  • When does setting shouldRasterize = true help versus hurt performance?
company:appleioscore-animationperformanceuikit

As asked

A Jira board view renders up to 500 issue cards across multiple swim lanes using React. Users report janky scrolling and slow re-renders when dragging an issue between columns. Walk me through how you would diagnose and fix the rendering performance problem.

Sample answer outline

Diagnosis: React DevTools Profiler to identify which components re-render on drag, check for missing memoization. Fix: virtualize the issue list in each column with a library like react-window so only visible cards are in the DOM; wrap issue cards with React.memo and stable key props; lift drag state out of the board rather than storing it in individual card components; use CSS transforms instead of reflows for the drag ghost. The candidate should also discuss reducing issue card re-renders by normalizing Redux/context state so a card only re-renders when its own data changes.

Expect these follow-ups

  • How do you test that your optimization actually works and doesn't regress?
  • What happens to your virtualization approach when a column has only two issues?
company:atlassianfrontendreactperformancevirtualizationjira