Skip to main content

Frontend Developer Interview Questions in 2026: The Complete Guide

30+ real frontend interview questions with answers — React 19, system design, JavaScript, Core Web Vitals, AI integration. Based on actual interviews at Swiggy, Flipkart, Zepto, and top startups.

May 30, 202622 min readBy Deepak Sharma
ShareXLinkedIn
Frontend Developer Interview Questions in 2026: The Complete Guide

Frontend technical interviews have shifted dramatically in 2026. Because AI assistants can instantly generate standard code challenges, interviewers have moved beyond basic definitions. They now test depth of understanding — browser execution, React 19 internals, AI token streaming, performance profiling, and architectural decision-making under real constraints.

This guide covers every round you'll face, with actual questions and answers from real interviews at top Indian and global product companies.

Cracking the Frontend Interview in 2026 — Full Breakdown


What to expect in each round

RoundWhat's TestedDuration
ScreeningDSA basics, JS fundamentals45–60 min
Technical Round 1React deep-dive, browser APIs60–90 min
Technical Round 2System design, architecture60–90 min
Hiring ManagerPast decisions, trade-offs45 min
Culture FitCommunication, collaboration30 min

JavaScript Fundamentals

Q1: What is the difference between == and === in JavaScript?

== performs type coercion before comparing. === compares both value and type with no coercion.

0 == false   // true  (false coerced to 0)
0 === false  // false (number !== boolean)
'' == false  // true
'' === false // false
null == undefined  // true  (special case)
null === undefined // false

Always use ===. The only acceptable use of == is checking for both null and undefined simultaneously: value == null.

Q2: Explain closures — and how they can cause memory leaks in SPAs.

A closure is a function that retains access to its lexical scope even after the outer function has returned.

function makeCounter() {
  let count = 0; // captured in closure
  return () => ++count;
}
const inc = makeCounter();
inc(); // 1
inc(); // 2

Memory leak scenario in React:

useEffect(() => {
  const handler = () => {
    // This handler captures a reference to a large data object
    console.log(expensiveData);
  };
  window.addEventListener('resize', handler);

  // BUG: no cleanup — handler and expensiveData leak after unmount
}, [expensiveData]);

Fix: Always return a cleanup function from useEffect.

useEffect(() => {
  const handler = () => console.log(expensiveData);
  window.addEventListener('resize', handler);
  return () => window.removeEventListener('resize', handler); // ✅
}, [expensiveData]);

Q3: What is the event loop, and what's the difference between microtasks and macrotasks?

The event loop processes the call stack, then microtask queue, then macrotask queue — in that exact order per iteration.

ExamplesWhen runs
MicrotasksPromise.then, queueMicrotask, MutationObserverAfter each task, before rendering
MacrotaskssetTimeout, setInterval, setImmediate, I/OOne per event loop tick
console.log('1');
setTimeout(() => console.log('2'), 0);     // macrotask
Promise.resolve().then(() => console.log('3')); // microtask
console.log('4');

// Output: 1, 4, 3, 2

Interviewers test this to see if you understand why Promise.then runs before setTimeout(fn, 0) even though both are async.

Q4: What is the difference between var, let, and const?

varletconst
ScopeFunctionBlockBlock
HoistedYes (as undefined)Yes (TDZ error)Yes (TDZ error)
Re-assignableYesYesNo
Re-declarableYesNoNo

Temporal Dead Zone (TDZ): Accessing let or const before their declaration throws a ReferenceError. The variable exists in the scope but is in a "dead zone" until the declaration line is reached.


React Deep Dive

Q5: Why does my memoized component still re-render?

The most common interview filter. If you pass a new object or function reference as a prop every render, React.memo's shallow comparison will fail.

// BUG: new object on every render defeats memo
function Parent() {
  return <MemoChild style={{ color: 'red' }} />;
  //                ^^^^^^^^^^^^^^^^^^^^^^^^^^
  //                New reference each render
}

// FIX: stable reference with useMemo
function Parent() {
  const style = useMemo(() => ({ color: 'red' }), []);
  return <MemoChild style={style} />;
}

In React 19, the React Compiler handles many of these cases automatically. But interviewers still test this because the compiler doesn't handle every edge case, and you need to understand why it works.

Q6: When should you NOT use useMemo?

Memoization costs CPU. It adds overhead for dependency tracking and comparison. For cheap operations — sorting 10 items, string concatenation, simple math — recalculating is faster than memoizing.

Use useMemo when:

  • The computation is expensive (e.g., filtering 10,000 items, complex math)
  • The result is a dependency in another useEffect or useMemo
  • You're passing the result to a memoized child component

