Uber Frontend Interview Questions
Algorithms, a UI coding round and frontend architecture.
Uber's web work covers rider and driver products, internal tools and large real-time systems such as maps and dispatch dashboards. It maintains Base Web, an open-source React design system. Interviews mix general software engineering with frontend-specific rounds.
Candidates commonly describe a coding screen followed by a loop with an algorithm round, a UI or JavaScript coding round, an architecture round and a behavioural round.
- Questions
- 15, 5 with free answers
- Interview rounds
- 5
- Last reviewed
- October 2026
Uber frontend interview process
The usual shape of the loop. A guide to what to prepare, not a promise of what you will get.
- 1
Phone screen
A coding problem in a shared editor.
- 2
Algorithms
Data structures and algorithms at a moderate to hard level.
- 3
Frontend coding
Build a component, or implement an async utility such as a task scheduler or a retry wrapper, usually in a plain editor.
- 4
Frontend architecture
Design a feature end to end: data flow, API contract, real-time updates and performance.
- 5
Behavioural and hiring manager
Collaboration and ownership, often with an interviewer from another team.
What interviewers tend to look for
- Async control flow: concurrency limits, retries, cancellation.
- Real-time updates and what happens when the connection drops.
- Algorithm fluency, since those rounds carry full weight.
- Explaining an architecture and its trade-offs without being led.
Your Uber 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
5 questions- Question 1 of 15JavaScript
Uber interviewer
Explain the event loop. In what order do a setTimeout, a promise callback and synchronous logs run?
Model answer
JavaScript runs on one call stack. When the stack is empty, the event loop first runs every queued microtask (promise callbacks,
queueMicrotask,MutationObserver), then takes one macrotask (a timer, an I/O callback, a UI event), and the browser may render in between.console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D'); // A, D, C, BSynchronous code finishes first (A, D). The promise callback is a microtask, so it runs before the timer, which is a macrotask, even with a 0 ms delay.
With
asyncfunctions, everything after anawaitis scheduled as a microtask. A common follow-up is a longer snippet mixing both; work through it queue by queue instead of guessing. - Question 2 of 15JavaScript
Uber interviewer
Run a list of async tasks with at most K running at the same time.
Model answer
async function runWithLimit(tasks, limit) { const results = new Array(tasks.length); let next = 0; async function worker() { while (next < tasks.length) { const index = next++; results[index] = await tasks[index](); } } const workers = Array.from( { length: Math.min(limit, tasks.length) }, worker ); await Promise.all(workers); return results; }tasksmust be functions that return promises. A promise has already started by the time you hold it, so an array of promises cannot be limited.Each worker pulls the next index until none are left.
next++is safe because JavaScript is single-threaded: nothing else runs between reading and incrementing.Follow-ups: keep going when one task fails (catch inside the worker and store the error), add a per-task timeout, and support adding tasks while it runs.
- Question 3 of 15JavaScript
Uber interviewer
Write a function that retries a failing async call with exponential backoff.
Model answer
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); async function retry(fn, { retries = 3, delay = 300 } = {}) { for (let attempt = 0; ; attempt++) { try { return await fn(); } catch (error) { if (attempt >= retries) throw error; await wait(delay * 2 ** attempt); // 300, 600, 1200 ms } } }What a strong answer adds without being asked:
- Retry only what can succeed later: network errors, 5xx and 429. A 400 or a 401 will fail the same way again.
- Retry only idempotent requests, or use an idempotency key, so a payment is not made twice.
- Add random jitter to the delay so many clients do not retry at the same instant.
- Cap the delay and accept an
AbortSignalso the caller can cancel.
- Question 4 of 15JavaScript
Uber interviewer
Implement an LRU cache with O(1) get and put.
Model answer
A JavaScript
Mapremembers insertion order, so the first key is always the oldest. Deleting and re-inserting a key on every read keeps the least recently used entry at the front.class LRUCache { constructor(capacity) { this.capacity = capacity; this.map = new Map(); } get(key) { if (!this.map.has(key)) return undefined; const value = this.map.get(key); this.map.delete(key); this.map.set(key, value); // now the most recent return value; } put(key, value) { this.map.delete(key); this.map.set(key, value); if (this.map.size > this.capacity) { const oldest = this.map.keys().next().value; this.map.delete(oldest); } } }Many interviewers then ask for the language-independent version: a hash map for lookup plus a doubly linked list for order, with the map pointing at list nodes. Be ready to write that too.
- Question 5 of 15JavaScript
Uber interviewer
Implement an EventEmitter with on, off, once and emit.
Model answer
class EventEmitter { #listeners = new Map(); on(event, listener) { if (!this.#listeners.has(event)) this.#listeners.set(event, new Set()); this.#listeners.get(event).add(listener); return () => this.off(event, listener); } off(event, listener) { this.#listeners.get(event)?.delete(listener); } once(event, listener) { const unsubscribe = this.on(event, (...args) => { unsubscribe(); listener(...args); }); return unsubscribe; } emit(event, ...args) { // Copy first: a listener may remove itself while we iterate. for (const listener of [...(this.#listeners.get(event) ?? [])]) { listener(...args); } } }Points to raise:
onreturns an unsubscribe function,emititerates over a copy, and aSetprevents the same listener being added twice. Ask whether one listener throwing should stop the others; wrapping each call intry/catchis the usual answer.
TypeScript
1 question- Question 6 of 15TypeScript
Uber interviewer
What are conditional types, and what does infer do?
Model answer
A conditional type chooses between two types with a test:
T extends U ? X : Y.inferdeclares a type variable inside that test and captures whatever matches there.type MyReturnType<T> = T extends (...args: never[]) => infer R ? R : never; type ElementOf<T> = T extends readonly (infer E)[] ? E : never; type MyAwaited<T> = T extends Promise<infer V> ? MyAwaited<V> : T; type A = MyReturnType<() => number>; // number type B = ElementOf<string[]>; // string type C = MyAwaited<Promise<Promise<boolean>>>; // booleanOne behaviour to know: when
Tis a bare type parameter and you pass a union, the condition is applied to each member separately. That is howExclude<T, U>works. Wrapping both sides in a tuple,[T] extends [U], turns that off.
React
3 questions- Question 7 of 15React
Uber interviewer
What makes a component re-render, and how do you cut unnecessary re-renders?
Model answer
A component re-renders when its own state changes, when its parent re-renders, or when a context it reads changes. A changed prop is not a separate cause: props change because the parent rendered.
A re-render is React calling the function again. It is not a DOM update, and most are cheap. Measure with the React DevTools Profiler before changing anything.
When one is a real problem, in rough order of preference:
- Move state down into the component that uses it, so fewer components sit below it.
- Pass expensive content as
children. Elements created by a parent that did not re-render are reused as they are. - Wrap a costly child in
React.memoand give it stable props. - Split a large context, or use a store that lets components subscribe to a slice.
- Virtualise long lists.
- Question 8 of 15React
Uber interviewer
What are useTransition, useDeferredValue and Suspense for?
Model answer
Since React 18, rendering can be interrupted. These APIs let you tell React which updates are urgent.
const [query, setQuery] = useState(''); const [filter, setFilter] = useState(''); const [isPending, startTransition] = useTransition(); function onChange(event) { setQuery(event.target.value); // urgent: the input must keep up startTransition(() => setFilter(event.target.value)); // can wait }useTransitionmarks an update as non-urgent. If the user types again, React drops the half-finished render and starts over.isPendinglets you show that work is in progress.useDeferredValuedoes the same for a value you receive and do not control: it gives you a copy that lags behind during heavy renders.Suspenseshows a fallback while something below it is not ready: lazily loaded code, or data read through a framework or theusehook.
None of this makes rendering faster. It keeps slow rendering from blocking typing and clicks. Unlike a debounce there is no fixed delay, so a fast device shows the result immediately.
- Question 9 of 15React
Uber interviewer
What are React Server Components, and how are they different from server-side rendering?
Model answer
A Server Component runs only on the server. Its code is never sent to the browser. It can be
async, read a database or a secret directly, and import a heavy library at no cost to the bundle. It cannot use state, effects, event handlers or browser APIs.A Client Component, marked with
'use client', is the kind React has always had. It is included in the bundle and hydrated so that it can be interactive.- A Server Component can render Client Components and pass them serialisable props.
- A Client Component cannot import a Server Component, but it can receive one as
children.
Server-side rendering is a different thing. SSR turns a component tree into HTML for the first load, and then the browser downloads and hydrates all of it. Server Components reduce what has to be downloaded and hydrated at all. A framework such as Next.js uses both: Server Components and Client Components are rendered to HTML on the server, and only the Client Components hydrate.
CSS
1 question- Question 10 of 15CSS
Uber interviewer
What is the difference between reflow and repaint? How do you keep animations smooth?
Model answer
The browser renders in steps: style, layout, paint, composite.
- Changing geometry (
width,top,margin, font size, adding nodes) forces layout, also called reflow, for the affected part of the tree, and then a repaint. - Changing appearance only (
color,background,box-shadow) skips layout but still repaints. - Changing
transformoropacitycan be done by the compositor alone, without layout or paint. These are the properties to animate.
/* Janky: recalculates layout every frame */ .panel { transition: left 200ms; } /* Smooth: handled by the compositor */ .panel { transition: transform 200ms; } .panel.open { transform: translateX(240px); }Layout thrashing is the other common cause of jank. Reading a layout value such as
offsetHeightorgetBoundingClientRect()straight after writing a style forces the browser to run layout immediately. In a loop that is one forced layout per iteration. Do all the reads first, then all the writes, or schedule writes inrequestAnimationFrame.will-changehints that an element will animate. Use it on a few elements, since each one takes memory. - Changing geometry (
Browser, performance and system design
4 questions- Question 11 of 15Browser, performance and system design
Uber interviewer
Compare polling, Server-Sent Events and WebSockets for real-time updates.
Model answer
- Short polling: ask the server every few seconds. Simple and works everywhere. It wastes requests when nothing has changed, and updates are late by up to one interval.
- Long polling: the server holds each request open until it has something to send, then the client asks again. Closer to real time, more connection handling.
- Server-Sent Events: one long-lived HTTP response over which the server pushes text events. One direction only, server to client. The browser's
EventSourcereconnects by itself. Good for notifications, live scores, order status and streamed AI output. - WebSocket: a two-way connection with very little overhead per message. Use it when the client also sends often: chat, collaborative editing, multiplayer, trading screens.
Choose by direction and frequency. If only the server pushes, SSE is simpler to run. If both sides talk frequently, use a WebSocket. If updates are rare, polling is enough.
Operational points that show experience: reconnect with backoff, send heartbeats to detect dead connections, fetch missed events after a reconnect, pause updates when the tab is hidden, and remember that many open connections change how the backend has to scale.
- Question 12 of 15Browser, performance and system design
Uber interviewer
How does HTTP caching work? What caching strategy would you use for a web app?
Model answer
The server controls caching with the
Cache-Controlresponse header:max-age=N: reuse the response for N seconds without asking.no-cache: keep a copy, but check with the server before each use.no-store: do not keep it at all.immutable: this will never change, so do not recheck even on reload.s-maxage: a separate lifetime for CDNs.stale-while-revalidate: serve the old copy now and refresh it in the background.
Checking uses validators. The server sends an
ETag; the browser sends it back asIf-None-Match; if nothing changed the server answers304 Not Modifiedwith no body.# JS, CSS and images with a content hash in the file name Cache-Control: public, max-age=31536000, immutable # HTML Cache-Control: no-cacheThat pair is the standard strategy. Hashed assets are cached for a year because any change produces a new file name. The HTML is always revalidated, so a new deployment is picked up at once and points at the new assets.
Other layers to mention: the CDN, a service worker for offline use, and the in-memory data cache of a library such as TanStack Query.
- Question 13 of 15Browser, performance and system design
Uber interviewer
Frontend system design: design a search autocomplete.
Model answer
Begin with questions: how many results, what is being searched, mobile or desktop, is offline needed, are results personalised.
- Component API: a controlled input, a
fetchSuggestions(query)function, arenderItemslot, anonSelectcallback and a minimum query length. - Network: debounce input by about 300 ms. Cancel the previous request with
AbortController, or ignore any response that is not for the latest query. Out-of-order responses are the classic bug here. - Cache: keep results per query in memory with a size limit and an expiry, so that deleting a character is instant.
- States: idle, loading, results, no results, error with retry.
- Keyboard and accessibility: Up and Down move the highlight, Enter selects, Escape closes. Use the combobox pattern:
role="combobox",aria-expanded,aria-controlsandaria-activedescendant. - Rendering: highlight the matching part safely, without
innerHTML, and virtualise the list if it can be long. - Server contract: prefix search with a limit, ranked results, short cache headers.
End with what you would measure, such as time to first suggestion and how often a suggestion is picked, and what you left out on purpose.
- Component API: a controlled input, a
- Question 14 of 15Browser, performance and system design
Uber interviewer
Frontend system design: design an infinite-scrolling news feed.
Model answer
- API: cursor-based pagination (
?cursor=abc&limit=20). Offsets break when new posts are inserted at the top. - First load: render the first page on the server so that content appears without waiting for JavaScript.
- Loading more: an
IntersectionObserveron a sentinel near the end of the list fetches the next page. Guard against sending the same request twice. - Long lists: virtualise, so that scrolling through thousands of posts does not keep thousands of DOM nodes.
- State: store posts by id, with the feed as an ordered list of ids. A like then updates one record wherever the post is shown.
- Interactions: update optimistically and roll back if the request fails.
- Media: lazy-load images, reserve their space with an aspect ratio, and serve sizes that fit the device.
- New posts: receive them over SSE or a WebSocket, but show a "New posts" button instead of pushing content down while someone is reading.
- Resilience: skeletons while loading, a retry row on failure, and scroll position restored when the user comes back from a post.
Then cover accessibility (
role="feed", working keyboard navigation), the metrics you would track (LCP, INP while scrolling, memory over a long session) and the trade-offs of each choice. - API: cursor-based pagination (
Machine coding
1 question- Question 15 of 15Machine coding
Uber interviewer
Build a countdown timer or a stopwatch with start, pause and reset.
Model answer
The trap is counting ticks.
setIntervalis not exact, and browsers slow timers down in background tabs, so a counter that subtracts one every second drifts. Store timestamps and compute the time from the clock.function useCountdown(durationMs) { const [remaining, setRemaining] = useState(durationMs); const [running, setRunning] = useState(false); const endTime = useRef(0); useEffect(() => { if (!running) return; const id = setInterval(() => { const left = Math.max(0, endTime.current - Date.now()); setRemaining(left); if (left === 0) setRunning(false); }, 250); return () => clearInterval(id); }, [running]); const start = () => { endTime.current = Date.now() + remaining; // resume from where it paused setRunning(true); }; const pause = () => setRunning(false); const reset = () => { setRunning(false); setRemaining(durationMs); }; return { remaining, running, start, pause, reset }; }- The interval only refreshes the display. The remaining time always comes from
Date.now(). - Pause keeps the remaining time. Start computes a new end time from it.
- The interval is cleared on pause, on finish and on unmount.
- Format as
mm:sswith leading zeros, and disable Start while running.
Follow-ups: several timers on one page, laps for a stopwatch, and surviving a page refresh by saving the end time.
- The interval only refreshes the display. The remaining time always comes from
Practise for Uber
Reading answers is the easy half. These problems run in the browser against real tests.