Skip useMemo when:

  • The calculation is simple
  • The component isn't a performance bottleneck (profile first!)
  • You're doing premature optimization

Q7: What is the difference between Server Components and Client Components in Next.js?

This is the most tested Next.js question in 2025–2026.

Server ComponentClient Component
Renders onServerClient (+ SSR)
Ships JS?NoYes
Can use hooks?NoYes
Can access DB?YesNo (directly)
DirectiveDefault'use client'

Rules:

  • Server Components can import and render Client Components
  • Client Components cannot import Server Components
  • The boundary should be as low in the tree as possible
// ✅ Correct: Client component wraps only the interactive part
// page.tsx (Server)
import { getJobs } from '@/lib/queries';
import JobList from './JobList'; // Server Component
export default async function Page() {
  const jobs = await getJobs();
  return <JobList jobs={jobs} />; // No 'use client' needed
}

// JobList.tsx (Server) — renders the list
import ApplyButton from './ApplyButton'; // Client Component for just the button
export function JobList({ jobs }) {
  return jobs.map(job => (
    <div key={job.id}>
      <h2>{job.title}</h2>
      <ApplyButton jobId={job.id} /> {/* ← client boundary pushed low */}
    </div>
  ));
}

Q8: Explain useTransition and useDeferredValue.

Both mark updates as non-urgent so React can prioritize more important updates (like user input) first.

// useTransition: mark a state update as deferrable
const [isPending, startTransition] = useTransition();
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);

function handleChange(e) {
  setQuery(e.target.value); // urgent — update input immediately
  startTransition(() => {
    setResults(search(e.target.value)); // non-urgent — can wait
  });
}

// useDeferredValue: defer a derived value
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => search(deferredQuery), [deferredQuery]);

Use for: search inputs, filter panels, tab switching — any UI where typing should stay responsive while a heavy re-render catches up.

Q9: What are React 19 Actions and useActionState?

Actions are functions that handle async data mutations — form submissions, server updates. useActionState wraps them and provides pending state, error handling, and optimistic updates automatically.

'use client';
import { useActionState } from 'react';

async function submitForm(prevState: unknown, formData: FormData) {
  const name = formData.get('name');
  const res = await fetch('/api/profile', {
    method: 'POST',
    body: JSON.stringify({ name }),
  });
  if (!res.ok) return { error: 'Failed to save' };
  return { success: true };
}

export function ProfileForm() {
  const [state, action, isPending] = useActionState(submitForm, null);

  return (
    <form action={action}>
      <input name="name" />
      <button type="submit" disabled={isPending}>
        {isPending ? 'Saving…' : 'Save'}
      </button>
      {state?.error && <p className="text-red-400">{state.error}</p>}
    </form>
  );
}

Q10: What is hydration mismatch and how do you fix it?

Hydration is when React attaches event listeners to server-rendered HTML. A mismatch happens when the HTML React generates on the client differs from what the server sent.

Common causes:

  • Date.now() or Math.random() in render
  • window or navigator access in initial render
  • Invalid HTML nesting (<p> inside <p>)
  • Browser extensions modifying the DOM

Fixes:

// ❌ Causes hydration mismatch
function Component() {
  return <p>Current time: {Date.now()}</p>;
}

// ✅ Fix: move to useEffect (client-only)
function Component() {
  const [time, setTime] = useState<number | null>(null);
  useEffect(() => setTime(Date.now()), []);
  return <p>Current time: {time ?? 'Loading…'}</p>;
}

Core Web Vitals & Performance

Important

Every senior frontend interview in 2026 includes at least one Core Web Vitals question. INP replaced FID as a Core Web Vital in March 2024, and many candidates are still answering with outdated information.

Q11: Explain LCP, INP, and CLS.

MetricStands forGood thresholdWhat it measures
LCPLargest Contentful Paint≤ 2.5sWhen the main content loads
INPInteraction to Next Paint≤ 200msResponsiveness to user input
CLSCumulative Layout Shift≤ 0.1Visual stability

How to improve LCP:

  • Preload the hero image: <link rel="preload" as="image" href="/hero.webp">
  • Use fetchpriority="high" on the LCP image
  • Move critical CSS inline
  • Reduce server response time (TTFB)

How to improve INP:

  • Break long tasks into smaller chunks using scheduler.yield() (new API) or setTimeout
  • Use startTransition for non-urgent updates
  • Avoid blocking the main thread during animations

How to improve CLS:

  • Always set explicit width and height on images
  • Reserve space for ads and dynamic content with min-height
  • Avoid inserting DOM above existing content after load

Q12: What is the difference between SSR, SSG, and ISR?

StrategyRendersBest forTrade-offs
SSR (Server-Side Rendering)On every requestDynamic, personalized pagesSlower TTFB than static
SSG (Static Site Generation)At build timeBlogs, docs, marketingStale data until rebuild
ISR (Incremental Static Regeneration)At build + on-demand revalidationJob boards, e-commerceComplex cache logic
CSR (Client-Side Rendering)In browserDashboards, auth-gated UIsPoor SEO, slower LCP

In Next.js, ISR is implemented with revalidate:

// Revalidate every 60 seconds
export const revalidate = 60;

// Or on-demand in an API route:
import { revalidatePath } from 'next/cache';
revalidatePath('/jobs');

Frontend System Design

Q13: Design a real-time AI chat UI

This is asked at senior level at companies like Razorpay, Flipkart, and funded startups. Walk through each layer:

1. Streaming Architecture

User → React Client → Next.js API Route → LLM API (Gemini/OpenAI)
                   ↑ Server-Sent Events (SSE) ←←←←←←←←←←←←←←

2. Client implementation (Vercel AI SDK)

'use client';
import { useChat } from 'ai/react';

export function ChatUI() {
  const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
    api: '/api/chat',
  });

  return (
    <div>
      {messages.map(m => (
        <div key={m.id} className={m.role === 'user' ? 'text-right' : ''}>
          {m.content}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit" disabled={isLoading}>Send</button>
      </form>
    </div>
  );
}

3. Key design decisions to discuss:

  • Token batching: don't setState on every token — batch every 50ms
  • Error boundaries for model failures / rate limits
  • Optimistic UI: show user message immediately before API response
  • Message streaming to ref first, then commit to state on completion for better INP

Q14: Design a virtualized infinite scroll list

For a job board with 10,000+ listings.

Key concepts:

  • Only render DOM nodes for visible items (typically 10–20)
  • Use IntersectionObserver to trigger next page fetch
  • react-window or @tanstack/virtual for virtualization
import { useVirtualizer } from '@tanstack/react-virtual';

function VirtualJobList({ jobs }: { jobs: Job[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const rowVirtualizer = useVirtualizer({
    count: jobs.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 120, // estimated row height
  });

  return (
    <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
      <div style={{ height: rowVirtualizer.getTotalSize() }}>
        {rowVirtualizer.getVirtualItems().map(virtualRow => (
          <div
            key={virtualRow.index}
            style={{ transform: `translateY(${virtualRow.start}px)` }}
          >
            <JobCard job={jobs[virtualRow.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}

AI-Native Integration (2026 Special)

Q15: How do you protect LLM API keys in a React app?

Never expose API keys in client code. Route all LLM requests through a server-side layer.

// ❌ Never: exposes key in client bundle
const res = await fetch('https://api.openai.com/v1/...', {
  headers: { Authorization: `Bearer ${process.env.NEXT_PUBLIC_OPENAI_KEY}` }
});

// ✅ Always: BFF pattern — client calls your API route, not the LLM directly
// app/api/chat/route.ts
import { streamText } from 'ai';
import { google } from '@ai-sdk/google';

export async function POST(req: Request) {
  const { messages } = await req.json();
  const result = streamText({
    model: google('gemini-2.0-flash'),
    messages,
  });
  return result.toDataStreamResponse();
}

Q16: How do you prevent INP degradation when streaming tokens?

Streaming 1–5 tokens per chunk at 20+ chunks per second can cause heavy re-renders that block the main thread.

Strategies:

// Strategy 1: Throttle state updates
const THROTTLE_MS = 50;
let buffered = '';
let timer: ReturnType<typeof setTimeout> | null = null;

function onChunk(token: string) {
  buffered += token;
  if (!timer) {
    timer = setTimeout(() => {
      setContent(c => c + buffered);
      buffered = '';
      timer = null;
    }, THROTTLE_MS);
  }
}

// Strategy 2: Write directly to DOM via ref, commit to state on completion
function StreamingMessage() {
  const ref = useRef<HTMLSpanElement>(null);

  function onToken(token: string) {
    if (ref.current) ref.current.textContent += token; // no re-render!
  }

  function onComplete(fullText: string) {
    setMessage(fullText); // single re-render at the end
  }

  return <span ref={ref} />;
}

Accessibility (a11y)

Q17: What is ARIA and when should you use it?

ARIA (Accessible Rich Internet Applications) attributes convey semantic meaning to screen readers when native HTML isn't sufficient.

Rule 1: Use native HTML first

<!-- ❌ Bad: custom button with ARIA -->
<div role="button" tabindex="0" aria-pressed="false" onclick="...">Click me</div>

<!-- ✅ Good: native button — keyboard, focus, and semantics built-in -->
<button type="button">Click me</button>

When ARIA is appropriate: custom widgets (comboboxes, carousels, tabs) where there's no HTML equivalent.

<div role="tablist" aria-label="Job categories">
  <button role="tab" aria-selected="true" aria-controls="remote-panel">Remote</button>
  <button role="tab" aria-selected="false" aria-controls="onsite-panel">Onsite</button>
</div>

Testing

Q18: What separates a good component test from a bad one?

Good tests test behavior — what the user sees and does. Bad tests test implementation — how the code works internally.

// ❌ Bad: tests implementation details
expect(onSubmit).toHaveBeenCalled();
expect(setIsLoading).toHaveBeenCalledWith(true);

// ✅ Good: tests what the user sees
// Submit button disables during loading
const submitBtn = screen.getByRole('button', { name: /submit/i });
userEvent.click(submitBtn);
expect(submitBtn).toBeDisabled();
await screen.findByText(/success/i);

Prefer userEvent over fireEvent — userEvent simulates real browser events (pointerdown + mousedown + click + mouseup + pointerup), while fireEvent only fires a single event.


What actual companies pay

swiggy — Frontend Engineer Salary Data
Levelindia
Junior (0-2y)₹15L – ₹25L
Mid (2-5y)₹25L – ₹40L
Senior (5-8y)₹40L – ₹55L
Staff (8y+)₹55L – ₹90L
Source: LeetCode compensation threads, Levels.fyi — Base salary only, excludes equity & bonus
razorpay — Frontend Engineer Salary Data
Levelindia
Junior (0-2y)₹10L – ₹18L
Mid (2-5y)₹18L – ₹27L
Senior (5-8y)₹27L – ₹45L
Staff (8y+)₹45L – ₹65L
Source: LeetCode compensation threads, Levels.fyi — Base salary only, excludes equity & bonus

Real interview formats at top companies

CompanyRoundsFocus
Swiggy / Zomato4 roundsJS fundamentals, React, System Design, Culture
Razorpay5 roundsDSA, React deep-dive, System Design, Leadership, HR
Dream114 roundsReact internals, Performance, System Design
Flipkart5 roundsDSA, React, Micro-frontends, System Design, Bar Raiser
Zepto3 roundsReact, Full-stack awareness, Culture
Remote US startups3–4 roundsPractical coding, Architecture, System Design

Tip

At product companies, system design rounds weigh more than DSA. Prepare to whiteboard a real-time feed, infinite scroll list, or AI chat interface. Those questions appear in almost every senior loop in 2026.


How to prepare in 4 weeks

Week 1 — JavaScript fundamentals

  • Closures, event loop, prototypes, this binding
  • ES2023+ features: Array.at(), Object.groupBy(), Promise.all vs allSettled

Week 2 — React deep-dive

  • Re-render optimization, useTransition, useDeferredValue
  • React 19: Actions, useActionState, the React Compiler
  • Server Components architecture

Week 3 — Performance & browser

  • Core Web Vitals (LCP, INP, CLS)
  • Network waterfall analysis
  • CSS rendering performance

Week 4 — System design

  • AI chat streaming UI
  • Virtualized list with infinite scroll
  • Design system architecture

FAQ

Are PropTypes still used in React 19?

No. PropTypes have been officially deprecated. TypeScript is the industry standard for component typing in all professional codebases.

What is the most-tested React concept?

Hooks — specifically stale closures in useEffect, incorrect dependency arrays, and referential equality breaking React.memo.

What is the difference between hydration and mounting?

Mounting is when React builds the DOM tree from scratch in the browser. Hydration is when React attaches event listeners and state to pre-rendered HTML sent from the server — it's faster because the DOM already exists.

How should I answer system design questions?

Start with requirements (scale, constraints), then propose a high-level architecture, then drill into the hardest technical challenge. Talk through trade-offs — there's rarely one right answer, and interviewers want to see how you think.



Open roles

Looking for a frontend job?

Browse curated frontend jobs updated daily.

Browse Jobs →

Found this useful? Share it.

ShareXLinkedIn

Ready to land your next frontend role?

Browse curated frontend jobs from top companies — updated daily.

Browse Frontend Jobs →